Merge "[Settings] Do not creat multiple database"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a3a6fcf..88aa9bd 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4654,17 +4654,6 @@
                        android:value="UsageStats"/>
         </activity-alias>
 
-        <activity
-            android:name="com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity"
-            android:label="@string/mobile_bundled_apps_details_title"
-            android:exported="true"
-            android:icon="@drawable/ic_homepage_connected_device">
-            <intent-filter android:priority="1">
-                <action android:name="android.settings.TRANSPARENCY_METADATA" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
         <!-- [b/197780098] Disable eager initialization of Jetpack libraries. -->
         <provider
             android:name="androidx.startup.InitializationProvider"
diff --git a/res/layout/mobile_bundled_apps_details_fragment.xml b/res/layout/mobile_bundled_apps_details_fragment.xml
deleted file mode 100644
index b2b8a23..0000000
--- a/res/layout/mobile_bundled_apps_details_fragment.xml
+++ /dev/null
@@ -1,107 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/app_details_layout"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:padding="10dp">
-
-    <TextView
-        android:id="@+id/contains_ads_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_contains_ad_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <TextView
-        android:id="@+id/contains_ads"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-
-    <TextView
-        android:id="@+id/developer_info_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_developer_info_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <LinearLayout
-        android:id="@+id/developer_list"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" />
-
-    <TextView
-        android:id="@+id/contact_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_contact_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <TextView
-        android:id="@+id/contact_url"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-    <TextView
-        android:id="@+id/contact_email"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-
-    <TextView
-        android:id="@+id/description_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_description_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <TextView
-        android:id="@+id/description"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-
-    <TextView
-        android:id="@+id/privacy_policy_url_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_privacy_policy_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <TextView
-        android:id="@+id/privacy_policy_url"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-
-    <TextView
-        android:id="@+id/category_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:text="@string/mobile_bundled_apps_details_category_title"
-        android:textSize="18sp"
-        android:textStyle="bold" />
-    <TextView
-        android:id="@+id/category"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-</LinearLayout>
diff --git a/res/layout/mobile_bundled_apps_developer_fragment_row.xml b/res/layout/mobile_bundled_apps_developer_fragment_row.xml
deleted file mode 100644
index 1ec0694..0000000
--- a/res/layout/mobile_bundled_apps_developer_fragment_row.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical"
-    android:padding="10dp">
-    <TextView
-        android:id="@+id/mba_info"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="14sp"
-        android:textAppearance="?android:attr/textAppearanceListItemSecondary"/>
-    <TextView
-        android:id="@+id/developer_name"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-    <TextView
-        android:id="@+id/developer_relationship"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-    <TextView
-        android:id="@+id/developer_email"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-    <TextView
-        android:id="@+id/developer_country"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textSize="12sp" />
-</LinearLayout>
\ No newline at end of file
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 2689f65..fa26dd3 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -983,6 +983,20 @@
         <item>90</item>
     </string-array>
 
+    <!-- Options for screensaver "When to start" for devices that do not support screensavers
+         while on battery -->
+    <string-array name="when_to_start_screensaver_entries_no_battery" translatable="false">
+        <item>@string/screensaver_settings_summary_sleep</item>
+        <item>@string/screensaver_settings_summary_dock_and_charging</item>
+    </string-array>
+
+    <!-- Values for screensaver "When to start" for devices that do not support screensavers
+         while on battery -->
+    <string-array name="when_to_start_screensaver_values_no_battery" translatable="false">
+        <item>while_charging_only</item>
+        <item>while_docked_only</item>
+    </string-array>
+
     <string-array name="when_to_start_screensaver_entries" translatable="false">
         <item>@string/screensaver_settings_summary_sleep</item>
         <item>@string/screensaver_settings_summary_dock</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 10df2bb..5eae3b4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2605,6 +2605,8 @@
     <string name="screensaver_settings_toggle_title">Use screen saver</string>
     <!-- Display settings screen, summary fragment for screen saver options, activated when docked or asleep and charging [CHAR LIMIT=35] -->
     <string name="screensaver_settings_summary_either_long">While charging or docked</string>
+    <!-- Display settings screen, summary fragment for screen saver options, actived while docked and charging [CHAR LIMIT=35] -->
+    <string name="screensaver_settings_summary_dock_and_charging">While docked and charging</string>
     <!-- Display settings screen, summary fragment for screen saver options, activated when asleep and charging [CHAR LIMIT=35] -->
     <string name="screensaver_settings_summary_sleep">While charging</string>
     <!-- Display settings screen, summary fragment for screen saver options, activated when docked [CHAR LIMIT=35] -->
@@ -8163,6 +8165,9 @@
     <!-- [CHAR LIMIT=NONE] Text appearing when channel group notifications are off -->
     <string name="channel_group_notifications_off_desc">At your request, Android is blocking this group of notifications from appearing on this device</string>
 
+    <!-- [CHAR LIMIT=NONE] Text appearing when app does not send notifications -->
+    <string name="app_notifications_not_send_desc">This app does not send notifications</string>
+
     <!-- [CHAR LIMIT=NONE] App notification settings: channels title -->
     <string name="notification_channels">Categories</string>
 
@@ -11882,26 +11887,11 @@
     <!-- [CHAR LIMIT=NONE] Hint for QR code process failure -->
     <string name="bt_le_audio_qr_code_is_not_valid_format">QR code isn\u0027t a valid format</string>
 
-    <!-- Mobile Bundled Apps Transparency Metadata-->
+    <!-- Accessibility Software Cursor -->
 
-    <!-- [CHAR_LIMIT=NONE] Label for mobile bundled apps screen -->
-    <string name="mobile_bundled_apps">Mobile bundled apps</string>
-    <!-- [CHAR_LIMIT=NONE] Title for mobile bundled apps screen -->
-    <string name="mobile_bundled_apps_title">Mobile bundled apps</string>
-    <!-- [CHAR_LIMIT=NONE] Title for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_title">Mobile bundled apps transparency info</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_contains_ad_title">Contains ads</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_developer_info_title">Developer(s) Information</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_contact_title">Contact Information</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_description_title">Description</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_privacy_policy_title">Privacy Policy</string>
-    <!-- [CHAR_LIMIT=NONE] Subheading for mobile bundled apps transparency information details screen -->
-    <string name="mobile_bundled_apps_details_category_title">Category</string>
-    <!-- [CHAR_LIMIT=NONE] Summary for App Details in App Info page if app is mobile bundled app -->
-    <string name="app_install_details_mba_summary">Mobile Bundled App</string>
+    <!-- [CHAR LIMIT=NONE] Title for Accessibility Software Cursor setting for trigger hints. -->
+    <string name="software_cursor_trigger_hints_enabled_title" translatable="false">Gesture detection area hints</string>
+    <!-- [CHAR LIMIT=NONE] Title for Accessibility Software Cursor setting for keyboard shift. -->
+    <string name="software_cursor_trigger_keyboard_shift_enabled_title" translatable="false">Shift gesture detection region above keyboard</string>
+
 </resources>
diff --git a/res/xml/mobile_bundled_apps_details_preference.xml b/res/xml/mobile_bundled_apps_details_preference.xml
deleted file mode 100644
index 2bef352..0000000
--- a/res/xml/mobile_bundled_apps_details_preference.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!-- Copyright (C) 2022 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<PreferenceScreen
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:settings="http://schemas.android.com/apk/res-auto"
-    android:title="">
-    <com.android.settingslib.widget.LayoutPreference
-        android:key="metadata"
-        android:selectable="false"
-        android:layout="@layout/mobile_bundled_apps_details_fragment" />
-</PreferenceScreen>
\ No newline at end of file
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 0fcf4a3..b2de004 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -21,7 +21,6 @@
 import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
 import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
 
-import android.annotation.Nullable;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -96,6 +95,7 @@
 
 import androidx.annotation.ColorInt;
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.core.graphics.drawable.IconCompat;
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
@@ -799,7 +799,9 @@
         }
     }
 
-    public static CharSequence getApplicationLabel(Context context, String packageName) {
+    /** Gets the application label of the given package name. */
+    @Nullable
+    public static CharSequence getApplicationLabel(Context context, @NonNull String packageName) {
         try {
             final ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
                     packageName,
diff --git a/src/com/android/settings/applications/AppStoreUtil.java b/src/com/android/settings/applications/AppStoreUtil.java
index 79a4f35..b18a68f 100644
--- a/src/com/android/settings/applications/AppStoreUtil.java
+++ b/src/com/android/settings/applications/AppStoreUtil.java
@@ -24,7 +24,9 @@
 import android.content.pm.ResolveInfo;
 import android.util.Log;
 
-// This class provides methods that help dealing with app stores.
+import androidx.annotation.Nullable;
+
+/** This class provides methods that help dealing with app stores. */
 public class AppStoreUtil {
     private static final String LOG_TAG = "AppStoreUtil";
 
@@ -34,8 +36,11 @@
                 .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null;
     }
 
-    // Returns the package name of the app that we consider to be the user-visible 'installer'
-    // of given packageName, if one is available.
+    /**
+     * Returns the package name of the app that we consider to be the user-visible 'installer'
+     * of given packageName, if one is available.
+     */
+    @Nullable
     public static String getInstallerPackageName(Context context, String packageName) {
         String installerPackageName;
         try {
@@ -62,7 +67,8 @@
         return installerPackageName;
     }
 
-    // Returns a link to the installer app store for a given package name.
+    /** Returns a link to the installer app store for a given package name. */
+    @Nullable
     public static Intent getAppStoreLink(Context context, String installerPackageName,
             String packageName) {
         Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO)
@@ -75,7 +81,7 @@
         return null;
     }
 
-    // Convenience method that looks up the installerPackageName for you.
+    /** Convenience method that looks up the installerPackageName for you. */
     public static Intent getAppStoreLink(Context context, String packageName) {
       String installerPackageName = getInstallerPackageName(context, packageName);
       return getAppStoreLink(context, installerPackageName, packageName);
diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
index c1b49c2..5b55f76 100755
--- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
+++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java
@@ -56,7 +56,6 @@
 import com.android.settings.SettingsActivity;
 import com.android.settings.SettingsPreferenceFragment;
 import com.android.settings.applications.manageapplications.ManageApplications;
-import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController;
 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController;
 import com.android.settings.core.SubSettingLauncher;
@@ -163,8 +162,6 @@
                 use(AppInstallerInfoPreferenceController.class);
         installer.setPackageName(packageName);
         installer.setParentFragment(this);
-        installer.setMbaWithMetadataStatus(ApplicationMetadataUtils.getDefaultInstance(),
-                packageName);
         use(AppInstallerPreferenceCategoryController.class).setChildren(Arrays.asList(installer));
         use(AppNotificationPreferenceController.class).setParentFragment(this);
 
diff --git a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java
index c91d288..5e99e8b 100644
--- a/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java
+++ b/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceController.java
@@ -16,50 +16,42 @@
 
 package com.android.settings.applications.appinfo;
 
-import static com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity.ACTION_TRANSPARENCY_METADATA;
-
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserManager;
-import android.provider.DeviceConfig;
-import android.text.TextUtils;
 
-import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
 import com.android.settings.R;
 import com.android.settings.Utils;
 import com.android.settings.applications.AppStoreUtil;
-import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
 import com.android.settingslib.applications.AppUtils;
+
 public class AppInstallerInfoPreferenceController extends AppInfoPreferenceControllerBase {
 
-    private static final String KEY_ENABLE_PROMPT = "enable_prompt";
     private String mPackageName;
     private String mInstallerPackage;
     private CharSequence mInstallerLabel;
-    private Boolean mAppIsMbaWithMetadata;
-    private Boolean mEnableMbaUiFlag = false;
 
     public AppInstallerInfoPreferenceController(Context context, String key) {
         super(context, key);
-        updateFromDeviceConfigFlags();
     }
 
     @Override
     public int getAvailabilityStatus() {
-        if (UserManager.get(mContext).isManagedProfile()
-                || AppUtils.isMainlineModule(mContext.getPackageManager(), mPackageName)) {
+        if (UserManager.get(mContext).isManagedProfile()) {
             return DISABLED_FOR_USER;
         }
-        if (mInstallerLabel != null || (mAppIsMbaWithMetadata && mEnableMbaUiFlag)) {
-            return AVAILABLE;
+
+        if (AppUtils.isMainlineModule(mContext.getPackageManager(), mPackageName)) {
+            return DISABLED_FOR_USER;
         }
-        return DISABLED_FOR_USER;
+
+        return mInstallerLabel != null ? AVAILABLE : DISABLED_FOR_USER;
     }
 
     @Override
-    public void updateState(final Preference preference) {
+    public void updateState(Preference preference) {
         final int detailsStringId = AppUtils.isInstant(mParent.getPackageInfo().applicationInfo)
                 ? R.string.instant_app_details_summary
                 : R.string.app_install_details_summary;
@@ -68,52 +60,14 @@
         Intent intent = AppStoreUtil.getAppStoreLink(mContext, mInstallerPackage, mPackageName);
         if (intent != null) {
             preference.setIntent(intent);
-        } else if (mAppIsMbaWithMetadata && mEnableMbaUiFlag) {
-            preference.setIntent(generateMetadataXmlViewerIntent());
-            preference.setSummary(mContext.getString(R.string.app_install_details_mba_summary));
         } else {
             preference.setEnabled(false);
         }
     }
 
-    /**
-     * Sets the packageName in context for the controller.
-     */
-    public void setPackageName(final String packageName) {
+    public void setPackageName(String packageName) {
         mPackageName = packageName;
         mInstallerPackage = AppStoreUtil.getInstallerPackageName(mContext, mPackageName);
         mInstallerLabel = Utils.getApplicationLabel(mContext, mInstallerPackage);
     }
-
-    /**
-     * Setups and determines if the current package in context is an mobile-bundled-app with
-     * an application metadata file embedded within.
-     */
-    public void setMbaWithMetadataStatus(final ApplicationMetadataUtils appMetadataUtils,
-            final String packageName) {
-        mAppIsMbaWithMetadata = appMetadataUtils.packageContainsXmlFile(
-                mContext.getPackageManager(), packageName);
-    }
-
-    private Intent generateMetadataXmlViewerIntent() {
-        final Intent metadataXmlIntent = new Intent(ACTION_TRANSPARENCY_METADATA)
-                .setPackage(mContext.getPackageName());
-        metadataXmlIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
-        return metadataXmlIntent;
-    }
-
-    private void updateFromDeviceConfigFlags() {
-        String enablePromptFlag = DeviceConfig.getProperty(
-                DeviceConfig.NAMESPACE_TRANSPARENCY_METADATA,
-                KEY_ENABLE_PROMPT);
-        //No-op for empty field and relies on default value of false
-        if (!TextUtils.isEmpty(enablePromptFlag)) {
-            setEnableMbaFlag(Boolean.parseBoolean(enablePromptFlag));
-        }
-    }
-
-    @VisibleForTesting
-    void setEnableMbaFlag(final boolean flagValue) {
-        mEnableMbaUiFlag = flagValue;
-    }
 }
diff --git a/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtils.java b/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtils.java
deleted file mode 100644
index 5611fc4..0000000
--- a/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtils.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.mobilebundledapps;
-
-import android.content.pm.PackageManager;
-import android.util.Log;
-
-import androidx.annotation.VisibleForTesting;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.NodeList;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.zip.ZipFile;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-/**
- * Used for parsing application-metadata.xml and return relevant fields
- */
-public class ApplicationMetadataUtils {
-    private static final String TAG = ApplicationMetadataUtils.class.getSimpleName();
-
-    private static final ApplicationMetadataUtils DEFAULT_INSTANCE = new ApplicationMetadataUtils();
-    private static final String TRANSPARENCY_XML_DIR = "APP-INF/application-metadata.xml";
-    private static final String DESCRIPTION_TAG = "description";
-    private static final String CONTAINS_ADS_TAG = "contains-ads";
-    private static final String PRIVACY_POLICY_TAG = "privacy-policy";
-    private static final String CONTACT_TAG = "contact";
-    private static final String CATEGORY_TAG = "category";
-    private static final String DEVELOPER_TAG = "developer";
-    private static final String URL_TAG = "url";
-    private static final String EMAIL_TAG = "email";
-    private static final String NAME_TAG = "name";
-    private static final String RELATIONSHIP_TAG = "relationship";
-    private static final String COUNTRY_TAG = "country";
-
-    private final PackageManager mPackageManager;
-
-    private Document mXmlDoc;
-
-    @VisibleForTesting
-    ApplicationMetadataUtils() {
-        mPackageManager = null;
-    }
-
-    //Need to create singleton factory as Android is unable to mock static for testing.
-    public static ApplicationMetadataUtils getDefaultInstance() {
-        return DEFAULT_INSTANCE;
-    }
-
-    /**
-     * Generates a new instance that also provisions and reads the XML file.
-     */
-    public static ApplicationMetadataUtils newInstance(final PackageManager packageManager,
-            String packageName) {
-        return new ApplicationMetadataUtils(packageManager, packageName);
-    }
-    private ApplicationMetadataUtils(final PackageManager packageManager,
-            final String packageName) {
-        mPackageManager = packageManager;
-        try (ZipFile apk = new ZipFile(getApkDirectory(packageName, mPackageManager))) {
-            mXmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
-                    .parse(apk.getInputStream(apk.getEntry(TRANSPARENCY_XML_DIR)));
-        } catch (final Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    @VisibleForTesting
-    void setXmlDoc(final Document xmlDoc) {
-        mXmlDoc = xmlDoc;
-    }
-
-    private static String getApkDirectory(final String packageName,
-            final PackageManager packageManager)
-            throws PackageManager.NameNotFoundException {
-        return packageManager
-                .getApplicationInfo(packageName,
-                        PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA))
-                .sourceDir;
-    }
-    public boolean getContainsAds() {
-        return mXmlDoc != null
-                && mXmlDoc.getElementsByTagName(CONTAINS_ADS_TAG) != null
-                && mXmlDoc.getElementsByTagName(CONTAINS_ADS_TAG).getLength() > 0;
-    }
-
-    public String getPrivacyPolicyUrl() {
-        return retrieveElementAttributeValue(PRIVACY_POLICY_TAG, URL_TAG);
-    }
-
-    private String retrieveElementAttributeValue(final String elementTag, final String attribute) {
-        try {
-            return mXmlDoc.getElementsByTagName(elementTag).item(0)
-                    .getAttributes().getNamedItem(attribute).getNodeValue();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    public String getDescription() {
-        return retrieveElementValue(DESCRIPTION_TAG);
-    }
-
-    private String retrieveElementValue(final String elementTag) {
-        try {
-            return mXmlDoc.getElementsByTagName(elementTag).item(0).getTextContent();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-    public String getCategoryName() {
-        return retrieveElementAttributeValue(CATEGORY_TAG, NAME_TAG);
-    }
-
-    public String getContactUrl() {
-        return retrieveElementAttributeValue(CONTACT_TAG, URL_TAG);
-    }
-
-    public String getContactEmail() {
-        return retrieveElementAttributeValue(CONTACT_TAG, EMAIL_TAG);
-    }
-
-    public String getPlayStoreUrl() {
-        return retrieveElementValue(DESCRIPTION_TAG);
-    }
-
-    /**
-     * Retrieves the list of relevant major parties involved with this MBA
-     */
-    public List<MbaDeveloper> getDevelopers() {
-        final List<MbaDeveloper> developersDetails = new ArrayList();
-        try {
-            final NodeList developers = mXmlDoc.getElementsByTagName(DEVELOPER_TAG);
-            if (developers == null) return developersDetails;
-            for (int i = 0; i < developers.getLength(); ++i) {
-                final NamedNodeMap developerAttributes = developers.item(i).getAttributes();
-                developersDetails.add(new MbaDeveloper(
-                        developerAttributes.getNamedItem(NAME_TAG).getNodeValue(),
-                        developerAttributes.getNamedItem(RELATIONSHIP_TAG).getNodeValue(),
-                        developerAttributes.getNamedItem(EMAIL_TAG).getNodeValue(),
-                        developerAttributes.getNamedItem(COUNTRY_TAG).getNodeValue()
-                ));
-            }
-        } catch (final Exception e) {
-            Log.d(TAG, e.getMessage());
-        }
-        return developersDetails;
-    }
-
-    /**
-     * Determines if the a package can be parsed and extrapolate metadata from.
-     */
-    public boolean packageContainsXmlFile(final PackageManager packageManager,
-            final String packageName) {
-        try (ZipFile apk = new ZipFile(getApkDirectory(packageName, packageManager))) {
-            return apk.getEntry(TRANSPARENCY_XML_DIR) != null;
-        } catch (final Exception e) {
-            Log.d(TAG, e.getMessage());
-            return false;
-        }
-    }
-
-    /**
-     * Used to return developer details
-     */
-    public static class MbaDeveloper {
-        public final String name;
-        public final String relationship;
-        public final String email;
-        public final String country;
-
-        public MbaDeveloper(final String name,
-                final String relationship,
-                final String email,
-                final String country) {
-            this.name = name;
-            this.relationship = relationship;
-            this.email = email;
-            this.country = country;
-        }
-    }
-}
diff --git a/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppDetailsActivity.java b/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppDetailsActivity.java
deleted file mode 100644
index 52e3268..0000000
--- a/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppDetailsActivity.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.mobilebundledapps;
-
-import android.content.Intent;
-import android.net.Uri;
-
-import com.android.settings.SettingsActivity;
-/**
- * An activity that is used to parse and display mobile-bundled apps application metadata xml file.
- */
-public class MobileBundledAppDetailsActivity extends SettingsActivity {
-    public static final String ACTION_TRANSPARENCY_METADATA =
-            "android.settings.TRANSPARENCY_METADATA";
-
-    public MobileBundledAppDetailsActivity() {
-        super();
-    }
-
-    @Override
-    public Intent getIntent() {
-        final Intent modIntent = new Intent(super.getIntent());
-        modIntent.setData(Uri.parse("package:"
-                + super.getIntent().getExtra(Intent.EXTRA_PACKAGE_NAME).toString()));
-        modIntent.putExtra(EXTRA_SHOW_FRAGMENT, MobileBundledAppsDetailsFragment.class.getName());
-        return modIntent;
-    }
-
-    @Override
-    protected boolean isValidFragment(final String fragmentName) {
-        return MobileBundledAppsDetailsFragment.class.getName().equals(fragmentName);
-    }
-}
diff --git a/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppsDetailsFragment.java b/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppsDetailsFragment.java
deleted file mode 100644
index 2779467..0000000
--- a/src/com/android/settings/applications/mobilebundledapps/MobileBundledAppsDetailsFragment.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.mobilebundledapps;
-
-import android.app.Application;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AlertDialog;
-
-import com.android.settings.R;
-import com.android.settings.applications.AppInfoWithHeader;
-import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils.MbaDeveloper;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.widget.LayoutPreference;
-
-import java.util.List;
-
-/**
- * A fragment for retrieving the transparency metadata and PSL in the in-APK XML file and displaying
- * them.
- */
-public class MobileBundledAppsDetailsFragment extends AppInfoWithHeader {
-    private static final String METADATA_PREF_KEY = "metadata";
-
-    protected PackageManager mPackageManager;
-    private Context mContext;
-    private LayoutPreference mMetadataPreferenceView;
-    private ApplicationsState mApplicationState;
-    private boolean mCreated = false;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mContext = getActivity();
-        mPackageManager = mContext.getPackageManager();
-        addPreferencesFromResource(R.xml.mobile_bundled_apps_details_preference);
-    }
-
-    @Override
-    public void onActivityCreated(Bundle savedInstanceState) {
-        if (mCreated) {
-            return;
-        }
-        super.onActivityCreated(savedInstanceState);
-        final ApplicationMetadataUtils appUtil = ApplicationMetadataUtils.newInstance(
-                mPackageManager,
-                mPackageName);
-        if (mAppEntry == null) {
-            mApplicationState =
-                    ApplicationsState.getInstance((Application) (mContext.getApplicationContext()));
-            mAppEntry = mApplicationState.getEntry(mPackageName, mContext.getUserId());
-        }
-        mMetadataPreferenceView = findPreference(METADATA_PREF_KEY);
-        createView(appUtil);
-        mCreated = true;
-    }
-
-    private void createView(final ApplicationMetadataUtils appUtil) {
-        final LinearLayout devListLayout =
-                mMetadataPreferenceView.findViewById(R.id.developer_list);
-        populateDeveloperList(appUtil.getDevelopers(), devListLayout);
-
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.contains_ads))
-                .setText(Boolean.toString(appUtil.getContainsAds()));
-
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.contact_url))
-                .setText(appUtil.getContactUrl());
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.contact_email))
-                .setText(appUtil.getContactEmail());
-
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.privacy_policy_url))
-                .setText(appUtil.getPrivacyPolicyUrl());
-
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.description))
-                .setText(appUtil.getDescription());
-
-        ((TextView) mMetadataPreferenceView.findViewById(R.id.category))
-                .setText(appUtil.getCategoryName());
-    }
-
-    private void populateDeveloperList(List<MbaDeveloper> developersDetails, ViewGroup parent) {
-        for (MbaDeveloper dev : developersDetails) {
-            View itemView = LayoutInflater.from(mContext)
-                    .inflate(R.layout.mobile_bundled_apps_developer_fragment_row, parent, false);
-
-            ((TextView) itemView.findViewById(R.id.developer_name)).setText(dev.name);
-            ((TextView) itemView.findViewById(R.id.developer_relationship))
-                    .setText(dev.relationship);
-            ((TextView) itemView.findViewById(R.id.developer_email)).setText(dev.email);
-            ((TextView) itemView.findViewById(R.id.developer_country)).setText(dev.country);
-
-            parent.addView(itemView);
-        }
-    }
-
-    @Override
-    protected AlertDialog createDialog(int id, int errorCode) {
-        return null;
-    }
-
-    @Override
-    protected boolean refreshUi() {
-        return true;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return SettingsEnums.TRANSPARENCY_METADATA;
-    }
-
-}
diff --git a/src/com/android/settings/dream/DreamSettings.java b/src/com/android/settings/dream/DreamSettings.java
index ae4610a..80b321d 100644
--- a/src/com/android/settings/dream/DreamSettings.java
+++ b/src/com/android/settings/dream/DreamSettings.java
@@ -90,12 +90,14 @@
         }
     }
 
-    static int getDreamSettingDescriptionResId(@WhenToDream int dreamSetting) {
+    static int getDreamSettingDescriptionResId(@WhenToDream int dreamSetting,
+            boolean enabledOnBattery) {
         switch (dreamSetting) {
             case WHILE_CHARGING:
                 return R.string.screensaver_settings_summary_sleep;
             case WHILE_DOCKED:
-                return R.string.screensaver_settings_summary_dock;
+                return enabledOnBattery ? R.string.screensaver_settings_summary_dock
+                        : R.string.screensaver_settings_summary_dock_and_charging;
             case EITHER:
                 return R.string.screensaver_settings_summary_either_long;
             case NEVER:
diff --git a/src/com/android/settings/dream/WhenToDreamPicker.java b/src/com/android/settings/dream/WhenToDreamPicker.java
index 1c5e25e..13cdadf 100644
--- a/src/com/android/settings/dream/WhenToDreamPicker.java
+++ b/src/com/android/settings/dream/WhenToDreamPicker.java
@@ -32,12 +32,15 @@
 
     private static final String TAG = "WhenToDreamPicker";
     private DreamBackend mBackend;
+    private boolean mDreamsSupportedOnBattery;
 
     @Override
     public void onAttach(Context context) {
         super.onAttach(context);
 
         mBackend = DreamBackend.getInstance(context);
+        mDreamsSupportedOnBattery = getResources().getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledOnBattery);
     }
 
     @Override
@@ -69,11 +72,17 @@
     }
 
     private String[] entries() {
-        return getResources().getStringArray(R.array.when_to_start_screensaver_entries);
+        if (mDreamsSupportedOnBattery) {
+            return getResources().getStringArray(R.array.when_to_start_screensaver_entries);
+        }
+        return getResources().getStringArray(R.array.when_to_start_screensaver_entries_no_battery);
     }
 
     private String[] keys() {
-        return getResources().getStringArray(R.array.when_to_start_screensaver_values);
+        if (mDreamsSupportedOnBattery) {
+            return getResources().getStringArray(R.array.when_to_start_screensaver_values);
+        }
+        return getResources().getStringArray(R.array.when_to_start_screensaver_values_no_battery);
     }
 
     @Override
diff --git a/src/com/android/settings/dream/WhenToDreamPreferenceController.java b/src/com/android/settings/dream/WhenToDreamPreferenceController.java
index 02ae6a7..c3bae00 100644
--- a/src/com/android/settings/dream/WhenToDreamPreferenceController.java
+++ b/src/com/android/settings/dream/WhenToDreamPreferenceController.java
@@ -33,19 +33,24 @@
     private static final String WHEN_TO_START = "when_to_start";
     private final DreamBackend mBackend;
     private final boolean mDreamsDisabledByAmbientModeSuppression;
+    private final boolean mDreamsEnabledOnBattery;
 
     WhenToDreamPreferenceController(Context context) {
         this(context, context.getResources().getBoolean(
-                com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig));
+                com.android.internal.R.bool.config_dreamsDisabledByAmbientModeSuppressionConfig),
+                context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_dreamsEnabledOnBattery));
     }
 
     @VisibleForTesting
     WhenToDreamPreferenceController(Context context,
-            boolean dreamsDisabledByAmbientModeSuppression) {
+            boolean dreamsDisabledByAmbientModeSuppression,
+            boolean dreamsEnabledOnBattery) {
         super(context);
 
         mBackend = DreamBackend.getInstance(context);
         mDreamsDisabledByAmbientModeSuppression = dreamsDisabledByAmbientModeSuppression;
+        mDreamsEnabledOnBattery = dreamsEnabledOnBattery;
     }
 
     @Override
@@ -57,7 +62,7 @@
             preference.setSummary(R.string.screensaver_settings_when_to_dream_bedtime);
         } else {
             final int resId = DreamSettings.getDreamSettingDescriptionResId(
-                    mBackend.getWhenToDreamSetting());
+                    mBackend.getWhenToDreamSetting(), mDreamsEnabledOnBattery);
             preference.setSummary(resId);
         }
     }
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 32e2e2f..e3919b0 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -134,6 +134,14 @@
     public static void startBatteryDetailPage(
             Activity caller, InstrumentedPreferenceFragment fragment,
             BatteryDiffEntry diffEntry, String usagePercent, String slotInformation) {
+        startBatteryDetailPage(
+                caller, fragment.getMetricsCategory(), diffEntry, usagePercent, slotInformation);
+    }
+
+    /** Launches battery details page for an individual battery consumer fragment. */
+    public static void startBatteryDetailPage(
+            Context context, int sourceMetricsCategory,
+            BatteryDiffEntry diffEntry, String usagePercent, String slotInformation) {
         final BatteryHistEntry histEntry = diffEntry.mBatteryHistEntry;
         final LaunchBatteryDetailPageArgs launchArgs = new LaunchBatteryDetailPageArgs();
         // configure the launch argument.
@@ -147,7 +155,7 @@
         launchArgs.mForegroundTimeMs = diffEntry.mForegroundUsageTimeInMs;
         launchArgs.mBackgroundTimeMs = diffEntry.mBackgroundUsageTimeInMs;
         launchArgs.mIsUserEntry = histEntry.isUserEntry();
-        startBatteryDetailPage(caller, fragment, launchArgs);
+        startBatteryDetailPage(context, sourceMetricsCategory, launchArgs);
     }
 
     /** Launches battery details page for an individual battery consumer. */
@@ -165,11 +173,11 @@
         launchArgs.mForegroundTimeMs = isValidToShowSummary ? entry.getTimeInForegroundMs() : 0;
         launchArgs.mBackgroundTimeMs = isValidToShowSummary ? entry.getTimeInBackgroundMs() : 0;
         launchArgs.mIsUserEntry = entry.isUserEntry();
-        startBatteryDetailPage(caller, fragment, launchArgs);
+        startBatteryDetailPage(caller, fragment.getMetricsCategory(), launchArgs);
     }
 
-    private static void startBatteryDetailPage(Activity caller,
-            InstrumentedPreferenceFragment fragment, LaunchBatteryDetailPageArgs launchArgs) {
+    private static void startBatteryDetailPage(
+            Context context, int sourceMetricsCategory, LaunchBatteryDetailPageArgs launchArgs) {
         final Bundle args = new Bundle();
         if (launchArgs.mPackageName == null) {
             // populate data for system app
@@ -190,11 +198,11 @@
         final int userId = launchArgs.mIsUserEntry ? ActivityManager.getCurrentUser()
             : UserHandle.getUserId(launchArgs.mUid);
 
-        new SubSettingLauncher(caller)
+        new SubSettingLauncher(context)
                 .setDestination(AdvancedPowerUsageDetail.class.getName())
                 .setTitleRes(R.string.battery_details_title)
                 .setArguments(args)
-                .setSourceMetricsCategory(fragment.getMetricsCategory())
+                .setSourceMetricsCategory(sourceMetricsCategory)
                 .setUserHandle(new UserHandle(userId))
                 .launch();
     }
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index efc53b9..aa7d547 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -117,6 +117,7 @@
             if (app.requestedPermissions == null || Arrays.stream(app.requestedPermissions)
                     .noneMatch(p -> p.equals(android.Manifest.permission.POST_NOTIFICATIONS))) {
                 row.lockedImportance = true;
+                row.permissionStateLocked = true;
             }
         }
     }
@@ -675,6 +676,9 @@
         public boolean systemApp;
         public boolean lockedImportance;
         public boolean showBadge;
+        // For apps target T but have not but has not requested the permission
+        // we cannot change the permission state
+        public boolean permissionStateLocked;
         public int bubblePreference = NotificationManager.BUBBLE_PREFERENCE_NONE;
         public int userId;
         public int blockedChannelCount;
diff --git a/src/com/android/settings/notification/VibrateIconPreferenceController.java b/src/com/android/settings/notification/VibrateIconPreferenceController.java
index 25d2326..d772b47 100644
--- a/src/com/android/settings/notification/VibrateIconPreferenceController.java
+++ b/src/com/android/settings/notification/VibrateIconPreferenceController.java
@@ -19,6 +19,7 @@
 import static com.android.settings.notification.SettingPref.TYPE_SECURE;
 
 import android.content.Context;
+import android.os.Vibrator;
 import android.provider.Settings.Secure;
 
 import com.android.settings.SettingsPreferenceFragment;
@@ -27,16 +28,18 @@
 public class VibrateIconPreferenceController extends SettingPrefController {
 
     private static final String KEY_VIBRATE_ICON = "vibrate_icon";
+    private final boolean mHasVibrator;
 
     public VibrateIconPreferenceController(Context context, SettingsPreferenceFragment parent,
             Lifecycle lifecycle) {
         super(context, parent, lifecycle);
+        mHasVibrator = context.getSystemService(Vibrator.class).hasVibrator();
         mPreference = new SettingPref(
             TYPE_SECURE, KEY_VIBRATE_ICON, Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0 /*default off*/);
     }
 
     @Override
     public boolean isAvailable() {
-        return true;
+        return mHasVibrator;
     }
 }
diff --git a/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java b/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
index 0c7cd23..04e3f0e 100644
--- a/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
+++ b/src/com/android/settings/notification/app/NotificationsOffPreferenceController.java
@@ -62,6 +62,8 @@
                 preference.setTitle(R.string.channel_notifications_off_desc);
             } else if (mChannelGroup != null) {
                 preference.setTitle(R.string.channel_group_notifications_off_desc);
+            } else if (mAppRow.permissionStateLocked) {
+                preference.setTitle(R.string.app_notifications_not_send_desc);
             } else {
                 preference.setTitle(R.string.app_notifications_off_desc);
             }
diff --git a/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
new file mode 100644
index 0000000..a2164b2
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppBatteryPreference.kt
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.res.stringResource
+import androidx.core.os.bundleOf
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
+import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.app.installed
+import com.android.settingslib.spaprivileged.model.app.userId
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@Composable
+fun AppBatteryPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val presenter = remember { AppBatteryPresenter(context, app) }
+    if (!presenter.isAvailable()) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.app_battery_usage_title)
+        override val summary = presenter.summary
+        override val enabled = presenter.enabled
+        override val onClick = presenter::startActivity
+    })
+
+    presenter.Updater()
+}
+
+private class AppBatteryPresenter(private val context: Context, private val app: ApplicationInfo) {
+    private var batteryDiffEntryState: LoadingState<BatteryDiffEntry?>
+        by mutableStateOf(LoadingState.Loading)
+
+    @Composable
+    fun isAvailable() = remember {
+        context.resources.getBoolean(R.bool.config_show_app_info_settings_battery)
+    }
+
+    @Composable
+    fun Updater() {
+        if (!app.installed) return
+        val current = LocalLifecycleOwner.current
+        LaunchedEffect(app) {
+            current.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch { batteryDiffEntryState = LoadingState.Done(getBatteryDiffEntry()) }
+            }
+        }
+    }
+
+    private suspend fun getBatteryDiffEntry(): BatteryDiffEntry? = withContext(Dispatchers.IO) {
+        BatteryChartPreferenceController.getAppBatteryUsageData(
+            context, app.packageName, app.userId
+        ).also {
+            Log.d(TAG, "loadBatteryDiffEntries():\n$it")
+        }
+    }
+
+    val enabled = derivedStateOf { batteryDiffEntryState is LoadingState.Done }
+
+    val summary = derivedStateOf<String> {
+        if (!app.installed) return@derivedStateOf ""
+        batteryDiffEntryState.let { batteryDiffEntryState ->
+            when (batteryDiffEntryState) {
+                is LoadingState.Loading -> context.getString(R.string.summary_placeholder)
+                is LoadingState.Done -> batteryDiffEntryState.result.getSummary()
+            }
+        }
+    }
+
+    private fun BatteryDiffEntry?.getSummary(): String =
+        this?.takeIf { mConsumePower > 0 }?.let {
+            context.getString(
+                R.string.battery_summary, Utils.formatPercentage(percentOfTotal, true)
+            )
+        } ?: context.getString(R.string.no_battery_summary)
+
+    fun startActivity() {
+        batteryDiffEntryState.resultOrNull?.run {
+            startBatteryDetailPage()
+            return
+        }
+
+        fallbackStartBatteryDetailPage()
+    }
+
+    private fun BatteryDiffEntry.startBatteryDetailPage() {
+        Log.i(TAG, "handlePreferenceTreeClick():\n$this")
+        AdvancedPowerUsageDetail.startBatteryDetailPage(
+            context,
+            SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+            this,
+            Utils.formatPercentage(percentOfTotal, true),
+            null,
+        )
+    }
+
+    private fun fallbackStartBatteryDetailPage() {
+        Log.i(TAG, "Launch : ${app.packageName} with package name")
+        val args = bundleOf(
+            AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME to app.packageName,
+            AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT to Utils.formatPercentage(0),
+            AdvancedPowerUsageDetail.EXTRA_UID to app.uid,
+        )
+        SubSettingLauncher(context)
+            .setDestination(AdvancedPowerUsageDetail::class.java.name)
+            .setTitleRes(R.string.battery_details_title)
+            .setArguments(args)
+            .setSourceMetricsCategory(SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS)
+            .launch()
+    }
+
+    companion object {
+        private const val TAG = "AppBatteryPresenter"
+    }
+}
+
+private sealed class LoadingState<out T> {
+    object Loading : LoadingState<Nothing>()
+
+    data class Done<T>(val result: T) : LoadingState<T>()
+
+    val resultOrNull: T? get() = if (this is Done) result else null
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
new file mode 100644
index 0000000..d13d108
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppDataUsagePreference.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.net.NetworkStats
+import android.net.NetworkTemplate
+import android.os.Process
+import android.text.format.DateUtils
+import android.text.format.Formatter
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.datausage.AppDataUsage
+import com.android.settings.datausage.DataUsageUtils
+import com.android.settingslib.net.NetworkCycleDataForUid
+import com.android.settingslib.net.NetworkCycleDataForUidLoader
+import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.framework.compose.toState
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.app.hasFlag
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.withContext
+
+@Composable
+fun AppDataUsagePreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val presenter = remember { AppDataUsagePresenter(context, app) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.data_usage_app_summary_title)
+        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+            initialValue = stringResource(R.string.computing_size),
+        )
+        override val enabled = presenter.isEnabled().toState()
+        override val onClick = presenter::startActivity
+    })
+}
+
+private class AppDataUsagePresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+) {
+    val isAvailableFlow = flow { emit(isAvailable()) }
+
+    private suspend fun isAvailable(): Boolean = withContext(Dispatchers.IO) {
+        Utils.isBandwidthControlEnabled()
+    }
+
+    fun isEnabled() = app.hasFlag(ApplicationInfo.FLAG_INSTALLED)
+
+    val summaryFlow = flow { emit(getSummary()) }
+
+    private suspend fun getSummary() = withContext(Dispatchers.IO) {
+        val appUsageData = getAppUsageData()
+        val totalBytes = appUsageData.sumOf { it.totalUsage }
+        if (totalBytes == 0L) {
+            context.getString(R.string.no_data_usage)
+        } else {
+            val startTime = appUsageData.minOfOrNull { it.startTime } ?: System.currentTimeMillis()
+            context.getString(
+                R.string.data_summary_format,
+                Formatter.formatFileSize(context, totalBytes, Formatter.FLAG_IEC_UNITS),
+                DateUtils.formatDateTime(context, startTime, DATE_FORMAT),
+            )
+        }
+    }
+
+    private suspend fun getAppUsageData(): List<NetworkCycleDataForUid> =
+        withContext(Dispatchers.IO) {
+            createLoader().loadInBackground() ?: emptyList()
+        }
+
+    private fun createLoader(): NetworkCycleDataForUidLoader =
+        NetworkCycleDataForUidLoader.builder(context).apply {
+            setRetrieveDetail(false)
+            setNetworkTemplate(getTemplate())
+            addUid(app.uid)
+            if (Process.isApplicationUid(app.uid)) {
+                // Also add in network usage for the app's SDK sandbox
+                addUid(Process.toSdkSandboxUid(app.uid))
+            }
+        }.build()
+
+    private fun getTemplate(): NetworkTemplate = when {
+        DataUsageUtils.hasReadyMobileRadio(context) -> {
+            NetworkTemplate.Builder(NetworkTemplate.MATCH_MOBILE)
+                .setMeteredness(NetworkStats.METERED_YES)
+                .build()
+        }
+        DataUsageUtils.hasWifiRadio(context) -> {
+            NetworkTemplate.Builder(NetworkTemplate.MATCH_WIFI).build()
+        }
+        else -> NetworkTemplate.Builder(NetworkTemplate.MATCH_ETHERNET).build()
+    }
+
+    fun startActivity() {
+        AppInfoDashboardFragment.startAppInfoFragment(
+            AppDataUsage::class.java,
+            app,
+            context,
+            SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+        )
+    }
+
+    private companion object {
+        const val DATE_FORMAT = DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_ABBREV_MONTH
+    }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
index 2e3e45f..a1f3c1c 100644
--- a/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppInfoSettings.kt
@@ -16,6 +16,7 @@
 
 package com.android.settings.spa.app.appinfo
 
+import android.app.settings.SettingsEnums
 import android.content.pm.ApplicationInfo
 import android.os.Bundle
 import androidx.compose.runtime.Composable
@@ -50,6 +51,8 @@
         navArgument(USER_ID) { type = NavType.IntType },
     )
 
+    const val METRICS_CATEGORY = SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS
+
     @Composable
     override fun Page(arguments: Bundle?) {
         val packageName = arguments!!.getString(PACKAGE_NAME)!!
@@ -90,12 +93,15 @@
 
         AppButtons(packageInfoPresenter)
 
+        AppSettingsPreference(app)
+        // TODO: all_services_settings
+        // TODO: notification_settings
         AppPermissionPreference(app)
         AppStoragePreference(app)
         // TODO: instant_app_launch_supported_domain_urls
-        // TODO: data_settings
+        AppDataUsagePreference(app)
         AppTimeSpentPreference(app)
-        // TODO: battery
+        AppBatteryPreference(app)
         AppLocalePreference(app)
         AppOpenByDefaultPreference(app)
         DefaultAppShortcuts(app)
@@ -113,7 +119,9 @@
             AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app)
         }
 
-        // TODO: app_installer
+        Category(title = stringResource(R.string.app_install_details_group_title)) {
+            AppInstallerInfoPreference(app)
+        }
         appInfoProvider.FooterAppVersion()
     }
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
new file mode 100644
index 0000000..8d9c98a
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreference.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.applications.AppStoreUtil
+import com.android.settingslib.applications.AppUtils
+import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.framework.common.asUser
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.userId
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@Composable
+fun AppInstallerInfoPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val coroutineScope = rememberCoroutineScope()
+    val presenter = remember { AppInstallerInfoPresenter(context, app, coroutineScope) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.app_install_details_title)
+        override val summary = presenter.summaryFlow.collectAsStateWithLifecycle(
+            initialValue = stringResource(R.string.summary_placeholder),
+        )
+        override val enabled =
+            presenter.enabledFlow.collectAsStateWithLifecycle(initialValue = false)
+        override val onClick = presenter::startActivity
+    })
+}
+
+private class AppInstallerInfoPresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+    private val coroutineScope: CoroutineScope,
+) {
+    private val userContext = context.asUser(app.userHandle)
+    private val packageManager = userContext.packageManager
+    private val userManager = context.userManager
+
+    private val installerPackageFlow = flow {
+        emit(withContext(Dispatchers.IO) {
+            AppStoreUtil.getInstallerPackageName(userContext, app.packageName)
+        })
+    }.sharedFlow()
+
+    private val installerLabelFlow = installerPackageFlow.map { installerPackage ->
+        installerPackage ?: return@map null
+        withContext(Dispatchers.IO) {
+            Utils.getApplicationLabel(context, installerPackage)
+        }
+    }.sharedFlow()
+
+    val isAvailableFlow = installerLabelFlow.map { installerLabel ->
+        withContext(Dispatchers.IO) {
+            !userManager.isManagedProfile(app.userId) &&
+                !AppUtils.isMainlineModule(packageManager, app.packageName) &&
+                installerLabel != null
+        }
+    }
+
+    val summaryFlow = installerLabelFlow.map { installerLabel ->
+        val detailsStringId = when {
+            app.isInstantApp -> R.string.instant_app_details_summary
+            else -> R.string.app_install_details_summary
+        }
+        context.getString(detailsStringId, installerLabel)
+    }
+
+    private val intentFlow = installerPackageFlow.map { installerPackage ->
+        withContext(Dispatchers.IO) {
+            AppStoreUtil.getAppStoreLink(context, installerPackage, app.packageName)
+        }
+    }.sharedFlow()
+
+    val enabledFlow = intentFlow.map { it != null }
+
+    fun startActivity() {
+        coroutineScope.launch {
+            intentFlow.collect { intent ->
+                if (intent != null) {
+                    context.startActivityAsUser(intent, app.userHandle)
+                }
+            }
+        }
+    }
+
+    private fun <T> Flow<T>.sharedFlow() =
+        shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
index 936dee6..4cc24b3 100644
--- a/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppOpenByDefaultPreference.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
@@ -86,7 +85,7 @@
             AppLaunchSettings::class.java,
             app,
             context,
-            SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+            AppInfoSettingsProvider.METRICS_CATEGORY,
         )
     }
 }
diff --git a/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt b/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt
new file mode 100644
index 0000000..babd607
--- /dev/null
+++ b/src/com/android/settings/spa/app/appinfo/AppSettingsPreference.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.ResolveInfoFlags
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.overlay.FeatureFactory
+import com.android.settingslib.spa.framework.compose.collectAsStateWithLifecycle
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.userId
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@Composable
+fun AppSettingsPreference(app: ApplicationInfo) {
+    val context = LocalContext.current
+    val coroutineScope = rememberCoroutineScope()
+    val presenter = remember { AppSettingsPresenter(context, app, coroutineScope) }
+    if (!presenter.isAvailableFlow.collectAsStateWithLifecycle(initialValue = false).value) return
+
+    Preference(object : PreferenceModel {
+        override val title = stringResource(R.string.app_settings_link)
+        override val onClick = presenter::startActivity
+    })
+}
+
+private class AppSettingsPresenter(
+    private val context: Context,
+    private val app: ApplicationInfo,
+    private val coroutineScope: CoroutineScope,
+) {
+    private val packageManager = context.packageManager
+
+    private val intentFlow = flow {
+        emit(resolveIntent())
+    }.shareIn(coroutineScope, SharingStarted.WhileSubscribed(), 1)
+
+    val isAvailableFlow = intentFlow.map { it != null }
+
+    fun startActivity() {
+        coroutineScope.launch {
+            intentFlow.collect { intent ->
+                if (intent != null) {
+                    FeatureFactory.getFactory(context).metricsFeatureProvider
+                        .action(
+                            SettingsEnums.PAGE_UNKNOWN,
+                            SettingsEnums.ACTION_OPEN_APP_SETTING,
+                            AppInfoSettingsProvider.METRICS_CATEGORY,
+                            null,
+                            0,
+                        )
+                    context.startActivityAsUser(intent, app.userHandle)
+                }
+            }
+        }
+    }
+
+    private suspend fun resolveIntent(): Intent? = withContext(Dispatchers.IO) {
+        val intent = Intent(Intent.ACTION_APPLICATION_PREFERENCES).apply {
+            `package` = app.packageName
+        }
+        packageManager.resolveActivityAsUser(intent, ResolveInfoFlags.of(0), app.userId)
+            ?.activityInfo
+            ?.let { activityInfo ->
+                Intent(intent.action).apply {
+                    setClassName(activityInfo.packageName, activityInfo.name)
+                }
+            }
+    }
+}
diff --git a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
index 265f882..e8b1018 100644
--- a/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
+++ b/src/com/android/settings/spa/app/appinfo/AppStoragePreference.kt
@@ -16,7 +16,6 @@
 
 package com.android.settings.spa.app.appinfo
 
-import android.app.settings.SettingsEnums
 import android.content.Context
 import android.content.pm.ApplicationInfo
 import androidx.compose.runtime.Composable
@@ -70,6 +69,6 @@
         AppStorageSettings::class.java,
         app,
         context,
-        SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+        AppInfoSettingsProvider.METRICS_CATEGORY,
     )
 }
diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java
index 242f9a4..deb5a3f 100644
--- a/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppInstallerInfoPreferenceControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.applications.appinfo;
 
-import static com.android.settings.applications.mobilebundledapps.MobileBundledAppDetailsActivity.ACTION_TRANSPARENCY_METADATA;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -43,14 +41,11 @@
 
 import androidx.preference.Preference;
 
-import com.android.settings.applications.mobilebundledapps.ApplicationMetadataUtils;
 import com.android.settings.core.BasePreferenceController;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -58,8 +53,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 public class AppInstallerInfoPreferenceControllerTest {
-    private static final String TEST_PACKAGE_NAME = "Package1";
-    private static final String TEST_CONTEXT_KEY = "test_key";
+
     @Mock
     private UserManager mUserManager;
     @Mock
@@ -73,17 +67,11 @@
     @Mock
     private Preference mPreference;
 
-    @Mock
-    private ApplicationMetadataUtils mApplicationMetadataUtils;
-
-    @Captor
-    ArgumentCaptor<Intent> mIntentArgumentCaptor;
-
     private Context mContext;
     private AppInstallerInfoPreferenceController mController;
 
     @Before
-    public void setup() throws PackageManager.NameNotFoundException {
+    public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -93,13 +81,9 @@
         when(mInstallSourceInfo.getInstallingPackageName()).thenReturn(installerPackage);
         when(mPackageManager.getApplicationInfo(eq(installerPackage), anyInt()))
                 .thenReturn(mAppInfo);
-        mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
+        mController = new AppInstallerInfoPreferenceController(mContext, "test_key");
+        mController.setPackageName("Package1");
         mController.setParentFragment(mFragment);
-        mController.setPackageName(TEST_PACKAGE_NAME);
-        when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
-                .thenReturn(false);
-        mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
-        mController.setEnableMbaFlag(true);
     }
 
     @Test
@@ -111,42 +95,8 @@
     }
 
     @Test
-    public void getAvailabilityStatus_noAppLabel_andNotMbaWithMetadata_shouldReturnDisabled()
-            throws PackageManager.NameNotFoundException {
+    public void getAvailabilityStatus_noAppLabel_shouldReturnDisabled() {
         when(mUserManager.isManagedProfile()).thenReturn(false);
-        mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
-
-        assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
-    }
-
-    @Test
-    public void getAvailabilityStatus_noAppLabel_andHaveMbaFile_shouldReturnAvailable()
-            throws PackageManager.NameNotFoundException {
-        mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
-        mController.setPackageName(TEST_PACKAGE_NAME);
-        mController.setParentFragment(mFragment);
-        when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
-                .thenReturn(true);
-        mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
-        mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
-
-        assertThat(mController.getAvailabilityStatus())
-                .isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
-    }
-
-    @Test
-    public void getAvailabilityStatus_noAppLabel_andMbaFeatureFlagDisabled_shouldReturnDisabled()
-            throws PackageManager.NameNotFoundException {
-        mController.setEnableMbaFlag(false);
-        when(mUserManager.isManagedProfile()).thenReturn(false);
-        mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
-        mController.setPackageName(TEST_PACKAGE_NAME);
-        mController.setParentFragment(mFragment);
-        when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
-                .thenReturn(true);
-        mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
-        mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
 
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.DISABLED_FOR_USER);
@@ -155,12 +105,13 @@
     @Test
     public void getAvailabilityStatus_hasAppLabel_shouldReturnAvailable()
             throws PackageManager.NameNotFoundException {
+        final String packageName = "Package1";
         when(mUserManager.isManagedProfile()).thenReturn(false);
         when(mAppInfo.loadLabel(mPackageManager)).thenReturn("Label1");
-        mController = new AppInstallerInfoPreferenceController(mContext, TEST_CONTEXT_KEY);
-        mController.setPackageName(TEST_PACKAGE_NAME);
+        mController = new AppInstallerInfoPreferenceController(mContext, "test_key");
+        mController.setPackageName(packageName);
         mController.setParentFragment(mFragment);
-        mockMainlineModule(TEST_PACKAGE_NAME, false /* isMainlineModule */);
+        mockMainlineModule(packageName, false /* isMainlineModule */);
 
         assertThat(mController.getAvailabilityStatus())
                 .isEqualTo(BasePreferenceController.AVAILABLE);
@@ -178,7 +129,7 @@
     }
 
     @Test
-    public void updateState_noAppStoreLink_andNotMbaWithMetadata_shouldDisablePreference() {
+    public void updateState_noAppStoreLink_shouldDisablePreference() {
         final PackageInfo packageInfo = mock(PackageInfo.class);
         packageInfo.applicationInfo = mAppInfo;
         when(mFragment.getPackageInfo()).thenReturn(packageInfo);
@@ -188,39 +139,6 @@
 
         verify(mPreference).setEnabled(false);
     }
-    @Test
-    public void updateState_noAppStoreLink_andMbaFeatureFlagDisabled_shouldDisablePreference() {
-        mController.setEnableMbaFlag(false);
-        when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
-                .thenReturn(true);
-        mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        packageInfo.applicationInfo = mAppInfo;
-        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
-        when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference).setEnabled(false);
-    }
-
-    @Test
-    public void updateState_noAppStoreLink_andMbaWithMetadata_shouldSetPreferenceIntent() {
-        when(mApplicationMetadataUtils.packageContainsXmlFile(mPackageManager, TEST_PACKAGE_NAME))
-                .thenReturn(true);
-        mController.setMbaWithMetadataStatus(mApplicationMetadataUtils, TEST_PACKAGE_NAME);
-        final PackageInfo packageInfo = mock(PackageInfo.class);
-        packageInfo.applicationInfo = mAppInfo;
-        when(mFragment.getPackageInfo()).thenReturn(packageInfo);
-        when(mPackageManager.resolveActivity(any(), anyInt())).thenReturn(null);
-
-        mController.updateState(mPreference);
-
-        verify(mPreference, never()).setEnabled(false);
-        verify(mPreference).setIntent(mIntentArgumentCaptor.capture());
-        assertThat(mIntentArgumentCaptor.getValue().getAction())
-                .isEqualTo(ACTION_TRANSPARENCY_METADATA);
-    }
 
     @Test
     public void updateState_hasAppStoreLink_shouldSetPreferenceIntent() {
@@ -236,9 +154,7 @@
         mController.updateState(mPreference);
 
         verify(mPreference, never()).setEnabled(false);
-        verify(mPreference).setIntent(mIntentArgumentCaptor.capture());
-        assertThat(mIntentArgumentCaptor.getValue().getAction())
-                .isEqualTo(Intent.ACTION_SHOW_APP_INFO);
+        verify(mPreference).setIntent(any(Intent.class));
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtilsTest.java b/tests/robotests/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtilsTest.java
deleted file mode 100644
index a9eaec4..0000000
--- a/tests/robotests/src/com/android/settings/applications/mobilebundledapps/ApplicationMetadataUtilsTest.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.settings.applications.mobilebundledapps;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-
-import com.google.common.io.CharSource;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.List;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-@RunWith(RobolectricTestRunner.class)
-public class ApplicationMetadataUtilsTest {
-    private static final String TEST_PACKAGE_NAME = "test";
-    private static final String TEST_SOURCE_DIR = "sourcedir";
-
-    private static final String TEST_XML_SCHEMA = "<transparency-info>\n"
-            + "  <template/>\n"
-            + "  <contains-ads/>\n"
-            + "  <developers>\n"
-            + "    <developer name=\"Example ODM\" relationship=\"ODM\" email=\"odm@example.com\""
-            + " \n"
-            + "               website=\"http://odm.example.com\" country=\"US\"/>\n"
-            + "    <developer name=\"Example carrier\" relationship=\"CARRIER\" "
-            + "email=\"carrier@example.com\" \n"
-            + "               country=\"US\"/>\n"
-            + "  </developers>\n"
-            + "  <contact url=\"http://example.com/contact-us\" email=\"contact@example.com\"/>\n"
-            + "  <privacy-policy url=\"https://www.example.com/privacy-policy.html\"/>\n"
-            + "  <description>This application provides the user with news "
-            + "headlines</description>\n"
-            + "  <category name=\"News and magazines\"/>\n"
-            + "</transparency-info>";
-    @Mock
-    private PackageManager mPackageManager;
-
-    private Document mDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder()
-            .parse(CharSource.wrap(TEST_XML_SCHEMA).asByteSource(StandardCharsets.UTF_8)
-                    .openStream());
-
-
-    public ApplicationMetadataUtilsTest()
-            throws IOException, ParserConfigurationException, SAXException {
-    }
-
-    @Before
-    public void setup()
-            throws PackageManager.NameNotFoundException, IOException, ParserConfigurationException,
-            SAXException {
-        MockitoAnnotations.initMocks(this);
-        final ApplicationInfo appInfo = new ApplicationInfo();
-        appInfo.sourceDir = TEST_SOURCE_DIR;
-        when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME),
-                any(PackageManager.ApplicationInfoFlags.class))).thenReturn(appInfo);
-    }
-
-    @Test
-    public void getDefaultInstance_alwaysReturnSameInstance() {
-        final ApplicationMetadataUtils firstInstance =
-                ApplicationMetadataUtils.getDefaultInstance();
-
-        assertThat(firstInstance).isEqualTo(ApplicationMetadataUtils.getDefaultInstance());
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void createInstance_bubblesUpException() throws PackageManager.NameNotFoundException {
-        final String testErrorMsg = "test";
-        when(mPackageManager.getApplicationInfo(eq(TEST_PACKAGE_NAME),
-                any(PackageManager.ApplicationInfoFlags.class)))
-                .thenThrow(new Exception(testErrorMsg));
-
-        ApplicationMetadataUtils.newInstance(mPackageManager, TEST_PACKAGE_NAME);
-    }
-
-    @Test
-    public void fieldGetters_toReturnNull_whenEmptyOrError() {
-        final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
-        assertThat(appUtils.getContainsAds()).isEqualTo(false);
-        assertThat(appUtils.getCategoryName()).isNull();
-        assertThat(appUtils.getPrivacyPolicyUrl()).isNull();
-        assertThat(appUtils.getDescription()).isNull();
-        assertThat(appUtils.getDevelopers()).isEmpty();
-    }
-
-    @Test
-    public void fieldGetters_toReturnCorrectValues_whenExists() {
-        final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
-        appUtils.setXmlDoc(mDocument);
-        assertThat(appUtils.getContainsAds()).isEqualTo(true);
-        assertThat(appUtils.getCategoryName()).isEqualTo("News and magazines");
-        assertThat(appUtils.getPrivacyPolicyUrl())
-                .isEqualTo("https://www.example.com/privacy-policy.html");
-        assertThat(appUtils.getDescription())
-                .isEqualTo("This application provides the user with news headlines");
-    }
-
-    @Test
-    public void getDevelopers_returnsCorrectValues() {
-        final ApplicationMetadataUtils appUtils = new ApplicationMetadataUtils();
-        appUtils.setXmlDoc(mDocument);
-        final List<ApplicationMetadataUtils.MbaDeveloper> developers = appUtils.getDevelopers();
-
-        assertThat(developers.size()).isEqualTo(2);
-        assertThat(developers.get(0).country).isEqualTo("US");
-        assertThat(developers.get(0).email).isEqualTo("odm@example.com");
-        assertThat(developers.get(0).name).isEqualTo("Example ODM");
-        assertThat(developers.get(1).relationship).isEqualTo("CARRIER");
-        assertThat(developers.get(1).country).isEqualTo("US");
-        assertThat(developers.get(1).email).isEqualTo("carrier@example.com");
-        assertThat(developers.get(1).name).isEqualTo("Example carrier");
-        assertThat(developers.get(1).relationship).isEqualTo("CARRIER");
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/dream/DreamSettingsTest.java b/tests/robotests/src/com/android/settings/dream/DreamSettingsTest.java
index 4d0adb4..e9e8072 100644
--- a/tests/robotests/src/com/android/settings/dream/DreamSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/dream/DreamSettingsTest.java
@@ -38,24 +38,31 @@
 public class DreamSettingsTest {
 
     private static final List<String> KEYS = Arrays.asList(
-        DreamSettings.WHILE_CHARGING_ONLY,
-        DreamSettings.WHILE_DOCKED_ONLY,
-        DreamSettings.EITHER_CHARGING_OR_DOCKED,
-        DreamSettings.NEVER_DREAM
+            DreamSettings.WHILE_CHARGING_ONLY,
+            DreamSettings.WHILE_DOCKED_ONLY,
+            DreamSettings.EITHER_CHARGING_OR_DOCKED,
+            DreamSettings.NEVER_DREAM
     );
 
     private static final @WhenToDream int[] SETTINGS = {
-        DreamBackend.WHILE_CHARGING,
-        DreamBackend.WHILE_DOCKED,
-        DreamBackend.EITHER,
-        DreamBackend.NEVER,
+            DreamBackend.WHILE_CHARGING,
+            DreamBackend.WHILE_DOCKED,
+            DreamBackend.EITHER,
+            DreamBackend.NEVER,
     };
 
     private static final int[] RES_IDS = {
-        R.string.screensaver_settings_summary_sleep,
-        R.string.screensaver_settings_summary_dock,
-        R.string.screensaver_settings_summary_either_long,
-        R.string.screensaver_settings_summary_never
+            R.string.screensaver_settings_summary_sleep,
+            R.string.screensaver_settings_summary_dock,
+            R.string.screensaver_settings_summary_either_long,
+            R.string.screensaver_settings_summary_never
+    };
+
+    private static final int[] RES_IDS_NO_BATTERY = {
+            R.string.screensaver_settings_summary_sleep,
+            R.string.screensaver_settings_summary_dock_and_charging,
+            R.string.screensaver_settings_summary_either_long,
+            R.string.screensaver_settings_summary_never
     };
 
     @Test
@@ -81,11 +88,17 @@
     @Test
     public void getDreamSettingDescriptionResId() {
         for (int i = 0; i < SETTINGS.length; i++) {
-            assertThat(DreamSettings.getDreamSettingDescriptionResId(SETTINGS[i]))
+            assertThat(DreamSettings.getDreamSettingDescriptionResId(
+                    SETTINGS[i], /* enabledOnBattery= */ false))
+                    .isEqualTo(RES_IDS_NO_BATTERY[i]);
+            assertThat(DreamSettings.getDreamSettingDescriptionResId(
+                    SETTINGS[i], /* enabledOnBattery= */ true))
                     .isEqualTo(RES_IDS[i]);
         }
         // Default
-        assertThat(DreamSettings.getDreamSettingDescriptionResId(-1))
+        assertThat(DreamSettings.getDreamSettingDescriptionResId(-1, /* enabledOnBattery= */ false))
+                .isEqualTo(R.string.screensaver_settings_summary_never);
+        assertThat(DreamSettings.getDreamSettingDescriptionResId(-1, /* enabledOnBattery= */ true))
                 .isEqualTo(R.string.screensaver_settings_summary_never);
     }
 
diff --git a/tests/robotests/src/com/android/settings/dream/WhenToDreamPickerTest.java b/tests/robotests/src/com/android/settings/dream/WhenToDreamPickerTest.java
index 16f8599..91ec299 100644
--- a/tests/robotests/src/com/android/settings/dream/WhenToDreamPickerTest.java
+++ b/tests/robotests/src/com/android/settings/dream/WhenToDreamPickerTest.java
@@ -28,6 +28,7 @@
 import androidx.test.core.app.ApplicationProvider;
 
 import com.android.settings.testutils.FakeFeatureFactory;
+import com.android.settings.testutils.shadow.SettingsShadowResources;
 import com.android.settingslib.dream.DreamBackend;
 
 import org.junit.Before;
@@ -37,9 +38,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = SettingsShadowResources.class)
 public class WhenToDreamPickerTest {
 
     private WhenToDreamPicker mPicker;
@@ -53,10 +56,15 @@
         MockitoAnnotations.initMocks(this);
         final Context context = spy(ApplicationProvider.getApplicationContext());
 
+        SettingsShadowResources.overrideResource(
+                com.android.internal.R.bool.config_dreamsEnabledOnBattery,
+                true);
+
         when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         FakeFeatureFactory.setupForTest();
 
-        mPicker = new WhenToDreamPicker();
+        mPicker = spy(new WhenToDreamPicker());
+        when(mPicker.getContext()).thenReturn(context);
         mPicker.onAttach(context);
 
         ReflectionHelpers.setField(mPicker, "mBackend", mBackend);
diff --git a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
index 458c5c6..6e687bb 100644
--- a/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/dream/WhenToDreamPreferenceControllerTest.java
@@ -64,7 +64,7 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mContext = spy(ApplicationProvider.getApplicationContext());
-        mController = new WhenToDreamPreferenceController(mContext, true);
+        mController = new WhenToDreamPreferenceController(mContext, true, true);
         ReflectionHelpers.setField(mController, "mBackend", mBackend);
         when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
         when(mPowerManager.isAmbientDisplaySuppressedForTokenByApp(anyString(), anyInt()))
@@ -88,7 +88,7 @@
         final Preference mockPref = mock(Preference.class);
         when(mockPref.getContext()).thenReturn(mContext);
         when(mBackend.getWhenToDreamSetting()).thenReturn(testSetting);
-        final int expectedResId = DreamSettings.getDreamSettingDescriptionResId(testSetting);
+        final int expectedResId = DreamSettings.getDreamSettingDescriptionResId(testSetting, true);
 
         mController.updateState(mockPref);
         verify(mockPref).setSummary(expectedResId);
diff --git a/tests/robotests/src/com/android/settings/notification/app/NotificationsOffPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/NotificationsOffPreferenceControllerTest.java
index 34e94a0..2eebd3a 100644
--- a/tests/robotests/src/com/android/settings/notification/app/NotificationsOffPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/notification/app/NotificationsOffPreferenceControllerTest.java
@@ -20,6 +20,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
@@ -32,6 +33,7 @@
 
 import androidx.preference.Preference;
 
+import com.android.settings.R;
 import com.android.settings.notification.NotificationBackend;
 
 import com.google.common.collect.ImmutableList;
@@ -157,4 +159,23 @@
         assertThat(pref.getTitle().toString()).contains("app");
         assertThat(pref.isSelectable()).isFalse();
     }
+
+    @Test
+    public void testUpdateState_whenToggleDisabled() {
+        // Given: the app does not request to post notifications
+        // and it's preference toggle is disabled
+        NotificationBackend.AppRow appRow = new NotificationBackend.AppRow();
+        appRow.banned = true;
+        appRow.permissionStateLocked = true;
+        mController.onResume(appRow, null, null, null, null, null, null);
+        Preference pref = new Preference(RuntimeEnvironment.application);
+
+        // When: updateState(Preference preference) is called
+        mController.updateState(pref);
+
+        // Then: title of pref should be app_notifications_not_send_desc
+        assertEquals(
+                RuntimeEnvironment.application.getString(R.string.app_notifications_not_send_desc),
+                pref.getTitle().toString());
+    }
 }
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
new file mode 100644
index 0000000..0657435
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppBatteryPreferenceTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasTextExactly
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.settings.R
+import com.android.settings.fuelgauge.AdvancedPowerUsageDetail
+import com.android.settings.fuelgauge.batteryusage.BatteryChartPreferenceController
+import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry
+import com.android.settingslib.spaprivileged.model.app.userId
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppBatteryPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Spy
+    private val resources = context.resources
+
+    @Before
+    fun setUp() {
+        mockSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .mockStatic(BatteryChartPreferenceController::class.java)
+            .mockStatic(AdvancedPowerUsageDetail::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
+            .thenReturn(true)
+    }
+
+    private fun mockBatteryDiffEntry(batteryDiffEntry: BatteryDiffEntry?) {
+        whenever(BatteryChartPreferenceController.getAppBatteryUsageData(
+            context, PACKAGE_NAME, APP.userId
+        )).thenReturn(batteryDiffEntry)
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun whenConfigIsFalse_notDisplayed() {
+        whenever(resources.getBoolean(R.bool.config_show_app_info_settings_battery))
+            .thenReturn(false)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenAppNotInstalled_noSummary() {
+        val notInstalledApp = ApplicationInfo()
+
+        setContent(notInstalledApp)
+
+        composeTestRule.onNode(hasTextExactly(context.getString(R.string.app_battery_usage_title)))
+            .assertIsDisplayed()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun batteryDiffEntryIsNull() {
+        mockBatteryDiffEntry(null)
+
+        setContent()
+
+        composeTestRule.onNode(
+            hasTextExactly(
+                context.getString(R.string.app_battery_usage_title),
+                context.getString(R.string.no_battery_summary),
+            ),
+        ).assertIsDisplayed().assertIsEnabled()
+    }
+
+    @Test
+    fun noConsumePower() {
+        val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
+            mConsumePower = 0.0
+        }
+        mockBatteryDiffEntry(batteryDiffEntry)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.no_battery_summary))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun hasConsumePower() {
+        val batteryDiffEntry = mock(BatteryDiffEntry::class.java).apply {
+            mConsumePower = 12.3
+        }
+        whenever(batteryDiffEntry.percentOfTotal).thenReturn(45.6)
+        mockBatteryDiffEntry(batteryDiffEntry)
+
+        setContent()
+
+        composeTestRule.onNodeWithText("46% use since last full charge").assertIsDisplayed()
+    }
+
+    @Test
+    fun whenClick_openDetailsPage() {
+        val batteryDiffEntry = mock(BatteryDiffEntry::class.java)
+        whenever(batteryDiffEntry.percentOfTotal).thenReturn(10.0)
+        mockBatteryDiffEntry(batteryDiffEntry)
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        ExtendedMockito.verify {
+            AdvancedPowerUsageDetail.startBatteryDetailPage(
+                context,
+                SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+                batteryDiffEntry,
+                "10%",
+                null,
+            )
+        }
+    }
+
+    private fun setContent(app: ApplicationInfo = APP) {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AppBatteryPreference(app)
+            }
+        }
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+            flags = ApplicationInfo.FLAG_INSTALLED
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt
new file mode 100644
index 0000000..22876d1
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppDataUsagePreferenceTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.net.NetworkTemplate
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.printToLog
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.applications.appinfo.AppInfoDashboardFragment
+import com.android.settings.datausage.AppDataUsage
+import com.android.settings.testutils.waitUntilExists
+import com.android.settingslib.net.NetworkCycleDataForUid
+import com.android.settingslib.net.NetworkCycleDataForUidLoader
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppDataUsagePreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var builder: NetworkCycleDataForUidLoader.Builder<NetworkCycleDataForUidLoader>
+
+    @Mock
+    private lateinit var loader: NetworkCycleDataForUidLoader
+
+    @Before
+    fun setUp() {
+        mockSession = mockitoSession()
+            .initMocks(this)
+            .mockStatic(Utils::class.java)
+            .mockStatic(NetworkCycleDataForUidLoader::class.java)
+            .mockStatic(NetworkTemplate::class.java)
+            .mockStatic(AppInfoDashboardFragment::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        whenever(Utils.isBandwidthControlEnabled()).thenReturn(true)
+        whenever(NetworkCycleDataForUidLoader.builder(context)).thenReturn(builder)
+        whenever(builder.build()).thenReturn(loader)
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun whenBandwidthControlDisabled_notDisplayed() {
+        whenever(Utils.isBandwidthControlEnabled()).thenReturn(false)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenAppNotInstalled_disabled() {
+        val notInstalledApp = ApplicationInfo()
+
+        setContent(notInstalledApp)
+
+        composeTestRule.onNodeWithText(context.getString(R.string.data_usage_app_summary_title))
+            .assertIsDisplayed()
+            .assertIsNotEnabled()
+    }
+
+    @Test
+    fun whenAppInstalled_enabled() {
+        setContent(APP)
+
+        composeTestRule.onNodeWithText(context.getString(R.string.data_usage_app_summary_title))
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun setCorrectValuesForBuilder() {
+        setContent()
+
+        verify(builder).setRetrieveDetail(false)
+        verify(builder).addUid(UID)
+    }
+
+    @Test
+    fun whenNoDataUsage() {
+        whenever(loader.loadInBackground()).thenReturn(emptyList())
+
+        setContent()
+
+        composeTestRule.onRoot().printToLog("AAA")
+        composeTestRule.onNodeWithText(context.getString(R.string.no_data_usage))
+            .assertIsDisplayed()
+    }
+
+    @Test
+    fun whenHasDataUsage() {
+        val cycleData = mock(NetworkCycleDataForUid::class.java)
+        whenever(cycleData.totalUsage).thenReturn(123)
+        whenever(cycleData.startTime).thenReturn(1666666666666)
+        whenever(loader.loadInBackground()).thenReturn(listOf(cycleData))
+
+        setContent()
+
+        composeTestRule.waitUntilExists(hasText("123 B used since Oct 25"))
+    }
+
+    @Test
+    fun whenClick_startActivity() {
+        whenever(loader.loadInBackground()).thenReturn(emptyList())
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        ExtendedMockito.verify {
+            AppInfoDashboardFragment.startAppInfoFragment(
+                AppDataUsage::class.java,
+                APP,
+                context,
+                SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS,
+            )
+        }
+    }
+
+    private fun setContent(app: ApplicationInfo = APP) {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AppDataUsagePreference(app)
+            }
+        }
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+            flags = ApplicationInfo.FLAG_INSTALLED
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
new file mode 100644
index 0000000..b66967a
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppInstallerInfoPreferenceTest.kt
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ApplicationInfo
+import android.os.UserManager
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.printToLog
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.settings.R
+import com.android.settings.Utils
+import com.android.settings.applications.AppStoreUtil
+import com.android.settings.testutils.waitUntilExists
+import com.android.settingslib.applications.AppUtils
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppInstallerInfoPreferenceTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var userManager: UserManager
+
+    @Before
+    fun setUp() {
+        mockSession = mockitoSession()
+            .initMocks(this)
+            .mockStatic(AppStoreUtil::class.java)
+            .mockStatic(Utils::class.java)
+            .mockStatic(AppUtils::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+        whenever(context.userManager).thenReturn(userManager)
+        whenever(userManager.isManagedProfile(anyInt())).thenReturn(false)
+        whenever(AppStoreUtil.getInstallerPackageName(any(), eq(PACKAGE_NAME)))
+            .thenReturn(INSTALLER_PACKAGE_NAME)
+        whenever(AppStoreUtil.getAppStoreLink(context, INSTALLER_PACKAGE_NAME, PACKAGE_NAME))
+            .thenReturn(STORE_LINK)
+        whenever(Utils.getApplicationLabel(context, INSTALLER_PACKAGE_NAME))
+            .thenReturn(INSTALLER_PACKAGE_LABEL)
+        whenever(AppUtils.isMainlineModule(any(), eq(PACKAGE_NAME)))
+            .thenReturn(false)
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun whenNoInstaller_notDisplayed() {
+        whenever(AppStoreUtil.getInstallerPackageName(any(), eq(PACKAGE_NAME))).thenReturn(null)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenInstallerLabelIsNull_notDisplayed() {
+        whenever(Utils.getApplicationLabel(context, INSTALLER_PACKAGE_NAME)).thenReturn(null)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenIsManagedProfile_notDisplayed() {
+        whenever(userManager.isManagedProfile(anyInt())).thenReturn(true)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenIsMainlineModule_notDisplayed() {
+        whenever(AppUtils.isMainlineModule(any(), eq(PACKAGE_NAME))).thenReturn(true)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun whenStoreLinkIsNull_disabled() {
+        whenever(AppStoreUtil.getAppStoreLink(context, INSTALLER_PACKAGE_NAME, PACKAGE_NAME))
+            .thenReturn(null)
+
+        setContent()
+        waitUntilDisplayed()
+
+        composeTestRule.onNode(preferenceNode).assertIsNotEnabled()
+    }
+
+    @Test
+    fun whenIsInstantApp_hasSummaryForInstant() {
+        val instantApp = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+            privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
+        }
+
+        setContent(instantApp)
+        waitUntilDisplayed()
+
+        composeTestRule.onRoot().printToLog("AAA")
+        composeTestRule.onNodeWithText("More info on installer label")
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun whenNotInstantApp() {
+        setContent()
+        waitUntilDisplayed()
+
+        composeTestRule.onRoot().printToLog("AAA")
+        composeTestRule.onNodeWithText("App installed from installer label")
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun whenClick_startActivity() {
+        setContent()
+        waitUntilDisplayed()
+        composeTestRule.onRoot().performClick()
+
+        verify(context).startActivityAsUser(STORE_LINK, APP.userHandle)
+    }
+
+    private fun setContent(app: ApplicationInfo = APP) {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AppInstallerInfoPreference(app)
+            }
+        }
+    }
+
+    private fun waitUntilDisplayed() {
+        composeTestRule.waitUntilExists(preferenceNode)
+    }
+
+    private val preferenceNode = hasText(context.getString(R.string.app_install_details_title))
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val INSTALLER_PACKAGE_NAME = "installer"
+        const val INSTALLER_PACKAGE_LABEL = "installer label"
+        val STORE_LINK = Intent("store/link")
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePresenterTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
similarity index 98%
rename from tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePresenterTest.kt
rename to tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
index d330a92..688ced1 100644
--- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePresenterTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppLocalePreferenceTest.kt
@@ -56,7 +56,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @RunWith(AndroidJUnit4::class)
-class AppLocalePresenterTest {
+class AppLocalePreferenceTest {
     @get:Rule
     val composeTestRule = createComposeRule()
 
diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt
new file mode 100644
index 0000000..1184ee7
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppSettingsPreferenceTest.kt
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.spa.app.appinfo
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ResolveInfoFlags
+import android.content.pm.ResolveInfo
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotDisplayed
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.printToLog
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.R
+import com.android.settings.testutils.waitUntilExists
+import com.android.settingslib.applications.AppUtils
+import com.android.settingslib.spaprivileged.model.app.userHandle
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Spy
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.Mockito.`when` as whenever
+
+@RunWith(AndroidJUnit4::class)
+class AppSettingsPreferenceTest {
+    @JvmField
+    @Rule
+    val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Mock
+    private lateinit var packageManager: PackageManager
+
+    @Before
+    fun setUp() {
+        whenever(context.packageManager).thenReturn(packageManager)
+    }
+
+    private fun mockResolveActivityAsUser(resolveInfo: ResolveInfo?) {
+        whenever(
+            packageManager.resolveActivityAsUser(any(), any<ResolveInfoFlags>(), eq(APP.userId))
+        ).thenReturn(resolveInfo)
+    }
+
+    @Test
+    fun callResolveActivityAsUser_withIntent() {
+        mockResolveActivityAsUser(null)
+
+        setContent()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(packageManager).resolveActivityAsUser(
+            intentCaptor.capture(), any<ResolveInfoFlags>(), eq(APP.userId)
+        )
+        val intent = intentCaptor.value
+        assertThat(intent.action).isEqualTo(Intent.ACTION_APPLICATION_PREFERENCES)
+        assertThat(intent.`package`).isEqualTo(PACKAGE_NAME)
+    }
+
+    @Test
+    fun noResolveInfo_notDisplayed() {
+        mockResolveActivityAsUser(null)
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun noSettingsActivity_notDisplayed() {
+        mockResolveActivityAsUser(ResolveInfo())
+
+        setContent()
+
+        composeTestRule.onRoot().assertIsNotDisplayed()
+    }
+
+    @Test
+    fun hasSettingsActivity_displayed() {
+        mockResolveActivityAsUser(RESOLVE_INFO)
+
+        setContent()
+
+        composeTestRule.onNodeWithText(context.getString(R.string.app_settings_link))
+            .assertIsDisplayed()
+            .assertIsEnabled()
+    }
+
+    @Test
+    fun whenClick_startActivity() {
+        mockResolveActivityAsUser(RESOLVE_INFO)
+
+        setContent()
+        composeTestRule.onRoot().performClick()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(context).startActivityAsUser(intentCaptor.capture(), eq(APP.userHandle))
+        val intent = intentCaptor.value
+        assertThat(intent.action).isEqualTo(Intent.ACTION_APPLICATION_PREFERENCES)
+        assertThat(intent.component).isEqualTo(ComponentName(PACKAGE_NAME, ACTIVITY_NAME))
+    }
+
+    private fun setContent() {
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                AppSettingsPreference(APP)
+            }
+        }
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "packageName"
+        const val ACTIVITY_NAME = "activityName"
+        const val UID = 123
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+            uid = UID
+        }
+        val RESOLVE_INFO = ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = PACKAGE_NAME
+                name = ACTIVITY_NAME
+            }
+        }
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/testutils/ComposeContentTestRuleExt.kt b/tests/spa_unit/src/com/android/settings/testutils/ComposeContentTestRuleExt.kt
new file mode 100644
index 0000000..f3eb529
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/testutils/ComposeContentTestRuleExt.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.testutils
+
+import androidx.compose.ui.test.SemanticsMatcher
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+
+/** Blocks until the found a semantics node that match the given condition. */
+fun ComposeContentTestRule.waitUntilExists(matcher: SemanticsMatcher) = waitUntil {
+    onAllNodes(matcher).fetchSemanticsNodes().isNotEmpty()
+}