Merge "[Audiosharing] Add prefix to device list item text in adapter." into main
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2a45624..2b49148 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4200,8 +4200,23 @@
                        android:value="TogglePermissionAppList/MediaRoutingControl"/>
             <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
                        android:value="@string/menu_key_apps"/>
-            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                       android:value="true" />
+        </activity-alias>
+
+        <activity-alias
+            android:name="AppMediaRoutingControlActivity"
+            android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
+            android:exported="true"
+            android:targetActivity=".spa.SpaAppBridgeActivity"
+            android:label="@string/media_routing_control_title">
+            <intent-filter android:priority="1">
+                <action android:name="android.settings.REQUEST_MEDIA_ROUTING_CONTROL" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:scheme="package" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.spa.DESTINATION"
+                       android:value="TogglePermissionAppInfoPage/MediaRoutingControl"/>
+            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
+                       android:value="@string/menu_key_apps"/>
         </activity-alias>
 
         <!-- Keep compatibility with old WebView-picker implementation -->
diff --git a/res/drawable/ic_calls_sms.xml b/res/drawable/ic_calls_sms.xml
new file mode 100644
index 0000000..2033e8f
--- /dev/null
+++ b/res/drawable/ic_calls_sms.xml
@@ -0,0 +1,29 @@
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?android:attr/colorControlNormal"
+    >
+
+    <path
+        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.17,14.85l-3.26-0.65c-0.33-0.07-0.67,0.04-0.9,0.27l-2.62,2.62c-2.75-1.49-5.01-3.75-6.5-6.5l2.62-2.62 c0.24-0.24,0.34-0.58,0.27-0.9L9.13,3.82c-0.09-0.47-0.5-0.8-0.98-0.8H4c-0.56,0-1.03,0.47-1,1.03c0.17,2.91,1.04,5.63,2.43,8.01 c1.57,2.69,3.81,4.93,6.5,6.5c2.38,1.39,5.1,2.26,8.01,2.43c0.56,0.03,1.03-0.44,1.03-1v-4.15C20.97,15.36,20.64,14.95,20.17,14.85 L20.17,14.85z M12,3v10l3-3h6V3H12z M19,8h-5V5h5V8z" />
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b76555b..33cae6d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3896,6 +3896,8 @@
     <string name="controls_label">Controls</string>
     <!-- Manage applications, text label for button to kill / force stop an application -->
     <string name="force_stop">Force stop</string>
+    <!-- Manage applications, text label for button to archive an application. Archiving means uninstalling the app without deleting user's personal data and replacing the app with a stub app with minimum size. So, the user can unarchive the app later and not lose any personal data. -->
+    <string name="archive">Archive</string>
     <!-- Manage applications, individual application info screen,label under Storage heading.  The total storage space taken up by this app. -->
     <string name="total_size_label">Total</string>
     <!-- Manage applications, individual application info screen, label under Storage heading. The amount of space taken up by the application itself (for example, the java compield files and things like that) -->
@@ -4006,6 +4008,11 @@
     <!-- Manage applications, text for Move button -->
     <string name="move_app">Move</string>
 
+    <!-- Toast message when archiving an app failed. -->
+    <string name="archiving_failed">Archiving failed</string>
+    <!-- Toast message when archiving an app succeeded. -->
+    <string name="archiving_succeeded">Archived <xliff:g id="package_label" example="Translate">%1$s</xliff:g></string>
+
     <!-- Text of pop up message if the request for a "migrate primary storage" operation
          (see storage_menu_migrate) is denied as another is already in progress. [CHAR LIMIT=75] -->
     <string name="another_migration_already_in_progress">Another migration is already in progress.</string>
@@ -4639,8 +4646,8 @@
     <string name="accessibility_tutorial_dialog_title_volume">Hold volume keys to open</string>
     <!-- Title for the accessibility tutorial dialog in accessibility service with triple tap. [CHAR LIMIT=100] -->
     <string name="accessibility_tutorial_dialog_title_triple">Triple tap screen to open</string>
-    <!-- Title for the accessibility tutorial dialog in accessibility service with two finger triple tap. [CHAR LIMIT=100] -->
-    <string name="accessibility_tutorial_dialog_title_two_finger_triple">Two finger triple tap screen to open</string>
+    <!-- Title for the accessibility tutorial dialog in accessibility service with two finger double tap. [CHAR LIMIT=100] -->
+    <string name="accessibility_tutorial_dialog_title_two_finger_double">Two finger double tap screen to open</string>
     <!-- Title for the accessibility tutorial dialog in accessibility service with gesture. [CHAR LIMIT=50] -->
     <string name="accessibility_tutorial_dialog_title_gesture">Use gesture to open</string>
     <!-- Title for the accessibility tutorial dialog in gesture navigation settings. [CHAR LIMIT=50] -->
@@ -4653,8 +4660,8 @@
     <string name="accessibility_tutorial_dialog_message_volume">To use this feature, press &amp; hold both volume keys.</string>
     <!-- Instruction for the accessibility tutorial dialog in accessibility service with triple tap. [CHAR LIMIT=100] -->
     <string name="accessibility_tutorial_dialog_message_triple">To start and stop magnification, triple-tap anywhere on your screen.</string>
-    <!-- Instruction for the accessibility tutorial dialog in accessibility service with two finger triple tap. [CHAR LIMIT=100] -->
-    <string name="accessibility_tutorial_dialog_message_two_finger_triple">To start and stop magnification, triple-tap anywhere on your screen with two fingers.</string>
+    <!-- Instruction for the accessibility tutorial dialog in accessibility service with two finger double tap. [CHAR LIMIT=100] -->
+    <string name="accessibility_tutorial_dialog_message_two_finger_triple">To start and stop magnification, double-tap anywhere on your screen with two fingers.</string>
     <!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is not enabled. [CHAR LIMIT=NONE] -->
     <string name="accessibility_tutorial_dialog_message_gesture">To use this feature, swipe up from the bottom of the screen with 2 fingers.\n\nTo switch between features, swipe up with 2 fingers and hold.</string>
     <!-- Message for the accessibility tutorial dialog when user enables an accessibility service while using gesture navigation and touch exploration is enabled. [CHAR LIMIT=NONE] -->
@@ -4697,12 +4704,12 @@
     <string name="accessibility_shortcut_hardware_keyword">hold volume keys</string>
     <!-- Summary for hardware shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_summary_hardware">Press &amp; hold both volume keys</string>
-    <!-- Title for two finger triple tap shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_shortcut_edit_dialog_title_two_finger_triple_tap">Two-finger triple-tap screen</string>
+    <!-- Title for two finger double tap shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_edit_dialog_title_two_finger_double_tap">Two-finger double-tap screen</string>
     <!-- Part of list to compose user's accessibility shortcut list. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_shortcut_two_finger_triple_tap_keyword">two-finger triple-tap screen</string>
-    <!-- Summary for two finger triple tap shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_shortcut_edit_dialog_summary_two_finger_triple_tap">Quickly tap screen {0,number,integer} times with two fingers</string>
+    <string name="accessibility_shortcut_two_finger_double_tap_keyword">two-finger double-tap screen</string>
+    <!-- Summary for two finger double tap shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_edit_dialog_summary_two_finger_double_tap">Quickly tap screen {0,number,integer} times with two fingers</string>
     <!-- Title for triple tap shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_edit_dialog_title_triple_tap">Triple-tap screen</string>
     <!-- Part of list to compose user's accessibility shortcut list. [CHAR LIMIT=NONE] -->
diff --git a/res/xml/network_provider_internet.xml b/res/xml/network_provider_internet.xml
index cd44ab1..1a8ee08 100644
--- a/res/xml/network_provider_internet.xml
+++ b/res/xml/network_provider_internet.xml
@@ -31,10 +31,16 @@
         settings:keywords="@string/keywords_internet"
         settings:useAdminDisabledSummary="true" />
 
-    <com.android.settings.spa.preference.ComposePreference
+    <com.android.settingslib.RestrictedPreference
         android:key="calls_and_sms"
         android:title="@string/calls_and_sms"
+        android:icon="@drawable/ic_calls_sms"
         android:order="-20"
+        android:summary="@string/summary_placeholder"
+        android:fragment="com.android.settings.network.NetworkProviderCallsSmsFragment"
+        settings:userRestriction="no_config_mobile_networks"
+        settings:allowDividerBelow="true"
+        settings:useAdminDisabledSummary="true"
         settings:controller="com.android.settings.network.NetworkProviderCallsSmsController" />
 
     <com.android.settingslib.RestrictedPreference
diff --git a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
index c429e0b..ca3a7b1 100644
--- a/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
+++ b/src/com/android/settings/accessibility/AccessibilityDialogUtils.java
@@ -249,7 +249,7 @@
                 initSoftwareShortcut(context, contentView);
                 initHardwareShortcut(context, contentView);
                 if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
-                    initTwoFingerTripleTapMagnificationShortcut(context, contentView);
+                    initTwoFingerDoubleTapMagnificationShortcut(context, contentView);
                 }
                 initMagnifyShortcut(context, contentView);
                 initAdvancedWidget(contentView);
@@ -260,7 +260,7 @@
                 initSoftwareShortcutForSUW(context, contentView);
                 initHardwareShortcut(context, contentView);
                 if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
-                    initTwoFingerTripleTapMagnificationShortcut(context, contentView);
+                    initTwoFingerDoubleTapMagnificationShortcut(context, contentView);
                 }
                 initMagnifyShortcut(context, contentView);
                 initAdvancedWidget(contentView);
@@ -365,15 +365,15 @@
                 R.raw.a11y_shortcut_type_triple_tap);
     }
 
-    private static void initTwoFingerTripleTapMagnificationShortcut(Context context, View view) {
+    private static void initTwoFingerDoubleTapMagnificationShortcut(Context context, View view) {
         // TODO(b/306153204): Update shortcut string and image when UX provides them
         final View dialogView = view.findViewById(R.id.two_finger_triple_tap_shortcut);
         final CharSequence title = context.getText(
-                R.string.accessibility_shortcut_edit_dialog_title_two_finger_triple_tap);
+                R.string.accessibility_shortcut_edit_dialog_title_two_finger_double_tap);
         String summary = context.getString(
-                R.string.accessibility_shortcut_edit_dialog_summary_two_finger_triple_tap);
-        // Format the number '3' in the summary.
-        final Object[] arguments = {3};
+                R.string.accessibility_shortcut_edit_dialog_summary_two_finger_double_tap);
+        // Format the number '2' in the summary.
+        final Object[] arguments = {2};
         summary = MessageFormat.format(summary, arguments);
 
         setupShortcutWidgetWithImageRawResource(context, dialogView, title, summary,
diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
index e90ed87..1f71ab0 100644
--- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
+++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java
@@ -416,7 +416,7 @@
         // TODO(b/308088945): Update tutorial string and image when UX provides them
         final int type = UserShortcutType.TWOFINGERTRIPLETAP;
         final CharSequence title =
-                context.getText(R.string.accessibility_tutorial_dialog_title_two_finger_triple);
+                context.getText(R.string.accessibility_tutorial_dialog_title_two_finger_double);
         final View image =
                 createIllustrationViewWithImageRawResource(context,
                         R.raw.a11y_shortcut_type_triple_tap);
diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
index 9f6eec3..1d946fb 100644
--- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
+++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java
@@ -477,7 +477,7 @@
         if (Flags.enableMagnificationMultipleFingerMultipleTapGesture()) {
             if (hasShortcutType(shortcutTypes, UserShortcutType.TWOFINGERTRIPLETAP)) {
                 final CharSequence twoFingerTripleTapTitle = context.getText(
-                        R.string.accessibility_shortcut_two_finger_triple_tap_keyword);
+                        R.string.accessibility_shortcut_two_finger_double_tap_keyword);
                 list.add(twoFingerTripleTapTitle);
             }
         }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index 308b3d5..6904342 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -93,6 +93,7 @@
 import com.google.android.setupdesign.util.DeviceHelper;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 
@@ -876,6 +877,8 @@
         @Override
         protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
             if (!isFingerprintHardwareDetected(context)) {
+                Log.e(TAG, "Fingerprint hardware is not detected");
+                mControllers = Collections.emptyList();
                 return null;
             }
 
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
index a673cb4..a418415 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsDashboardFragment.java
@@ -16,15 +16,25 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamsScanQrCodeController.REQUEST_SCAN_BT_BROADCAST_QR_CODE;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.util.Log;
 
 import com.android.settings.R;
+import com.android.settings.connecteddevice.audiosharing.audiostreams.qrcode.QrCodeScanModeFragment;
 import com.android.settings.dashboard.DashboardFragment;
+import com.android.settingslib.bluetooth.BluetoothLeBroadcastMetadataExt;
+import com.android.settingslib.bluetooth.BluetoothUtils;
 
 public class AudioStreamsDashboardFragment extends DashboardFragment {
     private static final String TAG = "AudioStreamsDashboardFrag";
+    private static final boolean DEBUG = BluetoothUtils.D;
+    private AudioStreamsScanQrCodeController mAudioStreamsScanQrCodeController;
 
     public AudioStreamsDashboardFragment() {
         super();
@@ -59,7 +69,8 @@
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
-        use(AudioStreamsScanQrCodeController.class).setFragment(this);
+        mAudioStreamsScanQrCodeController = use(AudioStreamsScanQrCodeController.class);
+        mAudioStreamsScanQrCodeController.setFragment(this);
     }
 
     @Override
@@ -70,6 +81,34 @@
     @Override
     public void onActivityResult(int requestCode, int resultCode, Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
-        // TODO(chelseahao): implementation.
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "onActivityResult() requestCode : "
+                            + requestCode
+                            + " resultCode : "
+                            + resultCode);
+        }
+        if (requestCode == REQUEST_SCAN_BT_BROADCAST_QR_CODE) {
+            if (resultCode == Activity.RESULT_OK) {
+                String broadcastMetadata =
+                        data.getStringExtra(QrCodeScanModeFragment.KEY_BROADCAST_METADATA);
+                BluetoothLeBroadcastMetadata source =
+                        BluetoothLeBroadcastMetadataExt.INSTANCE.convertToBroadcastMetadata(
+                                broadcastMetadata);
+                if (source == null) {
+                    Log.w(TAG, "onActivityResult() source is null!");
+                    return;
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "onActivityResult() broadcastId : " + source.getBroadcastId());
+                }
+                if (mAudioStreamsScanQrCodeController == null) {
+                    Log.w(TAG, "onActivityResult() AudioStreamsScanQrCodeController is null!");
+                    return;
+                }
+                mAudioStreamsScanQrCodeController.addSource(source);
+            }
+        }
     }
 }
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
index 12b46e5..549e725 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsScanQrCodeController.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.connecteddevice.audiosharing.audiostreams;
 
+import android.bluetooth.BluetoothLeBroadcastMetadata;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.Intent;
@@ -41,10 +42,10 @@
 
 public class AudioStreamsScanQrCodeController extends BasePreferenceController
         implements DefaultLifecycleObserver {
+    static final int REQUEST_SCAN_BT_BROADCAST_QR_CODE = 0;
     private static final String TAG = "AudioStreamsProgressCategoryController";
     private static final boolean DEBUG = BluetoothUtils.D;
     private static final String KEY = "audio_streams_scan_qr_code";
-    private static final int REQUEST_SCAN_BT_BROADCAST_QR_CODE = 0;
     private final BluetoothCallback mBluetoothCallback =
             new BluetoothCallback() {
                 @Override
@@ -57,12 +58,14 @@
             };
 
     private final LocalBluetoothManager mLocalBtManager;
+    private final AudioStreamsHelper mAudioStreamsHelper;
     private AudioStreamsDashboardFragment mFragment;
     private Preference mPreference;
 
     public AudioStreamsScanQrCodeController(Context context, String preferenceKey) {
         super(context, preferenceKey);
         mLocalBtManager = Utils.getLocalBtManager(mContext);
+        mAudioStreamsHelper = new AudioStreamsHelper(mLocalBtManager);
     }
 
     public void setFragment(AudioStreamsDashboardFragment fragment) {
@@ -121,6 +124,10 @@
                 });
     }
 
+    void addSource(BluetoothLeBroadcastMetadata source) {
+        mAudioStreamsHelper.addSource(source);
+    }
+
     private void updateVisibility() {
         ThreadUtils.postOnBackgroundThread(
                 () -> {
diff --git a/src/com/android/settings/development/BackAnimationPreferenceController.java b/src/com/android/settings/development/BackAnimationPreferenceController.java
index aa4faf5..95ffc25 100644
--- a/src/com/android/settings/development/BackAnimationPreferenceController.java
+++ b/src/com/android/settings/development/BackAnimationPreferenceController.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.development;
 
+import static com.android.window.flags.Flags.predictiveBackSystemAnimations;
+
 import android.content.Context;
 import android.provider.Settings;
 
@@ -56,6 +58,11 @@
     }
 
     @Override
+    public boolean isAvailable() {
+        return !predictiveBackSystemAnimations();
+    }
+
+    @Override
     public String getPreferenceKey() {
         return BACK_NAVIGATION_ANIMATION_KEY;
     }
diff --git a/src/com/android/settings/development/GrammaticalGenderPreferenceController.java b/src/com/android/settings/development/GrammaticalGenderPreferenceController.java
index 7b8ec65..0540974 100644
--- a/src/com/android/settings/development/GrammaticalGenderPreferenceController.java
+++ b/src/com/android/settings/development/GrammaticalGenderPreferenceController.java
@@ -95,4 +95,9 @@
         listPreference.setValue(mListValues[index]);
         listPreference.setSummary(mListSummaries[index]);
     }
+
+    @Override
+    public boolean isAvailable() {
+        return android.app.Flags.systemTermsOfAddressEnabled();
+    }
 }
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 86ef3cf..23680d9 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -148,7 +148,8 @@
         launchArgs.mShowTimeInformation = showTimeInformation;
         if (launchArgs.mShowTimeInformation) {
             launchArgs.mForegroundTimeMs = diffEntry.mForegroundUsageTimeInMs;
-            launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs;
+            launchArgs.mBackgroundTimeMs =
+                    diffEntry.mBackgroundUsageTimeInMs + diffEntry.mForegroundServiceUsageTimeInMs;
             launchArgs.mScreenOnTimeMs = diffEntry.mScreenOnTimeInMs;
             launchArgs.mAnomalyHintPrefKey = anomalyHintPrefKey;
             launchArgs.mAnomalyHintText = anomalyHintText;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
index bad1b76..2c376e5 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntry.java
@@ -92,6 +92,7 @@
     public String mLegacyLabel;
     public int mConsumerType;
     public long mForegroundUsageTimeInMs;
+    public long mForegroundServiceUsageTimeInMs;
     public long mBackgroundUsageTimeInMs;
     public long mScreenOnTimeInMs;
     public double mConsumePower;
@@ -125,6 +126,7 @@
             String legacyLabel,
             int consumerType,
             long foregroundUsageTimeInMs,
+            long foregroundServiceUsageTimeInMs,
             long backgroundUsageTimeInMs,
             long screenOnTimeInMs,
             double consumePower,
@@ -142,6 +144,7 @@
         mLegacyLabel = legacyLabel;
         mConsumerType = consumerType;
         mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
+        mForegroundServiceUsageTimeInMs = foregroundServiceUsageTimeInMs;
         mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
         mScreenOnTimeInMs = screenOnTimeInMs;
         mConsumePower = consumePower;
@@ -164,6 +167,7 @@
                 legacyLabel,
                 consumerType,
                 /* foregroundUsageTimeInMs= */ 0,
+                /* foregroundServiceUsageTimeInMs= */ 0,
                 /* backgroundUsageTimeInMs= */ 0,
                 /* screenOnTimeInMs= */ 0,
                 /* consumePower= */ 0,
@@ -232,6 +236,7 @@
                 this.mLegacyLabel,
                 this.mConsumerType,
                 this.mForegroundUsageTimeInMs,
+                this.mForegroundServiceUsageTimeInMs,
                 this.mBackgroundUsageTimeInMs,
                 this.mScreenOnTimeInMs,
                 this.mConsumePower,
@@ -515,48 +520,50 @@
 
     @Override
     public String toString() {
-        final StringBuilder builder =
-                new StringBuilder()
-                        .append("BatteryDiffEntry{")
-                        .append(
-                                String.format(
-                                        "\n\tname=%s restrictable=%b",
-                                        mAppLabel, mValidForRestriction))
-                        .append(
-                                String.format(
-                                        "\n\tconsume=%.2f%% %f/%f",
-                                        mPercentage, mConsumePower, mTotalConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\tconsume power= foreground:%f foregroundService:%f",
-                                        mForegroundUsageConsumePower,
-                                        mForegroundServiceUsageConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\tconsume power= background:%f cached:%f",
-                                        mBackgroundUsageConsumePower, mCachedUsageConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\ttime= foreground:%s background:%s screen-on:%s",
-                                        StringUtil.formatElapsedTime(
-                                                mContext,
-                                                (double) mForegroundUsageTimeInMs,
-                                                /* withSeconds= */ true,
-                                                /* collapseTimeUnit= */ false),
-                                        StringUtil.formatElapsedTime(
-                                                mContext,
-                                                (double) mBackgroundUsageTimeInMs,
-                                                /* withSeconds= */ true,
-                                                /* collapseTimeUnit= */ false),
-                                        StringUtil.formatElapsedTime(
-                                                mContext,
-                                                (double) mScreenOnTimeInMs,
-                                                /* withSeconds= */ true,
-                                                /* collapseTimeUnit= */ false)))
-                        .append(
-                                String.format(
-                                        "\n\tpackage:%s|%s uid:%d userId:%d",
-                                        mLegacyPackageName, getPackageName(), mUid, mUserId));
+        final StringBuilder builder = new StringBuilder();
+        builder.append("BatteryDiffEntry{");
+        builder.append(
+                String.format("\n\tname=%s restrictable=%b", mAppLabel, mValidForRestriction));
+        builder.append(
+                String.format(
+                        "\n\tconsume=%.2f%% %f/%f",
+                        mPercentage, mConsumePower, mTotalConsumePower));
+        builder.append(
+                String.format(
+                        "\n\tconsume power= foreground:%f foregroundService:%f",
+                        mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower));
+        builder.append(
+                String.format(
+                        "\n\tconsume power= background:%f cached:%f",
+                        mBackgroundUsageConsumePower, mCachedUsageConsumePower));
+        builder.append(
+                String.format(
+                        "\n\ttime= foreground:%s foregroundService:%s "
+                                + "background:%s screen-on:%s",
+                        StringUtil.formatElapsedTime(
+                                mContext,
+                                (double) mForegroundUsageTimeInMs,
+                                /* withSeconds= */ true,
+                                /* collapseTimeUnit= */ false),
+                        StringUtil.formatElapsedTime(
+                                mContext,
+                                (double) mForegroundServiceUsageTimeInMs,
+                                /* withSeconds= */ true,
+                                /* collapseTimeUnit= */ false),
+                        StringUtil.formatElapsedTime(
+                                mContext,
+                                (double) mBackgroundUsageTimeInMs,
+                                /* withSeconds= */ true,
+                                /* collapseTimeUnit= */ false),
+                        StringUtil.formatElapsedTime(
+                                mContext,
+                                (double) mScreenOnTimeInMs,
+                                /* withSeconds= */ true,
+                                /* collapseTimeUnit= */ false)));
+        builder.append(
+                String.format(
+                        "\n\tpackage:%s|%s uid:%d userId:%d",
+                        mLegacyPackageName, getPackageName(), mUid, mUserId));
         return builder.toString();
     }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
index 751e7ad..4b65cc9 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryEntry.java
@@ -111,6 +111,7 @@
     @BatteryConsumer.PowerComponent private final int mPowerComponentId;
     private long mUsageDurationMs;
     private long mTimeInForegroundMs;
+    private long mTimeInForegroundServiceMs;
     private long mTimeInBackgroundMs;
 
     public String mName;
@@ -188,9 +189,14 @@
                 }
             }
             mTimeInForegroundMs =
-                    uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_FOREGROUND);
+            mTimeInForegroundServiceMs =
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
             mTimeInBackgroundMs =
-                    uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_BACKGROUND);
             mConsumedPowerInForeground =
                     safeGetConsumedPower(
                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
@@ -433,20 +439,19 @@
 
     /** Returns foreground time/ms that is attributed to this entry. */
     public long getTimeInForegroundMs() {
-        if (mBatteryConsumer instanceof UidBatteryConsumer) {
-            return mTimeInForegroundMs;
-        } else {
-            return mUsageDurationMs;
-        }
+        return (mBatteryConsumer instanceof UidBatteryConsumer)
+                ? mTimeInForegroundMs
+                : mUsageDurationMs;
+    }
+
+    /** Returns foreground service time/ms that is attributed to this entry. */
+    public long getTimeInForegroundServiceMs() {
+        return (mBatteryConsumer instanceof UidBatteryConsumer) ? mTimeInForegroundServiceMs : 0;
     }
 
     /** Returns background activity time/ms that is attributed to this entry. */
     public long getTimeInBackgroundMs() {
-        if (mBatteryConsumer instanceof UidBatteryConsumer) {
-            return mTimeInBackgroundMs;
-        } else {
-            return 0;
-        }
+        return (mBatteryConsumer instanceof UidBatteryConsumer) ? mTimeInBackgroundMs : 0;
     }
 
     /** Returns total amount of power (in milli-amp-hours) that is attributed to this entry. */
@@ -510,9 +515,14 @@
         if (batteryConsumer instanceof UidBatteryConsumer) {
             UidBatteryConsumer uidBatteryConsumer = (UidBatteryConsumer) batteryConsumer;
             mTimeInForegroundMs +=
-                    uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND);
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_FOREGROUND);
+            mTimeInForegroundServiceMs +=
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
             mTimeInBackgroundMs +=
-                    uidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND);
+                    uidBatteryConsumer.getTimeInProcessStateMs(
+                            UidBatteryConsumer.PROCESS_STATE_BACKGROUND);
             mConsumedPowerInForeground +=
                     safeGetConsumedPower(
                             uidBatteryConsumer, BATTERY_DIMENSIONS[BATTERY_USAGE_INDEX_FOREGROUND]);
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
index 97cdc34..b42d373 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntry.java
@@ -20,8 +20,6 @@
 import android.os.BatteryConsumer;
 import android.util.Log;
 
-import java.time.Duration;
-
 /** A container class to carry data from {@link ContentValues}. */
 public class BatteryHistEntry {
     private static final boolean DEBUG = false;
@@ -57,6 +55,7 @@
     public final double mCachedUsageConsumePower;
     public final double mPercentOfTotal;
     public final long mForegroundUsageTimeInMs;
+    public final long mForegroundServiceUsageTimeInMs;
     public final long mBackgroundUsageTimeInMs;
     @BatteryConsumer.PowerComponent public final int mDrainType;
     @ConvertUtils.ConsumerType public final int mConsumerType;
@@ -89,6 +88,7 @@
         mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower();
         mPercentOfTotal = batteryInformation.getPercentOfTotal();
         mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs();
+        mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs();
         mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs();
         mDrainType = batteryInformation.getDrainType();
         final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
@@ -118,6 +118,7 @@
         mCachedUsageConsumePower = batteryInformation.getCachedUsageConsumePower();
         mPercentOfTotal = batteryInformation.getPercentOfTotal();
         mForegroundUsageTimeInMs = batteryInformation.getForegroundUsageTimeInMs();
+        mForegroundServiceUsageTimeInMs = batteryInformation.getForegroundServiceUsageTimeInMs();
         mBackgroundUsageTimeInMs = batteryInformation.getBackgroundUsageTimeInMs();
         mDrainType = batteryInformation.getDrainType();
         final DeviceBatteryState deviceBatteryState = batteryInformation.getDeviceBatteryState();
@@ -137,6 +138,7 @@
             double backgroundUsageConsumePower,
             double cachedUsageConsumePower,
             long foregroundUsageTimeInMs,
+            long foregroundServiceUsageTimeInMs,
             long backgroundUsageTimeInMs,
             int batteryLevel) {
         mUid = fromEntry.mUid;
@@ -155,6 +157,7 @@
         mCachedUsageConsumePower = cachedUsageConsumePower;
         mPercentOfTotal = fromEntry.mPercentOfTotal;
         mForegroundUsageTimeInMs = foregroundUsageTimeInMs;
+        mForegroundServiceUsageTimeInMs = foregroundServiceUsageTimeInMs;
         mBackgroundUsageTimeInMs = backgroundUsageTimeInMs;
         mDrainType = fromEntry.mDrainType;
         mConsumerType = fromEntry.mConsumerType;
@@ -189,45 +192,40 @@
     @Override
     public String toString() {
         final String recordAtDateTime = ConvertUtils.utcToLocalTimeForLogging(mTimestamp);
-        final StringBuilder builder =
-                new StringBuilder()
-                        .append("\nBatteryHistEntry{")
-                        .append(
-                                String.format(
-                                        "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
-                                        mPackageName, mAppLabel, mUid, mUserId, mIsHidden))
-                        .append(
-                                String.format(
-                                        "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
-                                        recordAtDateTime,
-                                        mZoneId,
-                                        Duration.ofMillis(mBootTimestamp).getSeconds()))
-                        .append(
-                                String.format(
-                                        "\n\tusage=%f|total=%f|consume=%f",
-                                        mPercentOfTotal, mTotalPower, mConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\tforeground=%f|foregroundService=%f",
-                                        mForegroundUsageConsumePower,
-                                        mForegroundServiceUsageConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\tbackground=%f|cached=%f",
-                                        mBackgroundUsageConsumePower, mCachedUsageConsumePower))
-                        .append(
-                                String.format(
-                                        "\n\telapsedTime=%d|%d",
-                                        Duration.ofMillis(mForegroundUsageTimeInMs).getSeconds(),
-                                        Duration.ofMillis(mBackgroundUsageTimeInMs).getSeconds()))
-                        .append(
-                                String.format(
-                                        "\n\tdrainType=%d|consumerType=%d",
-                                        mDrainType, mConsumerType))
-                        .append(
-                                String.format(
-                                        "\n\tbattery=%d|status=%d|health=%d\n}",
-                                        mBatteryLevel, mBatteryStatus, mBatteryHealth));
+        final StringBuilder builder = new StringBuilder();
+        builder.append("\nBatteryHistEntry{");
+        builder.append(
+                String.format(
+                        "\n\tpackage=%s|label=%s|uid=%d|userId=%d|isHidden=%b",
+                        mPackageName, mAppLabel, mUid, mUserId, mIsHidden));
+        builder.append(
+                String.format(
+                        "\n\ttimestamp=%s|zoneId=%s|bootTimestamp=%d",
+                        recordAtDateTime, mZoneId, TimestampUtils.getSeconds(mBootTimestamp)));
+        builder.append(
+                String.format(
+                        "\n\tusage=%f|total=%f|consume=%f",
+                        mPercentOfTotal, mTotalPower, mConsumePower));
+        builder.append(
+                String.format(
+                        "\n\tforeground=%f|foregroundService=%f",
+                        mForegroundUsageConsumePower, mForegroundServiceUsageConsumePower));
+        builder.append(
+                String.format(
+                        "\n\tbackground=%f|cached=%f",
+                        mBackgroundUsageConsumePower, mCachedUsageConsumePower));
+        builder.append(
+                String.format(
+                        "\n\telapsedTime,fg=%d|fgs=%d|bg=%d",
+                        TimestampUtils.getSeconds(mBackgroundUsageTimeInMs),
+                        TimestampUtils.getSeconds(mForegroundServiceUsageTimeInMs),
+                        TimestampUtils.getSeconds(mBackgroundUsageTimeInMs)));
+        builder.append(
+                String.format("\n\tdrainType=%d|consumerType=%d", mDrainType, mConsumerType));
+        builder.append(
+                String.format(
+                        "\n\tbattery=%d|status=%d|health=%d\n}",
+                        mBatteryLevel, mBatteryStatus, mBatteryHealth));
         return builder.toString();
     }
 
@@ -323,19 +321,20 @@
                         ratio);
         final double foregroundUsageTimeInMs =
                 interpolate(
-                        (double)
-                                (lowerHistEntry == null
-                                        ? 0
-                                        : lowerHistEntry.mForegroundUsageTimeInMs),
-                        (double) upperHistEntry.mForegroundUsageTimeInMs,
+                        (lowerHistEntry == null ? 0 : lowerHistEntry.mForegroundUsageTimeInMs),
+                        upperHistEntry.mForegroundUsageTimeInMs,
+                        ratio);
+        final double foregroundServiceUsageTimeInMs =
+                interpolate(
+                        (lowerHistEntry == null
+                                ? 0
+                                : lowerHistEntry.mForegroundServiceUsageTimeInMs),
+                        upperHistEntry.mForegroundServiceUsageTimeInMs,
                         ratio);
         final double backgroundUsageTimeInMs =
                 interpolate(
-                        (double)
-                                (lowerHistEntry == null
-                                        ? 0
-                                        : lowerHistEntry.mBackgroundUsageTimeInMs),
-                        (double) upperHistEntry.mBackgroundUsageTimeInMs,
+                        (lowerHistEntry == null ? 0 : lowerHistEntry.mBackgroundUsageTimeInMs),
+                        upperHistEntry.mBackgroundUsageTimeInMs,
                         ratio);
         // Checks whether there is any abnormal cases!
         if (upperHistEntry.mConsumePower < consumePower
@@ -345,6 +344,7 @@
                 || upperHistEntry.mBackgroundUsageConsumePower < backgroundUsageConsumePower
                 || upperHistEntry.mCachedUsageConsumePower < cachedUsageConsumePower
                 || upperHistEntry.mForegroundUsageTimeInMs < foregroundUsageTimeInMs
+                || upperHistEntry.mForegroundServiceUsageTimeInMs < foregroundServiceUsageTimeInMs
                 || upperHistEntry.mBackgroundUsageTimeInMs < backgroundUsageTimeInMs) {
             if (DEBUG) {
                 Log.w(
@@ -371,6 +371,7 @@
                 backgroundUsageConsumePower,
                 cachedUsageConsumePower,
                 Math.round(foregroundUsageTimeInMs),
+                Math.round(foregroundServiceUsageTimeInMs),
                 Math.round(backgroundUsageTimeInMs),
                 (int) Math.round(batteryLevel));
     }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
index d6f8709..0ffd090 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownController.java
@@ -400,7 +400,7 @@
                         mPrefContext,
                         entry.isSystemEntry(),
                         entry.mForegroundUsageTimeInMs,
-                        entry.mBackgroundUsageTimeInMs,
+                        entry.mBackgroundUsageTimeInMs + entry.mForegroundServiceUsageTimeInMs,
                         entry.mScreenOnTimeInMs));
     }
 }
diff --git a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
index ae93734..e23e219 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/ConvertUtils.java
@@ -544,6 +544,7 @@
                 batteryUsageDiff.getLabel(),
                 batteryUsageDiff.getConsumerType(),
                 batteryUsageDiff.getForegroundUsageTime(),
+                batteryUsageDiff.getForegroundServiceUsageTime(),
                 batteryUsageDiff.getBackgroundUsageTime(),
                 batteryUsageDiff.getScreenOnTime(),
                 batteryUsageDiff.getConsumePower(),
@@ -612,6 +613,7 @@
                     .setPercentOfTotal(entry.mPercent)
                     .setDrainType(entry.getPowerComponentId())
                     .setForegroundUsageTimeInMs(entry.getTimeInForegroundMs())
+                    .setForegroundServiceUsageTimeInMs(entry.getTimeInForegroundServiceMs())
                     .setBackgroundUsageTimeInMs(entry.getTimeInBackgroundMs());
         }
 
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
index d8f0a77..2ef12f1 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DataProcessor.java
@@ -675,6 +675,7 @@
                                 entry.mAppLabel,
                                 entry.mConsumerType,
                                 entry.mForegroundUsageTimeInMs,
+                                entry.mForegroundServiceUsageTimeInMs,
                                 entry.mBackgroundUsageTimeInMs,
                                 /* screenOnTimeInMs= */ 0,
                                 entry.mConsumePower,
@@ -1412,6 +1413,7 @@
 
             // Cumulative values is a specific time slot for a specific app.
             long foregroundUsageTimeInMs = 0;
+            long foregroundServiceUsageTimeInMs = 0;
             long backgroundUsageTimeInMs = 0;
             double consumePower = 0;
             double foregroundUsageConsumePower = 0;
@@ -1425,6 +1427,10 @@
                         getDiffValue(
                                 currentEntry.mForegroundUsageTimeInMs,
                                 nextEntry.mForegroundUsageTimeInMs);
+                foregroundServiceUsageTimeInMs +=
+                        getDiffValue(
+                                currentEntry.mForegroundServiceUsageTimeInMs,
+                                nextEntry.mForegroundServiceUsageTimeInMs);
                 backgroundUsageTimeInMs +=
                         getDiffValue(
                                 currentEntry.mBackgroundUsageTimeInMs,
@@ -1453,24 +1459,32 @@
                 foregroundUsageTimeInMs = slotScreenOnTime;
             }
             // Excludes entry since we don't have enough data to calculate.
-            if (foregroundUsageTimeInMs == 0 && backgroundUsageTimeInMs == 0 && consumePower == 0) {
+            if (foregroundUsageTimeInMs == 0
+                    && foregroundServiceUsageTimeInMs == 0
+                    && backgroundUsageTimeInMs == 0
+                    && consumePower == 0) {
                 continue;
             }
             // Forces refine the cumulative value since it may introduce deviation error since we
             // will apply the interpolation arithmetic.
-            final float totalUsageTimeInMs = foregroundUsageTimeInMs + backgroundUsageTimeInMs;
+            final float totalUsageTimeInMs =
+                    foregroundUsageTimeInMs
+                            + backgroundUsageTimeInMs
+                            + foregroundServiceUsageTimeInMs;
             if (totalUsageTimeInMs > slotDuration) {
                 final float ratio = slotDuration / totalUsageTimeInMs;
                 if (sDebug) {
                     Log.w(
                             TAG,
                             String.format(
-                                    "abnormal usage time %d|%d for:\n%s",
+                                    "abnormal usage time %d|%d|%d for:\n%s",
                                     Duration.ofMillis(foregroundUsageTimeInMs).getSeconds(),
+                                    Duration.ofMillis(foregroundServiceUsageTimeInMs).getSeconds(),
                                     Duration.ofMillis(backgroundUsageTimeInMs).getSeconds(),
                                     selectedBatteryEntry));
                 }
                 foregroundUsageTimeInMs = Math.round(foregroundUsageTimeInMs * ratio);
+                foregroundServiceUsageTimeInMs = Math.round(foregroundServiceUsageTimeInMs * ratio);
                 backgroundUsageTimeInMs = Math.round(backgroundUsageTimeInMs * ratio);
                 consumePower = consumePower * ratio;
                 foregroundUsageConsumePower = foregroundUsageConsumePower * ratio;
@@ -1487,9 +1501,14 @@
                                     appUsageMap,
                                     selectedBatteryEntry.mUserId,
                                     selectedBatteryEntry.mPackageName));
-            // Make sure the background + screen-on time will not exceed the threshold.
+            // Ensure the following value will not exceed the threshold.
+            // value = background + foregroundService + screen-on
             backgroundUsageTimeInMs =
                     Math.min(backgroundUsageTimeInMs, (long) slotDuration - screenOnTime);
+            foregroundServiceUsageTimeInMs =
+                    Math.min(
+                            foregroundServiceUsageTimeInMs,
+                            (long) slotDuration - screenOnTime - backgroundUsageTimeInMs);
             final BatteryDiffEntry currentBatteryDiffEntry =
                     new BatteryDiffEntry(
                             context,
@@ -1502,6 +1521,7 @@
                             selectedBatteryEntry.mAppLabel,
                             selectedBatteryEntry.mConsumerType,
                             foregroundUsageTimeInMs,
+                            foregroundServiceUsageTimeInMs,
                             backgroundUsageTimeInMs,
                             screenOnTime,
                             consumePower,
@@ -1647,6 +1667,8 @@
         } else {
             // Sums up some field data into the existing one.
             oldBatteryDiffEntry.mForegroundUsageTimeInMs += entry.mForegroundUsageTimeInMs;
+            oldBatteryDiffEntry.mForegroundServiceUsageTimeInMs +=
+                    entry.mForegroundServiceUsageTimeInMs;
             oldBatteryDiffEntry.mBackgroundUsageTimeInMs += entry.mBackgroundUsageTimeInMs;
             oldBatteryDiffEntry.mScreenOnTimeInMs += entry.mScreenOnTimeInMs;
             oldBatteryDiffEntry.mConsumePower += entry.mConsumePower;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
index 7160da4..ee0e449 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/DatabaseUtils.java
@@ -660,36 +660,39 @@
         // Creates the ContentValues list to insert them into provider.
         final List<ContentValues> valuesList = new ArrayList<>();
         if (batteryEntryList != null) {
-            batteryEntryList.stream()
-                    .filter(
-                            entry -> {
-                                final long foregroundMs = entry.getTimeInForegroundMs();
-                                final long backgroundMs = entry.getTimeInBackgroundMs();
-                                if (entry.getConsumedPower() == 0
-                                        && (foregroundMs != 0 || backgroundMs != 0)) {
-                                    Log.w(
-                                            TAG,
-                                            String.format(
-                                                    "no consumed power but has running time for %s"
-                                                        + " time=%d|%d",
-                                                    entry.getLabel(), foregroundMs, backgroundMs));
-                                }
-                                return entry.getConsumedPower() != 0
-                                        || foregroundMs != 0
-                                        || backgroundMs != 0;
-                            })
-                    .forEach(
-                            entry ->
-                                    valuesList.add(
-                                            ConvertUtils.convertBatteryEntryToContentValues(
-                                                    entry,
-                                                    batteryUsageStats,
-                                                    batteryLevel,
-                                                    batteryStatus,
-                                                    batteryHealth,
-                                                    snapshotBootTimestamp,
-                                                    snapshotTimestamp,
-                                                    isFullChargeStart)));
+            for (BatteryEntry entry : batteryEntryList) {
+                final long foregroundMs = entry.getTimeInForegroundMs();
+                final long foregroundServiceMs = entry.getTimeInForegroundServiceMs();
+                final long backgroundMs = entry.getTimeInBackgroundMs();
+                if (entry.getConsumedPower() == 0
+                        && (foregroundMs != 0 || foregroundServiceMs != 0 || backgroundMs != 0)) {
+                    Log.w(
+                            TAG,
+                            String.format(
+                                    "no consumed power but has running time for %s"
+                                            + " time=%d|%d|%d",
+                                    entry.getLabel(),
+                                    foregroundMs,
+                                    foregroundServiceMs,
+                                    backgroundMs));
+                }
+                if (entry.getConsumedPower() == 0
+                        && foregroundMs == 0
+                        && foregroundServiceMs == 0
+                        && backgroundMs == 0) {
+                    continue;
+                }
+                valuesList.add(
+                        ConvertUtils.convertBatteryEntryToContentValues(
+                                entry,
+                                batteryUsageStats,
+                                batteryLevel,
+                                batteryStatus,
+                                batteryHealth,
+                                snapshotBootTimestamp,
+                                snapshotTimestamp,
+                                isFullChargeStart));
+            }
         }
 
         int size = 1;
diff --git a/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java b/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java
index 594a0ef..41a2254 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/TimestampUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.fuelgauge.batteryusage;
 
+import java.time.Duration;
 import java.util.Calendar;
 
 /** A utility class for timestamp operations. */
@@ -48,6 +49,10 @@
         return calendar.getTimeInMillis();
     }
 
+    static long getSeconds(final long timeInMs) {
+        return Duration.ofMillis(timeInMs).getSeconds();
+    }
+
     static boolean isMidnight(final long timestamp) {
         final Calendar calendar = Calendar.getInstance();
         calendar.setTimeInMillis(timestamp);
diff --git a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
index 5bc1a3e..7f67770 100644
--- a/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
+++ b/src/com/android/settings/fuelgauge/protos/battery_usage_slot.proto
@@ -31,4 +31,5 @@
   optional int64 foreground_usage_time = 14;
   optional int64 background_usage_time = 15;
   optional int64 screen_on_time = 16;
+  optional int64 foreground_service_usage_time = 17;
 }
diff --git a/src/com/android/settings/fuelgauge/protos/fuelgauge_usage_state.proto b/src/com/android/settings/fuelgauge/protos/fuelgauge_usage_state.proto
index b9b05a3..d53b814 100644
--- a/src/com/android/settings/fuelgauge/protos/fuelgauge_usage_state.proto
+++ b/src/com/android/settings/fuelgauge/protos/fuelgauge_usage_state.proto
@@ -36,4 +36,5 @@
   optional double foreground_service_usage_consume_power = 17;
   optional double background_usage_consume_power = 18;
   optional double cached_usage_consume_power = 19;
+  optional int64 foreground_service_usage_time_in_ms = 20;
 }
\ No newline at end of file
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index e5d9242..323d935 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -59,6 +59,7 @@
         super.onAttach(context);
 
         use(AirplaneModePreferenceController.class).setFragment(this);
+        use(NetworkProviderCallsSmsController.class).init(this);
     }
 
     @Override
diff --git a/src/com/android/settings/network/NetworkProviderCallsSmsController.kt b/src/com/android/settings/network/NetworkProviderCallsSmsController.kt
index a265041..7346e23 100644
--- a/src/com/android/settings/network/NetworkProviderCallsSmsController.kt
+++ b/src/com/android/settings/network/NetworkProviderCallsSmsController.kt
@@ -16,35 +16,23 @@
 
 package com.android.settings.network
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.IntentFilter
-import android.os.UserManager
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import androidx.annotation.VisibleForTesting
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.PermPhoneMsg
-import androidx.compose.material3.HorizontalDivider
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.ui.res.stringResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import androidx.lifecycle.viewmodel.compose.viewModel
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.LifecycleOwner
+import androidx.preference.PreferenceScreen
 import com.android.settings.R
-import com.android.settings.core.SubSettingLauncher
-import com.android.settings.spa.preference.ComposePreferenceController
+import com.android.settings.core.BasePreferenceController
+import com.android.settingslib.RestrictedPreference
 import com.android.settingslib.Utils
-import com.android.settingslib.spa.widget.preference.PreferenceModel
-import com.android.settingslib.spa.widget.ui.SettingsIcon
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
 import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
 import com.android.settingslib.spaprivileged.framework.common.userManager
-import com.android.settingslib.spaprivileged.framework.compose.placeholder
-import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
-import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -64,7 +52,14 @@
         SubscriptionUtil.getUniqueSubscriptionDisplayName(subInfo, context)
     },
     private val isInService: (Int) -> Boolean = IsInServiceImpl(context)::isInService,
-) : ComposePreferenceController(context, preferenceKey) {
+) : BasePreferenceController(context, preferenceKey) {
+
+    private lateinit var lazyViewModel: Lazy<SubscriptionInfoListViewModel>
+    private lateinit var preference: RestrictedPreference
+
+    fun init(fragment: Fragment) {
+        lazyViewModel = fragment.viewModels()
+    }
 
     override fun getAvailabilityStatus() = when {
         !SubscriptionUtil.isSimHardwareVisible(mContext) -> UNSUPPORTED_ON_DEVICE
@@ -72,35 +67,23 @@
         else -> AVAILABLE
     }
 
-    @Composable
-    override fun Content() {
-        Column {
-            CallsAndSms()
-            HorizontalDivider()
-        }
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
     }
 
-    @Composable
-    private fun CallsAndSms() {
-        val viewModel: SubscriptionInfoListViewModel = viewModel()
-        val subscriptionInfos by viewModel.subscriptionInfoListFlow.collectAsStateWithLifecycle()
-        val summary by remember { summaryFlow(viewModel.subscriptionInfoListFlow) }
-            .collectAsStateWithLifecycle(initialValue = placeholder())
-        RestrictedPreference(
-            model = object : PreferenceModel {
-                override val title = stringResource(R.string.calls_and_sms)
-                override val icon = @Composable { SettingsIcon(Icons.Outlined.PermPhoneMsg) }
-                override val summary = { summary }
-                override val enabled = { subscriptionInfos.isNotEmpty() }
-                override val onClick = {
-                    SubSettingLauncher(mContext).apply {
-                        setDestination(NetworkProviderCallsSmsFragment::class.qualifiedName)
-                        setSourceMetricsCategory(SettingsEnums.SETTINGS_NETWORK_CATEGORY)
-                    }.launch()
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        val viewModel by lazyViewModel
+
+        summaryFlow(viewModel.subscriptionInfoListFlow)
+            .collectLatestWithLifecycle(viewLifecycleOwner) { preference.summary = it }
+
+        viewModel.subscriptionInfoListFlow
+            .collectLatestWithLifecycle(viewLifecycleOwner) { subscriptionInfoList ->
+                if (!preference.isDisabledByAdmin) {
+                    preference.isEnabled = subscriptionInfoList.isNotEmpty()
                 }
-            },
-            restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)),
-        )
+            }
     }
 
     private fun summaryFlow(subscriptionInfoListFlow: Flow<List<SubscriptionInfo>>) = combine(
diff --git a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt
index d30b21d..ee88177 100644
--- a/src/com/android/settings/network/SubscriptionInfoListViewModel.kt
+++ b/src/com/android/settings/network/SubscriptionInfoListViewModel.kt
@@ -19,8 +19,10 @@
 import android.app.Application
 import android.telephony.SubscriptionInfo
 import android.telephony.SubscriptionManager
+
 import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.viewModelScope
+
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.asExecutor
 import kotlinx.coroutines.channels.awaitClose
@@ -32,13 +34,12 @@
 
 class SubscriptionInfoListViewModel(application: Application) : AndroidViewModel(application) {
     private val scope = viewModelScope + Dispatchers.Default
-
     val subscriptionInfoListFlow = callbackFlow<List<SubscriptionInfo>> {
         val subscriptionManager = application.getSystemService(SubscriptionManager::class.java)!!
 
         val listener = object : SubscriptionManager.OnSubscriptionsChangedListener() {
             override fun onSubscriptionsChanged() {
-                trySend(subscriptionManager.activeSubscriptionInfoList ?: emptyList())
+                trySend(SubscriptionUtil.getActiveSubscriptions(subscriptionManager))
             }
         }
 
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index 9974ba2..e416760 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -18,6 +18,8 @@
 
 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
 import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
+
 import static com.android.internal.util.CollectionUtils.emptyIfNull;
 
 import android.annotation.Nullable;
@@ -36,9 +38,11 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.flags.Flags;
 import com.android.settings.R;
 import com.android.settings.network.helper.SelectableSubscriptions;
 import com.android.settings.network.helper.SubscriptionAnnotation;
@@ -84,6 +88,8 @@
     }
 
     public static List<SubscriptionInfo> getActiveSubscriptions(SubscriptionManager manager) {
+        //TODO (b/315499317) : Refactor the subscription utils.
+
         if (sActiveResultsForTesting != null) {
             return sActiveResultsForTesting;
         }
@@ -94,7 +100,12 @@
         if (subscriptions == null) {
             return new ArrayList<>();
         }
-        return subscriptions;
+        // Since the SubscriptionManager.getActiveSubscriptionInfoList() has checked whether the
+        // sim visible by the SubscriptionManager.isSubscriptionVisible(), here only checks whether
+        // the esim visible here.
+        return subscriptions.stream()
+                .filter(subInfo -> subInfo != null && isEmbeddedSubscriptionVisible(subInfo))
+                .collect(Collectors.toList());
     }
 
     /**
@@ -128,7 +139,7 @@
     }
 
     /**
-     * Get subscription which is available to be displayed to the user
+     * Get subscriptionInfo which is available to be displayed to the user
      * per subscription id.
      *
      * @param context {@code Context}
@@ -138,13 +149,20 @@
      * @return {@code SubscriptionInfo} based on the given subscription id. Null of subscription
      *         is invalid or not allowed to be displayed to the user.
      */
-    public static SubscriptionInfo getAvailableSubscription(Context context,
+    public static SubscriptionInfo getAvailableSubscriptionBySubIdAndShowingForUser(Context context,
             ProxySubscriptionManager subscriptionManager, int subId) {
+        //TODO (b/315499317) : Refactor the subscription utils.
         final SubscriptionInfo subInfo = subscriptionManager.getAccessibleSubscriptionInfo(subId);
         if (subInfo == null) {
             return null;
         }
 
+        // hide provisioning/bootstrap and satellite profiles for user
+        if (!isEmbeddedSubscriptionVisible(subInfo)) {
+            Log.d(TAG, "Do not insert the provision eSIM or NTN eSim");
+            return null;
+        }
+
         final ParcelUuid groupUuid = subInfo.getGroupUuid();
 
         if (groupUuid != null) {
@@ -567,6 +585,12 @@
     public static boolean isSubscriptionVisible(
             SubscriptionManager subscriptionManager, Context context, SubscriptionInfo info) {
         if (info == null) return false;
+
+        // hide provisioning/bootstrap and satellite profiles for user
+        if (!isEmbeddedSubscriptionVisible(info)) {
+            return false;
+        }
+
         // If subscription is NOT grouped opportunistic subscription, it's visible.
         if (info.getGroupUuid() == null || !info.isOpportunistic()) return true;
 
@@ -786,4 +810,14 @@
         }
         return (currentSubInfo == null) ? null : currentSubInfo.getSubInfo();
     }
+
+    private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) {
+        if (subInfo.isEmbedded()
+                && (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING
+                || (Flags.oemEnabledSatelliteFlag()
+                && subInfo.isOnlyNonTerrestrialNetwork()))) {
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/src/com/android/settings/network/SubscriptionsPreferenceController.java b/src/com/android/settings/network/SubscriptionsPreferenceController.java
index 0c3e6bd..6601828 100644
--- a/src/com/android/settings/network/SubscriptionsPreferenceController.java
+++ b/src/com/android/settings/network/SubscriptionsPreferenceController.java
@@ -521,7 +521,7 @@
          * Uses to inject function and value for class and test class.
          */
         public boolean canSubscriptionBeDisplayed(Context context, int subId) {
-            return (SubscriptionUtil.getAvailableSubscription(context,
+            return (SubscriptionUtil.getAvailableSubscriptionBySubIdAndShowingForUser(context,
                     ProxySubscriptionManager.getInstance(context), subId) != null);
         }
 
diff --git a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
index 3ec7c92..b5e76920 100644
--- a/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
+++ b/src/com/android/settings/privatespace/PrivateSpaceMaintainer.java
@@ -34,6 +34,7 @@
 import android.util.Log;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -226,6 +227,26 @@
                 HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
     }
 
+    /**
+     * Returns true if private space exists and quiet mode is successfully enabled, otherwise
+     * returns false
+     */
+    public synchronized boolean lockPrivateSpace() {
+        if (isPrivateProfileRunning()) {
+            return mUserManager.requestQuietModeEnabled(true, mUserHandle);
+        }
+        return false;
+    }
+
+    /** Returns true if private space exists and is running, otherwise returns false */
+    @VisibleForTesting
+    synchronized boolean isPrivateProfileRunning() {
+        if (doesPrivateSpaceExist() && mUserHandle != null) {
+            return mUserManager.isUserRunning(mUserHandle);
+        }
+        return false;
+    }
+
     private void resetPrivateSpaceSettings() {
         setHidePrivateSpaceEntryPointSetting(HIDE_PRIVATE_SPACE_ENTRY_POINT_DISABLED_VAL);
     }
diff --git a/src/com/android/settings/privatespace/SetupSuccessFragment.java b/src/com/android/settings/privatespace/SetupSuccessFragment.java
index 0b1b9d9..ebeae7a 100644
--- a/src/com/android/settings/privatespace/SetupSuccessFragment.java
+++ b/src/com/android/settings/privatespace/SetupSuccessFragment.java
@@ -84,6 +84,8 @@
             if (activity != null) {
                 mMetricsFeatureProvider.action(
                         getContext(), SettingsEnums.ACTION_PRIVATE_SPACE_SETUP_DONE);
+                //TODO(b/307729746): Add a test to verify PS is locked after setup completion.
+                PrivateSpaceMaintainer.getInstance(activity).lockPrivateSpace();
                 Intent allAppsIntent = new Intent(Intent.ACTION_ALL_APPS);
                 ResolveInfo resolveInfo =
                         activity.getPackageManager()
diff --git a/src/com/android/settings/slices/SliceBackgroundWorker.java b/src/com/android/settings/slices/SliceBackgroundWorker.java
index 2b02999..5145f18 100644
--- a/src/com/android/settings/slices/SliceBackgroundWorker.java
+++ b/src/com/android/settings/slices/SliceBackgroundWorker.java
@@ -29,6 +29,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import java.io.Closeable;
 import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
@@ -172,7 +174,8 @@
     /**
      * Notify that data was updated and attempt to sync changes to the Slice.
      */
-    protected final void notifySliceChange() {
+    @VisibleForTesting
+    public final void notifySliceChange() {
         NotifySliceChangeHandler.getInstance().updateSlice(this);
     }
 
diff --git a/src/com/android/settings/spa/SpaActivity.kt b/src/com/android/settings/spa/SpaActivity.kt
index e5bee8b..5eade81 100644
--- a/src/com/android/settings/spa/SpaActivity.kt
+++ b/src/com/android/settings/spa/SpaActivity.kt
@@ -18,9 +18,11 @@
 
 import android.content.Context
 import android.content.Intent
+import android.os.Bundle
 import android.util.Log
 import androidx.annotation.VisibleForTesting
 import com.android.settings.spa.app.appinfo.AppInfoSettingsProvider
+import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin
 import com.android.settingslib.spa.framework.BrowseActivity
 import com.android.settingslib.spa.framework.common.SettingsPage
 import com.android.settingslib.spa.framework.util.SESSION_BROWSE
@@ -31,6 +33,11 @@
     override fun isPageEnabled(page: SettingsPage) =
         super.isPageEnabled(page) && !isSuwAndPageBlocked(page.sppName)
 
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        lifecycle.addObserver(HideNonSystemOverlayMixin(this))
+    }
+
     companion object {
         private const val TAG = "SpaActivity"
 
diff --git a/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
new file mode 100644
index 0000000..913da65
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppArchiveButton.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInstaller
+import android.os.UserHandle
+import android.util.Log
+import android.widget.Toast
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.CloudUpload
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.settings.R
+import com.android.settingslib.spa.widget.button.ActionButton
+import com.android.settingslib.spaprivileged.framework.compose.DisposableBroadcastReceiverAsUser
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+
+class AppArchiveButton(packageInfoPresenter: PackageInfoPresenter) {
+    private companion object {
+        private const val LOG_TAG = "AppArchiveButton"
+        private const val INTENT_ACTION = "com.android.settings.archive.action"
+    }
+
+    private val context = packageInfoPresenter.context
+    private val appButtonRepository = AppButtonRepository(context)
+    private val userPackageManager = packageInfoPresenter.userPackageManager
+    private val packageInstaller = userPackageManager.packageInstaller
+    private val packageName = packageInfoPresenter.packageName
+    private val userHandle = UserHandle.of(packageInfoPresenter.userId)
+    private var broadcastReceiverIsCreated = false
+
+    @Composable
+    fun getActionButton(app: ApplicationInfo): ActionButton {
+        if (!broadcastReceiverIsCreated) {
+            val intentFilter = IntentFilter(INTENT_ACTION)
+            DisposableBroadcastReceiverAsUser(intentFilter, userHandle) { intent ->
+                if (app.packageName == intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)) {
+                    onReceive(intent, app)
+                }
+            }
+            broadcastReceiverIsCreated = true
+        }
+        return ActionButton(
+            text = context.getString(R.string.archive),
+            imageVector = Icons.Outlined.CloudUpload,
+            enabled = remember(app) {
+                flow {
+                    emit(
+                        app.isActionButtonEnabled() && appButtonRepository.isAllowUninstallOrArchive(
+                            context,
+                            app
+                        )
+                    )
+                }.flowOn(Dispatchers.Default)
+            }.collectAsStateWithLifecycle(false).value
+        ) { onArchiveClicked(app) }
+    }
+
+    private fun ApplicationInfo.isActionButtonEnabled(): Boolean {
+        return !isArchived
+            && userPackageManager.isAppArchivable(packageName)
+            // We apply the same device policy for both the uninstallation and archive
+            // button.
+            && !appButtonRepository.isUninstallBlockedByAdmin(this)
+    }
+
+    private fun onArchiveClicked(app: ApplicationInfo) {
+        val intent = Intent(INTENT_ACTION)
+        intent.setPackage(context.packageName)
+        val pendingIntent = PendingIntent.getBroadcastAsUser(
+            context, 0, intent,
+            // FLAG_MUTABLE is required by PackageInstaller#requestArchive
+            PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_MUTABLE,
+            userHandle
+        )
+        try {
+            packageInstaller.requestArchive(app.packageName, pendingIntent.intentSender, 0)
+        } catch (e: Exception) {
+            Log.e(LOG_TAG, "Request archive failed", e)
+            Toast.makeText(
+                context,
+                context.getString(R.string.archiving_failed),
+                Toast.LENGTH_SHORT
+            ).show()
+        }
+    }
+
+    private fun onReceive(intent: Intent, app: ApplicationInfo) {
+        when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Int.MIN_VALUE)) {
+            PackageInstaller.STATUS_SUCCESS -> {
+                val appLabel = userPackageManager.getApplicationLabel(app)
+                Toast.makeText(
+                    context,
+                    context.getString(R.string.archiving_succeeded, appLabel),
+                    Toast.LENGTH_SHORT
+                ).show()
+            }
+
+            else -> {
+                Log.e(LOG_TAG, "Request archiving failed for $packageName with code $status")
+                Toast.makeText(
+                    context,
+                    context.getString(R.string.archiving_failed),
+                    Toast.LENGTH_SHORT
+                ).show()
+            }
+        }
+    }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
index 2383ddb..f01c31c 100644
--- a/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppButtonRepository.kt
@@ -19,6 +19,7 @@
 import android.app.ActivityManager
 import android.content.ComponentName
 import android.content.Context
+import android.content.om.OverlayManager
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
 import android.content.pm.ResolveInfo
@@ -26,7 +27,9 @@
 import com.android.settingslib.RestrictedLockUtilsInternal
 import com.android.settingslib.Utils
 import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
+import com.android.settingslib.spaprivileged.model.app.hasFlag
 import com.android.settingslib.spaprivileged.model.app.isDisallowControl
+import com.android.settingslib.spaprivileged.model.app.userHandle
 import com.android.settingslib.spaprivileged.model.app.userId
 
 class AppButtonRepository(private val context: Context) {
@@ -77,6 +80,55 @@
         false
     }
 
+    /** Gets whether a package can be uninstalled or archived. */
+    fun isAllowUninstallOrArchive(
+        context: Context, app: ApplicationInfo
+    ): Boolean {
+        val overlayManager = checkNotNull(context.getSystemService(OverlayManager::class.java))
+        when {
+            !app.hasFlag(ApplicationInfo.FLAG_INSTALLED) && !app.isArchived -> return false
+
+            com.android.settings.Utils.isProfileOrDeviceOwner(
+                context.devicePolicyManager, app.packageName, app.userId
+            ) -> return false
+
+            isDisallowControl(app) -> return false
+
+            uninstallDisallowedDueToHomeApp(app.packageName) -> return false
+
+            // Resource overlays can be uninstalled iff they are public (installed on /data) and
+            // disabled. ("Enabled" means they are in use by resource management.)
+            app.isEnabledResourceOverlay(overlayManager) -> return false
+
+            else -> return true
+        }
+    }
+
+    /**
+     * Checks whether the given package cannot be uninstalled due to home app restrictions.
+     *
+     * Home launcher apps need special handling, we can't allow uninstallation of the only home
+     * app, and we don't want to allow uninstallation of an explicitly preferred one -- the user
+     * can go to Home settings and pick a different one, after which we'll permit uninstallation
+     * of the now-not-default one.
+     */
+    private fun uninstallDisallowedDueToHomeApp(packageName: String): Boolean {
+        val homePackageInfo = getHomePackageInfo()
+        return when {
+            packageName !in homePackageInfo.homePackages -> false
+
+            // Disallow uninstall when this is the only home app.
+            homePackageInfo.homePackages.size == 1 -> true
+
+            // Disallow if this is the explicit default home app.
+            else -> packageName == homePackageInfo.currentDefaultHome?.packageName
+        }
+    }
+
+    private fun ApplicationInfo.isEnabledResourceOverlay(overlayManager: OverlayManager): Boolean =
+        isResourceOverlay &&
+            overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true
+
     data class HomePackages(
         val homePackages: Set<String>,
         val currentDefaultHome: ComponentName?,
diff --git a/src/com/android/settings/spa/app/appinfo/AppButtons.kt b/src/com/android/settings/spa/app/appinfo/AppButtons.kt
index 307ff11..f6fafd7 100644
--- a/src/com/android/settings/spa/app/appinfo/AppButtons.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppButtons.kt
@@ -30,7 +30,10 @@
 /**
  * @param featureFlags can be overridden in tests
  */
-fun AppButtons(packageInfoPresenter: PackageInfoPresenter, featureFlags: FeatureFlags = FeatureFlagsImpl()) {
+fun AppButtons(
+    packageInfoPresenter: PackageInfoPresenter,
+    featureFlags: FeatureFlags = FeatureFlagsImpl()
+) {
     if (remember(packageInfoPresenter) { packageInfoPresenter.isMainlineModule() }) return
     val presenter = remember { AppButtonsPresenter(packageInfoPresenter, featureFlags) }
     ActionButtons(actionButtons = presenter.getActionButtons())
@@ -49,6 +52,7 @@
     private val appUninstallButton = AppUninstallButton(packageInfoPresenter)
     private val appClearButton = AppClearButton(packageInfoPresenter)
     private val appForceStopButton = AppForceStopButton(packageInfoPresenter)
+    private val appArchiveButton = AppArchiveButton(packageInfoPresenter)
 
     @Composable
     fun getActionButtons() =
@@ -58,7 +62,11 @@
 
     @Composable
     private fun getActionButtons(app: ApplicationInfo): List<ActionButton> = listOfNotNull(
-        if (featureFlags.archiving()) null else appLaunchButton.getActionButton(app),
+        if (featureFlags.archiving()) {
+            appArchiveButton.getActionButton(app)
+        } else {
+            appLaunchButton.getActionButton(app)
+        },
         appInstallButton.getActionButton(app),
         appDisableButton.getActionButton(app),
         appUninstallButton.getActionButton(app),
diff --git a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
index 5f6f097..ce72840 100644
--- a/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppUninstallButton.kt
@@ -18,7 +18,6 @@
 
 import android.app.settings.SettingsEnums
 import android.content.Intent
-import android.content.om.OverlayManager
 import android.content.pm.ApplicationInfo
 import android.os.UserHandle
 import android.os.UserManager
@@ -28,11 +27,8 @@
 import androidx.compose.ui.res.vectorResource
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.settings.R
-import com.android.settings.Utils
 import com.android.settings.applications.specialaccess.deviceadmin.DeviceAdminAdd
 import com.android.settingslib.spa.widget.button.ActionButton
-import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager
-import com.android.settingslib.spaprivileged.model.app.hasFlag
 import com.android.settingslib.spaprivileged.model.app.isActiveAdmin
 import com.android.settingslib.spaprivileged.model.app.userHandle
 import kotlinx.coroutines.Dispatchers
@@ -42,7 +38,6 @@
 class AppUninstallButton(private val packageInfoPresenter: PackageInfoPresenter) {
     private val context = packageInfoPresenter.context
     private val appButtonRepository = AppButtonRepository(context)
-    private val overlayManager = context.getSystemService(OverlayManager::class.java)!!
     private val userManager = context.getSystemService(UserManager::class.java)!!
 
     @Composable
@@ -51,49 +46,6 @@
         return uninstallButton(app)
     }
 
-    /** Gets whether a package can be uninstalled. */
-    private fun isUninstallButtonEnabled(app: ApplicationInfo): Boolean = when {
-        !app.hasFlag(ApplicationInfo.FLAG_INSTALLED) -> false
-
-        Utils.isProfileOrDeviceOwner(
-            context.devicePolicyManager, app.packageName, packageInfoPresenter.userId) -> false
-
-        appButtonRepository.isDisallowControl(app) -> false
-
-        uninstallDisallowedDueToHomeApp(app.packageName) -> false
-
-        // Resource overlays can be uninstalled iff they are public (installed on /data) and
-        // disabled. ("Enabled" means they are in use by resource management.)
-        app.isEnabledResourceOverlay() -> false
-
-        else -> true
-    }
-
-    /**
-     * Checks whether the given package cannot be uninstalled due to home app restrictions.
-     *
-     * Home launcher apps need special handling, we can't allow uninstallation of the only home
-     * app, and we don't want to allow uninstallation of an explicitly preferred one -- the user
-     * can go to Home settings and pick a different one, after which we'll permit uninstallation
-     * of the now-not-default one.
-     */
-    private fun uninstallDisallowedDueToHomeApp(packageName: String): Boolean {
-        val homePackageInfo = appButtonRepository.getHomePackageInfo()
-        return when {
-            packageName !in homePackageInfo.homePackages -> false
-
-            // Disallow uninstall when this is the only home app.
-            homePackageInfo.homePackages.size == 1 -> true
-
-            // Disallow if this is the explicit default home app.
-            else -> packageName == homePackageInfo.currentDefaultHome?.packageName
-        }
-    }
-
-    private fun ApplicationInfo.isEnabledResourceOverlay(): Boolean =
-        isResourceOverlay &&
-            overlayManager.getOverlayInfo(packageName, userHandle)?.isEnabled == true
-
     @Composable
     private fun uninstallButton(app: ApplicationInfo) = ActionButton(
         text = if (isCloneApp(app)) context.getString(R.string.delete) else
@@ -101,7 +53,7 @@
         imageVector = ImageVector.vectorResource(R.drawable.ic_settings_delete),
         enabled = remember(app) {
             flow {
-                emit(isUninstallButtonEnabled(app))
+                emit(appButtonRepository.isAllowUninstallOrArchive(context, app))
             }.flowOn(Dispatchers.Default)
         }.collectAsStateWithLifecycle(false).value,
     ) { onUninstallClicked(app) }
diff --git a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
index a6bd8f0..8c802d1 100644
--- a/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
+++ b/src/com/android/settings/spa/app/appinfo/PackageInfoPresenter.kt
@@ -87,19 +87,9 @@
     ).filter(::isInterestedAppChange).filter(::isForThisApp)
 
     @VisibleForTesting
-    fun isInterestedAppChange(intent: Intent) = when {
-        intent.action != Intent.ACTION_PACKAGE_REMOVED -> true
-
-        // filter out the fully removed case, in which the page will be closed, so no need to
-        // refresh
-        intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false) -> false
-
-        // filter out the updates are uninstalled (system app), which will followed by a replacing
-        // broadcast, we can refresh at that time
-        intent.getBooleanExtra(Intent.EXTRA_REPLACING, false) -> false
-
-        else -> true // App archived
-    }
+    fun isInterestedAppChange(intent: Intent) =
+        intent.action != Intent.ACTION_PACKAGE_REMOVED ||
+            intent.getBooleanExtra(Intent.EXTRA_ARCHIVAL, false)
 
     val flow: StateFlow<PackageInfo?> = merge(flowOf(null), appChangeFlow)
         .map { getPackageInfo() }
diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
index 5e90e55..ff851eb 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java
@@ -262,6 +262,13 @@
         mFragment.onResume();
     }
 
+    @Test
+    public void testFragmentVisibleWhenNoHardwareDetected() {
+        doReturn(false).when(mFingerprintManager).isHardwareDetected();
+        setUpFragment(false);
+        assertThat(mFragment.isVisible()).isTrue();
+    }
+
     private void setSensor(@FingerprintSensorProperties.SensorType int sensorType) {
         final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
         props.add(new FingerprintSensorPropertiesInternal(
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
index 4d9eaca..fa6cc6c 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDialogFragmentTest.java
@@ -41,7 +41,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -86,6 +85,7 @@
 
     @Before
     public void setUp() {
+        ShadowAlertDialogCompat.reset();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -98,11 +98,6 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
-    @After
-    public void tearDown() {
-        ShadowAlertDialogCompat.reset();
-    }
-
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     public void onCreateDialog_flagOff_dialogNotExist() {
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
index 71b681a..335bbe3 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingDisconnectDialogFragmentTest.java
@@ -38,7 +38,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -79,6 +78,7 @@
 
     @Before
     public void setUp() {
+        ShadowAlertDialogCompat.reset();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -96,16 +96,11 @@
         shadowMainLooper().idle();
     }
 
-    @After
-    public void tearDown() {
-        ShadowAlertDialogCompat.reset();
-    }
-
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     public void onCreateDialog_flagOff_dialogNotExist() {
         AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
-        assertThat(dialog).isNotNull();
+        assertThat(dialog).isNull();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
index 5eb0e8a..38f80e0 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingJoinDialogFragmentTest.java
@@ -38,7 +38,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -77,6 +76,7 @@
 
     @Before
     public void setUp() {
+        ShadowAlertDialogCompat.reset();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -89,11 +89,6 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
-    @After
-    public void tearDown() {
-        ShadowAlertDialogCompat.reset();
-    }
-
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     public void onCreateDialog_flagOff_dialogNotExist() {
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
index 1de7b2d..61bc88a 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/AudioSharingStopDialogFragmentTest.java
@@ -35,7 +35,6 @@
 import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -70,6 +69,7 @@
 
     @Before
     public void setUp() {
+        ShadowAlertDialogCompat.reset();
         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
         mShadowBluetoothAdapter.setEnabled(true);
         mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
@@ -82,11 +82,6 @@
                 mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
     }
 
-    @After
-    public void tearDown() {
-        ShadowAlertDialogCompat.reset();
-    }
-
     @Test
     @RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
     public void onCreateDialog_flagOff_dialogNotExist() {
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index d01d7e0..2f83da6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -49,7 +49,9 @@
 
 import com.android.settings.R;
 import com.android.settings.SettingsActivity;
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry;
 import com.android.settings.fuelgauge.batteryusage.BatteryEntry;
+import com.android.settings.fuelgauge.batteryusage.ConvertUtils;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.testutils.shadow.ShadowActivityManager;
 import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
@@ -91,11 +93,10 @@
     private static final String USAGE_PERCENT = "16%";
     private static final int ICON_ID = 123;
     private static final int UID = 1;
+    private static final long FOREGROUND_TIME_MS = 444;
+    private static final long FOREGROUND_SERVICE_TIME_MS = 123;
     private static final long BACKGROUND_TIME_MS = 100;
-    private static final long FOREGROUND_ACTIVITY_TIME_MS = 123;
-    private static final long FOREGROUND_SERVICE_TIME_MS = 444;
-    private static final long FOREGROUND_TIME_MS =
-            FOREGROUND_ACTIVITY_TIME_MS + FOREGROUND_SERVICE_TIME_MS;
+    private static final long SCREEN_ON_TIME_MS = 321;
     private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage";
 
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
@@ -105,7 +106,6 @@
     @Mock private LayoutPreference mHeaderPreference;
     @Mock private ApplicationsState mState;
     @Mock private ApplicationsState.AppEntry mAppEntry;
-    @Mock private Bundle mBundle;
     @Mock private BatteryEntry mBatteryEntry;
     @Mock private PackageManager mPackageManager;
     @Mock private InstallSourceInfo mInstallSourceInfo;
@@ -120,6 +120,8 @@
     private SettingsActivity mTestActivity;
     private FakeFeatureFactory mFeatureFactory;
     private MetricsFeatureProvider mMetricsFeatureProvider;
+    private BatteryDiffEntry mBatteryDiffEntry;
+    private Bundle mBundle;
 
     @Before
     public void setUp() {
@@ -131,6 +133,7 @@
         mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider;
 
         mFragment = spy(new AdvancedPowerUsageDetail());
+        mBundle = spy(new Bundle());
         doReturn(mContext).when(mFragment).getContext();
         doReturn(mActivity).when(mFragment).getActivity();
         doReturn(SUMMARY).when(mFragment).getString(anyInt());
@@ -163,10 +166,35 @@
 
         when(mBatteryEntry.getUid()).thenReturn(UID);
         when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL);
-        when(mBatteryEntry.getTimeInBackgroundMs()).thenReturn(BACKGROUND_TIME_MS);
         when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(FOREGROUND_TIME_MS);
+        when(mBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(FOREGROUND_SERVICE_TIME_MS);
+        when(mBatteryEntry.getTimeInBackgroundMs()).thenReturn(BACKGROUND_TIME_MS);
         mBatteryEntry.mIconId = ICON_ID;
 
+        mBatteryDiffEntry =
+                spy(
+                        new BatteryDiffEntry(
+                                mContext,
+                                /* uid= */ UID,
+                                /* userId= */ 0,
+                                /* key= */ "key",
+                                /* isHidden= */ false,
+                                /* componentId= */ -1,
+                                /* legacyPackageName= */ null,
+                                /* legacyLabel= */ null,
+                                /*consumerType*/ ConvertUtils.CONSUMER_TYPE_USER_BATTERY,
+                                /* foregroundUsageTimeInMs= */ FOREGROUND_TIME_MS,
+                                /* foregroundSerUsageTimeInMs= */ FOREGROUND_SERVICE_TIME_MS,
+                                /* backgroundUsageTimeInMs= */ BACKGROUND_TIME_MS,
+                                /* screenOnTimeInMs= */ SCREEN_ON_TIME_MS,
+                                /* consumePower= */ 0,
+                                /* foregroundUsageConsumePower= */ 0,
+                                /* foregroundServiceUsageConsumePower= */ 0,
+                                /* backgroundUsageConsumePower= */ 0,
+                                /* cachedUsageConsumePower= */ 0));
+        when(mBatteryDiffEntry.getAppLabel()).thenReturn(APP_LABEL);
+        when(mBatteryDiffEntry.getAppIconId()).thenReturn(ICON_ID);
+
         mFragment.mHeaderPreference = mHeaderPreference;
         mFragment.mState = mState;
         mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils;
@@ -191,6 +219,7 @@
                 .when(mActivity)
                 .startActivityAsUser(captor.capture(), nullable(UserHandle.class));
         doAnswer(callable).when(mActivity).startActivity(captor.capture());
+        doAnswer(callable).when(mContext).startActivity(captor.capture());
 
         mAllowBackgroundUsagePreference = new PrimarySwitchPreference(mContext);
         mAllowBackgroundUsagePreference.setKey(KEY_ALLOW_BACKGROUND_USAGE);
@@ -256,6 +285,7 @@
 
     @Test
     public void startBatteryDetailPage_invalidToShowSummary_noFGBDData() {
+        mBundle.clear();
         AdvancedPowerUsageDetail.startBatteryDetailPage(
                 mActivity, mFragment, mBatteryEntry, USAGE_PERCENT);
 
@@ -268,6 +298,35 @@
     }
 
     @Test
+    public void startBatteryDetailPage_showSummary_hasFGBDData() {
+        final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        mBundle.clear();
+        AdvancedPowerUsageDetail.startBatteryDetailPage(
+                mContext,
+                mFragment.getMetricsCategory(),
+                mBatteryDiffEntry,
+                USAGE_PERCENT,
+                /* slotInformation= */ null,
+                /* showTimeInformation= */ true,
+                /* anomalyHintPrefKey= */ null,
+                /* anomalyHintText= */ null);
+
+        verify(mContext).startActivity(captor.capture());
+        assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID);
+        assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_BACKGROUND_TIME))
+                .isEqualTo(BACKGROUND_TIME_MS + FOREGROUND_SERVICE_TIME_MS);
+        assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_FOREGROUND_TIME))
+                .isEqualTo(FOREGROUND_TIME_MS);
+        assertThat(mBundle.getLong(AdvancedPowerUsageDetail.EXTRA_SCREEN_ON_TIME))
+                .isEqualTo(SCREEN_ON_TIME_MS);
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT))
+                .isEqualTo(USAGE_PERCENT);
+        assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME))
+                .isEqualTo(null);
+    }
+
+
+    @Test
     public void startBatteryDetailPage_noBatteryUsage_hasBasicData() {
         final ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
 
@@ -292,6 +351,7 @@
     @Test
     public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName()
             throws PackageManager.NameNotFoundException {
+        mBundle.clear();
         doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */);
 
         AdvancedPowerUsageDetail.startBatteryDetailPage(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
index ffe3c44..d351ca3 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffDataTest.java
@@ -207,6 +207,7 @@
                 batteryHistEntry.mAppLabel,
                 batteryHistEntry.mConsumerType,
                 /* foregroundUsageTimeInMs= */ 0,
+                /* foregroundServiceUsageTimeInMs= */ 0,
                 /* backgroundUsageTimeInMs= */ 0,
                 /* screenOnTimeInMs= */ 0,
                 consumePower,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
index d8b733c..6f1dce6 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryDiffEntryTest.java
@@ -111,8 +111,9 @@
                         /* legacyLabel= */ null,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
                         /* foregroundUsageTimeInMs= */ 10001L,
-                        /* backgroundUsageTimeInMs= */ 20002L,
-                        /* screenOnTimeInMs= */ 30003L,
+                        /* foregroundServiceUsageTimeInMs= */ 20002L,
+                        /* backgroundUsageTimeInMs= */ 30003L,
+                        /* screenOnTimeInMs= */ 40004L,
                         /* consumePower= */ 22.0,
                         /* foregroundUsageConsumePower= */ 10.0,
                         /* foregroundServiceUsageConsumePower= */ 10.0,
@@ -137,8 +138,9 @@
                         /* legacyLabel= */ null,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
                         /* foregroundUsageTimeInMs= */ 10001L,
-                        /* backgroundUsageTimeInMs= */ 20002L,
-                        /* screenOnTimeInMs= */ 30003L,
+                        /* foregroundServiceUsageTimeInMs= */ 20002L,
+                        /* backgroundUsageTimeInMs= */ 30003L,
+                        /* screenOnTimeInMs= */ 40004L,
                         /* consumePower= */ 22.0,
                         /* foregroundUsageConsumePower= */ 10.0,
                         /* foregroundServiceUsageConsumePower= */ 10.0,
@@ -165,6 +167,7 @@
                         /* legacyLabel= */ BatteryDiffEntry.SYSTEM_APPS_KEY,
                         /*consumerType*/ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0,
                         /* screenOnTimeInMs= */ 0,
                         /* consumePower= */ 0,
@@ -552,6 +555,7 @@
                 /* legacyLabel= */ null,
                 /*consumerType*/ consumerType,
                 /* foregroundUsageTimeInMs= */ 0,
+                /* foregroundServiceUsageTimeInMs= */ 0,
                 /* backgroundUsageTimeInMs= */ 0,
                 /* screenOnTimeInMs= */ 0,
                 /* consumePower= */ 0,
@@ -576,6 +580,7 @@
                         batteryHistEntry.mConsumerType,
                         /* foregroundUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* screenOnTimeInMs= */ 0,
                         consumePower,
                         /* foregroundUsageConsumePower= */ 0,
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryEntryTest.java
index 83b4458..450d058 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryEntryTest.java
@@ -195,7 +195,8 @@
 
     @Test
     public void getTimeInForegroundMs_app() {
-        when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
+        when(mUidBatteryConsumer.getTimeInProcessStateMs(
+                        UidBatteryConsumer.PROCESS_STATE_FOREGROUND))
                 .thenReturn(100L);
 
         final BatteryEntry entry =
@@ -226,8 +227,9 @@
 
     @Test
     public void getTimeInBackgroundMs_app() {
-        when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
-                .thenReturn(100L);
+        when(mUidBatteryConsumer.getTimeInProcessStateMs(
+                        UidBatteryConsumer.PROCESS_STATE_BACKGROUND))
+                .thenReturn(30L);
 
         final BatteryEntry entry =
                 new BatteryEntry(
@@ -239,7 +241,26 @@
                         null,
                         null);
 
-        assertThat(entry.getTimeInBackgroundMs()).isEqualTo(100L);
+        assertThat(entry.getTimeInBackgroundMs()).isEqualTo(30L);
+    }
+
+    @Test
+    public void getTimeInForegroundServiceMs_app() {
+        when(mUidBatteryConsumer.getTimeInProcessStateMs(
+                        UidBatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE))
+                .thenReturn(70L);
+
+        final BatteryEntry entry =
+                new BatteryEntry(
+                        RuntimeEnvironment.application,
+                        mMockUserManager,
+                        mUidBatteryConsumer,
+                        false,
+                        0,
+                        null,
+                        null);
+
+        assertThat(entry.getTimeInForegroundServiceMs()).isEqualTo(70L);
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
index 02800f7..dc868c8 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryHistEntryTest.java
@@ -65,6 +65,7 @@
         when(mMockBatteryEntry.getConsumedPowerInCached()).thenReturn(1.5);
         mMockBatteryEntry.mPercent = 0.3;
         when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
+        when(mMockBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(3456L);
         when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
         when(mMockBatteryEntry.getPowerComponentId()).thenReturn(expectedType);
         when(mMockBatteryEntry.getConsumerType())
@@ -105,6 +106,7 @@
                         /* backgroundUsageConsumePower= */ 1.4,
                         /* cachedUsageConsumePower= */ 1.5,
                         /* foregroundUsageTimeInMs= */ 1234L,
+                        /* foregroundServiceUsageTimeInMs= */ 3456L,
                         /* backgroundUsageTimeInMs= */ 5689L,
                         /* batteryLevel= */ 12),
                 /* drainType= */ 3,
@@ -211,6 +213,7 @@
                         /* backgroundUsageConsumePower= */ 3,
                         /* cachedUsageConsumePower= */ 4,
                         /* foregroundUsageTimeInMs= */ 100,
+                        /* foregroundServiceUsageTimeInMs= */ 150,
                         /* backgroundUsageTimeInMs= */ 200,
                         /* batteryLevel= */ 90);
         final BatteryHistEntry upperHistEntry =
@@ -224,6 +227,7 @@
                         /* backgroundUsageConsumePower= */ 6,
                         /* cachedUsageConsumePower= */ 5,
                         /* foregroundUsageTimeInMs= */ 200,
+                        /* foregroundServiceUsageTimeInMs= */ 250,
                         /* backgroundUsageTimeInMs= */ 300,
                         /* batteryLevel= */ 80);
 
@@ -244,6 +248,7 @@
                 /* backgroundUsageConsumePower= */ 3 + 0.5 * (6 - 3),
                 /* cachedUsageConsumePower= */ 4 + 0.5 * (5 - 4),
                 /* foregroundUsageTimeInMs= */ Math.round(100 + 0.5 * (200 - 100)),
+                /* foregroundServiceUsageTimeInMs= */ Math.round(150 + 0.5 * (250 - 150)),
                 /* backgroundUsageTimeInMs= */ Math.round(200 + 0.5 * (300 - 200)),
                 /* batteryLevel= */ (int) Math.round(90 + 0.5 * (80 - 90)));
     }
@@ -264,6 +269,7 @@
                         /* backgroundUsageConsumePower= */ 6,
                         /* cachedUsageConsumePower= */ 5,
                         /* foregroundUsageTimeInMs= */ 200,
+                        /* foregroundServiceUsageTimeInMs= */ 250,
                         /* backgroundUsageTimeInMs= */ 300,
                         /* batteryLevel= */ 80);
 
@@ -288,6 +294,7 @@
                 /* backgroundUsageConsumePower= */ 0.5 * 6,
                 /* cachedUsageConsumePower= */ 0.5 * 5,
                 /* foregroundUsageTimeInMs= */ Math.round(0.5 * 200),
+                /* foregroundServiceUsageTimeInMs= */ Math.round(0.5 * 250),
                 /* backgroundUsageTimeInMs= */ Math.round(0.5 * 300),
                 /* batteryLevel= */ upperHistEntry.mBatteryLevel);
     }
@@ -317,6 +324,7 @@
                 /* backgroundUsageConsumePower= */ 1.4,
                 /* cachedUsageConsumePower= */ 1.5,
                 /* foregroundUsageTimeInMs= */ 1234L,
+                /*foregroundServiceUsageTimeInMs=*/ 3456L,
                 /* backgroundUsageTimeInMs= */ 5689L,
                 /* batteryLevel= */ 12);
     }
@@ -334,6 +342,7 @@
             double backgroundUsageConsumePower,
             double cachedUsageConsumePower,
             long foregroundUsageTimeInMs,
+            long foregroundServiceUsageTimeInMs,
             long backgroundUsageTimeInMs,
             int batteryLevel) {
         assertThat(entry.isValidEntry()).isTrue();
@@ -354,6 +363,7 @@
         assertThat(entry.mCachedUsageConsumePower).isEqualTo(cachedUsageConsumePower);
         assertThat(entry.mPercentOfTotal).isEqualTo(percentOfTotal);
         assertThat(entry.mForegroundUsageTimeInMs).isEqualTo(foregroundUsageTimeInMs);
+        assertThat(entry.mForegroundServiceUsageTimeInMs).isEqualTo(foregroundServiceUsageTimeInMs);
         assertThat(entry.mBackgroundUsageTimeInMs).isEqualTo(backgroundUsageTimeInMs);
         assertThat(entry.mDrainType).isEqualTo(drainType);
         assertThat(entry.mConsumerType).isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
@@ -372,6 +382,7 @@
             double backgroundUsageConsumePower,
             double cachedUsageConsumePower,
             long foregroundUsageTimeInMs,
+            long foregroundServiceUsageTimeInMs,
             long backgroundUsageTimeInMs,
             int batteryLevel) {
         final MatrixCursor cursor =
@@ -406,6 +417,7 @@
                         .setPercentOfTotal(0.3)
                         .setDrainType(3)
                         .setForegroundUsageTimeInMs(foregroundUsageTimeInMs)
+                        .setForegroundServiceUsageTimeInMs(foregroundServiceUsageTimeInMs)
                         .setBackgroundUsageTimeInMs(backgroundUsageTimeInMs)
                         .build();
         cursor.addRow(
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
index fc30702..5704be9 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageBreakdownControllerTest.java
@@ -100,7 +100,8 @@
                         /* legacyLabel= */ null,
                         /* consumerType= */ ConvertUtils.CONSUMER_TYPE_UID_BATTERY,
                         /* foregroundUsageTimeInMs= */ 1,
-                        /* backgroundUsageTimeInMs= */ 2,
+                        /* foregroundServiceUsageTimeInMs= */ 2,
+                        /* backgroundUsageTimeInMs= */ 3,
                         /* screenOnTimeInMs= */ 0,
                         /* consumePower= */ 3,
                         /* foregroundUsageConsumePower= */ 0,
@@ -275,6 +276,7 @@
                         /* isSystem= */ true,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0);
         batteryDiffEntry.mConsumePower = 0.8;
         batteryDiffEntry.setTotalConsumePower(100);
@@ -293,6 +295,7 @@
                         /* isSystem= */ true,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0);
         batteryDiffEntry.mConsumePower = 16;
         batteryDiffEntry.setTotalConsumePower(100);
@@ -314,6 +317,7 @@
                         /* isSystem= */ true,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
         assertThat(pref.getSummary().toString().isEmpty()).isTrue();
     }
@@ -329,6 +333,7 @@
                         /* isSystem= */ true,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
         assertThat(pref.getSummary().toString()).isEqualTo("Total: less than a min");
     }
@@ -344,6 +349,7 @@
                         /* isSystem= */ true,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS * 2,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
         assertThat(pref.getSummary().toString()).isEqualTo("Total: 2 min");
     }
@@ -359,6 +365,7 @@
                         /* isSystem= */ false,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
         assertThat(pref.getSummary().toString().isEmpty()).isTrue();
     }
@@ -374,11 +381,28 @@
                         /* isSystem= */ false,
                         /* screenOnTimeInMs= */ 0,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS));
         assertThat(pref.getSummary().toString()).isEqualTo("Background: 1 min");
     }
 
     @Test
+    public void setPreferenceSummary_appEntryWithFGSTime_expectedSummary() {
+        final PowerGaugePreference pref = new PowerGaugePreference(mContext);
+        pref.setSummary(PREF_SUMMARY);
+
+        mBatteryUsageBreakdownController.setPreferenceSummary(
+                pref,
+                createBatteryDiffEntry(
+                        /* isSystem= */ false,
+                        /* screenOnTimeInMs= */ 0,
+                        /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS / 2,
+                        /* backgroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS / 2));
+        assertThat(pref.getSummary().toString()).isEqualTo("Background: 1 min");
+    }
+
+    @Test
     public void setPreferenceSummary_appEntryScreenOnTimeOnly_expectedSummary() {
         final PowerGaugePreference pref = new PowerGaugePreference(mContext);
         pref.setSummary(PREF_SUMMARY);
@@ -389,6 +413,7 @@
                         /* isSystem= */ false,
                         /* screenOnTimeInMs= */ DateUtils.MINUTE_IN_MILLIS,
                         /* foregroundUsageTimeInMs= */ 0,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ 0));
         assertThat(pref.getSummary().toString()).isEqualTo("Screen time: 1 min");
     }
@@ -404,6 +429,7 @@
                         /* isSystem= */ false,
                         /* screenOnTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1,
                         /* foregroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1,
+                        /* foregroundServiceUsageTimeInMs= */ 0,
                         /* backgroundUsageTimeInMs= */ DateUtils.MINUTE_IN_MILLIS - 1));
         assertThat(pref.getSummary().toString())
                 .isEqualTo("Screen time: less than a min\nBackground: less than a min");
@@ -413,6 +439,7 @@
             boolean isSystem,
             long screenOnTimeInMs,
             long foregroundUsageTimeInMs,
+            long foregroundServiceUsageTimeInMs,
             long backgroundUsageTimeInMs) {
         final ContentValues contentValues = new ContentValues();
         contentValues.put(
@@ -435,6 +462,7 @@
                 batteryHistEntry.mConsumerType,
                 foregroundUsageTimeInMs,
                 backgroundUsageTimeInMs,
+                foregroundServiceUsageTimeInMs,
                 screenOnTimeInMs,
                 /* consumePower= */ 0,
                 /* foregroundUsageConsumePower= */ 0,
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 95b950f..950f828 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/BatteryUsageContentProviderTest.java
@@ -290,6 +290,7 @@
                         .setPercentOfTotal(0.9)
                         .setForegroundUsageTimeInMs(1000)
                         .setBackgroundUsageTimeInMs(2000)
+                        .setForegroundServiceUsageTimeInMs(1500)
                         .setDrainType(1)
                         .build();
         final String expectedBatteryInformationString =
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 672bc54..e68b892 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batteryusage/ConvertUtilsTest.java
@@ -92,6 +92,7 @@
         mMockBatteryEntry.mPercent = 0.3;
         when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
         when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
+        when(mMockBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(3456L);
         when(mMockBatteryEntry.getPowerComponentId()).thenReturn(-1);
         when(mMockBatteryEntry.getConsumerType())
                 .thenReturn(ConvertUtils.CONSUMER_TYPE_UID_BATTERY);
@@ -133,6 +134,7 @@
         assertThat(batteryInformation.getPercentOfTotal()).isEqualTo(0.3);
         assertThat(batteryInformation.getForegroundUsageTimeInMs()).isEqualTo(1234L);
         assertThat(batteryInformation.getBackgroundUsageTimeInMs()).isEqualTo(5689L);
+        assertThat(batteryInformation.getForegroundServiceUsageTimeInMs()).isEqualTo(3456L);
         assertThat(batteryInformation.getDrainType()).isEqualTo(-1);
         assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12);
         assertThat(deviceBatteryState.getBatteryStatus())
@@ -157,6 +159,7 @@
         mMockBatteryEntry.mPercent = 0.3;
         when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
         when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
+        when(mMockBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(3456L);
         when(mMockBatteryEntry.getConsumerType())
                 .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
 
@@ -196,6 +199,7 @@
         assertThat(batteryInformation.getPercentOfTotal()).isEqualTo(0.3);
         assertThat(batteryInformation.getForegroundUsageTimeInMs()).isEqualTo(1234L);
         assertThat(batteryInformation.getBackgroundUsageTimeInMs()).isEqualTo(5689L);
+        assertThat(batteryInformation.getForegroundServiceUsageTimeInMs()).isEqualTo(3456L);
         assertThat(batteryInformation.getDrainType())
                 .isEqualTo(BatteryConsumer.POWER_COMPONENT_CPU);
         assertThat(deviceBatteryState.getBatteryLevel()).isEqualTo(12);
@@ -307,6 +311,7 @@
         mMockBatteryEntry.mPercent = 0.3;
         when(mMockBatteryEntry.getTimeInForegroundMs()).thenReturn(1234L);
         when(mMockBatteryEntry.getTimeInBackgroundMs()).thenReturn(5689L);
+        when(mMockBatteryEntry.getTimeInForegroundServiceMs()).thenReturn(3456L);
         when(mMockBatteryEntry.getPowerComponentId()).thenReturn(expectedType);
         when(mMockBatteryEntry.getConsumerType())
                 .thenReturn(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
@@ -331,6 +336,7 @@
         assertThat(batteryHistEntry.mPercentOfTotal).isEqualTo(0.3);
         assertThat(batteryHistEntry.mForegroundUsageTimeInMs).isEqualTo(1234L);
         assertThat(batteryHistEntry.mBackgroundUsageTimeInMs).isEqualTo(5689L);
+        assertThat(batteryHistEntry.mForegroundServiceUsageTimeInMs).isEqualTo(3456L);
         assertThat(batteryHistEntry.mDrainType).isEqualTo(expectedType);
         assertThat(batteryHistEntry.mConsumerType)
                 .isEqualTo(ConvertUtils.CONSUMER_TYPE_SYSTEM_BATTERY);
diff --git a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
index f306693..43630e8 100644
--- a/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/contextualcards/slices/BluetoothUpdateWorkerTest.java
@@ -16,12 +16,9 @@
 
 package com.android.settings.homepage.contextualcards.slices;
 
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.net.Uri;
 
@@ -29,7 +26,6 @@
 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.RobolectricTestRunner;
@@ -43,50 +39,46 @@
     private static final Uri URI = Uri.parse("content://com.android.settings.slices/test");
 
     private BluetoothUpdateWorker mBluetoothUpdateWorker;
-    private ContentResolver mResolver;
     private Context mContext;
 
     @Before
     public void setUp() {
-        mContext = spy(RuntimeEnvironment.application);
-        mBluetoothUpdateWorker = new BluetoothUpdateWorker(mContext, URI);
-        mResolver = mock(ContentResolver.class);
-        doReturn(mResolver).when(mContext).getContentResolver();
+        mContext = RuntimeEnvironment.getApplication();
+        mBluetoothUpdateWorker = spy(new BluetoothUpdateWorker(mContext, URI));
     }
 
     @Test
     public void onAclConnectionStateChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onAclConnectionStateChanged(null, 0);
 
-        verify(mResolver).notifyChange(URI, null);
+        verify(mBluetoothUpdateWorker).notifySliceChange();
     }
 
-    @Ignore("b/315399487")
     @Test
     public void onActiveDeviceChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onActiveDeviceChanged(null, 0);
 
-        verify(mResolver).notifyChange(URI, null);
+        verify(mBluetoothUpdateWorker).notifySliceChange();
     }
 
     @Test
     public void onBluetoothStateChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onBluetoothStateChanged(0);
 
-        verify(mResolver).notifyChange(URI, null);
+        verify(mBluetoothUpdateWorker).notifySliceChange();
     }
 
     @Test
     public void onConnectionStateChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onConnectionStateChanged(null, 0);
 
-        verify(mResolver).notifyChange(URI, null);
+        verify(mBluetoothUpdateWorker).notifySliceChange();
     }
 
     @Test
     public void onProfileConnectionStateChanged_shouldNotifyChange() {
         mBluetoothUpdateWorker.onProfileConnectionStateChanged(null, 0, 0);
 
-        verify(mResolver).notifyChange(URI, null);
+        verify(mBluetoothUpdateWorker).notifySliceChange();
     }
 }
\ No newline at end of file
diff --git a/tests/screenshot/Android.bp b/tests/screenshot/Android.bp
index e20b5d3..5989381 100644
--- a/tests/screenshot/Android.bp
+++ b/tests/screenshot/Android.bp
@@ -29,42 +29,46 @@
         "androidx.fragment_fragment",
         "androidx.test.runner",
         "androidx.test.core",
-    ],
-    uses_libs: ["org.apache.http.legacy"],
-
-    aaptflags: ["--extra-packages com.android.settings"],
-    manifest: "AndroidManifest.xml",
-}
-
-android_test {
-    name: "SettingsScreenshotTests",
-    platform_apis: true,
-    certificate: "platform",
-    test_suites: ["device-tests"],
-    srcs: [
-        "src/**/*.kt",
-    ],
-    static_libs: [
-        "androidx.fragment_fragment-testing",
-        "androidx.fragment_fragment",
         "androidx.test.rules",
         "androidx.test.ext.junit",
         "platform-screenshot-diff-core",
         "Settings-testutils2",
-        "androidx.test.core",
         "androidx.test.espresso.core",
         "kotlinx-coroutines-android",
         "androidx.lifecycle_lifecycle-runtime-testing",
         "kotlinx_coroutines_test",
-        "Settings-core",
-        "androidx.test.runner",
     ],
     uses_libs: ["org.apache.http.legacy"],
-    compile_multilib: "both",
+    aaptflags: ["--extra-packages com.android.settings"],
     manifest: "AndroidManifest.xml",
-    test_config: "AndroidTest.xml",
-    use_embedded_native_libs: false,
-    asset_dirs: ["assets"],
+}
+
+// This is a RNG (Robolectric native graphics) test target.
+android_robolectric_test {
+    name: "SettingsScreenshotRNGTests",
+    srcs: [
+        "src/**/*.kt",
+    ],
+    test_suites: ["general-tests"],
+
+    // Do not add any libraries here, instead add them to the ScreenshotTestStub
+    static_libs: [
+        "androidx.compose.runtime_runtime",
+        "androidx.test.uiautomator_uiautomator",
+        "androidx.test.ext.junit",
+        "inline-mockito-robolectric-prebuilt",
+        "platform-parametric-runner-lib",
+        "uiautomator-helpers",
+
+    ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+        "truth",
+    ],
+
+    upstream: true,
     instrumentation_for: "ScreenshotTestStub",
-    data: [":ScreenshotTestStub"],
+    java_resource_dirs: ["config"],
 }
diff --git a/tests/screenshot/AndroidManifest.xml b/tests/screenshot/AndroidManifest.xml
index 9cbc882..6c8bb88 100644
--- a/tests/screenshot/AndroidManifest.xml
+++ b/tests/screenshot/AndroidManifest.xml
@@ -16,32 +16,13 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    package="com.android.settings.tests.screenshot"
-    >
-
-    <application android:debuggable="true">
-        <provider
-            android:name="com.android.settings.slices.SettingsSliceProvider"
-            android:authorities="com.android.settings.tests.screenshot.disabled"
-            android:enabled="false"
-            tools:node="remove"
-            tools:replace="android:authorities" />
-
-    </application>
-
-    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    <uses-permission android:name="android.permission.READ_LOGS" />
-    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+        coreApp="true"
+        package="com.android.settings">
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
 
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:label="Android Settings Screenshot tests"
-        android:targetPackage="com.android.settings.tests.screenshot" />
+    <application>
+        <activity android:name="com.android.settings.test.screenshot.ContainerActivity" android:exported="true" />
+    </application>
 
-</manifest>
+</manifest>
\ No newline at end of file
diff --git a/tests/screenshot/AndroidTest.xml b/tests/screenshot/AndroidTest.xml
deleted file mode 100644
index 7496ffd..0000000
--- a/tests/screenshot/AndroidTest.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<configuration description="Runs settings screendiff tests.">
-    <option name="test-suite-tag" value="apct-instrumentation" />
-    <option name="test-suite-tag" value="apct" />
-    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
-        <option name="optimized-property-setting" value="true" />
-    </target_preparer>
-    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
-        <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="SettingsScreenshotTests.apk" />
-    </target_preparer>
-    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
-        <option name="directory-keys"
-            value="/data/user/0/com.android.settings.tests.screenshot/" />
-        <option name="collect-on-run-ended-only" value="true" />
-    </metrics_collector>
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.settings.tests.screenshot" />
-        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
-    </test>
-</configuration>
diff --git "a/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png" "b/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png"
deleted file mode 100644
index 1129250..0000000
--- "a/tests/screenshot/assets/pixel_4a_\0505g\051/fp_enroll_intro.png"
+++ /dev/null
Binary files differ
diff --git a/tests/screenshot/assets/robolectric/fp_enroll_intro.png b/tests/screenshot/assets/robolectric/fp_enroll_intro.png
new file mode 100644
index 0000000..308ab55
--- /dev/null
+++ b/tests/screenshot/assets/robolectric/fp_enroll_intro.png
Binary files differ
diff --git a/tests/screenshot/config/robolectric.properties b/tests/screenshot/config/robolectric.properties
new file mode 100644
index 0000000..88443e3
--- /dev/null
+++ b/tests/screenshot/config/robolectric.properties
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+sdk=NEWEST_SDK
+graphicsMode=NATIVE
\ No newline at end of file
diff --git a/tests/screenshot/src/com/android/settings/tests/screenshot/ContainerActivity.kt b/tests/screenshot/src/com/android/settings/tests/screenshot/ContainerActivity.kt
new file mode 100644
index 0000000..a505ef5
--- /dev/null
+++ b/tests/screenshot/src/com/android/settings/tests/screenshot/ContainerActivity.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.tests.screenshot
+
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.fragment.app.FragmentContainerView
+
+/**
+ * This activity is a container for all RNG (Robolectric Native Graphic) Settings screenshot tests.
+ */
+class ContainerActivity : FragmentActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val contentView = FragmentContainerView(this)
+        contentView.setId(CONTAINER_VIEW_ID)
+        setContentView(contentView)
+    }
+
+    companion object {
+        const val CONTAINER_VIEW_ID = 1234
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt b/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt
new file mode 100644
index 0000000..020a470
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/network/SubscriptionInfoListViewModelTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.network
+
+import android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING
+
+import android.app.Application
+import android.content.Context
+import android.platform.test.flag.junit.SetFlagsRule
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.internal.telephony.flags.Flags
+import com.android.settings.network.telephony.CallStateFlowTest
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class SubscriptionInfoListViewModelTest {
+    @get:Rule
+    val mSetFlagsRule = SetFlagsRule()
+    private var subInfoListener: SubscriptionManager.OnSubscriptionsChangedListener? = null
+    private val mockSubscriptionManager = mock<SubscriptionManager> {
+        on { activeSubscriptionInfoList } doAnswer { activeSubscriptionInfoList }
+        on { addOnSubscriptionsChangedListener(any(), any()) } doAnswer {
+            subInfoListener =
+                it.arguments[1] as SubscriptionManager.OnSubscriptionsChangedListener
+            subInfoListener?.onSubscriptionsChanged()
+        }
+    }
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        on { getSystemService(SubscriptionManager::class.java) } doReturn mockSubscriptionManager
+    }
+
+    private val subscriptionInfoListViewModel: SubscriptionInfoListViewModel =
+        SubscriptionInfoListViewModel(context as Application);
+
+    private var activeSubscriptionInfoList: List<SubscriptionInfo>? = null
+
+    @Test
+    fun onSubscriptionsChanged_noProvisioning_resultSameAsInput() = runBlocking {
+        activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2)
+
+        val listDeferred = async {
+            subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
+        }
+        delay(100)
+        subInfoListener?.onSubscriptionsChanged()
+
+        assertThat(listDeferred.await()).contains(activeSubscriptionInfoList)
+    }
+
+    @Test
+    fun onSubscriptionsChanged_hasProvisioning_filterProvisioning() = runBlocking {
+        activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3)
+        val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
+
+        val listDeferred = async {
+            subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
+        }
+        delay(100)
+        subInfoListener?.onSubscriptionsChanged()
+
+        assertThat(listDeferred.await()).contains(expectation)
+    }
+
+    @Test
+    fun onSubscriptionsChanged_flagOffHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
+        runBlocking {
+            mSetFlagsRule.disableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
+            val expectation = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
+
+            val listDeferred = async {
+                subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
+            }
+            delay(100)
+            subInfoListener?.onSubscriptionsChanged()
+
+            assertThat(listDeferred.await()).contains(expectation)
+        }
+
+    @Test
+    fun onSubscriptionsChanged_flagOnHasNonTerrestrialNetwork_filterNonTerrestrialNetwork() =
+        runBlocking {
+            mSetFlagsRule.enableFlags(Flags.FLAG_OEM_ENABLED_SATELLITE_FLAG)
+
+            activeSubscriptionInfoList = listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_4)
+            val expectation = listOf(SUB_INFO_1, SUB_INFO_2)
+
+            val listDeferred = async {
+                subscriptionInfoListViewModel.subscriptionInfoListFlow.toListWithTimeout()
+            }
+            delay(100)
+            subInfoListener?.onSubscriptionsChanged()
+
+            assertThat(listDeferred.await()).contains(expectation)
+        }
+
+    private companion object {
+        val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
+            setId(1)
+        }.build()
+
+        val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
+            setId(2)
+        }.build()
+
+        val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
+            setId(3)
+            setEmbedded(true)
+            setProfileClass(PROFILE_CLASS_PROVISIONING)
+            setOnlyNonTerrestrialNetwork(false)
+        }.build()
+
+        val SUB_INFO_4: SubscriptionInfo = SubscriptionInfo.Builder().apply {
+            setId(4)
+            setEmbedded(true)
+            setOnlyNonTerrestrialNetwork(true)
+        }.build()
+    }
+}
\ No newline at end of file
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
new file mode 100644
index 0000000..cc5e365
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppArchiveButtonTest.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInstaller
+import android.content.pm.PackageManager
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.CloudUpload
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settingslib.spa.widget.button.ActionButton
+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.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppArchiveButtonTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
+
+    private val packageInfoPresenter = mock<PackageInfoPresenter>()
+
+    private val userPackageManager = mock<PackageManager>()
+
+    private val packageInstaller = mock<PackageInstaller>()
+
+    private lateinit var appArchiveButton: AppArchiveButton
+
+    @Before
+    fun setUp() {
+        whenever(packageInfoPresenter.context).thenReturn(context)
+        whenever(packageInfoPresenter.userPackageManager).thenReturn(userPackageManager)
+        whenever(userPackageManager.packageInstaller).thenReturn(packageInstaller)
+        whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME)
+        appArchiveButton = AppArchiveButton(packageInfoPresenter)
+    }
+
+    @Test
+    fun appArchiveButton_whenIsArchived_isDisabled() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            isArchived = true
+        }
+        whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
+
+        val actionButton = setContent(app)
+
+        assertThat(actionButton.enabled).isFalse()
+    }
+
+    @Test
+    fun appArchiveButton_whenIsNotAppArchivable_isDisabled() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            isArchived = false
+        }
+        whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(false)
+
+        val actionButton = setContent(app)
+
+        assertThat(actionButton.enabled).isFalse()
+    }
+
+    @Test
+    fun appArchiveButton_displaysRightTextAndIcon() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            isArchived = false
+        }
+        whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
+
+        val actionButton = setContent(app)
+
+        assertThat(actionButton.text).isEqualTo(context.getString(R.string.archive))
+        assertThat(actionButton.imageVector).isEqualTo(Icons.Outlined.CloudUpload)
+    }
+
+    @Test
+    fun appArchiveButton_clicked() {
+        val app = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            isArchived = false
+        }
+        whenever(userPackageManager.isAppArchivable(app.packageName)).thenReturn(true)
+
+        val actionButton = setContent(app)
+        actionButton.onClick()
+
+        verify(packageInstaller).requestArchive(
+            eq(PACKAGE_NAME),
+            any(),
+            eq(0)
+        )
+    }
+
+    private fun setContent(app: ApplicationInfo): ActionButton {
+        lateinit var actionButton: ActionButton
+        composeTestRule.setContent {
+            actionButton = appArchiveButton.getActionButton(app)
+        }
+        return actionButton
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
index e2f55ef..50094f2 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppButtonsTest.kt
@@ -22,8 +22,10 @@
 import android.content.pm.FakeFeatureFlagsImpl
 import android.content.pm.Flags
 import android.content.pm.PackageInfo
+import android.content.pm.PackageInstaller
 import android.content.pm.PackageManager
 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
@@ -62,6 +64,9 @@
     @Mock
     private lateinit var packageManager: PackageManager
 
+    @Mock
+    private lateinit var packageInstaller: PackageInstaller
+
     private val featureFlags = FakeFeatureFlagsImpl()
 
     @Before
@@ -74,6 +79,7 @@
         whenever(packageInfoPresenter.context).thenReturn(context)
         whenever(packageInfoPresenter.packageName).thenReturn(PACKAGE_NAME)
         whenever(packageInfoPresenter.userPackageManager).thenReturn(packageManager)
+        whenever(packageManager.packageInstaller).thenReturn(packageInstaller)
         whenever(packageManager.getPackageInfo(PACKAGE_NAME, 0)).thenReturn(PACKAGE_INFO)
         whenever(AppUtils.isMainlineModule(packageManager, PACKAGE_NAME)).thenReturn(false)
         featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
@@ -118,8 +124,24 @@
         composeTestRule.onNodeWithText(context.getString(R.string.launch_instant_app)).assertIsNotDisplayed()
     }
 
-    private fun setContent() {
-        whenever(packageInfoPresenter.flow).thenReturn(MutableStateFlow(PACKAGE_INFO))
+    @Test
+    fun uninstallButton_enabled_whenAppIsArchived() {
+        whenever(packageManager.getLaunchIntentForPackage(PACKAGE_NAME)).thenReturn(Intent())
+        featureFlags.setFlag(Flags.FLAG_ARCHIVING, true)
+        val packageInfo = PackageInfo().apply {
+            applicationInfo = ApplicationInfo().apply {
+                packageName = PACKAGE_NAME
+                isArchived = true
+            }
+            packageName = PACKAGE_NAME
+        }
+        setContent(packageInfo)
+
+        composeTestRule.onNodeWithText(context.getString(R.string.uninstall_text)).assertIsEnabled()
+    }
+
+    private fun setContent(packageInfo: PackageInfo = PACKAGE_INFO) {
+        whenever(packageInfoPresenter.flow).thenReturn(MutableStateFlow(packageInfo))
         composeTestRule.setContent {
             AppButtons(packageInfoPresenter, featureFlags)
         }
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
index d81bb1a..5dd66e8 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/PackageInfoPresenterTest.kt
@@ -105,6 +105,7 @@
     fun isInterestedAppChange_archived_interested() {
         val intent = Intent(Intent.ACTION_PACKAGE_REMOVED).apply {
             data = Uri.parse("package:$PACKAGE_NAME")
+            putExtra(Intent.EXTRA_ARCHIVAL, true)
         }
 
         val isInterestedAppChange = packageInfoPresenter.isInterestedAppChange(intent)
diff --git a/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java b/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
index 3669358..dc4f56a 100644
--- a/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/development/BackAnimationPreferenceControllerTest.java
@@ -29,6 +29,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 
 import androidx.preference.PreferenceManager;
@@ -37,8 +41,11 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.window.flags.Flags;
+
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.MockitoAnnotations;
@@ -58,6 +65,9 @@
     private BackAnimationPreferenceController mController;
     private Looper mLooper;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -81,6 +91,18 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMATIONS)
+    public void controllerNotAvailable_whenAconfigFlagEnabled() {
+        assertFalse(mController.isAvailable());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_PREDICTIVE_BACK_SYSTEM_ANIMATIONS)
+    public void controllerAvailable_whenAconfigFlagDisabled() {
+        assertTrue(mController.isAvailable());
+    }
+
+    @Test
     public void onPreferenceChange_switchEnabled_shouldEnableBackAnimations() {
         mController.onPreferenceChange(mPreference, true /* new value */);
 
diff --git a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
index 0a2f3d1..1d27326 100644
--- a/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
+++ b/tests/unit/src/com/android/settings/privatespace/PrivateSpaceMaintainerTest.java
@@ -21,8 +21,11 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.ActivityManager;
+import android.app.IActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.os.RemoteException;
 import android.provider.Settings;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -30,6 +33,8 @@
 
 import com.android.settings.privatespace.PrivateSpaceMaintainer.ErrorDeletingPrivateSpace;
 
+import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,6 +42,7 @@
 
 @RunWith(AndroidJUnit4.class)
 public class PrivateSpaceMaintainerTest {
+    private static final String TAG = "PSMaintainerTest";
     private Context mContext;
     private ContentResolver mContentResolver;
 
@@ -48,6 +54,13 @@
         mContentResolver = mContext.getContentResolver();
     }
 
+    @After
+    public void tearDown() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.deletePrivateSpace();
+    }
+
     /** Tests that {@link PrivateSpaceMaintainer#deletePrivateSpace()} deletes PS when PS exists. */
     @Test
     public void deletePrivateSpace_psExists_deletesPS() {
@@ -137,4 +150,52 @@
         assertThat(privateSpaceMaintainer.getHidePrivateSpaceEntryPointSetting())
                 .isEqualTo(HIDE_PRIVATE_SPACE_ENTRY_POINT_ENABLED_VAL);
     }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#lockPrivateSpace()} when PS exists and is running
+     * locks the private profile.
+     */
+    @Test
+    public void lockPrivateSpace_psExistsAndPrivateProfileRunning_locksCreatedPrivateSpace() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.createPrivateSpace();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+        assertThat(privateSpaceMaintainer.isPrivateProfileRunning()).isTrue();
+        assertThat(privateSpaceMaintainer.isPrivateSpaceLocked()).isFalse();
+        assertThat(privateSpaceMaintainer.lockPrivateSpace()).isTrue();
+        assertThat(privateSpaceMaintainer.isPrivateSpaceLocked()).isTrue();
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#lockPrivateSpace()} when PS exist and private
+     * profile not running returns false.
+     */
+    @Test
+    public void lockPrivateSpace_psExistsAndPrivateProfileNotRunning_returnsFalse() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        privateSpaceMaintainer.createPrivateSpace();
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isTrue();
+        assertThat(privateSpaceMaintainer.isPrivateProfileRunning()).isTrue();
+        IActivityManager am = ActivityManager.getService();
+        try {
+            am.stopProfile(privateSpaceMaintainer.getPrivateProfileHandle().getIdentifier());
+        } catch (RemoteException e) {
+            Assert.fail("Stop profile failed with exception " + e.getMessage());
+        }
+        assertThat(privateSpaceMaintainer.isPrivateProfileRunning()).isFalse();
+        assertThat(privateSpaceMaintainer.lockPrivateSpace()).isFalse();
+    }
+
+    /**
+     * Tests that {@link PrivateSpaceMaintainer#lockPrivateSpace()} when no PS exists returns false.
+     */
+    @Test
+    public void lockPrivateSpace_psDoesNotExist_returnsFalse() {
+        PrivateSpaceMaintainer privateSpaceMaintainer =
+                PrivateSpaceMaintainer.getInstance(mContext);
+        assertThat(privateSpaceMaintainer.doesPrivateSpaceExist()).isFalse();
+        assertThat(privateSpaceMaintainer.lockPrivateSpace()).isFalse();
+    }
 }