Merge "Fix should not let users adjust a11y button settings in SuW issue"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 88aa9bd..5d713b0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1798,6 +1798,8 @@
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                        android:value="com.android.settings.security.SecurityAdvancedSettings" />
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                       android:value="@string/menu_key_security"/>
         </activity>
 
         <activity android:name="MonitoringCertInfoActivity"
@@ -2976,11 +2978,31 @@
         <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>
 
+        <receiver
+            android:name=".fuelgauge.batteryusage.BootBroadcastReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+                <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
+                <action android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED"/>
+                <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED"/>
+                <action android:name="com.android.settings.battery.action.PERIODIC_JOB_RECHECK"/>
+            </intent-filter>
+        </receiver>
+
+        <receiver
+            android:name=".fuelgauge.batteryusage.PeriodicJobReceiver"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="com.android.settings.battery.action.PERIODIC_JOB_UPDATE"/>
+            </intent-filter>
+        </receiver>
+
         <activity
             android:name="Settings$BatterySaverSettingsActivity"
             android:label="@string/battery_saver"
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 5eae3b4..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] -->
@@ -11586,13 +11594,17 @@
     <!-- Title for the TARE policy factor that determines the initial maximum amount of credits that
     can be consumed by all the apps [CHAR LIMIT=80]-->
     <string name="tare_initial_consumption_limit">Initial Consumption Limit</string>
+    <!-- Title for the TARE policy factor that determines the minimum consumption limit the system
+     can have [CHAR LIMIT=80]-->
+    <string name="tare_min_consumption_limit">Minimum Consumption Limit</string>
     <!-- Title for the TARE policy factor that determines the maximum consumption limit the system
      can have [CHAR LIMIT=80]-->
-    <string name="tare_hard_consumption_limit">Maximum Consumption Limit</string>
+    <string name="tare_max_consumption_limit">Maximum Consumption Limit</string>
     <!-- Titles for the consumption limits factors. [CHAR LIMIT=40]-->
     <string-array name="tare_consumption_limit_subfactors" translatable="false">
         <item>@string/tare_initial_consumption_limit</item>
-        <item>@string/tare_hard_consumption_limit</item>
+        <item>@string/tare_min_consumption_limit</item>
+        <item>@string/tare_max_consumption_limit</item>
     </string-array>
     <!-- Title for the various modifiers that alter the cost of TARE tasks based on battery status
     (charging, power save mode, etc.) [CHAR LIMIT=40]-->
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/TetherSettings.java b/src/com/android/settings/TetherSettings.java
index f134514..331a86b 100644
--- a/src/com/android/settings/TetherSettings.java
+++ b/src/com/android/settings/TetherSettings.java
@@ -20,6 +20,7 @@
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.TetheringManager.TETHERING_ETHERNET;
 
+import static com.android.settings.wifi.WifiUtils.canShowWifiHotspot;
 import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfUsbDataSignalingIsDisabled;
 
 import android.app.Activity;
@@ -628,6 +629,9 @@
 
                     if (!TetherUtil.isTetherAvailable(context)) {
                         keys.add(KEY_TETHER_PREFS_SCREEN);
+                    }
+
+                    if (!canShowWifiHotspot(context) || !TetherUtil.isTetherAvailable(context)) {
                         keys.add(KEY_WIFI_TETHER);
                     }
 
diff --git a/src/com/android/settings/TrustedCredentialsDialogBuilder.java b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
index 0dc8c25..363560e 100644
--- a/src/com/android/settings/TrustedCredentialsDialogBuilder.java
+++ b/src/com/android/settings/TrustedCredentialsDialogBuilder.java
@@ -171,8 +171,8 @@
             } else {
                 new AlertDialog.Builder(mActivity)
                         .setMessage(R.string.trusted_credentials_remove_confirmation)
-                        .setPositiveButton(android.R.string.yes, onConfirm)
-                        .setNegativeButton(android.R.string.no, null)
+                        .setPositiveButton(android.R.string.ok, onConfirm)
+                        .setNegativeButton(android.R.string.cancel, null)
                         .show();
 
             }
diff --git a/src/com/android/settings/accounts/AccountDashboardFragment.java b/src/com/android/settings/accounts/AccountDashboardFragment.java
index 5456f0a..3e83d6f 100644
--- a/src/com/android/settings/accounts/AccountDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDashboardFragment.java
@@ -122,6 +122,7 @@
                     return controllers;
                 }
 
+                @SuppressWarnings("MissingSuperCall") // TODO: Fix me
                 @Override
                 public List<SearchIndexableRaw> getDynamicRawDataToIndex(Context context,
                         boolean enabled) {
diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
index c37f421..49f676e 100644
--- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java
@@ -18,12 +18,10 @@
 
 import android.content.Context;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.os.AsyncTask;
 import android.os.BatteryUsageStats;
 import android.os.Bundle;
 import android.os.UidBatteryConsumer;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
 
@@ -39,12 +37,10 @@
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
 import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController;
 import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
 import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
 import com.android.settings.fuelgauge.batteryusage.BatteryUsageStatsLoader;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.applications.AppUtils;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
@@ -71,8 +67,6 @@
     @VisibleForTesting
     BatteryDiffEntry mBatteryDiffEntry;
     @VisibleForTesting
-    boolean mIsChartGraphEnabled;
-    @VisibleForTesting
     final AppInfoDashboardFragment mParent;
 
     private Preference mPreference;
@@ -91,7 +85,6 @@
         mPackageName = packageName;
         mUid = uid;
         mUserId = mContext.getUserId();
-        refreshFeatureFlag(mContext);
         if (lifecycle != null) {
             lifecycle.addObserver(this);
         }
@@ -147,8 +140,7 @@
                     + entry.getUid()
                     + " with BatteryEntry data");
             AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent, entry,
-                    mIsChartGraphEnabled ? Utils.formatPercentage(0) : mBatteryPercent,
-                    !mIsChartGraphEnabled);
+                    Utils.formatPercentage(0), /*isValidToShowSummary=*/ false);
         } else {
             Log.i(TAG, "Launch : " + mPackageName + " with package name");
             AdvancedPowerUsageDetail.startBatteryDetailPage(mParent.getActivity(), mParent,
@@ -194,16 +186,14 @@
 
     @VisibleForTesting
     void updateBatteryWithDiffEntry() {
-        if (mIsChartGraphEnabled) {
-            if (mBatteryDiffEntry != null && mBatteryDiffEntry.mConsumePower > 0) {
-                mBatteryPercent = Utils.formatPercentage(
-                        mBatteryDiffEntry.getPercentOfTotal(), /* round */ true);
-                mPreference.setSummary(mContext.getString(
-                        R.string.battery_summary, mBatteryPercent));
-            } else {
-                mPreference.setSummary(
-                        mContext.getString(R.string.no_battery_summary));
-            }
+        if (mBatteryDiffEntry != null && mBatteryDiffEntry.mConsumePower > 0) {
+            mBatteryPercent = Utils.formatPercentage(
+                    mBatteryDiffEntry.getPercentOfTotal(), /* round */ true);
+            mPreference.setSummary(mContext.getString(
+                    R.string.battery_summary, mBatteryPercent));
+        } else {
+            mPreference.setSummary(
+                    mContext.getString(R.string.no_battery_summary));
         }
 
         mBatteryDiffEntriesLoaded = true;
@@ -225,42 +215,9 @@
         }
     }
 
-    private void refreshFeatureFlag(Context context) {
-        if (isWorkProfile(context)) {
-            try {
-                context = context.createPackageContextAsUser(
-                        context.getPackageName(), 0, UserHandle.OWNER);
-            } catch (PackageManager.NameNotFoundException e) {
-                Log.e(TAG, "context.createPackageContextAsUser() fail: " + e);
-            }
-        }
-
-        final PowerUsageFeatureProvider powerUsageFeatureProvider =
-                FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context);
-        mIsChartGraphEnabled = powerUsageFeatureProvider.isChartGraphEnabled(context);
-    }
-
-    private boolean isWorkProfile(Context context) {
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        return userManager.isManagedProfile() && !userManager.isSystemUser();
-    }
-
-    @VisibleForTesting
-    void updateBattery() {
+    private void updateBattery() {
         mBatteryUsageStatsLoaded = true;
         mPreference.setEnabled(mBatteryDiffEntriesLoaded);
-        if (mIsChartGraphEnabled) {
-            return;
-        }
-        if (isBatteryStatsAvailable()) {
-            final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
-                    mUidBatteryConsumer.getConsumedPower(), mBatteryUsageStats.getConsumedPower(),
-                    mBatteryUsageStats.getDischargePercentage());
-            mBatteryPercent = Utils.formatPercentage(percentOfMax);
-            mPreference.setSummary(mContext.getString(R.string.battery_summary, mBatteryPercent));
-        } else {
-            mPreference.setSummary(mContext.getString(R.string.no_battery_summary));
-        }
     }
 
     @VisibleForTesting
diff --git a/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java
index 0db4361..f8f9a71 100644
--- a/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java
+++ b/src/com/android/settings/applications/intentpicker/SupportedLinkWrapper.java
@@ -31,7 +31,7 @@
  *  A buffer of the supported link data. This {@link SupportedLinkWrapper} wraps the host, enabled
  *  and a list of {@link DomainOwner}.
  */
-public class SupportedLinkWrapper implements Comparable {
+public class SupportedLinkWrapper implements Comparable<SupportedLinkWrapper> {
     private static final String TAG = "SupportedLinkWrapper";
 
     private String mHost;
@@ -112,8 +112,7 @@
     }
 
     @Override
-    public int compareTo(Object o) {
-        final SupportedLinkWrapper that = (SupportedLinkWrapper) o;
+    public int compareTo(SupportedLinkWrapper that) {
         if (this.mIsEnabled != that.mIsEnabled) {
             return this.mIsEnabled ? -1 : 1;
         }
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/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 9598019..9b5f549 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -1038,6 +1038,7 @@
         }
     }
 
+    @SuppressWarnings("MissingSuperCall") // TODO: Fix me
     @Override
     public void onConfigurationChanged(@NonNull Configuration newConfig) {
         switch(newConfig.orientation) {
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/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index f8914c3..9898f2d 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -167,8 +167,11 @@
                 controlUri = null;
             }
         }
-        use(SlicePreferenceController.class).setSliceUri(sliceEnabled ? controlUri : null);
-        use(SlicePreferenceController.class).onStart();
+        final SlicePreferenceController slicePreferenceController = use(
+                SlicePreferenceController.class);
+        slicePreferenceController.setSliceUri(sliceEnabled ? controlUri : null);
+        slicePreferenceController.onStart();
+        slicePreferenceController.displayPreference(getPreferenceScreen());
     }
 
     private final ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener =
diff --git a/src/com/android/settings/development/DisableLogPersistWarningDialog.java b/src/com/android/settings/development/DisableLogPersistWarningDialog.java
index 76035cc..4514b1b 100644
--- a/src/com/android/settings/development/DisableLogPersistWarningDialog.java
+++ b/src/com/android/settings/development/DisableLogPersistWarningDialog.java
@@ -57,8 +57,8 @@
         return new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.dev_logpersist_clear_warning_title)
                 .setMessage(R.string.dev_logpersist_clear_warning_message)
-                .setPositiveButton(android.R.string.yes, this /* onClickListener */)
-                .setNegativeButton(android.R.string.no, this /* onClickListener */)
+                .setPositiveButton(android.R.string.ok, this /* onClickListener */)
+                .setNegativeButton(android.R.string.cancel, this /* onClickListener */)
                 .create();
     }
 
diff --git a/src/com/android/settings/development/EnableAdbWarningDialog.java b/src/com/android/settings/development/EnableAdbWarningDialog.java
index 1fcd350..5204130 100644
--- a/src/com/android/settings/development/EnableAdbWarningDialog.java
+++ b/src/com/android/settings/development/EnableAdbWarningDialog.java
@@ -52,8 +52,8 @@
         return new AlertDialog.Builder(getActivity())
                 .setTitle(R.string.adb_warning_title)
                 .setMessage(R.string.adb_warning_message)
-                .setPositiveButton(android.R.string.yes, this /* onClickListener */)
-                .setNegativeButton(android.R.string.no, this /* onClickListener */)
+                .setPositiveButton(android.R.string.ok, this /* onClickListener */)
+                .setNegativeButton(android.R.string.cancel, this /* onClickListener */)
                 .create();
     }
 
diff --git a/src/com/android/settings/development/EnableDevelopmentSettingWarningDialog.java b/src/com/android/settings/development/EnableDevelopmentSettingWarningDialog.java
index 983f55e..6b84cdc 100644
--- a/src/com/android/settings/development/EnableDevelopmentSettingWarningDialog.java
+++ b/src/com/android/settings/development/EnableDevelopmentSettingWarningDialog.java
@@ -53,8 +53,8 @@
         return new AlertDialog.Builder(getActivity())
                 .setMessage(R.string.dev_settings_warning_message)
                 .setTitle(R.string.dev_settings_warning_title)
-                .setPositiveButton(android.R.string.yes, this)
-                .setNegativeButton(android.R.string.no, this)
+                .setPositiveButton(android.R.string.ok, this)
+                .setNegativeButton(android.R.string.cancel, this)
                 .create();
     }
 
diff --git a/src/com/android/settings/development/tare/AlarmManagerFragment.java b/src/com/android/settings/development/tare/AlarmManagerFragment.java
index b9de81d..9b9eded 100644
--- a/src/com/android/settings/development/tare/AlarmManagerFragment.java
+++ b/src/com/android/settings/development/tare/AlarmManagerFragment.java
@@ -117,7 +117,8 @@
         mKeys = new String[][]{
                 {
                         EconomyManager.KEY_AM_INITIAL_CONSUMPTION_LIMIT,
-                        EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT
+                        EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT,
+                        EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT,
                 },
                 {
                         EconomyManager.KEY_AM_MAX_SATIATED_BALANCE,
diff --git a/src/com/android/settings/development/tare/JobSchedulerFragment.java b/src/com/android/settings/development/tare/JobSchedulerFragment.java
index 9b8fb47..e3b36a5 100644
--- a/src/com/android/settings/development/tare/JobSchedulerFragment.java
+++ b/src/com/android/settings/development/tare/JobSchedulerFragment.java
@@ -118,7 +118,8 @@
         mKeys = new String[][]{
                 {
                         EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT,
-                        EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT
+                        EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT,
+                        EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT,
                 },
                 {
                         EconomyManager.KEY_JS_MAX_SATIATED_BALANCE,
diff --git a/src/com/android/settings/development/tare/TareFactorController.java b/src/com/android/settings/development/tare/TareFactorController.java
index e6c2d8c..b5dd6ea 100644
--- a/src/com/android/settings/development/tare/TareFactorController.java
+++ b/src/com/android/settings/development/tare/TareFactorController.java
@@ -117,9 +117,13 @@
                 new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
                         EconomyManager.DEFAULT_AM_INITIAL_CONSUMPTION_LIMIT_CAKES,
                         POLICY_ALARM_MANAGER));
-        mAlarmManagerMap.put(EconomyManager.KEY_AM_HARD_CONSUMPTION_LIMIT,
-                new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
-                        EconomyManager.DEFAULT_AM_HARD_CONSUMPTION_LIMIT_CAKES,
+        mAlarmManagerMap.put(EconomyManager.KEY_AM_MIN_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_min_consumption_limit),
+                        EconomyManager.DEFAULT_AM_MIN_CONSUMPTION_LIMIT_CAKES,
+                        POLICY_ALARM_MANAGER));
+        mAlarmManagerMap.put(EconomyManager.KEY_AM_MAX_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_max_consumption_limit),
+                        EconomyManager.DEFAULT_AM_MAX_CONSUMPTION_LIMIT_CAKES,
                         POLICY_ALARM_MANAGER));
         mAlarmManagerMap.put(EconomyManager.KEY_AM_REWARD_TOP_ACTIVITY_INSTANT,
                 new TareFactorData(mResources.getString(R.string.tare_top_activity),
@@ -306,9 +310,13 @@
                 new TareFactorData(mResources.getString(R.string.tare_initial_consumption_limit),
                         EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES,
                         POLICY_JOB_SCHEDULER));
-        mJobSchedulerMap.put(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT,
-                new TareFactorData(mResources.getString(R.string.tare_hard_consumption_limit),
-                        EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
+        mJobSchedulerMap.put(EconomyManager.KEY_JS_MIN_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_min_consumption_limit),
+                        EconomyManager.DEFAULT_JS_MIN_CONSUMPTION_LIMIT_CAKES,
+                        POLICY_JOB_SCHEDULER));
+        mJobSchedulerMap.put(EconomyManager.KEY_JS_MAX_CONSUMPTION_LIMIT,
+                new TareFactorData(mResources.getString(R.string.tare_max_consumption_limit),
+                        EconomyManager.DEFAULT_JS_MAX_CONSUMPTION_LIMIT_CAKES,
                         POLICY_JOB_SCHEDULER));
         mJobSchedulerMap.put(EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT,
                 new TareFactorData(mResources.getString(R.string.tare_app_install),
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index e3919b0..c08a1b8 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -480,15 +480,10 @@
         final String slotTime = bundle.getString(EXTRA_SLOT_TIME, null);
         final long totalTimeMs = foregroundTimeMs + backgroundTimeMs;
         final CharSequence usageTimeSummary;
-        final boolean isChartGraphEnabled = FeatureFactory.getFactory(getContext())
-                .getPowerUsageFeatureProvider(getContext()).isChartGraphEnabled(getContext());
 
-        if (!isChartGraphEnabled && BatteryEntry.isSystemUid(uid)) {
-            return null;
-        }
         if (totalTimeMs == 0) {
             usageTimeSummary = getText(
-                    isChartGraphEnabled && consumedPower > 0 ? R.string.battery_usage_without_time
+                    consumedPower > 0 ? R.string.battery_usage_without_time
                             : R.string.battery_not_usage);
         } else if (slotTime == null) {
             // Shows summary text with last full charge if slot time is null.
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
index 1720845..7a33a62 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProvider.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.net.Uri;
 import android.util.SparseIntArray;
 
 import com.android.settings.fuelgauge.batteryusage.BatteryHistEntry;
@@ -125,11 +124,6 @@
     boolean isSmartBatterySupported();
 
     /**
-     * Checks whether we should enable chart graph design or not.
-     */
-    boolean isChartGraphEnabled(Context context);
-
-    /**
      * Checks whether we should show usage information by slots or not.
      */
     boolean isChartGraphSlotsEnabled(Context context);
@@ -155,16 +149,6 @@
     Map<Long, Map<String, BatteryHistEntry>> getBatteryHistory(Context context);
 
     /**
-     * Returns battery history data since last full charge with corresponding timestamp key.
-     */
-    Map<Long, Map<String, BatteryHistEntry>> getBatteryHistorySinceLastFullCharge(Context context);
-
-    /**
-     * Returns {@link Uri} to monitor battery history data is update.
-     */
-    Uri getBatteryHistoryUri();
-
-    /**
      * Returns {@link Set} for hidding applications background usage time.
      */
     Set<CharSequence> getHideBackgroundUsageTimeSet(Context context);
diff --git a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
index 48f4421..3fe4275 100644
--- a/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
+++ b/src/com/android/settings/fuelgauge/PowerUsageFeatureProviderImpl.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.net.Uri;
 import android.os.Process;
 import android.util.ArraySet;
 import android.util.SparseIntArray;
@@ -140,11 +139,6 @@
     }
 
     @Override
-    public boolean isChartGraphEnabled(Context context) {
-        return false;
-    }
-
-    @Override
     public boolean isChartGraphSlotsEnabled(Context context) {
         return false;
     }
@@ -170,17 +164,6 @@
     }
 
     @Override
-    public Map<Long, Map<String, BatteryHistEntry>> getBatteryHistorySinceLastFullCharge(
-            Context context) {
-        return null;
-    }
-
-    @Override
-    public Uri getBatteryHistoryUri() {
-        return null;
-    }
-
-    @Override
     public Set<CharSequence> getHideBackgroundUsageTimeSet(Context context) {
         return new ArraySet<>();
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index 419ae90..6dd59b7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -58,6 +58,7 @@
 import com.android.settingslib.widget.FooterPreference;
 
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -760,9 +761,7 @@
     public static List<BatteryDiffEntry> getAppBatteryUsageData(Context context) {
         final long start = System.currentTimeMillis();
         final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap =
-                FeatureFactory.getFactory(context)
-                        .getPowerUsageFeatureProvider(context)
-                        .getBatteryHistorySinceLastFullCharge(context);
+                DatabaseUtils.getHistoryMapSinceLastFullCharge(context, Calendar.getInstance());
         if (batteryHistoryMap == null || batteryHistoryMap.isEmpty()) {
             return null;
         }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java
index 83b2615..9a0e410 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoader.java
@@ -17,10 +17,9 @@
 
 import android.content.Context;
 
-import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
-import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.utils.AsyncLoaderCompat;
 
+import java.util.Calendar;
 import java.util.Map;
 
 /** Loader that can be used to load battery history information. */
@@ -41,8 +40,6 @@
 
     @Override
     public Map<Long, Map<String, BatteryHistEntry>> loadInBackground() {
-        final PowerUsageFeatureProvider powerUsageFeatureProvider =
-                FeatureFactory.getFactory(mContext).getPowerUsageFeatureProvider(mContext);
-        return powerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext);
+        return DatabaseUtils.getHistoryMapSinceLastFullCharge(mContext, Calendar.getInstance());
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java
index 6748223..c78b3c7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreference.java
@@ -19,8 +19,6 @@
 import android.content.Context;
 import android.os.BatteryUsageStats;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -31,8 +29,6 @@
 import com.android.settings.R;
 import com.android.settings.fuelgauge.BatteryInfo;
 import com.android.settings.fuelgauge.BatteryUtils;
-import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.UsageView;
 
 /**
  * Custom preference for displaying the battery level as chart graph.
@@ -41,49 +37,18 @@
     private static final String TAG = "BatteryHistoryPreference";
 
     @VisibleForTesting
-    boolean mHideSummary;
-    @VisibleForTesting
     BatteryInfo mBatteryInfo;
 
-    private boolean mIsChartGraphEnabled;
-
-    private TextView mSummaryView;
-    private CharSequence mSummaryContent;
     private BatteryChartView mDailyChartView;
     private BatteryChartView mHourlyChartView;
     private BatteryChartPreferenceController mChartPreferenceController;
 
     public BatteryHistoryPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mIsChartGraphEnabled =
-                FeatureFactory.getFactory(context).getPowerUsageFeatureProvider(context)
-                        .isChartGraphEnabled(context);
-        Log.i(TAG, "isChartGraphEnabled: " + mIsChartGraphEnabled);
-        setLayoutResource(
-                mIsChartGraphEnabled
-                        ? R.layout.battery_chart_graph
-                        : R.layout.battery_usage_graph);
+        setLayoutResource(R.layout.battery_chart_graph);
         setSelectable(false);
     }
 
-    /** Sets the text of bottom summary. */
-    public void setBottomSummary(CharSequence text) {
-        mSummaryContent = text;
-        if (mSummaryView != null) {
-            mSummaryView.setVisibility(View.VISIBLE);
-            mSummaryView.setText(mSummaryContent);
-        }
-        mHideSummary = false;
-    }
-
-    /** Hides the bottom summary. */
-    public void hideBottomSummary() {
-        if (mSummaryView != null) {
-            mSummaryView.setVisibility(View.GONE);
-        }
-        mHideSummary = true;
-    }
-
     void setBatteryUsageStats(@NonNull BatteryUsageStats batteryUsageStats) {
         BatteryInfo.getBatteryInfo(getContext(), info -> {
             mBatteryInfo = info;
@@ -105,28 +70,13 @@
         if (mBatteryInfo == null) {
             return;
         }
-        if (mIsChartGraphEnabled) {
-            final TextView companionTextView = (TextView) view.findViewById(R.id.companion_text);
-            mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
-            mDailyChartView.setCompanionTextView(companionTextView);
-            mHourlyChartView = (BatteryChartView) view.findViewById(R.id.hourly_battery_chart);
-            mHourlyChartView.setCompanionTextView(companionTextView);
-            if (mChartPreferenceController != null) {
-                mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
-            }
-        } else {
-            final TextView chargeView = (TextView) view.findViewById(R.id.charge);
-            chargeView.setText(mBatteryInfo.batteryPercentString);
-            mSummaryView = (TextView) view.findViewById(R.id.bottom_summary);
-            if (mSummaryContent != null) {
-                mSummaryView.setText(mSummaryContent);
-            }
-            if (mHideSummary) {
-                mSummaryView.setVisibility(View.GONE);
-            }
-            final UsageView usageView = (UsageView) view.findViewById(R.id.battery_usage);
-            usageView.findViewById(R.id.label_group).setAlpha(.7f);
-            mBatteryInfo.bindHistory(usageView);
+        final TextView companionTextView = (TextView) view.findViewById(R.id.companion_text);
+        mDailyChartView = (BatteryChartView) view.findViewById(R.id.daily_battery_chart);
+        mDailyChartView.setCompanionTextView(companionTextView);
+        mHourlyChartView = (BatteryChartView) view.findViewById(R.id.hourly_battery_chart);
+        mHourlyChartView.setCompanionTextView(companionTextView);
+        if (mChartPreferenceController != null) {
+            mChartPreferenceController.setBatteryChartView(mDailyChartView, mHourlyChartView);
         }
         BatteryUtils.logRuntime(TAG, "onBindViewHolder", startTime);
     }
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/BatteryUsageContentProvider.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
index 8d5ba33..5680414 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProvider.java
@@ -21,6 +21,7 @@
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -144,7 +145,7 @@
         } catch (RuntimeException e) {
             Log.e(TAG, "query() from:" + uri + " error:" + e);
         }
-        // TODO: Invokes hourly job recheck.
+        AsyncTask.execute(() -> BootBroadcastReceiver.invokeJobRecheck(getContext()));
         Log.w(TAG, "query battery states in " + (mClock.millis() - timestamp) + "/ms");
         return cursor;
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
new file mode 100644
index 0000000..5b48c9f
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiver.java
@@ -0,0 +1,88 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.time.Duration;
+
+/** Receives broadcasts to start or stop the periodic fetching job. */
+public final class BootBroadcastReceiver extends BroadcastReceiver {
+    private static final String TAG = "BootBroadcastReceiver";
+    private static final long RESCHEDULE_FOR_BOOT_ACTION = Duration.ofSeconds(6).toMillis();
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    public static final String ACTION_PERIODIC_JOB_RECHECK =
+            "com.android.settings.battery.action.PERIODIC_JOB_RECHECK";
+    public static final String ACTION_SETUP_WIZARD_FINISHED =
+            "com.google.android.setupwizard.SETUP_WIZARD_FINISHED";
+
+    /** Invokes periodic job rechecking process. */
+    public static void invokeJobRecheck(Context context) {
+        context = context.getApplicationContext();
+        final Intent intent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
+        intent.setClass(context, BootBroadcastReceiver.class);
+        context.sendBroadcast(intent);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent == null ? "" : intent.getAction();
+        if (DatabaseUtils.isWorkProfile(context)) {
+            Log.w(TAG, "do not start job for work profile action=" + action);
+            return;
+        }
+
+        switch (action) {
+            case Intent.ACTION_BOOT_COMPLETED:
+            case Intent.ACTION_MY_PACKAGE_REPLACED:
+            case Intent.ACTION_MY_PACKAGE_UNSUSPENDED:
+            case ACTION_SETUP_WIZARD_FINISHED:
+            case ACTION_PERIODIC_JOB_RECHECK:
+                Log.d(TAG, "refresh periodic job from action=" + action);
+                refreshJobs(context);
+                break;
+            case Intent.ACTION_TIME_CHANGED:
+                Log.d(TAG, "refresh job and clear all data from action=" + action);
+                DatabaseUtils.clearAll(context);
+                PeriodicJobManager.getInstance(context).refreshJob();
+                break;
+            default:
+                Log.w(TAG, "receive unsupported action=" + action);
+        }
+
+        // Waits a while to recheck the scheduler to avoid AlarmManager is not ready.
+        if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+            final Intent recheckIntent = new Intent(ACTION_PERIODIC_JOB_RECHECK);
+            recheckIntent.setClass(context, BootBroadcastReceiver.class);
+            mHandler.postDelayed(() -> context.sendBroadcast(recheckIntent),
+                    RESCHEDULE_FOR_BOOT_ACTION);
+        }
+    }
+
+    private static void refreshJobs(Context context) {
+        // Clears useless data from battery usage database if needed.
+        DatabaseUtils.clearExpiredDataIfNeeded(context);
+        PeriodicJobManager.getInstance(context).refreshJob();
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index f2c5c2b..29e0b0e 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -57,7 +57,9 @@
     /** Clear memory threshold for device booting phase. **/
     private static final long CLEAR_MEMORY_THRESHOLD_MS = Duration.ofMinutes(5).toMillis();
     private static final long CLEAR_MEMORY_DELAYED_MS = Duration.ofSeconds(2).toMillis();
-    private static final long DATA_RETENTION_INTERVAL_MS = Duration.ofDays(9).toMillis();
+
+    @VisibleForTesting
+    static final int DATA_RETENTION_INTERVAL_DAY = 9;
 
     /** An authority name of the battery content provider. */
     public static final String AUTHORITY = "com.android.settings.battery.usage.provider";
@@ -65,8 +67,6 @@
     public static final String BATTERY_STATE_TABLE = "BatteryState";
     /** A class name for battery usage data provider. */
     public static final String SETTINGS_PACKAGE_PATH = "com.android.settings";
-    public static final String BATTERY_PROVIDER_CLASS_PATH =
-            "com.android.settings.fuelgauge.batteryusage.BatteryUsageContentProvider";
 
     /** A content URI to access battery usage states data. */
     public static final Uri BATTERY_CONTENT_URI =
@@ -133,13 +133,20 @@
                 BatteryStateDatabase
                         .getInstance(context.getApplicationContext())
                         .batteryStateDao()
-                        .clearAllBefore(Clock.systemUTC().millis() - DATA_RETENTION_INTERVAL_MS);
+                        .clearAllBefore(Clock.systemUTC().millis()
+                                - Duration.ofDays(DATA_RETENTION_INTERVAL_DAY).toMillis());
             } catch (RuntimeException e) {
                 Log.e(TAG, "clearAllBefore() failed", e);
             }
         });
     }
 
+    /** 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,
@@ -299,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/fuelgauge/batteryusage/OWNERS b/src/com/android/settings/fuelgauge/batteryusage/OWNERS
new file mode 100644
index 0000000..a18b532
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/OWNERS
@@ -0,0 +1,4 @@
+# Default reviewers for this and subdirectories.
+clairewang@google.com
+xuezaiyue@google.com
+ykhung@google.com
\ No newline at end of file
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java
new file mode 100644
index 0000000..140ba5f
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManager.java
@@ -0,0 +1,123 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.text.SimpleDateFormat;
+import java.time.Clock;
+import java.time.Duration;
+import java.util.Date;
+import java.util.Locale;
+
+/** Manages the periodic job to schedule or cancel the next job. */
+public final class PeriodicJobManager {
+    private static final String TAG = "PeriodicJobManager";
+    private static final int ALARM_MANAGER_REQUEST_CODE = TAG.hashCode();
+
+    private static PeriodicJobManager sSingleton;
+
+    private final Context mContext;
+    private final AlarmManager mAlarmManager;
+    private final SimpleDateFormat mSimpleDateFormat =
+            new SimpleDateFormat("MMM dd,yyyy HH:mm:ss", Locale.ENGLISH);
+
+    @VisibleForTesting
+    static final int DATA_FETCH_INTERVAL_MINUTE = 60;
+
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    void reset() {
+        sSingleton = null; // for testing only
+    }
+
+    /** Gets or creates the new {@link PeriodicJobManager} instance. */
+    public static synchronized PeriodicJobManager getInstance(Context context) {
+        if (sSingleton == null || sSingleton.mAlarmManager == null) {
+            sSingleton = new PeriodicJobManager(context);
+        }
+        return sSingleton;
+    }
+
+    private PeriodicJobManager(Context context) {
+        this.mContext = context.getApplicationContext();
+        this.mAlarmManager = context.getSystemService(AlarmManager.class);
+    }
+
+    /** Schedules the next alarm job if it is available. */
+    @SuppressWarnings("JavaUtilDate")
+    public void refreshJob() {
+        if (mAlarmManager == null) {
+            Log.e(TAG, "cannot schedule next alarm job");
+            return;
+        }
+        // Cancels the previous alert job and schedules the next one.
+        final PendingIntent pendingIntent = getPendingIntent();
+        cancelJob(pendingIntent);
+        if (!canScheduleExactAlarms()) {
+            Log.w(TAG, "cannot schedule exact alarm job");
+            return;
+        }
+        // Uses UTC time to avoid scheduler is impacted by different timezone.
+        final long triggerAtMillis = getTriggerAtMillis(Clock.systemUTC());
+        mAlarmManager.setExactAndAllowWhileIdle(
+                AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
+        Log.d(TAG, "schedule next alarm job at "
+                + mSimpleDateFormat.format(new Date(triggerAtMillis)));
+    }
+
+    void cancelJob(PendingIntent pendingIntent) {
+        if (mAlarmManager != null) {
+            mAlarmManager.cancel(pendingIntent);
+        } else {
+            Log.e(TAG, "cannot cancel the alarm job");
+        }
+    }
+
+    /** Gets the next alarm trigger UTC time in milliseconds. */
+    static long getTriggerAtMillis(Clock clock) {
+        long currentTimeMillis = clock.millis();
+        // Rounds to the previous nearest time slot and shifts to the next one.
+        long timeSlotUnit = Duration.ofMinutes(DATA_FETCH_INTERVAL_MINUTE).toMillis();
+        return (currentTimeMillis / timeSlotUnit) * timeSlotUnit + timeSlotUnit;
+    }
+
+    private PendingIntent getPendingIntent() {
+        final Intent broadcastIntent =
+                new Intent(mContext, PeriodicJobReceiver.class)
+                        .setAction(PeriodicJobReceiver.ACTION_PERIODIC_JOB_UPDATE);
+        return PendingIntent.getBroadcast(
+                mContext.getApplicationContext(),
+                ALARM_MANAGER_REQUEST_CODE,
+                broadcastIntent,
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+    }
+
+    private boolean canScheduleExactAlarms() {
+        return canScheduleExactAlarms(mAlarmManager);
+    }
+
+    /** Whether we can schedule exact alarm or not? */
+    public static boolean canScheduleExactAlarms(AlarmManager alarmManager) {
+        return alarmManager.canScheduleExactAlarms();
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
new file mode 100644
index 0000000..d2345ab
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/** Receives the periodic alarm {@link PendingIntent} callback. */
+public final class PeriodicJobReceiver extends BroadcastReceiver {
+    private static final String TAG = "PeriodicJobReceiver";
+    public static final String ACTION_PERIODIC_JOB_UPDATE =
+            "com.android.settings.battery.action.PERIODIC_JOB_UPDATE";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent == null ? "" : intent.getAction();
+        if (!ACTION_PERIODIC_JOB_UPDATE.equals(action)) {
+            Log.w(TAG, "receive unexpected action=" + action);
+            return;
+        }
+        if (DatabaseUtils.isWorkProfile(context)) {
+            Log.w(TAG, "do not refresh job for work profile action=" + action);
+            return;
+        }
+        BatteryUsageDataLoader.enqueueWork(context);
+        Log.d(TAG, "refresh periodic job from action=" + action);
+        PeriodicJobManager.getInstance(context).refreshJob();
+        DatabaseUtils.clearExpiredDataIfNeeded(context);
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
index b88d85d..7b085f7 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/PowerUsageAdvanced.java
@@ -19,11 +19,8 @@
 
 import android.app.settings.SettingsEnums;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.database.ContentObserver;
 import android.net.Uri;
-import android.os.BatteryManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.SearchIndexableResource;
@@ -66,10 +63,8 @@
             new BatteryHistoryLoaderCallbacks();
 
     private boolean mIsChartDataLoaded = false;
-    private boolean mIsChartGraphEnabled = false;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
     private BatteryChartPreferenceController mBatteryChartPreferenceController;
-    private BatteryAppListPreferenceController mBatteryAppListPreferenceController;
 
     private final ContentObserver mBatteryObserver =
             new ContentObserver(new Handler()) {
@@ -88,11 +83,7 @@
         final Context context = getContext();
         refreshFeatureFlag(context);
         mHistPref = (BatteryHistoryPreference) findPreference(KEY_BATTERY_GRAPH);
-        if (mIsChartGraphEnabled) {
-            setBatteryChartPreferenceController();
-        } else {
-            updateHistPrefSummary(context);
-        }
+        setBatteryChartPreferenceController();
     }
 
     @Override
@@ -123,7 +114,7 @@
         super.onPause();
         // Resets the flag to reload usage data in onResume() callback.
         mIsChartDataLoaded = false;
-        final Uri uri = mPowerUsageFeatureProvider.getBatteryHistoryUri();
+        final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
         if (uri != null) {
             getContext().getContentResolver().unregisterContentObserver(mBatteryObserver);
         }
@@ -132,7 +123,7 @@
     @Override
     public void onResume() {
         super.onResume();
-        final Uri uri = mPowerUsageFeatureProvider.getBatteryHistoryUri();
+        final Uri uri = DatabaseUtils.BATTERY_CONTENT_URI;
         if (uri != null) {
             getContext().getContentResolver().registerContentObserver(
                     uri, /*notifyForDescendants*/ true, mBatteryObserver);
@@ -143,19 +134,11 @@
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         refreshFeatureFlag(context);
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        // Creates based on the chart design is enabled or not.
-        if (mIsChartGraphEnabled) {
-            mBatteryChartPreferenceController =
-                    new BatteryChartPreferenceController(context, KEY_APP_LIST,
-                            getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
-            controllers.add(mBatteryChartPreferenceController);
-            setBatteryChartPreferenceController();
-        } else {
-            mBatteryAppListPreferenceController =
-                    new BatteryAppListPreferenceController(context, KEY_APP_LIST,
-                            getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
-            controllers.add(mBatteryAppListPreferenceController);
-        }
+        mBatteryChartPreferenceController =
+                new BatteryChartPreferenceController(context, KEY_APP_LIST,
+                        getSettingsLifecycle(), (SettingsActivity) getActivity(), this);
+        controllers.add(mBatteryChartPreferenceController);
+        setBatteryChartPreferenceController();
         return controllers;
     }
 
@@ -171,11 +154,6 @@
             return;
         }
         updatePreference(mHistPref);
-        if (mBatteryAppListPreferenceController != null && mBatteryUsageStats != null) {
-            updateHistPrefSummary(context);
-            mBatteryAppListPreferenceController.refreshAppListGroup(
-                    mBatteryUsageStats, /* showAllApps */true);
-        }
         if (mBatteryChartPreferenceController != null && mBatteryHistoryMap != null) {
             mBatteryChartPreferenceController.setBatteryHistoryMap(mBatteryHistoryMap);
         }
@@ -185,25 +163,10 @@
     protected void restartBatteryStatsLoader(int refreshType) {
         final Bundle bundle = new Bundle();
         bundle.putInt(KEY_REFRESH_TYPE, refreshType);
-        // Uses customized battery history loader if chart design is enabled.
-        if (mIsChartGraphEnabled && !mIsChartDataLoaded) {
+        if (!mIsChartDataLoaded) {
             mIsChartDataLoaded = true;
             getLoaderManager().restartLoader(LOADER_BATTERY_USAGE_STATS, bundle,
                     mBatteryHistoryLoaderCallbacks);
-        } else if (!mIsChartGraphEnabled) {
-            super.restartBatteryStatsLoader(refreshType);
-        }
-    }
-
-    private void updateHistPrefSummary(Context context) {
-        final Intent batteryIntent =
-                context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
-        final boolean plugged = batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) != 0;
-        if (mPowerUsageFeatureProvider.isEnhancedBatteryPredictionEnabled(context) && !plugged) {
-            mHistPref.setBottomSummary(
-                    mPowerUsageFeatureProvider.getAdvancedUsageScreenInfoString());
-        } else {
-            mHistPref.hideBottomSummary();
         }
     }
 
@@ -211,7 +174,6 @@
         if (mPowerUsageFeatureProvider == null) {
             mPowerUsageFeatureProvider = FeatureFactory.getFactory(context)
                     .getPowerUsageFeatureProvider(context);
-            mIsChartGraphEnabled = mPowerUsageFeatureProvider.isChartGraphEnabled(context);
         }
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
index 9396546..9d13d9f 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/db/BatteryStateDatabase.java
@@ -40,7 +40,7 @@
         if (sBatteryStateDatabase == null) {
             sBatteryStateDatabase =
                     Room.databaseBuilder(
-                                    context, BatteryStateDatabase.class, "battery-usage-db-v1")
+                                    context, BatteryStateDatabase.class, "battery-usage-db-v5")
                             // Allows accessing data in the main thread for dumping bugreport.
                             .allowMainThreadQueries()
                             .fallbackToDestructiveMigration()
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index dc1c1f2..038216f 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -66,6 +66,7 @@
 import com.android.settings.core.FeatureFlags;
 import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
 import com.android.settings.overlay.FeatureFactory;
+import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
 import com.android.settingslib.Utils;
 import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin;
 
@@ -504,12 +505,28 @@
             final String menuKey = intent.getStringExtra(
                     EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY);
             if (!TextUtils.isEmpty(menuKey)) {
-                return menuKey;
+                return maybeRemapMenuKey(menuKey);
             }
         }
         return getString(DEFAULT_HIGHLIGHT_MENU_KEY);
     }
 
+    private String maybeRemapMenuKey(String menuKey) {
+        boolean isPrivacyOrSecurityMenuKey =
+                getString(R.string.menu_key_privacy).equals(menuKey)
+                        || getString(R.string.menu_key_security).equals(menuKey);
+        boolean isSafetyCenterMenuKey = getString(R.string.menu_key_safety_center).equals(menuKey);
+
+        if (isPrivacyOrSecurityMenuKey && SafetyCenterManagerWrapper.get().isEnabled(this)) {
+            return getString(R.string.menu_key_safety_center);
+        }
+        if (isSafetyCenterMenuKey && !SafetyCenterManagerWrapper.get().isEnabled(this)) {
+            // We don't know if security or privacy, default to security as it is above.
+            return getString(R.string.menu_key_security);
+        }
+        return menuKey;
+    }
+
     private void reloadHighlightMenuKey() {
         mMainFragment.getArguments().putString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY,
                 getHighlightMenuKey());
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
index 4a02d91..a74204a 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardLookupTable.java
@@ -34,7 +34,6 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 public class ContextualCardLookupTable {
@@ -65,41 +64,39 @@
     }
 
     @VisibleForTesting
-    static final Set<ControllerRendererMapping> LOOKUP_TABLE =
-            new TreeSet<ControllerRendererMapping>() {{
-                add(new ControllerRendererMapping(CardType.CONDITIONAL,
-                        ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
-                        ConditionContextualCardController.class,
-                        ConditionContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.CONDITIONAL,
-                        ConditionContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
-                        ConditionContextualCardController.class,
-                        ConditionContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.LEGACY_SUGGESTION,
-                        LegacySuggestionContextualCardRenderer.VIEW_TYPE,
-                        LegacySuggestionContextualCardController.class,
-                        LegacySuggestionContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.SLICE,
-                        SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
-                        SliceContextualCardController.class,
-                        SliceContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.SLICE,
-                        SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
-                        SliceContextualCardController.class,
-                        SliceContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.SLICE,
-                        SliceContextualCardRenderer.VIEW_TYPE_STICKY,
-                        SliceContextualCardController.class,
-                        SliceContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.CONDITIONAL_FOOTER,
-                        ConditionFooterContextualCardRenderer.VIEW_TYPE,
-                        ConditionContextualCardController.class,
-                        ConditionFooterContextualCardRenderer.class));
-                add(new ControllerRendererMapping(CardType.CONDITIONAL_HEADER,
-                        ConditionHeaderContextualCardRenderer.VIEW_TYPE,
-                        ConditionContextualCardController.class,
-                        ConditionHeaderContextualCardRenderer.class));
-            }};
+    static final Set<ControllerRendererMapping> LOOKUP_TABLE = Set.of(
+            new ControllerRendererMapping(CardType.CONDITIONAL,
+                    ConditionContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
+                    ConditionContextualCardController.class,
+                    ConditionContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.CONDITIONAL,
+                    ConditionContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
+                    ConditionContextualCardController.class,
+                    ConditionContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.LEGACY_SUGGESTION,
+                    LegacySuggestionContextualCardRenderer.VIEW_TYPE,
+                    LegacySuggestionContextualCardController.class,
+                    LegacySuggestionContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.SLICE,
+                    SliceContextualCardRenderer.VIEW_TYPE_FULL_WIDTH,
+                    SliceContextualCardController.class,
+                    SliceContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.SLICE,
+                    SliceContextualCardRenderer.VIEW_TYPE_HALF_WIDTH,
+                    SliceContextualCardController.class,
+                    SliceContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.SLICE,
+                    SliceContextualCardRenderer.VIEW_TYPE_STICKY,
+                    SliceContextualCardController.class,
+                    SliceContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.CONDITIONAL_FOOTER,
+                    ConditionFooterContextualCardRenderer.VIEW_TYPE,
+                    ConditionContextualCardController.class,
+                    ConditionFooterContextualCardRenderer.class),
+            new ControllerRendererMapping(CardType.CONDITIONAL_HEADER,
+                    ConditionHeaderContextualCardRenderer.VIEW_TYPE,
+                    ConditionContextualCardController.class,
+                    ConditionHeaderContextualCardRenderer.class));
 
     public static Class<? extends ContextualCardController> getCardControllerClass(
             @CardType int cardType) {
diff --git a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
index ac35017..f5d0e66 100644
--- a/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
+++ b/src/com/android/settings/homepage/contextualcards/ContextualCardManager.java
@@ -55,7 +55,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 /**
@@ -196,11 +195,10 @@
         // except Conditional cards, all other cards are from the database. So when the map sent
         // here is empty, we only keep Conditional cards.
         if (cardTypes.isEmpty()) {
-            final Set<Integer> conditionalCardTypes = new TreeSet<Integer>() {{
-                add(ContextualCard.CardType.CONDITIONAL);
-                add(ContextualCard.CardType.CONDITIONAL_HEADER);
-                add(ContextualCard.CardType.CONDITIONAL_FOOTER);
-            }};
+            final Set<Integer> conditionalCardTypes = Set.of(
+                    ContextualCard.CardType.CONDITIONAL,
+                    ContextualCard.CardType.CONDITIONAL_HEADER,
+                    ContextualCard.CardType.CONDITIONAL_FOOTER);
             cardsToKeep = mContextualCards.stream()
                     .filter(card -> conditionalCardTypes.contains(card.getCardType()))
                     .collect(Collectors.toList());
diff --git a/src/com/android/settings/localepicker/LocaleListEditor.java b/src/com/android/settings/localepicker/LocaleListEditor.java
index eac2dd1..5947a87 100644
--- a/src/com/android/settings/localepicker/LocaleListEditor.java
+++ b/src/com/android/settings/localepicker/LocaleListEditor.java
@@ -202,7 +202,7 @@
             new AlertDialog.Builder(getActivity())
                     .setTitle(R.string.dlg_remove_locales_error_title)
                     .setMessage(R.string.dlg_remove_locales_error_message)
-                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                         @Override
                         public void onClick(DialogInterface dialog, int which) {
                         }
@@ -228,7 +228,7 @@
         }
 
         builder.setTitle(title)
-                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
+                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         setRemoveMode(false);
diff --git a/src/com/android/settings/network/MobileNetworkListFragment.java b/src/com/android/settings/network/MobileNetworkListFragment.java
index 9a41e05..c84e465 100644
--- a/src/com/android/settings/network/MobileNetworkListFragment.java
+++ b/src/com/android/settings/network/MobileNetworkListFragment.java
@@ -71,7 +71,7 @@
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         final List<AbstractPreferenceController> controllers = new ArrayList<>();
         if (!SubscriptionUtil.isSimHardwareVisible(getContext())) {
-            finishFragment();
+            finish();
             return controllers;
         }
 
diff --git a/src/com/android/settings/network/TetherProvisioningCarrierDialogActivity.java b/src/com/android/settings/network/TetherProvisioningCarrierDialogActivity.java
index 2506229..a8b02ac 100644
--- a/src/com/android/settings/network/TetherProvisioningCarrierDialogActivity.java
+++ b/src/com/android/settings/network/TetherProvisioningCarrierDialogActivity.java
@@ -35,7 +35,7 @@
                 .setTitle(R.string.wifi_tether_carrier_unsupport_dialog_title)
                 .setMessage(R.string.wifi_tether_carrier_unsupport_dialog_content)
                 .setCancelable(false)
-                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
+                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         dialog.cancel();
                         finish();
diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java
index bfb4943..25d8e84 100644
--- a/src/com/android/settings/network/apn/ApnEditor.java
+++ b/src/com/android/settings/network/apn/ApnEditor.java
@@ -580,7 +580,7 @@
                 // Network code
                 final String mnc = (subInfo == null) ? null : subInfo.getMncString();
 
-                if ((!TextUtils.isEmpty(mcc)) && (!TextUtils.isEmpty(mcc))) {
+                if (!TextUtils.isEmpty(mcc)) {
                     // Auto populate MNC and MCC for new entries, based on what SIM reports
                     mMcc.setText(mcc);
                     mMnc.setText(mnc);
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index bfca65e..a0c9679 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -121,7 +121,7 @@
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
         if (!SubscriptionUtil.isSimHardwareVisible(context)) {
-            finishFragment();
+            finish();
             return Arrays.asList();
         }
         if (getArguments() == null) {
diff --git a/src/com/android/settings/network/telephony/RoamingDialogFragment.java b/src/com/android/settings/network/telephony/RoamingDialogFragment.java
index 3e9875e..5b57992 100644
--- a/src/com/android/settings/network/telephony/RoamingDialogFragment.java
+++ b/src/com/android/settings/network/telephony/RoamingDialogFragment.java
@@ -70,8 +70,8 @@
         builder.setMessage(getResources().getString(message))
                 .setTitle(title)
                 .setIconAttribute(android.R.attr.alertDialogIcon)
-                .setPositiveButton(android.R.string.yes, this)
-                .setNegativeButton(android.R.string.no, this);
+                .setPositiveButton(android.R.string.ok, this)
+                .setNegativeButton(android.R.string.cancel, this);
         AlertDialog dialog = builder.create();
         dialog.setCanceledOnTouchOutside(false);
         return dialog;
diff --git a/src/com/android/settings/notification/SoundWorkSettingsController.java b/src/com/android/settings/notification/SoundWorkSettingsController.java
index c14a7fe..10a3483 100644
--- a/src/com/android/settings/notification/SoundWorkSettingsController.java
+++ b/src/com/android/settings/notification/SoundWorkSettingsController.java
@@ -363,7 +363,7 @@
                             () -> context.getString(R.string.work_sync_dialog_message)))
                     .setPositiveButton(R.string.work_sync_dialog_yes,
                             SoundWorkSettingsController.UnifyWorkDialogFragment.this)
-                    .setNegativeButton(android.R.string.no, /* listener= */ null)
+                    .setNegativeButton(android.R.string.cancel, /* listener= */ null)
                     .create();
         }
 
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index c4a3159..265cee8 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -954,6 +954,9 @@
                 return;
             }
 
+            ConfirmDeviceCredentialUtils.hideImeImmediately(
+                    getActivity().getWindow().getDecorView());
+
             mPasswordEntryInputDisabler.setInputEnabled(false);
             setNextEnabled(false);
 
diff --git a/src/com/android/settings/password/ConfirmDeviceCredentialUtils.java b/src/com/android/settings/password/ConfirmDeviceCredentialUtils.java
index a5febeb..26c877f 100644
--- a/src/com/android/settings/password/ConfirmDeviceCredentialUtils.java
+++ b/src/com/android/settings/password/ConfirmDeviceCredentialUtils.java
@@ -25,6 +25,11 @@
 import android.content.IntentSender;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+
+import androidx.annotation.NonNull;
 
 import com.android.internal.widget.LockPatternUtils;
 
@@ -67,4 +72,16 @@
             utils.userPresent(userId);
         }
     }
+
+    /**
+     * Request hiding soft-keyboard before animating away credential UI, in case IME
+     * insets animation get delayed by dismissing animation.
+     * @param view used to get root {@link WindowInsets} and {@link WindowInsetsController}.
+     */
+    public static void hideImeImmediately(@NonNull View view) {
+        if (view.isAttachedToWindow()
+                && view.getRootWindowInsets().isVisible(WindowInsets.Type.ime())) {
+            view.getWindowInsetsController().hide(WindowInsets.Type.ime());
+        }
+    }
 }
diff --git a/src/com/android/settings/password/ConfirmLockPassword.java b/src/com/android/settings/password/ConfirmLockPassword.java
index a81f975..48c9aa8 100644
--- a/src/com/android/settings/password/ConfirmLockPassword.java
+++ b/src/com/android/settings/password/ConfirmLockPassword.java
@@ -493,6 +493,9 @@
         }
 
         private void startDisappearAnimation(final Intent intent) {
+            ConfirmDeviceCredentialUtils.hideImeImmediately(
+                    getActivity().getWindow().getDecorView());
+
             if (mDisappearing) {
                 return;
             }
diff --git a/src/com/android/settings/password/SetupChooseLockPassword.java b/src/com/android/settings/password/SetupChooseLockPassword.java
index cca50e0..5306719 100644
--- a/src/com/android/settings/password/SetupChooseLockPassword.java
+++ b/src/com/android/settings/password/SetupChooseLockPassword.java
@@ -22,7 +22,6 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
-import android.view.inputmethod.InputMethodManager;
 import android.widget.Button;
 
 import androidx.annotation.Nullable;
@@ -121,9 +120,8 @@
                         forFace,
                         forBiometrics);
 
-                InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(
-                        Context.INPUT_METHOD_SERVICE);
-                imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+                ConfirmDeviceCredentialUtils.hideImeImmediately(
+                        getActivity().getWindow().getDecorView());
 
                 dialog.show(getFragmentManager());
                 return;
diff --git a/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
new file mode 100644
index 0000000..20e8aca
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppAllServicesPreference.kt
@@ -0,0 +1,116 @@
+/*
+ * 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.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.os.Bundle
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+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.model.app.resolveActionForApp
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.firstOrNull
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.plus
+
+@Composable
+fun AppAllServicesPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val coroutineScope = rememberCoroutineScope()
+    val presenter = remember { AppAllServicesPresenter(context, app, coroutineScope) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.app_info_all_services_label)
+        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+            initialValue = stringResource(R.string.summary_placeholder),
+        )
+        override val onClick = presenter::startActivity
+    })
+}
+
+private class AppAllServicesPresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+    private val coroutineScope: CoroutineScope,
+) {
+    private val packageManager = context.packageManager
+
+    private val activityInfoFlow = flow {
+        emit(packageManager.resolveActionForApp(
+            app = app,
+            action = Intent.ACTION_VIEW_APP_FEATURES,
+            flags = PackageManager.GET_META_DATA,
+        ))
+    }.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
+
+    val isAvailableFlow = activityInfoFlow.map { it != null }
+
+    val summaryFlow = activityInfoFlow.map { activityInfo ->
+        activityInfo?.metaData?.getSummary() ?: ""
+    }.flowOn(Dispatchers.IO)
+
+    private fun Bundle.getSummary(): String {
+        val resources = try {
+            packageManager.getResourcesForApplication(app)
+        } catch (exception: PackageManager.NameNotFoundException) {
+            Log.d(TAG, "Name not found for the application.")
+            return ""
+        }
+
+        return try {
+            resources.getString(getInt(SUMMARY_METADATA_KEY))
+        } catch (exception: Resources.NotFoundException) {
+            Log.d(TAG, "Resource not found for summary string.")
+            ""
+        }
+    }
+
+    fun startActivity() {
+        coroutineScope.launch {
+            activityInfoFlow.firstOrNull()?.let { activityInfo ->
+                val intent = Intent(Intent.ACTION_VIEW_APP_FEATURES).apply {
+                    component = activityInfo.componentName
+                }
+                context.startActivityAsUser(intent, app.userHandle)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "AppAllServicesPresenter"
+        private const val SUMMARY_METADATA_KEY = "app_features_preference_summary"
+    }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
index a2164b2..2766dfe 100644
--- a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.util.Log
@@ -123,7 +122,7 @@
         Log.i(TAG, "handlePreferenceTreeClick():\n$this")
         AdvancedPowerUsageDetail.startBatteryDetailPage(
             context,
-            SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+            AppInfoSettingsProvider.METRICS_CATEGORY,
             this,
             Utils.formatPercentage(percentOfTotal, true),
             null,
@@ -141,7 +140,7 @@
             .setDestination(AdvancedPowerUsageDetail::class.java.name)
             .setTitleRes(R.string.battery_details_title)
             .setArguments(args)
-            .setSourceMetricsCategory(SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS)
+            .setSourceMetricsCategory(AppInfoSettingsProvider.METRICS_CATEGORY)
             .launch()
     }
 
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
index d13d108..328f8a5 100644
--- a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.net.NetworkStats
@@ -122,7 +121,7 @@
             AppDataUsage::class.java,
             app,
             context,
-            SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+            AppInfoSettingsProvider.METRICS_CATEGORY,
         )
     }
 
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index a1f3c1c..e3d0805 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -94,7 +94,7 @@
         AppButtons(packageInfoPresenter)
 
         AppSettingsPreference(app)
-        // TODO: all_services_settings
+        AppAllServicesPreference(app)
         // TODO: notification_settings
         AppPermissionPreference(app)
         AppStoragePreference(app)
@@ -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/AppSettingsPreference.kt b/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt
index babd607..ee0212a 100644
--- a/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt
@@ -19,8 +19,8 @@
 import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.Intent
+import android.content.pm.ActivityInfo
 import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager.ResolveInfoFlags
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
@@ -31,16 +31,17 @@
 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.model.app.resolveActionForApp
 import com.android.settingslib.spaprivileged.model.app.userHandle
-import com.android.settingslib.spaprivileged.model.app.userId
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.plus
 
 @Composable
 fun AppSettingsPreference(app: ApplicationInfo) {
@@ -63,39 +64,28 @@
     private val packageManager = context.packageManager
 
     private val intentFlow = flow {
-        emit(resolveIntent())
-    }.shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
+        emit(packageManager.resolveActionForApp(app, Intent.ACTION_APPLICATION_PREFERENCES))
+    }.shareIn(coroutineScope + Dispatchers.IO, SharingStarted.WhileSubscribed(), 1)
 
     val isAvailableFlow = intentFlow.map { it != null }
 
     fun startActivity() {
         coroutineScope.launch {
-            intentFlow.collect { intent ->
-                if (intent != null) {
-                    FeatureFactory.getFactory(context).metricsFeatureProvider
-                        .action(
-                            SettingsEnums.PAGE_UNKNOWN,
-                            SettingsEnums.ACTION_OPEN_APP_SETTING,
-                            AppInfoSettingsProvider.METRICS_CATEGORY,
-                            null,
-                            0,
-                        )
-                    context.startActivityAsUser(intent, app.userHandle)
-                }
-            }
+            intentFlow.firstOrNull()?.let(::startActivity)
         }
     }
 
-    private suspend fun resolveIntent(): Intent? = withContext(Dispatchers.IO) {
+    private fun startActivity(activityInfo: ActivityInfo) {
+        FeatureFactory.getFactory(context).metricsFeatureProvider.action(
+            SettingsEnums.PAGE_UNKNOWN,
+            SettingsEnums.ACTION_OPEN_APP_SETTING,
+            AppInfoSettingsProvider.METRICS_CATEGORY,
+            null,
+            0,
+        )
         val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES).apply {
-            `package` = app.packageName
+            component = activityInfo.componentName
         }
-        packageManager.resolveActivityAsUser(intent, ResolveInfoFlags.of(0), app.userId)
-            ?.activityInfo
-            ?.let { activityInfo ->
-                Intent(intent.action).apply {
-                    setClassName(activityInfo.packageName, activityInfo.name)
-                }
-            }
+        context.startActivityAsUser(intent, app.userHandle)
     }
 }
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/src/com/android/settings/vpn2/AppVpnInfo.java b/src/com/android/settings/vpn2/AppVpnInfo.java
index 079f8d5..f225470 100644
--- a/src/com/android/settings/vpn2/AppVpnInfo.java
+++ b/src/com/android/settings/vpn2/AppVpnInfo.java
@@ -10,7 +10,7 @@
  * Holds packageName:userId pairs without any heavyweight fields.
  * {@see ApplicationInfo}
  */
-class AppVpnInfo implements Comparable {
+class AppVpnInfo implements Comparable<AppVpnInfo> {
     public final int userId;
     public final String packageName;
 
@@ -20,12 +20,10 @@
     }
 
     @Override
-    public int compareTo(Object other) {
-        AppVpnInfo that = (AppVpnInfo) other;
-
-        int result = packageName.compareTo(that.packageName);
+    public int compareTo(AppVpnInfo other) {
+        int result = packageName.compareTo(other.packageName);
         if (result == 0) {
-            result = that.userId - userId;
+            result = other.userId - userId;
         }
         return result;
     }
diff --git a/src/com/android/settings/wifi/WifiDialog.java b/src/com/android/settings/wifi/WifiDialog.java
index 9d0ab47..a1ff1ac 100644
--- a/src/com/android/settings/wifi/WifiDialog.java
+++ b/src/com/android/settings/wifi/WifiDialog.java
@@ -117,6 +117,7 @@
         }
     }
 
+    @SuppressWarnings("MissingSuperCall") // TODO: Fix me
     @Override
     protected void onStart() {
         final ImageButton ssidScannerButton = findViewById(R.id.ssid_scanner_button);
diff --git a/src/com/android/settings/wifi/WifiDialog2.java b/src/com/android/settings/wifi/WifiDialog2.java
index 05dad3f..26951d1 100644
--- a/src/com/android/settings/wifi/WifiDialog2.java
+++ b/src/com/android/settings/wifi/WifiDialog2.java
@@ -137,6 +137,7 @@
         window.setAttributes(lp);
     }
 
+    @SuppressWarnings("MissingSuperCall") // TODO: Fix me
     @Override
     protected void onStart() {
         final ImageButton ssidScannerButton = findViewById(R.id.ssid_scanner_button);
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSettings.java b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
index bee265e..47dba76 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSettings.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSettings.java
@@ -41,6 +41,7 @@
 import com.android.settings.dashboard.RestrictedDashboardFragment;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.wifi.WifiUtils;
 import com.android.settingslib.TetherUtil;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
@@ -296,12 +297,12 @@
 
         @Override
         protected boolean isPageSearchEnabled(Context context) {
+            if (context == null || !WifiUtils.canShowWifiHotspot(context)) return false;
             return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
         }
 
         @Override
-        public List<AbstractPreferenceController> createPreferenceControllers(
-                Context context) {
+        public List<AbstractPreferenceController> createPreferenceControllers(Context context) {
             return buildPreferenceControllers(context, null /* listener */);
         }
     }
diff --git a/tests/robotests/src/com/android/settings/TetherSettingsTest.java b/tests/robotests/src/com/android/settings/TetherSettingsTest.java
index 71cb9d2..79814b3 100644
--- a/tests/robotests/src/com/android/settings/TetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/TetherSettingsTest.java
@@ -20,6 +20,8 @@
 import static android.content.Intent.ACTION_MEDIA_UNSHARED;
 import static android.hardware.usb.UsbManager.ACTION_USB_STATE;
 
+import static com.android.settings.wifi.WifiUtils.setCanShowWifiHotspotCached;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -93,6 +95,7 @@
                 any(String.class), anyInt(), any(UserHandle.class));
 
         setupIsTetherAvailable(true);
+        setCanShowWifiHotspotCached(true);
 
         when(mTetheringManager.getTetherableUsbRegexs()).thenReturn(new String[0]);
         when(mTetheringManager.getTetherableBluetoothRegexs()).thenReturn(new String[0]);
@@ -124,6 +127,16 @@
     }
 
     @Test
+    public void getNonIndexableKeys_canNotShowWifiHotspot_containsWifiTether() {
+        setCanShowWifiHotspotCached(false);
+        setupIsTetherAvailable(true);
+
+        List<String> keys = TetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext);
+
+        assertThat(keys).contains(TetherSettings.KEY_WIFI_TETHER);
+    }
+
+    @Test
     public void testTetherNonIndexableKeys_usbNotAvailable_usbKeyReturned() {
         when(mTetheringManager.getTetherableUsbRegexs()).thenReturn(new String[0]);
 
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 25548f8..a596d93 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -398,6 +398,7 @@
             // do nothing
         }
 
+        @SuppressWarnings("MissingSuperCall")
         @Override
         public void onDestroyView() {
             // do nothing
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
index 4e74048..4bb5935 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragmentTest.java
@@ -489,6 +489,7 @@
             // do nothing
         }
 
+        @SuppressWarnings("MissingSuperCall")
         @Override
         public void onDestroyView() {
             // do nothing
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
index c95a509..f4cb3f3 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java
@@ -18,8 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
@@ -133,32 +131,8 @@
     }
 
     @Test
-    public void updateBattery_noBatteryStats_summaryNo() {
-        mController.displayPreference(mScreen);
-
-        mController.updateBattery();
-
-        assertThat(mBatteryPreference.getSummary())
-            .isEqualTo("No battery use since last full charge");
-    }
-
-    @Test
-    public void updateBattery_hasBatteryStats_summaryPercent() {
-        mController.mBatteryUsageStats = mBatteryUsageStats;
-        mController.mUidBatteryConsumer = mUidBatteryConsumer;
-        doReturn(BATTERY_LEVEL).when(mBatteryUtils).calculateBatteryPercent(anyDouble(),
-                anyDouble(), anyInt());
-        mController.displayPreference(mScreen);
-
-        mController.updateBattery();
-
-        assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use since last full charge");
-    }
-
-    @Test
     public void updateBatteryWithDiffEntry_noConsumePower_summaryNo() {
         mController.displayPreference(mScreen);
-        mController.mIsChartGraphEnabled = true;
 
         mController.updateBatteryWithDiffEntry();
 
@@ -169,7 +143,6 @@
     @Test
     public void updateBatteryWithDiffEntry_withConsumePower_summaryPercent() {
         mController.displayPreference(mScreen);
-        mController.mIsChartGraphEnabled = true;
         mBatteryDiffEntry.mConsumePower = 1;
         mController.mBatteryDiffEntry = mBatteryDiffEntry;
         when(mBatteryDiffEntry.getPercentOfTotal()).thenReturn(60.0);
diff --git a/tests/robotests/src/com/android/settings/applications/manageapplications/AppFilterItemTest.java b/tests/robotests/src/com/android/settings/applications/manageapplications/AppFilterItemTest.java
index 0ef94ec..36c0ab8 100644
--- a/tests/robotests/src/com/android/settings/applications/manageapplications/AppFilterItemTest.java
+++ b/tests/robotests/src/com/android/settings/applications/manageapplications/AppFilterItemTest.java
@@ -50,6 +50,7 @@
         assertThat(item).isEqualTo(item2);
     }
 
+    @SuppressWarnings("SelfComparison")
     @Test
     public void compare_sameContent_return0() {
         final AppFilterItem item = AppFilterRegistry.getInstance().get(FILTER_APPS_USAGE_ACCESS);
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/development/bluetooth/BluetoothSampleRateDialogPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothSampleRateDialogPreferenceControllerTest.java
index fa4a79c..b37a212 100644
--- a/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothSampleRateDialogPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/development/bluetooth/BluetoothSampleRateDialogPreferenceControllerTest.java
@@ -141,10 +141,9 @@
 
     @Test
     public void getSelectableIndex_verifyList() {
-        List<BluetoothCodecConfig> mCodecConfigs = new ArrayList() {{
-                    add(mCodecConfigAAC);
-                    add(mCodecConfigSBC);
-                }};
+        List<BluetoothCodecConfig> mCodecConfigs = List.of(
+                mCodecConfigAAC,
+                mCodecConfigSBC);
         mCodecStatus = new BluetoothCodecStatus.Builder()
                 .setCodecConfig(mCodecConfigAAC)
                 .setCodecsSelectableCapabilities(mCodecConfigs)
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index f408c4b..45e8089 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -154,8 +154,6 @@
         doReturn(APP_LABEL).when(mBundle).getString(nullable(String.class));
         when(mFragment.getArguments()).thenReturn(mBundle);
         doReturn(mLoaderManager).when(mFragment).getLoaderManager();
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(true);
 
         ShadowEntityHeaderController.setUseMock(mEntityHeaderController);
         doReturn(mEntityHeaderController).when(mEntityHeaderController)
@@ -272,144 +270,6 @@
     }
 
     @Test
-    public void initHeader_noUsageTimeAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, /* value */ 0);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("No usage from last full charge");
-    }
-
-    @Test
-    public void initHeader_bgTwoMinFgZeroAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        final long backgroundTimeTwoMinutes = 120000;
-        final long foregroundTimeZero = 0;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeTwoMinutes);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeZero);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("2 min background from last full charge");
-    }
-
-    @Test
-    public void initHeader_bgLessThanAMinFgZeroAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        final long backgroundTimeLessThanAMinute = 59999;
-        final long foregroundTimeZero = 0;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(
-                AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeLessThanAMinute);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeZero);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("Background less than a minute from last full charge");
-    }
-
-    @Test
-    public void initHeader_totalUsageLessThanAMinAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        final long backgroundTimeLessThanHalfMinute = 20000;
-        final long foregroundTimeLessThanHalfMinute = 20000;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(
-                AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeLessThanHalfMinute);
-        bundle.putLong(
-                AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeLessThanHalfMinute);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("Total less than a minute from last full charge");
-    }
-
-    @Test
-    public void initHeader_TotalAMinutesBgLessThanAMinAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        final long backgroundTimeZero = 59999;
-        final long foregroundTimeTwoMinutes = 1;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeZero);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeTwoMinutes);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("1 min total • background less than a minute\nfrom last full charge");
-    }
-
-    @Test
-    public void initHeader_TotalAMinBackgroundZeroAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-        final long backgroundTimeZero = 0;
-        final long foregroundTimeAMinutes = 60000;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeZero);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeAMinutes);
-        when(mFragment.getArguments()).thenReturn(bundle);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("1 min total from last full charge");
-    }
-
-    @Test
-    public void initHeader_fgTwoMinBgFourMinAndGraphDisabled_hasCorrectSummary() {
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-        final long backgroundTimeFourMinute = 240000;
-        final long foregroundTimeTwoMinutes = 120000;
-        Bundle bundle = new Bundle(2);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, backgroundTimeFourMinute);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, foregroundTimeTwoMinutes);
-        when(mFragment.getArguments()).thenReturn(bundle);
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue().toString())
-                .isEqualTo("6 min total • 4 min background\nfrom last full charge");
-    }
-
-    @Test
     public void initHeader_noUsageTime_hasCorrectSummary() {
         Bundle bundle = new Bundle(2);
         bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, /* value */ 0);
@@ -619,23 +479,6 @@
     }
 
     @Test
-    public void initHeader_systemUidWithChartIsDisabled_nullSummary() {
-        Bundle bundle = new Bundle(3);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
-        bundle.putLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME, 120000);
-        bundle.putInt(AdvancedPowerUsageDetail.EXTRA_UID, Process.SYSTEM_UID);
-        when(mFragment.getArguments()).thenReturn(bundle);
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mContext))
-                .thenReturn(false);
-
-        mFragment.initHeader();
-
-        ArgumentCaptor<CharSequence> captor = ArgumentCaptor.forClass(CharSequence.class);
-        verify(mEntityHeaderController).setSummary(captor.capture());
-        assertThat(captor.getValue()).isNull();
-    }
-
-    @Test
     public void initHeader_systemUidWithChartIsEnabled_notNullSummary() {
         Bundle bundle = new Bundle(3);
         bundle.putLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME, 240000);
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java
deleted file mode 100644
index 5717857..0000000
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryLoaderTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.settings.fuelgauge.batteryusage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.doReturn;
-
-import android.content.Context;
-
-import com.android.settings.testutils.FakeFeatureFactory;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@RunWith(RobolectricTestRunner.class)
-public final class BatteryHistoryLoaderTest {
-
-    private Context mContext;
-    private FakeFeatureFactory mFeatureFactory;
-    private BatteryHistoryLoader mBatteryHistoryLoader;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = RuntimeEnvironment.application;
-        mFeatureFactory = FakeFeatureFactory.setupForTest();
-        mBatteryHistoryLoader = new BatteryHistoryLoader(mContext);
-    }
-
-    @Test
-    public void testLoadIBackground_returnsMapFromPowerFeatureProvider() {
-        final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
-        doReturn(batteryHistoryMap).when(mFeatureFactory.powerUsageFeatureProvider)
-                .getBatteryHistorySinceLastFullCharge(mContext);
-
-        assertThat(mBatteryHistoryLoader.loadInBackground())
-                .isSameInstanceAs(batteryHistoryMap);
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreferenceTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreferenceTest.java
index 7017c43..e14ead5 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistoryPreferenceTest.java
@@ -15,10 +15,6 @@
  */
 package com.android.settings.fuelgauge.batteryusage;
 
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -32,7 +28,6 @@
 
 import com.android.settings.R;
 import com.android.settings.fuelgauge.BatteryInfo;
-import com.android.settings.widget.UsageView;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -44,8 +39,6 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class BatteryHistoryPreferenceTest {
-
-    private static final String TEST_STRING = "test";
     @Mock
     private PreferenceViewHolder mViewHolder;
     @Mock
@@ -53,9 +46,9 @@
     @Mock
     private TextView mTextView;
     @Mock
-    private UsageView mUsageView;
+    private BatteryChartView mDailyChartView;
     @Mock
-    private View mLabelView;
+    private BatteryChartView mHourlyChartView;
     private BatteryHistoryPreference mBatteryHistoryPreference;
 
     @Before
@@ -63,44 +56,23 @@
         MockitoAnnotations.initMocks(this);
         final Context context = RuntimeEnvironment.application;
         final View itemView =
-                LayoutInflater.from(context).inflate(R.layout.battery_usage_graph, null);
+                LayoutInflater.from(context).inflate(R.layout.battery_chart_graph, null);
 
         mBatteryHistoryPreference = new BatteryHistoryPreference(context, null);
         mBatteryHistoryPreference.mBatteryInfo = mBatteryInfo;
         mViewHolder = spy(PreferenceViewHolder.createInstanceForTests(itemView));
-        when(mViewHolder.findViewById(R.id.battery_usage)).thenReturn(mUsageView);
-        when(mViewHolder.findViewById(R.id.charge)).thenReturn(mTextView);
-        when(mUsageView.findViewById(anyInt())).thenReturn(mLabelView);
+        when(mViewHolder.findViewById(R.id.daily_battery_chart)).thenReturn(mDailyChartView);
+        when(mViewHolder.findViewById(R.id.hourly_battery_chart)).thenReturn(mHourlyChartView);
+        when(mViewHolder.findViewById(R.id.companion_text)).thenReturn(mTextView);
     }
 
     @Test
     public void testOnBindViewHolder_updateBatteryUsage() {
         mBatteryHistoryPreference.onBindViewHolder(mViewHolder);
 
-        verify(mViewHolder).findViewById(R.id.battery_usage);
-        verify(mTextView).setText(nullable(String.class));
-        verify(mBatteryInfo).bindHistory(mUsageView);
-    }
-
-    @Test
-    public void testSetBottomSummary_updatesBottomSummaryTextIfSet() {
-        mBatteryHistoryPreference.setBottomSummary(TEST_STRING);
-        mBatteryHistoryPreference.onBindViewHolder(mViewHolder);
-
-        TextView view = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
-        assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
-        assertThat(view.getText()).isEqualTo(TEST_STRING);
-        assertThat(mBatteryHistoryPreference.mHideSummary).isFalse();
-    }
-
-    @Test
-    public void testSetBottomSummary_leavesBottomSummaryTextBlankIfNotSet() {
-        mBatteryHistoryPreference.hideBottomSummary();
-        mBatteryHistoryPreference.onBindViewHolder(mViewHolder);
-
-        TextView view = (TextView) mViewHolder.findViewById(R.id.bottom_summary);
-        assertThat(view.getVisibility()).isEqualTo(View.GONE);
-        assertThat(view.getText()).isEqualTo("");
-        assertThat(mBatteryHistoryPreference.mHideSummary).isTrue();
+        verify(mViewHolder).findViewById(R.id.daily_battery_chart);
+        verify(mDailyChartView).setCompanionTextView(mTextView);
+        verify(mViewHolder).findViewById(R.id.hourly_battery_chart);
+        verify(mHourlyChartView).setCompanionTextView(mTextView);
     }
 }
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/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
index 61d4efa..713c2ee 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
@@ -20,9 +20,11 @@
 
 import static org.junit.Assert.assertThrows;
 
+import android.app.Application;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.database.Cursor;
 import android.net.Uri;
 
@@ -37,9 +39,11 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
 
 import java.time.Duration;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /** Tests for {@link BatteryUsageContentProvider}. */
 @RunWith(RobolectricTestRunner.class)
@@ -301,6 +305,11 @@
         final String actualPackageName3 = cursor.getString(packageNameIndex);
         assertThat(actualPackageName3).isEqualTo(packageName3);
         cursor.close();
-        // TODO: add verification for recheck broadcast.
+        // Verifies the broadcast intent.
+        TimeUnit.SECONDS.sleep(1);
+        final List<Intent> intents = Shadows.shadowOf((Application) mContext).getBroadcastIntents();
+        assertThat(intents).hasSize(1);
+        assertThat(intents.get(0).getAction()).isEqualTo(
+                BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
new file mode 100644
index 0000000..e42d6f5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BootBroadcastReceiverTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.AlarmManager;
+import android.app.Application;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowAlarmManager;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/** Tests of {@link BootBroadcastReceiver}. */
+@RunWith(RobolectricTestRunner.class)
+public final class BootBroadcastReceiverTest {
+    private Context mContext;
+    private BatteryStateDao mDao;
+    private BootBroadcastReceiver mReceiver;
+    private ShadowAlarmManager mShadowAlarmManager;
+    private PeriodicJobManager mPeriodicJobManager;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
+        mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
+        ShadowAlarmManager.setCanScheduleExactAlarms(true);
+        mReceiver = new BootBroadcastReceiver();
+
+        // Inserts fake data into database for testing.
+        final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, Clock.systemUTC().millis(), "com.android.systemui");
+        mDao = database.batteryStateDao();
+    }
+
+    @After
+    public void tearDown() {
+        mPeriodicJobManager.reset();
+    }
+
+    @Test
+    public void onReceive_withWorkProfile_notRefreshesJob() {
+        BatteryTestUtils.setWorkProfile(mContext);
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
+
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_withMyPackageReplacedIntent_refreshesJob() {
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_MY_PACKAGE_REPLACED));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_withMyPackageUnsuspendIntent_refreshesJob() {
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_MY_PACKAGE_UNSUSPENDED));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_withBootCompletedIntent_refreshesJob() {
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_withSetupWizardIntent_refreshesJob() {
+        mReceiver.onReceive(
+                mContext, new Intent(BootBroadcastReceiver.ACTION_SETUP_WIZARD_FINISHED));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_withRecheckIntent_refreshesJob() {
+        mReceiver.onReceive(
+                mContext, new Intent(BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_unexpectedIntent_notRefreshesJob() {
+        mReceiver.onReceive(mContext, new Intent("invalid intent action"));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_nullIntent_notRefreshesJob() {
+        mReceiver.onReceive(mContext, /*intent=*/ null);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_containsExpiredData_clearsExpiredDataFromDatabase()
+            throws InterruptedException {
+        insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY);
+
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).hasSize(1);
+    }
+
+    @Test
+    public void onReceive_withoutExpiredData_notClearsExpiredDataFromDatabase()
+            throws InterruptedException {
+        insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY - 1);
+
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_BOOT_COMPLETED));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).hasSize(3);
+    }
+
+    @Test
+    public void onReceive_withTimeChangedIntent_clearsAllDataAndRefreshesJob()
+            throws InterruptedException {
+        mReceiver.onReceive(mContext, new Intent(Intent.ACTION_TIME_CHANGED));
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).isEmpty();
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void invokeJobRecheck_broadcastsIntent() {
+        BootBroadcastReceiver.invokeJobRecheck(mContext);
+
+        final List<Intent> intents =
+                Shadows.shadowOf((Application) mContext).getBroadcastIntents();
+        assertThat(intents).hasSize(1);
+        assertThat(intents.get(0).getAction()).isEqualTo(
+                BootBroadcastReceiver.ACTION_PERIODIC_JOB_RECHECK);
+    }
+
+    private void insertExpiredData(int shiftDay) {
+        final long expiredTimeInMs =
+                Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, expiredTimeInMs - 1, "com.android.systemui");
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, expiredTimeInMs, "com.android.systemui");
+        // Ensures the testing environment is correct.
+        assertThat(mDao.getAllAfter(0)).hasSize(3);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
index 2ae73b1..46ef7bc 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -336,21 +336,6 @@
         // Verifies the fake data is cleared out.
         assertThat(entryList.get(0).getPackageName())
                 .isNotEqualTo(ConvertUtils.FAKE_PACKAGE_NAME);
-
-        // Adds lacked data into the battery history map.
-        final int remainingSize = 25 - batteryHistoryKeys.length;
-        for (int index = 0; index < remainingSize; index++) {
-            batteryHistoryMap.put(105L + index + 1, new HashMap<>());
-        }
-        when(mPowerUsageFeatureProvider.getBatteryHistorySinceLastFullCharge(mContext))
-                .thenReturn(batteryHistoryMap);
-
-        final List<BatteryDiffEntry> batteryDiffEntryList =
-                BatteryChartPreferenceController.getAppBatteryUsageData(mContext);
-
-        assertThat(batteryDiffEntryList).isNotEmpty();
-        final BatteryDiffEntry resultEntry = batteryDiffEntryList.get(0);
-        assertThat(resultEntry.getPackageName()).isEqualTo("package2");
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManagerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManagerTest.java
new file mode 100644
index 0000000..2ee21f5
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobManagerTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.AlarmManager;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.testutils.FakeClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowAlarmManager;
+
+import java.time.Duration;
+
+/** Tests of {@link PeriodicJobManager}. */
+@RunWith(RobolectricTestRunner.class)
+public final class PeriodicJobManagerTest {
+    private Context mContext;
+    private ShadowAlarmManager mShadowAlarmManager;
+    private PeriodicJobManager mPeriodicJobManager;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
+        mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
+        ShadowAlarmManager.setCanScheduleExactAlarms(true);
+    }
+
+    @After
+    public void tearDown() {
+        mPeriodicJobManager.reset();
+    }
+
+    @Test
+    public void refreshJob_refreshesAlarmJob() {
+        mPeriodicJobManager.refreshJob();
+
+        final ShadowAlarmManager.ScheduledAlarm alarm =
+                mShadowAlarmManager.peekNextScheduledAlarm();
+        // Verifies the alarm manager type.
+        assertThat(alarm.type).isEqualTo(AlarmManager.RTC_WAKEUP);
+        // Verifies there is pending intent in the alarm.
+        assertThat(alarm.operation).isNotNull();
+    }
+
+    @Test
+    public void refreshJob_withoutPermission_notRefreshesAlarmJob() {
+        ShadowAlarmManager.setCanScheduleExactAlarms(false);
+
+        mPeriodicJobManager.refreshJob();
+
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void getTriggerAtMillis_withoutOffset_returnsExpectedResult() {
+        long timeSlotUnit = PeriodicJobManager.DATA_FETCH_INTERVAL_MINUTE;
+        // Sets the current time.
+        Duration currentTimeDuration =
+                Duration.ofMinutes(timeSlotUnit * 2);
+        FakeClock fakeClock = new FakeClock();
+        fakeClock.setCurrentTime(currentTimeDuration);
+
+        assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock))
+                .isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
+    }
+
+    @Test
+    public void getTriggerAtMillis_withOffset_returnsExpectedResult() {
+        long timeSlotUnit = PeriodicJobManager.DATA_FETCH_INTERVAL_MINUTE;
+        // Sets the current time.
+        Duration currentTimeDuration = Duration.ofMinutes(timeSlotUnit * 2);
+        FakeClock fakeClock = new FakeClock();
+        fakeClock.setCurrentTime(
+                currentTimeDuration.plusMinutes(1L).plusMillis(51L));
+
+        assertThat(PeriodicJobManager.getTriggerAtMillis(fakeClock))
+                .isEqualTo(currentTimeDuration.plusMinutes(timeSlotUnit).toMillis());
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
new file mode 100644
index 0000000..b14ca80
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PeriodicJobReceiverTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.fuelgauge.batteryusage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
+import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
+import com.android.settings.testutils.BatteryTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowAlarmManager;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+
+/** Tests of {@link PeriodicJobReceiver}. */
+@RunWith(RobolectricTestRunner.class)
+public final class PeriodicJobReceiverTest {
+    private static final Intent JOB_UPDATE_INTENT =
+            new Intent(PeriodicJobReceiver.ACTION_PERIODIC_JOB_UPDATE);
+
+    private Context mContext;
+    private BatteryStateDao mDao;
+    private PeriodicJobReceiver mReceiver;
+    private PeriodicJobManager mPeriodicJobManager;
+    private ShadowAlarmManager mShadowAlarmManager;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mPeriodicJobManager = PeriodicJobManager.getInstance(mContext);
+        mShadowAlarmManager = shadowOf(mContext.getSystemService(AlarmManager.class));
+        ShadowAlarmManager.setCanScheduleExactAlarms(true);
+        mReceiver = new PeriodicJobReceiver();
+
+        // Inserts fake data into database for testing.
+        final BatteryStateDatabase database = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, Clock.systemUTC().millis(), "com.android.systemui");
+        mDao = database.batteryStateDao();
+    }
+
+    @After
+    public void tearDown() {
+        mPeriodicJobManager.reset();
+    }
+
+    @Test
+    public void onReceive_validAction_refreshesJob() {
+        mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNotNull();
+    }
+
+    @Test
+    public void onReceive_invalidAction_notRefreshesJob() {
+        mReceiver.onReceive(mContext, new Intent("invalid request update intent"));
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_nullIntent_notRefreshesJob() {
+        mReceiver.onReceive(mContext, /*intent=*/ null);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    @Test
+    public void onReceive_containsExpiredData_clearsExpiredDataFromDatabase()
+            throws InterruptedException {
+        insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY);
+
+        mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).hasSize(1);
+    }
+
+    @Test
+    public void onReceive_withoutExpiredData_notClearsExpiredDataFromDatabase()
+            throws InterruptedException {
+        insertExpiredData(/*shiftDay=*/ DatabaseUtils.DATA_RETENTION_INTERVAL_DAY - 1);
+
+        mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
+
+        TimeUnit.MILLISECONDS.sleep(100);
+        assertThat(mDao.getAllAfter(0)).hasSize(3);
+    }
+
+    @Test
+    public void onReceive_inWorkProfileMode_notRefreshesJob() {
+        BatteryTestUtils.setWorkProfile(mContext);
+        mReceiver.onReceive(mContext, JOB_UPDATE_INTENT);
+        assertThat(mShadowAlarmManager.peekNextScheduledAlarm()).isNull();
+    }
+
+    private void insertExpiredData(int shiftDay) {
+        final long expiredTimeInMs =
+                Clock.systemUTC().millis() - Duration.ofDays(shiftDay).toMillis();
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, expiredTimeInMs - 1, "com.android.systemui");
+        BatteryTestUtils.insertDataToBatteryStateDatabase(
+                mContext, expiredTimeInMs, "com.android.systemui");
+        // Ensures the testing environment is correct.
+        assertThat(mDao.getAllAfter(0)).hasSize(3);
+    }
+
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
index c049497..5cd6db0 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/PowerUsageSummaryTest.java
@@ -113,8 +113,6 @@
         when(mFragment.getActivity()).thenReturn(mSettingsActivity);
         when(mFeatureFactory.powerUsageFeatureProvider.getAdditionalBatteryInfoIntent())
                 .thenReturn(sAdditionalBatteryInfoIntent);
-        when(mFeatureFactory.powerUsageFeatureProvider.isChartGraphEnabled(mRealContext))
-                .thenReturn(true);
         mFragment.mBatteryUtils = Mockito.spy(new BatteryUtils(mRealContext));
         ReflectionHelpers.setField(mFragment, "mVisibilityLoggerMixin", mVisibilityLoggerMixin);
         ReflectionHelpers.setField(mFragment, "mBatteryBroadcastReceiver",
diff --git a/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java b/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
index cef43dd..e210c5f 100644
--- a/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
+++ b/tests/robotests/src/com/android/settings/password/ConfirmCredentialTest.java
@@ -43,16 +43,16 @@
 
         // Launch only one instance at a time.
         assertThat(LastTryDialog.show(
-                fm, "title", mContext.getString(android.R.string.yes),
+                fm, "title", mContext.getString(android.R.string.ok),
                 android.R.string.ok, false)).isTrue();
         assertThat(LastTryDialog.show(
-                fm, "title", mContext.getString(android.R.string.yes),
+                fm, "title", mContext.getString(android.R.string.ok),
                 android.R.string.ok, false)).isFalse();
 
         // After cancelling, the dialog should be re-shown when asked for.
         LastTryDialog.hide(fm);
         assertThat(LastTryDialog.show(
-                fm, "title", mContext.getString(android.R.string.yes),
+                fm, "title", mContext.getString(android.R.string.ok),
                 android.R.string.ok, false)).isTrue();
     }
 }
diff --git a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
index 6d3b879..7f8a06d 100644
--- a/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/wifi/tether/WifiTetherSettingsTest.java
@@ -37,6 +37,7 @@
 import android.net.wifi.WifiManager;
 import android.os.Bundle;
 import android.os.UserManager;
+import android.util.FeatureFlagUtils;
 import android.widget.TextView;
 
 import androidx.fragment.app.FragmentActivity;
@@ -44,6 +45,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.R;
+import com.android.settings.core.FeatureFlags;
 import com.android.settings.dashboard.RestrictedDashboardFragment;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowFragment;
@@ -93,6 +95,7 @@
 
     @Before
     public void setUp() {
+        FeatureFlagUtils.setEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE, false);
         setCanShowWifiHotspotCached(true);
         doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
         doReturn(mConnectivityManager)
@@ -218,6 +221,22 @@
         assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
     }
 
+    @Test
+    public void isPageSearchEnabled_canShowWifiHotspot_returnTrue() {
+        setCanShowWifiHotspotCached(true);
+
+        assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(mContext))
+                .isTrue();
+    }
+
+    @Test
+    public void isPageSearchEnabled_canNotShowWifiHotspot_returnFalse() {
+        setCanShowWifiHotspotCached(false);
+
+        assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.isPageSearchEnabled(mContext))
+                .isFalse();
+    }
+
     private void spyWifiTetherSettings() {
         mWifiTetherSettings = spy(new WifiTetherSettings(mWifiRestriction));
         final FragmentActivity activity = mock(FragmentActivity.class);
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt
new file mode 100644
index 0000000..9846e3f
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppAllServicesPreferenceTest.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.content.pm.ResolveInfo
+import android.content.res.Resources
+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.core.os.bundleOf
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.doThrow
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppAllServicesPreferenceTest {
+    @JvmField
+    @Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    @Mock
+    private lateinit var resources: Resources
+
+    @Before
+    fun setUp() {
+        whenever(context.packageManager).thenReturn(packageManager)
+        whenever(packageManager.getResourcesForApplication(APP)).thenReturn(resources)
+        doThrow(Resources.NotFoundException()).`when`(resources).getString(anyInt())
+    }
+
+    private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
+        whenever(
+            packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
+        ).thenReturn(resolveInfo)
+    }
+
+    @Test
+    fun callResolveActivityAsUser_withIntent() {
+        mockResolveActivityAsUser(null)
+
+        setContent()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(packageManager).resolveActivityAsUser(
+            intentCaptor.capture(), any<ResolveInfoFlags>(), eq(APP.userId)
+        )
+        val intent = intentCaptor.value
+        assertThat(intent.action).isEqualTo(Intent.ACTION_VIEW_APP_FEATURES)
+        assertThat(intent.`package`).isEqualTo(PACKAGE_NAME)
+    }
+
+    @Test
+    fun noResolveInfo_notDisplayed() {
+        mockResolveActivityAsUser(null)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun noAllServicesActivity_notDisplayed() {
+        mockResolveActivityAsUser(ResolveInfo())
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun hasAllServicesActivity_displayed() {
+        mockResolveActivityAsUser(RESOLVE_INFO)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.app_info_all_services_label))
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun hasSummary() {
+        mockResolveActivityAsUser(RESOLVE_INFO)
+        doReturn(SUMMARY).`when`(resources).getString(SUMMARY_RES_ID)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+    }
+
+    @Test
+    fun whenClick_startActivity() {
+        mockResolveActivityAsUser(RESOLVE_INFO)
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
+        val intent = intentCaptor.value
+        assertThat(intent.action).isEqualTo(Intent.ACTION_VIEW_APP_FEATURES)
+        assertThat(intent.component).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AppAllServicesPreference(APP)
+            }
+        }
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val ACTIVITY_NAME = "activityName"
+        const val UID = 123
+        const val SUMMARY_RES_ID = 456
+        const val SUMMARY = "summary"
+
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+        val RESOLVE_INFO = ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = PACKAGE_NAME
+                name = ACTIVITY_NAME
+                metaData = bundleOf(
+                    "app_features_preference_summary" to SUMMARY_RES_ID
+                )
+            }
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
index 0657435..01113cc 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.CompositionLocalProvider
@@ -159,7 +158,7 @@
         ExtendedMockito.verify {
             AdvancedPowerUsageDetail.startBatteryDetailPage(
                 context,
-                SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+                AppInfoSettingsProvider.METRICS_CATEGORY,
                 batteryDiffEntry,
                 "10%",
                 null,
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt
index 22876d1..174f508 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import android.net.NetworkTemplate
@@ -164,7 +163,7 @@
                 AppDataUsage::class.java,
                 APP,
                 context,
-                SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+                AppInfoSettingsProvider.METRICS_CATEGORY,
             )
         }
     }
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
+        }
+    }
+}
diff --git a/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java b/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
index 735992b..0c2a33f 100644
--- a/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
+++ b/tests/uitests/src/com/android/settings/ui/SoundSettingsTest.java
@@ -29,7 +29,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.Suppress;
 
-import java.util.HashMap;
+import java.util.Map;
 
 public class SoundSettingsTest extends InstrumentationTestCase {
     private static final String PAGE = Settings.ACTION_SOUND_SETTINGS;
@@ -40,60 +40,54 @@
     private SettingsHelper mHelper;
 
 
-    private HashMap ringtoneSounds = new HashMap<String, String>() {{
-        put("angler","Dione");
-        put("bullhead","Dione");
-        put("marlin","Spaceship");
-        put("sailfish","Spaceship");
-        put("walleye","Copycat");
-        put("taimen","Copycat");
-    }};
+    private final Map<String, String> ringtoneSounds = Map.of(
+            "angler", "Dione",
+            "bullhead", "Dione",
+            "marlin", "Spaceship",
+            "sailfish", "Spaceship",
+            "walleye", "Copycat",
+            "taimen", "Copycat");
 
-    private HashMap ringtoneCodes = new HashMap<String, String>() {{
-        put("angler","38");
-        put("bullhead","38");
-        put("marlin","37");
-        put("sailfish","37");
-        put("walleye","26");
-        put("taimen","26");
-    }};
+    private final Map<String, String> ringtoneCodes = Map.of(
+            "angler", "38",
+            "bullhead", "38",
+            "marlin", "37",
+            "sailfish", "37",
+            "walleye", "26",
+            "taimen", "26");
 
-    private HashMap alarmSounds = new HashMap<String, String>() {{
-        put("angler","Awaken");
-        put("bullhead","Awaken");
-        put("marlin","Bounce");
-        put("sailfish","Bounce");
-        put("walleye","Cuckoo clock");
-        put("taimen","Cuckoo clock");
-    }};
+    private final Map<String, String> alarmSounds = Map.of(
+            "angler", "Awaken",
+            "bullhead", "Awaken",
+            "marlin", "Bounce",
+            "sailfish", "Bounce",
+            "walleye", "Cuckoo clock",
+            "taimen", "Cuckoo clock");
 
-    private HashMap alarmCodes = new HashMap<String, String>() {{
-        put("angler","6");
-        put("bullhead","6");
-        put("marlin","49");
-        put("sailfish","49");
-        put("walleye","15");
-        put("taimen","15");
-    }};
+    private final Map<String, String> alarmCodes = Map.of(
+            "angler", "6",
+            "bullhead", "6",
+            "marlin", "49",
+            "sailfish", "49",
+            "walleye", "15",
+            "taimen", "15");
 
-    private HashMap notificationSounds = new HashMap<String, String>() {{
-        put("angler","Ceres");
-        put("bullhead","Ceres");
-        put("marlin","Trill");
-        put("sailfish","Trill");
-        put("walleye","Pipes");
-        put("taimen","Pipes");
-    }};
+    private final Map<String, String> notificationSounds = Map.of(
+            "angler", "Ceres",
+            "bullhead", "Ceres",
+            "marlin", "Trill",
+            "sailfish", "Trill",
+            "walleye", "Pipes",
+            "taimen", "Pipes");
 
 
-    private HashMap notificationCodes = new HashMap<String, String>() {{
-        put("angler","26");
-        put("bullhead","26");
-        put("marlin","57");
-        put("sailfish","57");
-        put("walleye","69");
-        put("taimen","69");
-    }};
+    private final Map<String, String> notificationCodes = Map.of(
+            "angler", "26",
+            "bullhead", "26",
+            "marlin", "57",
+            "sailfish", "57",
+            "walleye", "69",
+            "taimen", "69");
 
     @Override
     public void setUp() throws Exception {