Merge "Update Managed Device Info text for a financed device" into sc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eeb9694..6a4b8d5 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -19,6 +19,8 @@
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.HARDWARE_TEST" />
diff --git a/res/menu/storage_volume.xml b/res/menu/storage_volume.xml
index bf9f985..87f7515 100644
--- a/res/menu/storage_volume.xml
+++ b/res/menu/storage_volume.xml
@@ -28,9 +28,21 @@
         android:id="@+id/storage_format"
         android:title="@string/storage_menu_format" />
     <item
+        android:id="@+id/storage_format_as_portable"
+        android:title="@string/storage_menu_format_public"
+        android:visible="false" />
+    <item
+        android:id="@+id/storage_format_as_internal"
+        android:title="@string/storage_menu_format_private"
+        android:visible="false" />
+    <item
         android:id="@+id/storage_migrate"
         android:title="@string/storage_menu_migrate" />
     <item
         android:id="@+id/storage_free"
         android:title="@string/storage_menu_free" />
+    <item
+        android:id="@+id/storage_forget"
+        android:title="@string/storage_menu_forget"
+        android:visible="false" />
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0e29ea2..4dcc1e4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3368,6 +3368,10 @@
     <string name="storage_menu_manage">Manage storage</string>
     <!-- Storage setting. Keywords for Free up space. [CHAR LIMIT=NONE] -->
     <string name="keywords_storage_menu_free">clean, storage</string>
+    <!-- Storage setting. Title for storage free up option. [CHAR LIMIT=30] -->
+    <string name="storage_free_up_space_title">Free up space</string>
+    <!-- Storage setting. Summary for storage free up option. [CHAR LIMIT=NONE] -->
+    <string name="storage_free_up_space_summary">Go to Files app to manage and free up space</string>
 
     <!-- Storage setting.  Title for USB transfer settings [CHAR LIMIT=30]-->
     <string name="storage_title_usb">USB computer connection</string>
@@ -11522,6 +11526,25 @@
     <!-- Button label to allow the user to view additional information [CHAR LIMIT=NONE BACKUP_MESSAGE_ID=2416766240581561009] -->
     <string name="learn_more">Learn more</string>
 
+    <!-- Financed device Privacy --> <skip />
+
+    <!-- Title of setting on security settings screen on a financed device. This will take the user to a screen with information about what a device administrator can control and their impact on the user's privacy on a financed device. Shown on financed-managed devices only. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_settings">Financed device info</string>
+    <!-- Section header. This section shows what information a device administrator can see on a financed device. [CHAR LIMIT=60] -->
+    <string name="financed_privacy_exposure_category">Types of information your device administrator can see</string>
+    <!-- Label explaining that the device administrator can see data associated on the user's financed device. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_data">Data associated with your account, such as email and calendar info</string>
+    <!-- Section header. This section shows what changes a device administrator made to a financed device. [CHAR LIMIT=60] -->
+    <string name="financed_privacy_exposure_changes_category">Changes made by your device administrator</string>
+    <!-- Label explaining that the device admin can lock the device and change the user's password on their financed device. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_lock_device">Device administrator can lock this device and reset password</string>
+    <!-- Label explaining that the device admin can wipe the device remotely for a financed device. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_wipe_device">Device administrator can delete all device data</string>
+    <!-- Label explaining that the device admin configured the device to wipe itself when an incorrect password is entered too many times on a financed device. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_failed_password_wipe_device">Failed password attempts before deleting device data</string>
+    <!-- Financed Privacy settings activity header, summarizing the changes a credit provider can make to a financed device. [CHAR LIMIT=NONE] -->
+    <string name="financed_privacy_header">Your credit provider can change settings and install software on this device.\n\nTo learn more, contact your creditor provider.</string>
+
     <!-- Strings for displaying which applications were set as default for specific actions. -->
     <!-- Title for the apps that have been set as default handlers of camera-related intents. [CHAR LIMIT=30] -->
     <plurals name="default_camera_app_title">
@@ -11571,6 +11594,10 @@
     <!-- Follows the percent of storage used by a storage volume. Exposed inside of a donut graph. [CHAR LIMIT=7]-->
     <string name="storage_percent_full">used</string>
 
+    <!-- Summary of a single storage volume used space. [CHAR LIMIT=24] -->
+    <string name="storage_usage_summary"><xliff:g id="number" example="128">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g> used</string>
+    <!-- Summary of a single storage volume total space. [CHAR LIMIT=24] -->
+    <string name="storage_total_summary">Total <xliff:g id="number" example="128">%1$s</xliff:g> <xliff:g id="unit" example="KB">%2$s</xliff:g></string>
 
     <!-- Label for button allow user to remove the instant app from the device. -->
     <string name="clear_instant_app_data">Clear app</string>
@@ -12780,10 +12807,6 @@
     <string name="network_and_internet_preferences_summary">Connect to public networks</string>
     <!-- Search keywords for "Internet" settings [CHAR_LIMIT=NONE] -->
     <string name="keywords_internet">network connection, internet, wireless, data, wifi, wi-fi, wi fi, cellular, mobile, cell carrier, 4g, 3g, 2g, lte</string>
-    <!-- Label text to view airplane-safe networks. [CHAR LIMIT=40] -->
-    <string name="view_airplane_safe_networks">View airplane mode networks</string>
-    <!-- Text of message for viewing the networks that are available in airplane mode. [CHAR LIMIT=60] -->
-    <string name="viewing_airplane_mode_networks">Viewing airplane mode networks</string>
     <!-- Slice title text for turning on the Wi-Fi networks. [CHAR LIMIT=40] -->
     <string name="turn_on_wifi">Turn on Wi\u2011Fi</string>
     <!-- Title for interrupting the voice call alert. [CHAR_LIMIT=NONE] -->
@@ -12796,6 +12819,8 @@
     <string name="resetting_internet_text">Resetting your internet\u2026</string>
     <!-- Menu option for data connectivity recovery for all requested technologies. [CHAR_LIMIT=NONE] -->
     <string name="fix_connectivity">Fix connectivity</string>
+    <!-- Summary for networks available (includes no network connected). [CHAR_LIMIT=NONE] -->
+    <string name="networks_available">Networks available</string>
     <!-- Summary for warning to disconnect ethernet first then switch to other networks. [CHAR LIMIT=60] -->
     <string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
     <!-- Panel subtitle for Wi-Fi turned on. [CHAR LIMIT=60] -->
@@ -12808,6 +12833,9 @@
          Summary indicating that a SIM has an active mobile data connection [CHAR LIMIT=50] -->
     <string name="mobile_data_connection_active">Connected</string>
     <!-- Provider Model:
+         Summary indicating that a active SIM and no network available [CHAR LIMIT=50] -->
+    <string name="mobile_data_no_connection">No connection</string>
+    <!-- Provider Model:
          Summary indicating that a SIM has no mobile data connection [CHAR LIMIT=50] -->
     <string name="mobile_data_off_summary">Internet won\u0027t auto\u2011connect</string>
     <!-- Provider Model: Summary indicating that no other networks available [CHAR LIMIT=50] -->
@@ -12922,4 +12950,10 @@
 
     <!-- Label for extra app info settings for a specific app [CHAR LIMIT=40] -->
     <string name="extra_app_info_label" translatable="false"></string>
+
+    <!-- Title for toggle controlling whether notifications are shown when an app pastes from clipboard. [CHAR LIMIT=50] -->
+    <string name="show_clip_access_notification">Show clipboard access</string>
+
+    <!-- Summary for toggle controlling whether notifications are shown when an app pastes from clipboard. [CHAR LIMIT=NONE] -->
+    <string name="show_clip_access_notification_summary">Show a message when apps access text, images, or other content you\u2019ve copied</string>
 </resources>
diff --git a/res/xml/financed_privacy_settings.xml b/res/xml/financed_privacy_settings.xml
new file mode 100644
index 0000000..742d7e1
--- /dev/null
+++ b/res/xml/financed_privacy_settings.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2021 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="@string/financed_privacy_settings">
+
+    <PreferenceCategory android:key="exposure_category"
+        android:order="200"
+        android:title="@string/financed_privacy_exposure_category"
+        android:contentDescription="@string/financed_privacy_exposure_category">
+        <Preference android:key="enterprise_privacy_enterprise_data"
+            android:order="210"
+            android:layout_height="wrap_content"
+            android:title="@string/financed_privacy_data"
+            android:selectable="false"/>
+        <Preference android:key="enterprise_privacy_installed_packages"
+            android:order="220"
+            android:title="@string/enterprise_privacy_installed_packages"
+            android:selectable="false"/>
+        <Preference android:key="enterprise_privacy_usage_stats"
+            android:order="230"
+            android:title="@string/enterprise_privacy_usage_stats"
+            android:selectable="false"/>
+        <Preference android:key="network_logs"
+            android:order="240"
+            android:title="@string/enterprise_privacy_network_logs"
+            android:selectable="false"/>
+        <Preference android:key="bug_reports"
+            android:order="250"
+            android:title="@string/enterprise_privacy_bug_reports"
+            android:selectable="false"/>
+        <Preference android:key="security_logs"
+            android:order="260"
+            android:title="@string/enterprise_privacy_security_logs"
+            android:selectable="false"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/financed_privacy_exposure_changes_category"
+        android:order="300"
+        android:key="exposure_changes_category">
+        <Preference android:fragment="com.android.settings.enterprise.ApplicationListFragment$EnterpriseInstalledPackages"
+            android:order="310"
+            android:key="number_enterprise_installed_packages"
+            android:title="@string/enterprise_privacy_enterprise_installed_packages"/>
+    </PreferenceCategory>
+
+    <PreferenceCategory android:key="device_access_category"
+        android:order="500"
+        android:title="@string/enterprise_privacy_device_access_category">
+        <Preference android:key="enterprise_privacy_lock_device"
+            android:order="510"
+            android:title="@string/financed_privacy_lock_device"
+            android:selectable="false"/>
+        <Preference android:key="enterprise_privacy_wipe_device"
+            android:order="520"
+            android:title="@string/financed_privacy_wipe_device"
+            android:selectable="false"/>
+        <Preference android:key="failed_password_wipe_current_user"
+            android:order="530"
+            android:title="@string/financed_privacy_failed_password_wipe_device"
+            android:selectable="false"/>
+    </PreferenceCategory>
+
+    <com.android.settingslib.widget.FooterPreference
+        android:key="financed_privacy_footer"
+        android:title="@string/financed_privacy_header"
+        android:selectable="false"
+        settings:searchable="false"/>
+</PreferenceScreen>
diff --git a/res/xml/privacy_dashboard_settings.xml b/res/xml/privacy_dashboard_settings.xml
index f79da61..bd6a954 100644
--- a/res/xml/privacy_dashboard_settings.xml
+++ b/res/xml/privacy_dashboard_settings.xml
@@ -116,4 +116,11 @@
         settings:controller="com.android.settings.privacy.EnableContentCaptureWithServiceSettingsPreferenceController">
     </com.android.settings.widget.PrimarySwitchPreference>
 
+    <!-- Clipboard access notifications -->
+    <SwitchPreference
+        android:key="show_clip_access_notification"
+        android:title="@string/show_clip_access_notification"
+        android:summary="@string/show_clip_access_notification_summary"
+        settings:controller="com.android.settings.privacy.ShowClipAccessNotificationPreferenceController"/>
+
 </PreferenceScreen>
diff --git a/res/xml/storage_dashboard_fragment.xml b/res/xml/storage_dashboard_fragment.xml
index bc58d7e..b49228e 100644
--- a/res/xml/storage_dashboard_fragment.xml
+++ b/res/xml/storage_dashboard_fragment.xml
@@ -19,11 +19,22 @@
     xmlns:settings="http://schemas.android.com/apk/res-auto"
     android:title="@string/storage_settings"
     android:orderingFromXml="false">
-    <com.android.settings.deviceinfo.storage.StorageSummaryDonutPreference
-        android:key="storage_summary"
-        android:order="0"
+    <com.android.settingslib.widget.SettingsSpinnerPreference
+        android:key="storage_spinner"
+        android:order="-2"
         settings:searchable="false"
-        settings:controller="com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController"/>
+        settings:controller="com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController"/>
+    <com.android.settingslib.widget.UsageProgressBarPreference
+        android:key="storage_summary"
+        android:order="-1"
+        settings:searchable="false"
+        settings:controller="com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController"/>
+    <Preference
+        android:key="free_up_space"
+        android:order="0"
+        android:title="@string/storage_free_up_space_title"
+        android:summary="@string/storage_free_up_space_summary"
+        settings:allowDividerAbove="true"/>
     <com.android.settings.widget.PrimarySwitchPreference
         android:fragment="com.android.settings.deletionhelper.AutomaticStorageManagerSettings"
         android:key="toggle_asm"
@@ -74,4 +85,4 @@
         android:key="pref_secondary_users"
         android:title="@string/storage_other_users"
         android:order="200" />
-</PreferenceScreen>
\ No newline at end of file
+</PreferenceScreen>
diff --git a/src/com/android/settings/SettingsPreferenceFragment.java b/src/com/android/settings/SettingsPreferenceFragment.java
index 0c1ace9..a276ad7 100644
--- a/src/com/android/settings/SettingsPreferenceFragment.java
+++ b/src/com/android/settings/SettingsPreferenceFragment.java
@@ -446,6 +446,13 @@
     }
 
     /**
+     * Returns the specified system service from the owning Activity.
+     */
+    protected <T> T getSystemService(final Class<T> serviceClass) {
+        return getActivity().getSystemService(serviceClass);
+    }
+
+    /**
      * Returns the PackageManager from the owning Activity.
      */
     protected PackageManager getPackageManager() {
diff --git a/src/com/android/settings/UserCredentialsSettings.java b/src/com/android/settings/UserCredentialsSettings.java
index 5f72ca5..80b97e4 100644
--- a/src/com/android/settings/UserCredentialsSettings.java
+++ b/src/com/android/settings/UserCredentialsSettings.java
@@ -34,7 +34,6 @@
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
-import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
 import android.util.Log;
@@ -74,6 +73,8 @@
         implements View.OnClickListener {
     private static final String TAG = "UserCredentialsSettings";
 
+    private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+
     @Override
     public int getMetricsCategory() {
         return SettingsEnums.USER_CREDENTIALS;
@@ -210,15 +211,10 @@
 
             private void deleteWifiCredential(final Credential credential) {
                 try {
-                    KeyStore keyStore = null;
-                    if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
-                        keyStore = KeyStore.getInstance("AndroidKeyStore");
-                        keyStore.load(
-                                new AndroidKeyStoreLoadStoreParameter(
-                                        KeyProperties.NAMESPACE_WIFI));
-                    } else {
-                        keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
-                    }
+                    final KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+                    keyStore.load(
+                            new AndroidKeyStoreLoadStoreParameter(
+                                    KeyProperties.NAMESPACE_WIFI));
                     keyStore.deleteEntry(credential.getAlias());
                 } catch (Exception e) {
                     throw new RuntimeException("Failed to delete keys from keystore.");
@@ -278,18 +274,13 @@
             final int wifiUid = UserHandle.getUid(myUserId, Process.WIFI_UID);
 
             try {
-                KeyStore processKeystore = KeyStore.getInstance("AndroidKeyStore");
+                KeyStore processKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
                 processKeystore.load(null);
                 KeyStore wifiKeystore = null;
                 if (myUserId == 0) {
-                    // Only the primary user may see wifi configurations.
-                    if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
-                        wifiKeystore = KeyStore.getInstance("AndroidKeyStore");
-                        wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
-                                KeyProperties.NAMESPACE_WIFI));
-                    } else {
-                        wifiKeystore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
-                    }
+                    wifiKeystore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+                    wifiKeystore.load(new AndroidKeyStoreLoadStoreParameter(
+                            KeyProperties.NAMESPACE_WIFI));
                 }
 
                 List<Credential> credentials = new ArrayList<>();
diff --git a/src/com/android/settings/datausage/ChartDataUsagePreference.java b/src/com/android/settings/datausage/ChartDataUsagePreference.java
index 6c845a9..0577e9c 100644
--- a/src/com/android/settings/datausage/ChartDataUsagePreference.java
+++ b/src/com/android/settings/datausage/ChartDataUsagePreference.java
@@ -174,8 +174,8 @@
     @VisibleForTesting
     List<DataUsageSummaryNode> getDensedStatsData(List<NetworkCycleData> usageSummary) {
         final List<DataUsageSummaryNode> dataUsageSummaryNodes = new ArrayList<>();
-        final long overallDataUsage = usageSummary.stream()
-                .mapToLong(NetworkCycleData::getTotalUsage).sum();
+        final long overallDataUsage = Math.max(1L, usageSummary.stream()
+                .mapToLong(NetworkCycleData::getTotalUsage).sum());
         long cumulatedDataUsage = 0L;
         int cumulatedDataUsagePercentage = 0;
 
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java b/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java
deleted file mode 100644
index 00a79a0..0000000
--- a/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuController.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2017 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.deviceinfo;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.storage.VolumeInfo;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.settings.R;
-import com.android.settingslib.core.lifecycle.LifecycleObserver;
-import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
-import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
-import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
-
-import java.util.Objects;
-
-/**
- * Handles the option menu on the Storage settings.
- */
-public class PrivateVolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
-        OnPrepareOptionsMenu, OnOptionsItemSelected {
-    private static final int OPTIONS_MENU_MIGRATE_DATA = 100;
-
-    private Context mContext;
-    private VolumeInfo mVolumeInfo;
-    private PackageManager mPm;
-
-    public PrivateVolumeOptionMenuController(
-            Context context, VolumeInfo volumeInfo, PackageManager packageManager) {
-        mContext = context;
-        mVolumeInfo = volumeInfo;
-        mPm = packageManager;
-    }
-
-    @Override
-    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
-        menu.add(Menu.NONE, OPTIONS_MENU_MIGRATE_DATA, 0, R.string.storage_menu_migrate);
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        if (mVolumeInfo == null) {
-            return;
-        }
-
-        // Only offer to migrate when not current storage
-        final VolumeInfo privateVol = mPm.getPrimaryStorageCurrentVolume();
-        final MenuItem migrate = menu.findItem(OPTIONS_MENU_MIGRATE_DATA);
-        if (migrate != null) {
-            migrate.setVisible((privateVol != null)
-                    && (privateVol.getType() == VolumeInfo.TYPE_PRIVATE)
-                    && !Objects.equals(mVolumeInfo, privateVol)
-                    && privateVol.isMountedWritable());
-        }
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem menuItem) {
-        if (menuItem.getItemId() == OPTIONS_MENU_MIGRATE_DATA) {
-            final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
-            intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mVolumeInfo.getId());
-            mContext.startActivity(intent);
-            return true;
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
index 10c3a43..b8c4e28 100644
--- a/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
+++ b/src/com/android/settings/deviceinfo/StorageDashboardFragment.java
@@ -20,13 +20,18 @@
 import android.app.settings.SettingsEnums;
 import android.app.usage.StorageStatsManager;
 import android.content.Context;
+import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
 import android.provider.SearchIndexableResource;
+import android.text.TextUtils;
 import android.util.SparseArray;
 import android.view.View;
 
@@ -41,15 +46,22 @@
 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
 import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
 import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
+import com.android.settings.deviceinfo.storage.DiskInitFragment;
 import com.android.settings.deviceinfo.storage.SecondaryUserController;
 import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
+import com.android.settings.deviceinfo.storage.StorageEntry;
 import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageSelectionPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageUsageProgressBarPreferenceController;
+import com.android.settings.deviceinfo.storage.StorageUtils;
 import com.android.settings.deviceinfo.storage.UserIconLoader;
 import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
 import com.android.settings.widget.EntityHeaderController;
 import com.android.settingslib.applications.StorageStatsSource;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.deviceinfo.PrivateStorageInfo;
 import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
 import com.android.settingslib.search.SearchIndexable;
@@ -57,48 +69,229 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @SearchIndexable
 public class StorageDashboardFragment extends DashboardFragment
         implements
-        LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
+        LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>>,
+        Preference.OnPreferenceClickListener {
     private static final String TAG = "StorageDashboardFrag";
     private static final String SUMMARY_PREF_KEY = "storage_summary";
+    private static final String FREE_UP_SPACE_PREF_KEY = "free_up_space";
+    private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
     private static final int STORAGE_JOB_ID = 0;
     private static final int ICON_JOB_ID = 1;
     private static final int VOLUME_SIZE_JOB_ID = 2;
 
-    private VolumeInfo mVolume;
+    private StorageManager mStorageManager;
+    private final List<StorageEntry> mStorageEntries = new ArrayList<>();
+    private StorageEntry mSelectedStorageEntry;
     private PrivateStorageInfo mStorageInfo;
     private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
     private CachedStorageValuesHelper mCachedStorageValuesHelper;
 
     private StorageItemPreferenceController mPreferenceController;
-    private PrivateVolumeOptionMenuController mOptionMenuController;
+    private VolumeOptionMenuController mOptionMenuController;
+    private StorageSelectionPreferenceController mStorageSelectionController;
+    private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
     private List<AbstractPreferenceController> mSecondaryUsers;
     private boolean mPersonalOnly;
+    private Preference mFreeUpSpacePreference;
+
+    private final StorageEventListener mStorageEventListener = new StorageEventListener() {
+        @Override
+        public void onVolumeStateChanged(VolumeInfo volumeInfo, int oldState, int newState) {
+            if (!isInteresting(volumeInfo)) {
+                return;
+            }
+
+            final StorageEntry changedStorageEntry = new StorageEntry(getContext(), volumeInfo);
+            switch (volumeInfo.getState()) {
+                case VolumeInfo.STATE_MOUNTED:
+                case VolumeInfo.STATE_MOUNTED_READ_ONLY:
+                case VolumeInfo.STATE_UNMOUNTABLE:
+                    // Add mounted or unmountable storage in the list and show it on spinner.
+                    // Unmountable storages are the storages which has a problem format and android
+                    // is not able to mount it automatically.
+                    // Users can format an unmountable storage by the UI and then use the storage.
+                    mStorageEntries.removeIf(storageEntry -> {
+                        return storageEntry.equals(changedStorageEntry);
+                    });
+                    mStorageEntries.add(changedStorageEntry);
+                    if (changedStorageEntry.equals(mSelectedStorageEntry)) {
+                        mSelectedStorageEntry = changedStorageEntry;
+                    }
+                    refreshUi();
+                    break;
+                case VolumeInfo.STATE_REMOVED:
+                case VolumeInfo.STATE_UNMOUNTED:
+                case VolumeInfo.STATE_BAD_REMOVAL:
+                case VolumeInfo.STATE_EJECTING:
+                    // Remove removed storage from list and don't show it on spinner.
+                    if (mStorageEntries.remove(changedStorageEntry)) {
+                        if (changedStorageEntry.equals(mSelectedStorageEntry)) {
+                            mSelectedStorageEntry =
+                                    StorageEntry.getDefaultInternalStorageEntry(getContext());
+                        }
+                        refreshUi();
+                    }
+                    break;
+                default:
+                    // Do nothing.
+            }
+        }
+
+        @Override
+        public void onVolumeRecordChanged(VolumeRecord volumeRecord) {
+            if (isVolumeRecordMissed(volumeRecord)) {
+                // VolumeRecord is a metadata of VolumeInfo, if a VolumeInfo is missing
+                // (e.g., internal SD card is removed.) show the missing storage to users,
+                // users can insert the SD card or manually forget the storage from the device.
+                final StorageEntry storageEntry = new StorageEntry(volumeRecord);
+                if (!mStorageEntries.contains(storageEntry)) {
+                    mStorageEntries.add(storageEntry);
+                    refreshUi();
+                }
+            } else {
+                // Find mapped VolumeInfo and replace with existing one for something changed.
+                // (e.g., Renamed.)
+                final VolumeInfo mappedVolumeInfo =
+                            mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid());
+                if (mappedVolumeInfo == null) {
+                    return;
+                }
+
+                final boolean removeMappedStorageEntry = mStorageEntries.removeIf(storageEntry ->
+                        storageEntry.isVolumeInfo()
+                            && TextUtils.equals(storageEntry.getFsUuid(), volumeRecord.getFsUuid())
+                );
+                if (removeMappedStorageEntry) {
+                    mStorageEntries.add(new StorageEntry(getContext(), mappedVolumeInfo));
+                    refreshUi();
+                }
+            }
+        }
+
+        @Override
+        public void onVolumeForgotten(String fsUuid) {
+            final StorageEntry storageEntry = new StorageEntry(
+                    new VolumeRecord(VolumeInfo.TYPE_PUBLIC, fsUuid));
+            if (mStorageEntries.remove(storageEntry)) {
+                if (mSelectedStorageEntry.equals(storageEntry)) {
+                    mSelectedStorageEntry =
+                            StorageEntry.getDefaultInternalStorageEntry(getContext());
+                }
+                refreshUi();
+            }
+        }
+
+        @Override
+        public void onDiskScanned(DiskInfo disk, int volumeCount) {
+            if (!isDiskUnsupported(disk)) {
+                return;
+            }
+            final StorageEntry storageEntry = new StorageEntry(disk);
+            if (!mStorageEntries.contains(storageEntry)) {
+                mStorageEntries.add(storageEntry);
+                refreshUi();
+            }
+        }
+
+        @Override
+        public void onDiskDestroyed(DiskInfo disk) {
+            final StorageEntry storageEntry = new StorageEntry(disk);
+            if (mStorageEntries.remove(storageEntry)) {
+                if (mSelectedStorageEntry.equals(storageEntry)) {
+                    mSelectedStorageEntry =
+                            StorageEntry.getDefaultInternalStorageEntry(getContext());
+                }
+                refreshUi();
+            }
+        }
+    };
+
+    private static boolean isInteresting(VolumeInfo volumeInfo) {
+        switch (volumeInfo.getType()) {
+            case VolumeInfo.TYPE_PRIVATE:
+            case VolumeInfo.TYPE_PUBLIC:
+            case VolumeInfo.TYPE_STUB:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * VolumeRecord is a metadata of VolumeInfo, this is the case where a VolumeInfo is missing.
+     * (e.g., internal SD card is removed.)
+     */
+    private boolean isVolumeRecordMissed(VolumeRecord volumeRecord) {
+        return volumeRecord.getType() == VolumeInfo.TYPE_PRIVATE
+                && mStorageManager.findVolumeByUuid(volumeRecord.getFsUuid()) == null;
+    }
+
+    /**
+     * A unsupported disk is the disk of problem format, android is not able to mount automatically.
+     */
+    private static boolean isDiskUnsupported(DiskInfo disk) {
+        return disk.volumeCount == 0 && disk.size > 0;
+    }
+
+    private void refreshUi() {
+        mStorageSelectionController.setStorageEntries(mStorageEntries);
+        mStorageSelectionController.setSelectedStorageEntry(mSelectedStorageEntry);
+        mStorageUsageProgressBarController.setSelectedStorageEntry(mSelectedStorageEntry);
+
+        mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
+        getActivity().invalidateOptionsMenu();
+
+        mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
+
+        if (mSelectedStorageEntry.isPrivate() && mSelectedStorageEntry.isMounted()) {
+            // Stats data is only available on private volumes.
+            getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
+            getLoaderManager()
+                 .restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
+            getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
+        } else {
+            // Set null volume to hide category stats.
+            mPreferenceController.setVolume(null);
+        }
+    }
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
-        // Initialize the storage sizes that we can quickly calc.
         final Activity activity = getActivity();
-        StorageManager sm = activity.getSystemService(StorageManager.class);
-        mVolume = Utils.maybeInitializeVolume(sm, getArguments());
+        mStorageManager = activity.getSystemService(StorageManager.class);
         mPersonalOnly = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
                 == ProfileSelectFragment.ProfileType.PERSONAL;
-        if (mVolume == null) {
-            activity.finish();
-            return;
+
+        if (icicle == null) {
+            final VolumeInfo specifiedVolumeInfo =
+                    Utils.maybeInitializeVolume(mStorageManager, getArguments());
+            mSelectedStorageEntry = specifiedVolumeInfo == null
+                    ? StorageEntry.getDefaultInternalStorageEntry(getContext())
+                    : new StorageEntry(getContext(), specifiedVolumeInfo);
+        } else {
+            mSelectedStorageEntry = icicle.getParcelable(SELECTED_STORAGE_ENTRY_KEY);
         }
+
+        initializePreference();
         initializeOptionsMenu(activity);
+    }
+
+    private void initializePreference() {
         if (mPersonalOnly) {
             final Preference summary = getPreferenceScreen().findPreference(SUMMARY_PREF_KEY);
             if (summary != null) {
                 summary.setVisible(false);
             }
         }
+        mFreeUpSpacePreference = getPreferenceScreen().findPreference(FREE_UP_SPACE_PREF_KEY);
+        mFreeUpSpacePreference.setOnPreferenceClickListener(this);
     }
 
     @Override
@@ -106,12 +299,25 @@
         super.onAttach(context);
         use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
                 getFragmentManager());
+        mStorageSelectionController = use(StorageSelectionPreferenceController.class);
+        mStorageSelectionController.setOnItemSelectedListener(storageEntry -> {
+            mSelectedStorageEntry = storageEntry;
+            refreshUi();
+
+            if (storageEntry.isDiskInfoUnsupported() || storageEntry.isUnmountable()) {
+                DiskInitFragment.show(this, R.string.storage_dialog_unmountable,
+                        storageEntry.getDiskId());
+            } else if (storageEntry.isVolumeRecordMissed()) {
+                StorageUtils.launchForgetMissingVolumeRecordFragment(getContext(), storageEntry);
+            }
+        });
+        mStorageUsageProgressBarController = use(StorageUsageProgressBarPreferenceController.class);
     }
 
     @VisibleForTesting
     void initializeOptionsMenu(Activity activity) {
-        mOptionMenuController = new PrivateVolumeOptionMenuController(
-                activity, mVolume, activity.getPackageManager());
+        mOptionMenuController = new VolumeOptionMenuController(activity, this,
+                mSelectedStorageEntry);
         getSettingsLifecycle().addObserver(mOptionMenuController);
         setHasOptionsMenu(true);
         activity.invalidateOptionsMenu();
@@ -133,10 +339,34 @@
     @Override
     public void onResume() {
         super.onResume();
-        getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
-        getLoaderManager()
-                .restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
-        getLoaderManager().restartLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
+
+        mStorageEntries.clear();
+        mStorageEntries.addAll(mStorageManager.getVolumes().stream()
+                .filter(volumeInfo -> isInteresting(volumeInfo))
+                .map(volumeInfo -> new StorageEntry(getContext(), volumeInfo))
+                .collect(Collectors.toList()));
+        mStorageEntries.addAll(mStorageManager.getDisks().stream()
+                .filter(disk -> isDiskUnsupported(disk))
+                .map(disk -> new StorageEntry(disk))
+                .collect(Collectors.toList()));
+        mStorageEntries.addAll(mStorageManager.getVolumeRecords().stream()
+                .filter(volumeRecord -> isVolumeRecordMissed(volumeRecord))
+                .map(volumeRecord -> new StorageEntry(volumeRecord))
+                .collect(Collectors.toList()));
+        refreshUi();
+        mStorageManager.registerListener(mStorageEventListener);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mStorageManager.unregisterListener(mStorageEventListener);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        outState.putParcelable(SELECTED_STORAGE_ENTRY_KEY, mSelectedStorageEntry);
+        super.onSaveInstanceState(outState);
     }
 
     @Override
@@ -148,7 +378,7 @@
         boolean stopLoading = false;
         if (mStorageInfo != null) {
             long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
-            mPreferenceController.setVolume(mVolume);
+            mPreferenceController.setVolume(mSelectedStorageEntry.getVolumeInfo());
             mPreferenceController.setUsedSize(privateUsedBytes);
             mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
             for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
@@ -197,7 +427,7 @@
 
         StorageManager sm = context.getSystemService(StorageManager.class);
         mPreferenceController = new StorageItemPreferenceController(context, this,
-                mVolume, new StorageManagerVolumeProvider(sm));
+                null /* volume */, new StorageManagerVolumeProvider(sm));
         controllers.add(mPreferenceController);
 
         final UserManager userManager = context.getSystemService(UserManager.class);
@@ -209,7 +439,7 @@
 
     @VisibleForTesting
     protected void setVolume(VolumeInfo info) {
-        mVolume = info;
+        mSelectedStorageEntry = new StorageEntry(getContext(), info);
     }
 
     /**
@@ -260,7 +490,7 @@
             Bundle args) {
         final Context context = getContext();
         return new StorageAsyncLoader(context, context.getSystemService(UserManager.class),
-                mVolume.fsUuid,
+                mSelectedStorageEntry.getFsUuid(),
                 new StorageStatsSource(context),
                 context.getPackageManager());
     }
@@ -277,6 +507,21 @@
     public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
     }
 
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        if (preference == mFreeUpSpacePreference) {
+            final Context context = getContext();
+            final MetricsFeatureProvider metricsFeatureProvider =
+                    FeatureFactory.getFactory(context).getMetricsFeatureProvider();
+            metricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
+            metricsFeatureProvider.action(context, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
+            final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
+            context.startActivity(intent);
+            return true;
+        }
+        return false;
+    }
+
     @VisibleForTesting
     public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
         mCachedStorageValuesHelper = helper;
@@ -340,8 +585,9 @@
     }
 
     private boolean isQuotaSupported() {
-        final StorageStatsManager stats = getActivity().getSystemService(StorageStatsManager.class);
-        return stats.isQuotaSupported(mVolume.fsUuid);
+        return mSelectedStorageEntry.isMounted()
+                && getActivity().getSystemService(StorageStatsManager.class)
+                        .isQuotaSupported(mSelectedStorageEntry.getFsUuid());
     }
 
     /**
@@ -378,11 +624,12 @@
             implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
         @Override
         public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
-            Context context = getContext();
-            StorageManager sm = context.getSystemService(StorageManager.class);
-            StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(sm);
+            final Context context = getContext();
+            final StorageManagerVolumeProvider smvp =
+                    new StorageManagerVolumeProvider(mStorageManager);
             final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
-            return new VolumeSizesLoader(context, smvp, stats, mVolume);
+            return new VolumeSizesLoader(context, smvp, stats,
+                    mSelectedStorageEntry.getVolumeInfo());
         }
 
         @Override
diff --git a/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
new file mode 100644
index 0000000..0932447
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/VolumeOptionMenuController.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo;
+
+import android.app.ActivityManager;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+import com.android.settings.deviceinfo.storage.StorageEntry;
+import com.android.settings.deviceinfo.storage.StorageRenameFragment;
+import com.android.settings.deviceinfo.storage.StorageUtils;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnCreateOptionsMenu;
+import com.android.settingslib.core.lifecycle.events.OnOptionsItemSelected;
+import com.android.settingslib.core.lifecycle.events.OnPrepareOptionsMenu;
+
+import java.util.Objects;
+
+/**
+ * Handles the option menu on the Storage settings.
+ */
+public class VolumeOptionMenuController implements LifecycleObserver, OnCreateOptionsMenu,
+        OnPrepareOptionsMenu, OnOptionsItemSelected {
+
+    @VisibleForTesting
+    MenuItem mRename;
+    @VisibleForTesting
+    MenuItem mMount;
+    @VisibleForTesting
+    MenuItem mUnmount;
+    @VisibleForTesting
+    MenuItem mFormat;
+    @VisibleForTesting
+    MenuItem mFormatAsPortable;
+    @VisibleForTesting
+    MenuItem mFormatAsInternal;
+    @VisibleForTesting
+    MenuItem mMigrate;
+    @VisibleForTesting
+    MenuItem mFree;
+    @VisibleForTesting
+    MenuItem mForget;
+
+    private final Context mContext;
+    private final Fragment mFragment;
+    private final PackageManager mPackageManager;
+    private final StorageManager mStorageManager;
+    private StorageEntry mStorageEntry;
+
+    public VolumeOptionMenuController(Context context, Fragment parent, StorageEntry storageEntry) {
+        mContext = context;
+        mFragment = parent;
+        mPackageManager = context.getPackageManager();
+        mStorageManager = context.getSystemService(StorageManager.class);
+        mStorageEntry = storageEntry;
+    }
+
+    @Override
+    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
+        inflater.inflate(R.menu.storage_volume, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        mRename = menu.findItem(R.id.storage_rename);
+        mMount = menu.findItem(R.id.storage_mount);
+        mUnmount = menu.findItem(R.id.storage_unmount);
+        mFormat = menu.findItem(R.id.storage_format);
+        mFormatAsPortable = menu.findItem(R.id.storage_format_as_portable);
+        mFormatAsInternal = menu.findItem(R.id.storage_format_as_internal);
+        mMigrate = menu.findItem(R.id.storage_migrate);
+        mFree = menu.findItem(R.id.storage_free);
+        mForget = menu.findItem(R.id.storage_forget);
+
+        mRename.setVisible(false);
+        mMount.setVisible(false);
+        mUnmount.setVisible(false);
+        mFormat.setVisible(false);
+        mFormatAsPortable.setVisible(false);
+        mFormatAsInternal.setVisible(false);
+        mMigrate.setVisible(false);
+        mFree.setVisible(false);
+        mForget.setVisible(false);
+
+        if (mStorageEntry.isDiskInfoUnsupported()) {
+            mFormat.setVisible(true);
+            return;
+        }
+        if (mStorageEntry.isVolumeRecordMissed()) {
+            mForget.setVisible(true);
+            return;
+        }
+        if (mStorageEntry.isUnmounted()) {
+            mMount.setVisible(true);
+            return;
+        }
+        if (!mStorageEntry.isMounted()) {
+            return;
+        }
+
+        if (mStorageEntry.isPrivate()) {
+            if (!mStorageEntry.isDefaultInternalStorage()) {
+                mRename.setVisible(true);
+                mUnmount.setVisible(true);
+                mFormatAsPortable.setVisible(true);
+            }
+
+            // Only offer to migrate when not current storage.
+            final VolumeInfo primaryVolumeInfo = mPackageManager.getPrimaryStorageCurrentVolume();
+            final VolumeInfo selectedVolumeInfo = mStorageEntry.getVolumeInfo();
+            mMigrate.setVisible(primaryVolumeInfo != null
+                    && primaryVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
+                    && !Objects.equals(selectedVolumeInfo, primaryVolumeInfo)
+                    && primaryVolumeInfo.isMountedWritable());
+            return;
+        }
+
+        if (mStorageEntry.isPublic()) {
+            mRename.setVisible(true);
+            mUnmount.setVisible(true);
+            mFormat.setVisible(true);
+            final DiskInfo diskInfo = mStorageManager.findDiskById(mStorageEntry.getDiskId());
+            mFormatAsInternal.setVisible(diskInfo != null
+                    && diskInfo.isAdoptable()
+                    && UserManager.get(mContext).isAdminUser()
+                    && !ActivityManager.isUserAMonkey());
+            return;
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem menuItem) {
+        if (!mFragment.isAdded()) {
+            return false;
+        }
+
+        final int menuId = menuItem.getItemId();
+        if (menuId == R.id.storage_mount) {
+            if (mStorageEntry.isUnmounted()) {
+                new MountTask(mFragment.getActivity(), mStorageEntry.getVolumeInfo()).execute();
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_unmount) {
+            if (mStorageEntry.isMounted()) {
+                if (mStorageEntry.isPublic()) {
+                    new UnmountTask(mFragment.getActivity(),
+                            mStorageEntry.getVolumeInfo()).execute();
+                    return true;
+                }
+                if (mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage()) {
+                    final Bundle args = new Bundle();
+                    args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+                    new SubSettingLauncher(mContext)
+                            .setDestination(PrivateVolumeUnmount.class.getCanonicalName())
+                            .setTitleRes(R.string.storage_menu_unmount)
+                            .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
+                            .setArguments(args)
+                            .launch();
+                    return true;
+                }
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_rename) {
+            if ((mStorageEntry.isPrivate() && !mStorageEntry.isDefaultInternalStorage())
+                    ||  mStorageEntry.isPublic()) {
+                StorageRenameFragment.show(mFragment, mStorageEntry.getVolumeInfo());
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_format) {
+            if (mStorageEntry.isDiskInfoUnsupported() || mStorageEntry.isPublic()) {
+                StorageWizardFormatConfirm.showPublic(mFragment.getActivity(),
+                        mStorageEntry.getDiskId());
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_format_as_portable) {
+            if (mStorageEntry.isPrivate()) {
+                final Bundle args = new Bundle();
+                args.putString(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+                new SubSettingLauncher(mContext)
+                        .setDestination(PrivateVolumeFormat.class.getCanonicalName())
+                        .setTitleRes(R.string.storage_menu_format)
+                        .setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
+                        .setArguments(args)
+                        .launch();
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_format_as_internal) {
+            if (mStorageEntry.isPublic()) {
+                StorageWizardFormatConfirm.showPrivate(mFragment.getActivity(),
+                        mStorageEntry.getDiskId());
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_migrate) {
+            if (mStorageEntry.isPrivate()) {
+                final Intent intent = new Intent(mContext, StorageWizardMigrateConfirm.class);
+                intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, mStorageEntry.getId());
+                mContext.startActivity(intent);
+                return true;
+            }
+            return false;
+        }
+        if (menuId == R.id.storage_forget) {
+            if (mStorageEntry.isVolumeRecordMissed()) {
+                StorageUtils.launchForgetMissingVolumeRecordFragment(mContext, mStorageEntry);
+                return true;
+            }
+            return false;
+        }
+        return false;
+    }
+
+    public void setSelectedStorageEntry(StorageEntry storageEntry) {
+        mStorageEntry = storageEntry;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java b/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java
new file mode 100644
index 0000000..1e6a98d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/DiskInitFragment.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+import com.android.settings.deviceinfo.StorageWizardInit;
+
+/** A dialog which guides users to initialize a specified unsupported disk. */
+public class DiskInitFragment extends InstrumentedDialogFragment {
+
+    private static final String TAG_DISK_INIT = "disk_init";
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.DIALOG_VOLUME_INIT;
+    }
+
+    /** Shows the dialog for the specified diskId from DiskInfo. */
+    public static void show(Fragment parent, int resId, String diskId) {
+        final Bundle args = new Bundle();
+        args.putInt(Intent.EXTRA_TEXT, resId);
+        args.putString(DiskInfo.EXTRA_DISK_ID, diskId);
+
+        final DiskInitFragment dialog = new DiskInitFragment();
+        dialog.setArguments(args);
+        dialog.setTargetFragment(parent, 0);
+        dialog.show(parent.getFragmentManager(), TAG_DISK_INIT);
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Context context = getActivity();
+        final StorageManager storageManager = context.getSystemService(StorageManager.class);
+        final int resId = getArguments().getInt(Intent.EXTRA_TEXT);
+        final String diskId = getArguments().getString(DiskInfo.EXTRA_DISK_ID);
+        final DiskInfo disk = storageManager.findDiskById(diskId);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        return builder.setMessage(TextUtils.expandTemplate(getText(resId), disk.getDescription()))
+                .setPositiveButton(R.string.storage_menu_set_up, (dialog, which) -> {
+                    final Intent intent = new Intent(context, StorageWizardInit.class);
+                    intent.putExtra(DiskInfo.EXTRA_DISK_ID, diskId);
+                    startActivity(intent); })
+                .setNegativeButton(R.string.cancel, null)
+                .create();
+    }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageEntry.java b/src/com/android/settings/deviceinfo/storage/StorageEntry.java
new file mode 100644
index 0000000..f718116
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageEntry.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.text.TextUtils;
+
+import java.io.File;
+
+/**
+ * This object contains a {@link VolumeInfo} for a mountable storage or a {@link DiskInfo} for an
+ * unsupported disk which is not able to be mounted automatically.
+ */
+public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
+
+    private final VolumeInfo mVolumeInfo;
+    private final DiskInfo mUnsupportedDiskInfo;
+    private final VolumeRecord mMissingVolumeRecord;
+
+    private final String mVolumeInfoDescription;
+
+    public StorageEntry(@NonNull Context context, @NonNull VolumeInfo volumeInfo) {
+        mVolumeInfo = volumeInfo;
+        mUnsupportedDiskInfo = null;
+        mMissingVolumeRecord = null;
+        mVolumeInfoDescription = context.getSystemService(StorageManager.class)
+                .getBestVolumeDescription(mVolumeInfo);
+    }
+
+    public StorageEntry(@NonNull DiskInfo diskInfo) {
+        mVolumeInfo = null;
+        mUnsupportedDiskInfo = diskInfo;
+        mMissingVolumeRecord = null;
+        mVolumeInfoDescription = null;
+    }
+
+    public StorageEntry(@NonNull VolumeRecord volumeRecord) {
+        mVolumeInfo = null;
+        mUnsupportedDiskInfo = null;
+        mMissingVolumeRecord = volumeRecord;
+        mVolumeInfoDescription = null;
+    }
+
+    private StorageEntry(Parcel in) {
+        mVolumeInfo = in.readParcelable(VolumeInfo.class.getClassLoader());
+        mUnsupportedDiskInfo = in.readParcelable(DiskInfo.class.getClassLoader());
+        mMissingVolumeRecord = in.readParcelable(VolumeRecord.class.getClassLoader());
+        mVolumeInfoDescription = in.readString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mVolumeInfo, 0 /* parcelableFlags */);
+        out.writeParcelable(mUnsupportedDiskInfo, 0 /* parcelableFlags */);
+        out.writeParcelable(mMissingVolumeRecord , 0 /* parcelableFlags */);
+        out.writeString(mVolumeInfoDescription);
+    }
+
+    public static final Parcelable.Creator<StorageEntry> CREATOR =
+            new Parcelable.Creator<StorageEntry>() {
+                public StorageEntry createFromParcel(Parcel in) {
+                    return new StorageEntry(in);
+                }
+
+                public StorageEntry[] newArray(int size) {
+                    return new StorageEntry[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) {
+            return true;
+        }
+        if (!(o instanceof StorageEntry)) {
+            return false;
+        }
+
+        final StorageEntry StorageEntry = (StorageEntry) o;
+        if (isVolumeInfo()) {
+            return mVolumeInfo.equals(StorageEntry.mVolumeInfo);
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);
+        }
+        return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);
+    }
+
+    @Override
+    public int hashCode() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.hashCode();
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.hashCode();
+        }
+        return mMissingVolumeRecord.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.toString();
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.toString();
+        }
+        return mMissingVolumeRecord.toString();
+    }
+
+    @Override
+    public int compareTo(StorageEntry other) {
+        if (isDefaultInternalStorage() && !other.isDefaultInternalStorage()) {
+            return -1;
+        }
+        if (!isDefaultInternalStorage() && other.isDefaultInternalStorage()) {
+            return 1;
+        }
+
+        if (isVolumeInfo() && !other.isVolumeInfo()) {
+            return -1;
+        }
+        if (!isVolumeInfo() && other.isVolumeInfo()) {
+            return 1;
+        }
+
+        if (isPrivate() && !other.isPrivate()) {
+            return -1;
+        }
+        if (!isPrivate() && other.isPrivate()) {
+            return 1;
+        }
+
+        if (isMounted() && !other.isMounted()) {
+            return -1;
+        }
+        if (!isMounted() && other.isMounted()) {
+            return 1;
+        }
+
+        if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {
+            return -1;
+        }
+        if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {
+            return 1;
+        }
+
+        if (getDescription() == null) {
+            return 1;
+        }
+        if (other.getDescription() == null) {
+            return -1;
+        }
+        return getDescription().compareTo(other.getDescription());
+    }
+
+    /** Returns default internal storage. */
+    public static StorageEntry getDefaultInternalStorageEntry(Context context) {
+        return new StorageEntry(context, context.getSystemService(StorageManager.class)
+                .findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
+    }
+
+    /** If it's a VolumeInfo. */
+    public boolean isVolumeInfo() {
+        return mVolumeInfo != null;
+    }
+
+    /** If it's an unsupported DiskInfo. */
+    public boolean isDiskInfoUnsupported() {
+        return mUnsupportedDiskInfo != null;
+    }
+
+    /** If it's a missing VolumeRecord. */
+    public boolean isVolumeRecordMissed() {
+        return mMissingVolumeRecord != null;
+    }
+
+    /** If it's a default internal storage. */
+    public boolean isDefaultInternalStorage() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
+                    && TextUtils.equals(mVolumeInfo.getId(), VolumeInfo.ID_PRIVATE_INTERNAL);
+        }
+        return false;
+    }
+
+    /** If it's a mounted storage. */
+    public boolean isMounted() {
+        return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED
+                || mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
+    }
+
+    /** If it's an unmounted storage. */
+    public boolean isUnmounted() {
+        return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);
+    }
+
+    /** If it's an unmountable storage. */
+    public boolean isUnmountable() {
+        return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;
+    }
+
+    /** If it's a private storage. */
+    public boolean isPrivate() {
+        return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;
+    }
+
+    /** If it's a public storage. */
+    public boolean isPublic() {
+        return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;
+    }
+
+    /** Returns description. */
+    public String getDescription() {
+        if (isVolumeInfo()) {
+            return mVolumeInfoDescription;
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.getDescription();
+        }
+        return mMissingVolumeRecord.getNickname();
+    }
+
+    /** Returns ID. */
+    public String getId() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.getId();
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.getId();
+        }
+        return mMissingVolumeRecord.getFsUuid();
+    }
+
+    /** Returns disk ID. */
+    public String getDiskId() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.getDiskId();
+        }
+        if (isDiskInfoUnsupported()) {
+            return mUnsupportedDiskInfo.getId();
+        }
+        return null;
+    }
+
+    /** Returns fsUuid. */
+    public String getFsUuid() {
+        if (isVolumeInfo()) {
+            return mVolumeInfo.getFsUuid();
+        }
+        if (isDiskInfoUnsupported()) {
+            return null;
+        }
+        return mMissingVolumeRecord.getFsUuid();
+    }
+
+    /** Returns root file if it's a VolumeInfo. */
+    public File getPath() {
+        return mVolumeInfo == null ? null : mVolumeInfo.getPath();
+    }
+
+    /** Returns VolumeInfo of the StorageEntry. */
+    public VolumeInfo getVolumeInfo() {
+        return mVolumeInfo;
+    }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index c2a0b62..e007090 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -158,6 +158,9 @@
                 intent = getAppsIntent();
                 break;
             case FILES_KEY:
+                if (mVolume == null) {
+                    break;
+                }
                 intent = getFilesIntent();
                 FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(
                         mContext, SettingsEnums.STORAGE_FILES);
@@ -189,7 +192,27 @@
      */
     public void setVolume(VolumeInfo volume) {
         mVolume = volume;
-        setFilesPreferenceVisibility();
+        updateCategoryPreferencesVisibility();
+    }
+
+    private void updateCategoryPreferencesVisibility() {
+        // Stats data is only available on private volumes.
+        final boolean isValidVolume = mVolume != null
+                && mVolume.getType() == VolumeInfo.TYPE_PRIVATE
+                && (mVolume.getState() == VolumeInfo.STATE_MOUNTED
+                || mVolume.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
+
+        mPhotoPreference.setVisible(isValidVolume);
+        mAudioPreference.setVisible(isValidVolume);
+        mGamePreference.setVisible(isValidVolume);
+        mMoviesPreference.setVisible(isValidVolume);
+        mAppPreference.setVisible(isValidVolume);
+        mFilePreference.setVisible(isValidVolume);
+        mSystemPreference.setVisible(isValidVolume);
+
+        if (isValidVolume) {
+            setFilesPreferenceVisibility();
+        }
     }
 
     private void setFilesPreferenceVisibility() {
@@ -248,7 +271,7 @@
         mSystemPreference = screen.findPreference(SYSTEM_KEY);
         mFilePreference = screen.findPreference(FILES_KEY);
 
-        setFilesPreferenceVisibility();
+        updateCategoryPreferencesVisibility();
     }
 
     public void onLoadFinished(SparseArray<StorageAsyncLoader.AppsStorageResult> result,
diff --git a/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java b/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java
new file mode 100644
index 0000000..c67fe33
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageRenameFragment.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.Dialog;
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+/**
+ * Dialog that allows editing of volume nickname.
+ */
+public class StorageRenameFragment extends InstrumentedDialogFragment {
+    private static final String TAG_RENAME = "rename";
+
+    /** Shows the rename dialog. */
+    public static void show(Fragment parent, VolumeInfo vol) {
+        final StorageRenameFragment dialog = new StorageRenameFragment();
+        dialog.setTargetFragment(parent, 0 /* requestCode */);
+        final Bundle args = new Bundle();
+        args.putString(VolumeRecord.EXTRA_FS_UUID, vol.getFsUuid());
+        dialog.setArguments(args);
+        dialog.show(parent.getFragmentManager(), TAG_RENAME);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return SettingsEnums.DIALOG_VOLUME_RENAME;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        final Context context = getActivity();
+        final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+        final String fsUuid = getArguments().getString(VolumeRecord.EXTRA_FS_UUID);
+        final VolumeRecord rec = storageManager.findRecordByUuid(fsUuid);
+
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        final LayoutInflater dialogInflater = LayoutInflater.from(builder.getContext());
+
+        final View view = dialogInflater.inflate(R.layout.dialog_edittext, null, false);
+        final EditText nickname = (EditText) view.findViewById(R.id.edittext);
+        nickname.setText(rec.getNickname());
+
+        return builder.setTitle(R.string.storage_rename_title)
+                .setView(view)
+                .setPositiveButton(R.string.save, (dialog, which) ->
+                    // TODO: move to background thread
+                    storageManager.setVolumeNickname(fsUuid, nickname.getText().toString()))
+                .setNegativeButton(R.string.cancel, null)
+                .create();
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
new file mode 100644
index 0000000..03fddec
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.SettingsSpinnerPreference;
+import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Shows a spinner for users to select a storage volume.
+ */
+public class StorageSelectionPreferenceController extends BasePreferenceController implements
+        AdapterView.OnItemSelectedListener {
+
+    @VisibleForTesting
+    SettingsSpinnerPreference mSpinnerPreference;
+    @VisibleForTesting
+    StorageAdapter mStorageAdapter;
+
+    private final List<StorageEntry> mStorageEntries = new ArrayList<>();
+
+    /** The interface for spinner selection callback. */
+    public interface OnItemSelectedListener {
+        /** Callbacked when the spinner selection is changed. */
+        void onItemSelected(StorageEntry storageEntry);
+    }
+    private OnItemSelectedListener mOnItemSelectedListener;
+
+    public StorageSelectionPreferenceController(Context context, String key) {
+        super(context, key);
+
+        mStorageAdapter = new StorageAdapter(context);
+    }
+
+    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+        mOnItemSelectedListener = listener;
+    }
+
+    /** Set the storages in the spinner. */
+    public void setStorageEntries(List<StorageEntry> storageEntries) {
+        mStorageAdapter.clear();
+        mStorageEntries.clear();
+        if (storageEntries == null || storageEntries.isEmpty()) {
+            return;
+        }
+        Collections.sort(mStorageEntries);
+        mStorageEntries.addAll(storageEntries);
+        mStorageAdapter.addAll(storageEntries);
+    }
+
+    /** set selected storage in the spinner. */
+    public void setSelectedStorageEntry(StorageEntry selectedStorageEntry) {
+        if (mSpinnerPreference == null || !mStorageEntries.contains(selectedStorageEntry)) {
+            return;
+        }
+        mSpinnerPreference.setSelection(mStorageAdapter.getPosition(selectedStorageEntry));
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE_UNSEARCHABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mSpinnerPreference = screen.findPreference(getPreferenceKey());
+        mSpinnerPreference.setAdapter(mStorageAdapter);
+        mSpinnerPreference.setOnItemSelectedListener(this);
+    }
+
+    @Override
+    public void onItemSelected(AdapterView<?> arg0, View arg1, int position, long id) {
+        if (mOnItemSelectedListener == null) {
+            return;
+        }
+        mOnItemSelectedListener.onItemSelected(mStorageAdapter.getItem(position));
+    }
+
+    @Override
+    public void onNothingSelected(AdapterView<?> arg0) {
+        // Do nothing.
+    }
+
+    @VisibleForTesting
+    class StorageAdapter extends SettingsSpinnerAdapter<StorageEntry> {
+
+        StorageAdapter(Context context) {
+            super(context);
+        }
+
+        @Override
+        public View getView(int position, View view, ViewGroup parent) {
+            if (view == null) {
+                view = getDefaultView(position, view, parent);
+            }
+
+            TextView textView = null;
+            try {
+                textView = (TextView) view;
+            } catch (ClassCastException e) {
+                throw new IllegalStateException("Default view should be a TextView, ", e);
+            }
+            textView.setText(getItem(position).getDescription());
+            return textView;
+        }
+
+        @Override
+        public View getDropDownView(int position, View view, ViewGroup parent) {
+            if (view == null) {
+                view = getDefaultDropDownView(position, view, parent);
+            }
+
+            TextView textView = null;
+            try {
+                textView = (TextView) view;
+            } catch (ClassCastException e) {
+                throw new IllegalStateException("Default drop down view should be a TextView, ", e);
+            }
+            textView.setText(getItem(position).getDescription());
+            return textView;
+        }
+    }
+}
+
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
new file mode 100644
index 0000000..a00b25a
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceController.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.text.format.Formatter;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.utils.ThreadUtils;
+import com.android.settingslib.widget.UsageProgressBarPreference;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Shows storage summary and progress.
+ */
+public class StorageUsageProgressBarPreferenceController extends BasePreferenceController {
+
+    private static final String TAG = "StorageProgressCtrl";
+
+    private final StorageStatsManager mStorageStatsManager;
+    @VisibleForTesting
+    long mUsedBytes;
+    @VisibleForTesting
+    long mTotalBytes;
+    private UsageProgressBarPreference mUsageProgressBarPreference;
+    private StorageEntry mStorageEntry;
+
+    public StorageUsageProgressBarPreferenceController(Context context, String key) {
+        super(context, key);
+
+        mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
+    }
+
+    /** Set StorageEntry to display. */
+    public void setSelectedStorageEntry(StorageEntry storageEntry) {
+        mStorageEntry = storageEntry;
+        getStorageStatsAndUpdateUi();
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE_UNSEARCHABLE;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        mUsageProgressBarPreference = screen.findPreference(getPreferenceKey());
+        getStorageStatsAndUpdateUi();
+    }
+
+    private void getStorageStatsAndUpdateUi() {
+        ThreadUtils.postOnBackgroundThread(() -> {
+            try {
+                if (mStorageEntry == null || !mStorageEntry.isMounted()) {
+                    throw new IOException();
+                }
+
+                if (mStorageEntry.isPrivate()) {
+                    // StorageStatsManager can only query private storages.
+                    mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
+                    mUsedBytes = mTotalBytes
+                            - mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
+                } else {
+                    final File rootFile = mStorageEntry.getPath();
+                    if (rootFile == null) {
+                        Log.d(TAG, "Mounted public storage has null root path: " + mStorageEntry);
+                        throw new IOException();
+                    }
+                    mTotalBytes = rootFile.getTotalSpace();
+                    mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
+                }
+            } catch (IOException e) {
+                // The storage device isn't present.
+                mTotalBytes = 0;
+                mUsedBytes = 0;
+            }
+
+            if (mUsageProgressBarPreference == null) {
+                return;
+            }
+            ThreadUtils.postOnMainThread(() ->
+                    updateState(mUsageProgressBarPreference)
+            );
+        });
+    }
+
+    @Override
+    public void updateState(Preference preference) {
+        mUsageProgressBarPreference.setUsageSummary(
+                getStorageSummary(R.string.storage_usage_summary, mUsedBytes));
+        mUsageProgressBarPreference.setTotalSummary(
+                getStorageSummary(R.string.storage_total_summary, mTotalBytes));
+        mUsageProgressBarPreference.setPercent(mUsedBytes, mTotalBytes);
+    }
+
+    private String getStorageSummary(int resId, long bytes) {
+        final Formatter.BytesResult result = Formatter.formatBytes(mContext.getResources(),
+                bytes, 0);
+        return mContext.getString(resId, result.value, result.units);
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/storage/StorageUtils.java b/src/com/android/settings/deviceinfo/storage/StorageUtils.java
new file mode 100644
index 0000000..26bdec0
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/storage/StorageUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.storage.VolumeRecord;
+
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.deviceinfo.PrivateVolumeForget;
+
+/** Storage utilities */
+public class StorageUtils {
+
+    /** Launches the fragment to forget a specified missing volume record. */
+    public static void launchForgetMissingVolumeRecordFragment(Context context,
+            StorageEntry storageEntry) {
+        if (storageEntry == null || !storageEntry.isVolumeRecordMissed()) {
+            return;
+        }
+
+        final Bundle args = new Bundle();
+        args.putString(VolumeRecord.EXTRA_FS_UUID, storageEntry.getFsUuid());
+        new SubSettingLauncher(context)
+                .setDestination(PrivateVolumeForget.class.getCanonicalName())
+                .setTitleRes(R.string.storage_menu_forget)
+                .setSourceMetricsCategory(SettingsEnums.SETTINGS_STORAGE_CATEGORY)
+                .setArguments(args)
+                .launch();
+    }
+}
diff --git a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
index dd0a9ce..1aad544 100644
--- a/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
+++ b/src/com/android/settings/enterprise/EnterprisePrivacySettings.java
@@ -20,16 +20,13 @@
 import android.content.Context;
 import android.provider.SearchIndexableResource;
 
-import com.android.settings.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.dashboard.DashboardFragment;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.search.SearchIndexable;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 @SearchIndexable
@@ -37,6 +34,23 @@
 
     static final String TAG = "EnterprisePrivacySettings";
 
+    @VisibleForTesting
+    PrivacySettingsPreference mPrivacySettingsPreference;
+
+    @Override
+    public void onAttach(Context context) {
+        mPrivacySettingsPreference =
+                PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(context);
+
+        super.onAttach(context);
+    }
+
+    @Override
+    public void onDetach() {
+        mPrivacySettingsPreference = null;
+        super.onDetach();
+    }
+
     @Override
     public int getMetricsCategory() {
         return SettingsEnums.ENTERPRISE_PRIVACY_SETTINGS;
@@ -49,47 +63,12 @@
 
     @Override
     protected int getPreferenceScreenResId() {
-        return R.xml.enterprise_privacy_settings;
+        return mPrivacySettingsPreference.getPreferenceScreenResId();
     }
 
     @Override
     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
-        return buildPreferenceControllers(context, true /* async */);
-    }
-
-    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
-            boolean async) {
-        final List<AbstractPreferenceController> controllers = new ArrayList<>();
-        controllers.add(new NetworkLogsPreferenceController(context));
-        controllers.add(new BugReportsPreferenceController(context));
-        controllers.add(new SecurityLogsPreferenceController(context));
-        final List<AbstractPreferenceController> exposureChangesCategoryControllers =
-                new ArrayList<>();
-        exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
-                context, async));
-        exposureChangesCategoryControllers.add(
-                new AdminGrantedLocationPermissionsPreferenceController(context, async));
-        exposureChangesCategoryControllers.add(
-                new AdminGrantedMicrophonePermissionPreferenceController(context, async));
-        exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
-                context, async));
-        exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
-                context));
-        exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
-                context));
-        exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
-                context));
-        exposureChangesCategoryControllers.add(new ImePreferenceController(context));
-        exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(context));
-        exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(context));
-        exposureChangesCategoryControllers.add(new CaCertsManagedProfilePreferenceController(
-                context));
-        controllers.addAll(exposureChangesCategoryControllers);
-        controllers.add(new PreferenceCategoryController(context, "exposure_changes_category")
-                .setChildren(exposureChangesCategoryControllers));
-        controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(context));
-        controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(context));
-        return controllers;
+        return mPrivacySettingsPreference.createPreferenceControllers(true /* async */);
     }
 
     public static boolean isPageEnabled(Context context) {
@@ -99,17 +78,32 @@
     }
 
     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-            new BaseSearchIndexProvider(R.xml.enterprise_privacy_settings) {
+            new BaseSearchIndexProvider() {
+
+                private PrivacySettingsPreference mPrivacySettingsPreference;
+
                 @Override
                 protected boolean isPageSearchEnabled(Context context) {
                     return isPageEnabled(context);
                 }
 
+                @Override
+                public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
+                        boolean enabled) {
+                    mPrivacySettingsPreference =
+                            PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(
+                                    context);
+                    return mPrivacySettingsPreference.getXmlResourcesToIndex();
+                }
 
                 @Override
                 public List<AbstractPreferenceController> createPreferenceControllers(
                         Context context) {
-                    return buildPreferenceControllers(context, false /* async */);
+                    mPrivacySettingsPreference =
+                            PrivacySettingsPreferenceFactory.createPrivacySettingsPreference(
+                                    context);
+                    return mPrivacySettingsPreference.createPreferenceControllers(
+                            false /* async */);
                 }
             };
 }
diff --git a/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java b/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java
new file mode 100644
index 0000000..19556a1
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreference.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Privacy Settings preferences for an Enterprise device. */
+public class PrivacySettingsEnterprisePreference implements PrivacySettingsPreference {
+    private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+
+    private final Context mContext;
+
+    public PrivacySettingsEnterprisePreference(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Returns the XML Res Id that is used for an Enterprise device in the Privacy Settings screen.
+     */
+    @Override
+    public int getPreferenceScreenResId() {
+        return R.xml.enterprise_privacy_settings;
+    }
+
+    /**
+     * Returns the Enterprise XML resources to index for an Enterprise device.
+     */
+    @Override
+    public List<SearchIndexableResource> getXmlResourcesToIndex() {
+        final SearchIndexableResource sir = new SearchIndexableResource(mContext);
+        sir.xmlResId = getPreferenceScreenResId();
+        return Collections.singletonList(sir);
+    }
+
+    /**
+     * Returns the preference controllers used to populate the privacy preferences in the Privacy
+     * Settings screen for Enterprise devices.
+     */
+    @Override
+    public List<AbstractPreferenceController> createPreferenceControllers(boolean async) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new NetworkLogsPreferenceController(mContext));
+        controllers.add(new BugReportsPreferenceController(mContext));
+        controllers.add(new SecurityLogsPreferenceController(mContext));
+        final List<AbstractPreferenceController> exposureChangesCategoryControllers =
+                new ArrayList<>();
+        exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
+                mContext, async));
+        exposureChangesCategoryControllers.add(
+                new AdminGrantedLocationPermissionsPreferenceController(mContext, async));
+        exposureChangesCategoryControllers.add(
+                new AdminGrantedMicrophonePermissionPreferenceController(mContext, async));
+        exposureChangesCategoryControllers.add(new AdminGrantedCameraPermissionPreferenceController(
+                mContext, async));
+        exposureChangesCategoryControllers.add(new EnterpriseSetDefaultAppsPreferenceController(
+                mContext));
+        exposureChangesCategoryControllers.add(new AlwaysOnVpnCurrentUserPreferenceController(
+                mContext));
+        exposureChangesCategoryControllers.add(new AlwaysOnVpnManagedProfilePreferenceController(
+                mContext));
+        exposureChangesCategoryControllers.add(new ImePreferenceController(mContext));
+        exposureChangesCategoryControllers.add(new GlobalHttpProxyPreferenceController(mContext));
+        exposureChangesCategoryControllers.add(new CaCertsCurrentUserPreferenceController(
+                mContext));
+        exposureChangesCategoryControllers.add(new CaCertsManagedProfilePreferenceController(
+                mContext));
+        controllers.addAll(exposureChangesCategoryControllers);
+        controllers.add(new PreferenceCategoryController(mContext, KEY_EXPOSURE_CHANGES_CATEGORY)
+                .setChildren(exposureChangesCategoryControllers));
+        controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(mContext));
+        controllers.add(new FailedPasswordWipeManagedProfilePreferenceController(mContext));
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java b/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java
new file mode 100644
index 0000000..12901a6
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsFinancedPreference.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** Privacy Settings preferences for a financed device. */
+public class PrivacySettingsFinancedPreference implements PrivacySettingsPreference {
+    private static final String KEY_EXPOSURE_CHANGES_CATEGORY = "exposure_changes_category";
+
+    private final Context mContext;
+
+    public PrivacySettingsFinancedPreference(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
+    /**
+     * Returns the XML Res Id that is used for financed devices in the Privacy Settings screen.
+     */
+    @Override
+    public int getPreferenceScreenResId() {
+        return R.xml.financed_privacy_settings;
+    }
+
+    /**
+     * Returns the XML resources to index for a financed device.
+     */
+    @Override
+    public List<SearchIndexableResource> getXmlResourcesToIndex() {
+        final SearchIndexableResource sir = new SearchIndexableResource(mContext);
+        sir.xmlResId = getPreferenceScreenResId();
+        return Collections.singletonList(sir);
+    }
+
+    /**
+     * Returns the preference controllers used to populate the privacy preferences in the Privacy
+     * Settings screen for a financed device.
+     */
+    @Override
+    public List<AbstractPreferenceController> createPreferenceControllers(boolean async) {
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new NetworkLogsPreferenceController(mContext));
+        controllers.add(new BugReportsPreferenceController(mContext));
+        controllers.add(new SecurityLogsPreferenceController(mContext));
+        final List<AbstractPreferenceController> exposureChangesCategoryControllers =
+                new ArrayList<>();
+        exposureChangesCategoryControllers.add(new EnterpriseInstalledPackagesPreferenceController(
+                mContext, async));
+        controllers.addAll(exposureChangesCategoryControllers);
+        controllers.add(new PreferenceCategoryController(mContext, KEY_EXPOSURE_CHANGES_CATEGORY)
+                .setChildren(exposureChangesCategoryControllers));
+        controllers.add(new FailedPasswordWipeCurrentUserPreferenceController(mContext));
+        return controllers;
+    }
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsPreference.java b/src/com/android/settings/enterprise/PrivacySettingsPreference.java
new file mode 100644
index 0000000..4310f5e
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsPreference.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.List;
+
+/** Interface for configuring what is displayed on the Privacy Settings. */
+public interface PrivacySettingsPreference {
+
+    /**
+     * Returns the XML Res Id that is used in the Privacy Settings screen.
+     */
+    int getPreferenceScreenResId();
+
+    /**
+     * Returns the XML resources to index.
+     */
+    List<SearchIndexableResource> getXmlResourcesToIndex();
+
+    /**
+     * Returns the preference controllers used to populate the privacy preferences.
+     */
+    List<AbstractPreferenceController> createPreferenceControllers(boolean async);
+}
diff --git a/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.java b/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.java
new file mode 100644
index 0000000..0ec2498
--- /dev/null
+++ b/src/com/android/settings/enterprise/PrivacySettingsPreferenceFactory.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+
+/** Factory for creating the privacy settings preference for a managed device. */
+public class PrivacySettingsPreferenceFactory {
+
+    /**
+     * Determines which preference to use in the Privacy Settings based off of the type of managed
+     * device.
+     */
+    public static PrivacySettingsPreference createPrivacySettingsPreference(Context context) {
+        if (isFinancedDevice(context)) {
+            return createPrivacySettingsFinancedPreference(context);
+        } else {
+            return createPrivacySettingsEnterprisePreference(context);
+        }
+    }
+
+    private static PrivacySettingsEnterprisePreference createPrivacySettingsEnterprisePreference(
+            Context context) {
+        return new PrivacySettingsEnterprisePreference(context);
+    }
+
+    private static PrivacySettingsFinancedPreference createPrivacySettingsFinancedPreference(
+            Context context) {
+        return new PrivacySettingsFinancedPreference(context);
+    }
+
+    private static boolean isFinancedDevice(Context context) {
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        return dpm.isDeviceManaged() && dpm.getDeviceOwnerType(
+                dpm.getDeviceOwnerComponentOnAnyUser()) == DEVICE_OWNER_TYPE_FINANCED;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
new file mode 100644
index 0000000..73b875b
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProvider.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge;
+
+/**
+ * Feature provider for battery settings usage.
+ */
+public interface BatterySettingsFeatureProvider {
+
+    /**
+     * Get replacement activity for a given activity or fragment path.
+     */
+    String getReplacingActivityName(String activity);
+
+}
diff --git a/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
new file mode 100644
index 0000000..e410695
--- /dev/null
+++ b/src/com/android/settings/fuelgauge/BatterySettingsFeatureProviderImpl.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.fuelgauge;
+
+/**
+ * Feature provider implementation for battery settings usage.
+ */
+public class BatterySettingsFeatureProviderImpl implements BatterySettingsFeatureProvider {
+
+    @Override
+    public String getReplacingActivityName(String activity) {
+        return null;
+    }
+}
diff --git a/src/com/android/settings/fuelgauge/BatteryUtils.java b/src/com/android/settings/fuelgauge/BatteryUtils.java
index 1645590..68a0b03 100644
--- a/src/com/android/settings/fuelgauge/BatteryUtils.java
+++ b/src/com/android/settings/fuelgauge/BatteryUtils.java
@@ -298,7 +298,7 @@
      */
     public long calculateLastFullChargeTime(BatteryUsageStats batteryUsageStats,
             long currentTimeMs) {
-        return currentTimeMs - batteryUsageStats.getStatsStartRealtime();
+        return currentTimeMs - batteryUsageStats.getStatsStartTimestamp();
     }
 
     public static void logRuntime(String tag, String message, long startTime) {
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
index 433c06d..1052afe 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoader.java
@@ -28,7 +28,6 @@
 import com.android.settings.fuelgauge.batterytip.detectors.HighUsageDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.LowBatteryDetector;
 import com.android.settings.fuelgauge.batterytip.detectors.SmartBatteryDetector;
-import com.android.settings.fuelgauge.batterytip.detectors.SummaryDetector;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.LowBatteryTip;
 import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
@@ -73,7 +72,6 @@
         tips.add(new SmartBatteryDetector(policy, context.getContentResolver()).detect());
         tips.add(new EarlyWarningDetector(policy, context).detect());
         tips.add(new BatteryDefenderDetector(batteryInfo).detect());
-        tips.add(new SummaryDetector(policy, batteryInfo.averageTimeToDischarge).detect());
         // Disable this feature now since it introduces false positive cases. We will try to improve
         // it in the future.
         // tips.add(new RestrictAppDetector(context, policy).detect());
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
index 487adf8..3fbbf5b 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicy.java
@@ -230,7 +230,7 @@
         }
 
         batteryTipEnabled = mParser.getBoolean(KEY_BATTERY_TIP_ENABLED, true);
-        summaryEnabled = mParser.getBoolean(KEY_SUMMARY_ENABLED, true);
+        summaryEnabled = mParser.getBoolean(KEY_SUMMARY_ENABLED, false);
         batterySaverTipEnabled = mParser.getBoolean(KEY_BATTERY_SAVER_TIP_ENABLED, true);
         highUsageEnabled = mParser.getBoolean(KEY_HIGH_USAGE_ENABLED, true);
         highUsageAppCount = mParser.getInt(KEY_HIGH_USAGE_APP_COUNT, 3);
diff --git a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
index 4317bff..00b1e87 100644
--- a/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceController.java
@@ -28,11 +28,9 @@
 import com.android.settings.core.InstrumentedPreferenceFragment;
 import com.android.settings.fuelgauge.batterytip.actions.BatteryTipAction;
 import com.android.settings.fuelgauge.batterytip.tips.BatteryTip;
-import com.android.settings.fuelgauge.batterytip.tips.SummaryTip;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.widget.CardPreference;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.fuelgauge.EstimateKt;
 
 import java.util.HashMap;
 import java.util.List;
@@ -91,10 +89,8 @@
         mPrefContext = screen.getContext();
         mCardPreference = screen.findPreference(getPreferenceKey());
 
-        // Add summary tip in advance to avoid UI flakiness
-        final SummaryTip summaryTip = new SummaryTip(BatteryTip.StateType.NEW,
-                EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
-        summaryTip.updatePreference(mCardPreference);
+        // Set preference as invisible since there is no default tips.
+        mCardPreference.setVisible(false);
     }
 
     public void updateBatteryTips(List<BatteryTip> batteryTips) {
@@ -110,10 +106,12 @@
             }
         }
 
+        mCardPreference.setVisible(false);
         for (int i = 0, size = batteryTips.size(); i < size; i++) {
             final BatteryTip batteryTip = mBatteryTips.get(i);
             batteryTip.validateCheck(mContext);
             if (batteryTip.getState() != BatteryTip.StateType.INVISIBLE) {
+                mCardPreference.setVisible(true);
                 batteryTip.updatePreference(mCardPreference);
                 mBatteryTipMap.put(mCardPreference.getKey(), batteryTip);
                 batteryTip.log(mContext, mMetricsFeatureProvider);
diff --git a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
index 04a32de..5eba539 100644
--- a/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
+++ b/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceController.java
@@ -76,7 +76,7 @@
     @Override
     public boolean isChecked() {
         return Settings.Secure.getInt(mContext.getContentResolver(),
-                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, ON) == ON;
+                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, OFF) == ON;
     }
 
     @Override
diff --git a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
index 686558c..d41a378 100644
--- a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
@@ -91,11 +91,19 @@
         List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
         final Context context = getPrefContext();
         final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
+        final List<InputMethodInfo> enabledImis = mImm.getEnabledInputMethodList();
         final int numImis = (imis == null ? 0 : imis.size());
         for (int i = 0; i < numImis; ++i) {
             final InputMethodInfo imi = imis.get(i);
+            // TODO (b/182876800): Move this logic out of isAllowedByOrganization and
+            // into a new boolean.
+            // If an input method is enabled but not included in the permitted list, then set it as
+            // allowed by organization. Doing so will allow the user to disable the input method and
+            // remain complaint with the organization's policy. Once disabled, the input method
+            // cannot be re-enabled because it is not in the permitted list.
             final boolean isAllowedByOrganization = permittedList == null
-                    || permittedList.contains(imi.getPackageName());
+                    || permittedList.contains(imi.getPackageName())
+                    || enabledImis.contains(imi);
             final InputMethodPreference pref = new InputMethodPreference(
                     context, imi, true, isAllowedByOrganization, this);
             pref.setIcon(imi.loadIcon(context.getPackageManager()));
diff --git a/src/com/android/settings/network/InternetPreferenceController.java b/src/com/android/settings/network/InternetPreferenceController.java
index a6c8574..639bab5 100644
--- a/src/com/android/settings/network/InternetPreferenceController.java
+++ b/src/com/android/settings/network/InternetPreferenceController.java
@@ -75,7 +75,7 @@
     private static Map<Integer, Integer> sSummaryMap = new HashMap<>();
     static {
         sSummaryMap.put(INTERNET_OFF, R.string.condition_airplane_title);
-        sSummaryMap.put(INTERNET_NETWORKS_AVAILABLE, R.string.disconnected);
+        sSummaryMap.put(INTERNET_NETWORKS_AVAILABLE, R.string.networks_available);
         sSummaryMap.put(INTERNET_WIFI, 0);
         sSummaryMap.put(INTERNET_CELLULAR, 0);
         sSummaryMap.put(INTERNET_ETHERNET, R.string.to_switch_networks_disconnect_ethernet);
diff --git a/src/com/android/settings/network/InternetUpdater.java b/src/com/android/settings/network/InternetUpdater.java
index 2f704e7..3967276 100644
--- a/src/com/android/settings/network/InternetUpdater.java
+++ b/src/com/android/settings/network/InternetUpdater.java
@@ -32,7 +32,6 @@
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.NetworkCapabilities.Transport;
 import android.net.wifi.WifiManager;
 import android.util.Log;
 
@@ -132,7 +131,7 @@
     @VisibleForTesting
     boolean mInternetAvailable;
     @VisibleForTesting
-    @Transport int mTransport;
+    int mTransport;
     private static Map<Integer, Integer> sTransportMap = new HashMap<>();
     static {
         sTransportMap.put(TRANSPORT_WIFI, INTERNET_WIFI);
@@ -227,7 +226,7 @@
         }
 
         boolean internetAvailable = false;
-        for (@Transport int transport : networkCapabilities.getTransportTypes()) {
+        for (int transport : networkCapabilities.getTransportTypes()) {
             if (sTransportMap.containsKey(transport)) {
                 mTransport = transport;
                 internetAvailable = true;
diff --git a/src/com/android/settings/network/ProviderModelSliceHelper.java b/src/com/android/settings/network/ProviderModelSliceHelper.java
index 440d425..3a0ceeac 100644
--- a/src/com/android/settings/network/ProviderModelSliceHelper.java
+++ b/src/com/android/settings/network/ProviderModelSliceHelper.java
@@ -24,6 +24,7 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.net.wifi.WifiManager;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionInfo;
@@ -231,6 +232,7 @@
     }
 
     private String getMobileSummary(String networkTypeDescription) {
+        final WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
         String summary = networkTypeDescription;
         if (isDataSimActive()) {
             summary = mContext.getString(R.string.preference_summary_default_combination,
@@ -238,6 +240,8 @@
                     networkTypeDescription);
         } else if (!isMobileDataEnabled()) {
             summary = mContext.getString(R.string.mobile_data_off_summary);
+        } else if (!wifiManager.isWifiEnabled() && !isDataSimActive()) {
+            summary = mContext.getString(R.string.mobile_data_no_connection);
         }
         return summary;
     }
diff --git a/src/com/android/settings/overlay/FeatureFactory.java b/src/com/android/settings/overlay/FeatureFactory.java
index 42d98e0..0e47556 100644
--- a/src/com/android/settings/overlay/FeatureFactory.java
+++ b/src/com/android/settings/overlay/FeatureFactory.java
@@ -32,6 +32,7 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
 import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -112,6 +113,12 @@
     public abstract BatteryStatusFeatureProvider getBatteryStatusFeatureProvider(
             Context context);
 
+    /**
+     * Get implementation for Battery Settings provider.
+     */
+    public abstract BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(
+            Context context);
+
     public abstract DashboardFeatureProvider getDashboardFeatureProvider(Context context);
 
     public abstract DockUpdaterFeatureProvider getDockUpdaterFeatureProvider();
diff --git a/src/com/android/settings/overlay/FeatureFactoryImpl.java b/src/com/android/settings/overlay/FeatureFactoryImpl.java
index 46b263a..dc08547 100644
--- a/src/com/android/settings/overlay/FeatureFactoryImpl.java
+++ b/src/com/android/settings/overlay/FeatureFactoryImpl.java
@@ -45,6 +45,8 @@
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProviderImpl;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProviderImpl;
 import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.BatteryStatusFeatureProviderImpl;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
@@ -86,6 +88,7 @@
     private SuggestionFeatureProvider mSuggestionFeatureProvider;
     private PowerUsageFeatureProvider mPowerUsageFeatureProvider;
     private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
+    private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
     private AssistGestureFeatureProvider mAssistGestureFeatureProvider;
     private UserFeatureProvider mUserFeatureProvider;
     private SlicesFeatureProvider mSlicesFeatureProvider;
@@ -130,6 +133,14 @@
     }
 
     @Override
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+        if (mBatterySettingsFeatureProvider == null) {
+            mBatterySettingsFeatureProvider = new BatterySettingsFeatureProviderImpl();
+        }
+        return mBatterySettingsFeatureProvider;
+    }
+
+    @Override
     public DashboardFeatureProvider getDashboardFeatureProvider(Context context) {
         if (mDashboardFeatureProvider == null) {
             mDashboardFeatureProvider = new DashboardFeatureProviderImpl(
diff --git a/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java b/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java
new file mode 100644
index 0000000..4622431
--- /dev/null
+++ b/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import android.content.Context;
+import android.provider.Settings;
+
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Controller for preference to toggle whether clipboard access notifications should be shown.
+ */
+public class ShowClipAccessNotificationPreferenceController extends TogglePreferenceController {
+
+    private static final String KEY_SHOW_CLIP_ACCESS_NOTIFICATION = "show_clip_access_notification";
+
+    public ShowClipAccessNotificationPreferenceController(Context context) {
+        super(context, KEY_SHOW_CLIP_ACCESS_NOTIFICATION);
+    }
+
+    @Override
+    public boolean isChecked() {
+        // TODO(b/182349993) Retrieve default value from DeviceConfig.
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, 1) != 0;
+    }
+
+    @Override
+    public boolean setChecked(boolean isChecked) {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, isChecked ? 1 : 0);
+        return true;
+    }
+
+    @Override
+    public int getAvailabilityStatus() {
+        return AVAILABLE;
+    }
+
+}
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 53d9849..a9ddf95 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -290,6 +290,9 @@
     @VisibleForTesting
     void switchUser() {
         try {
+            if (mUserInfo.isGuest()) {
+                mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_SWITCH_TO_GUEST);
+            }
             ActivityManager.getService().switchUser(mUserInfo.id);
         } catch (RemoteException re) {
             Log.e(TAG, "Error while switching to other user.");
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 8010f41..6e82aab 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -820,6 +820,8 @@
      */
     private void exitGuest() {
         // Just to be safe
+        mMetricsFeatureProvider.action(getActivity(),
+                SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
         if (!isCurrentUserGuest()) {
             return;
         }
@@ -1077,6 +1079,7 @@
             return true;
         } else if (pref == mAddGuest) {
             mAddGuest.setEnabled(false); // prevent multiple tap issue
+            mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_GUEST_ADD);
             UserInfo guest = mUserManager.createGuest(
                     getContext(), getString(com.android.settingslib.R.string.user_guest));
             openUserDetails(guest, true);
diff --git a/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java b/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
index e8eab68..dee5817 100644
--- a/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
+++ b/src/com/android/settings/utils/AndroidKeystoreAliasLoader.java
@@ -16,8 +16,6 @@
 
 package com.android.settings.utils;
 
-import android.os.Process;
-import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
 import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
 import android.util.Log;
@@ -41,6 +39,8 @@
 public class AndroidKeystoreAliasLoader {
     private static final String TAG = "SettingsKeystoreUtils";
 
+    private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+
     private final Collection<String> mKeyCertAliases;
     private final Collection<String> mCaCertAliases;
     /**
@@ -58,21 +58,13 @@
     public AndroidKeystoreAliasLoader(Integer namespace) {
         mKeyCertAliases = new ArrayList<>();
         mCaCertAliases = new ArrayList<>();
-        KeyStore keyStore = null;
+        final KeyStore keyStore;
         final Enumeration<String> aliases;
         try {
+            keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
             if (namespace != null && namespace != KeyProperties.NAMESPACE_APPLICATION) {
-                if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
-                    keyStore = KeyStore.getInstance("AndroidKeyStore");
-                    keyStore.load(new AndroidKeyStoreLoadStoreParameter(namespace));
-                } else {
-                    // In the legacy case we pass in the WIFI UID because that is the only
-                    // possible special namespace that existed as of this writing,
-                    // and new namespaces must only be added using the new mechanism.
-                    keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(Process.WIFI_UID);
-                }
+                keyStore.load(new AndroidKeyStoreLoadStoreParameter(namespace));
             } else {
-                keyStore = KeyStore.getInstance("AndroidKeyStore");
                 keyStore.load(null);
             }
             aliases = keyStore.aliases();
diff --git a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
index 5869705..e9fd350 100644
--- a/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
+++ b/src/com/android/settings/wifi/WifiWakeupPreferenceController.java
@@ -26,7 +26,6 @@
 import android.location.LocationManager;
 import android.net.wifi.WifiManager;
 import android.provider.Settings;
-import android.util.FeatureFlagUtils;
 
 import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.Fragment;
@@ -45,7 +44,6 @@
  * {@link TogglePreferenceController} that controls whether the Wi-Fi Wakeup feature should be
  * enabled.
  */
-// TODO(b/167474581): Should clean up this controller when Provider Model finished.
 public class WifiWakeupPreferenceController extends TogglePreferenceController implements
         LifecycleObserver, OnPause, OnResume {
 
@@ -91,9 +89,6 @@
 
     @Override
     public int getAvailabilityStatus() {
-        if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
-            return CONDITIONALLY_UNAVAILABLE;
-        }
         return AVAILABLE;
     }
 
diff --git a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
index b154a9b..104761f 100644
--- a/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
+++ b/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2.java
@@ -75,7 +75,8 @@
     // Interval between initiating SavedNetworkTracker scans
     private static final long SCAN_INTERVAL_MILLIS = 10_000;
 
-    private NetworkDetailsTracker mNetworkDetailsTracker;
+    @VisibleForTesting
+    NetworkDetailsTracker mNetworkDetailsTracker;
     private HandlerThread mWorkerThread;
     private WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
     private List<WifiDialog2.WifiDialog2Listener> mWifiDialogListeners = new ArrayList<>();
@@ -125,9 +126,11 @@
 
     @Override
     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
-        item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
-        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        if (isEditable()) {
+            MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
+            item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
+            item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+        }
         super.onCreateOptionsMenu(menu, inflater);
     }
 
@@ -252,6 +255,17 @@
                         getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
     }
 
+    private boolean isEditable() {
+        if (mNetworkDetailsTracker == null) {
+            return false;
+        }
+        final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
+        if (wifiEntry == null) {
+            return false;
+        }
+        return wifiEntry.isSaved();
+    }
+
     /**
      * API call for refreshing the preferences in this fragment.
      */
diff --git a/tests/anomaly-tester/AndroidManifest.xml b/tests/anomaly-tester/AndroidManifest.xml
index d6f68a8..3c5fb1f 100644
--- a/tests/anomaly-tester/AndroidManifest.xml
+++ b/tests/anomaly-tester/AndroidManifest.xml
@@ -19,6 +19,8 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.BLUETOOTH"/>
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/>
     <uses-permission android:name="android.permission.WAKE_LOCK"/>
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
@@ -49,4 +51,4 @@
         android:label="Settings Test Cases">
     </instrumentation>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/componenttests/AndroidManifest.xml b/tests/componenttests/AndroidManifest.xml
index 54ea374..fb6c26f 100644
--- a/tests/componenttests/AndroidManifest.xml
+++ b/tests/componenttests/AndroidManifest.xml
@@ -20,6 +20,8 @@
 
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@ -37,4 +39,4 @@
                      android:label="Settings Test Cases">
     </instrumentation>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java b/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java
deleted file mode 100644
index ed7f16b..0000000
--- a/tests/robotests/src/com/android/settings/deviceinfo/PrivateVolumeOptionMenuControllerTest.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2017 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.deviceinfo;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.content.pm.PackageManager;
-import android.os.storage.VolumeInfo;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import com.android.settings.R;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Answers;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.shadows.ShadowApplication;
-
-@RunWith(RobolectricTestRunner.class)
-public class PrivateVolumeOptionMenuControllerTest {
-
-    @Mock
-    private MenuItem mMigrateMenuItem;
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Menu mMenu;
-    @Mock
-    private MenuInflater mMenuInflater;
-    @Mock
-    private PackageManager mPm;
-    @Mock
-    private VolumeInfo mVolumeInfo;
-    @Mock
-    private VolumeInfo mPrimaryInfo;
-
-    private PrivateVolumeOptionMenuController mController;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
-        when(mVolumeInfo.isMountedWritable()).thenReturn(true);
-        when(mPrimaryInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
-        when(mMenu.findItem(anyInt())).thenReturn(mMigrateMenuItem);
-        when(mMigrateMenuItem.getItemId()).thenReturn(100);
-
-        mController = new PrivateVolumeOptionMenuController(
-                Robolectric.setupActivity(Activity.class), mPrimaryInfo, mPm);
-    }
-
-    @Test
-    public void testMigrateDataMenuItemIsAdded() {
-        mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-
-        verify(mMenu).add(Menu.NONE, 100, Menu.NONE, R.string.storage_menu_migrate);
-    }
-
-    @Test
-    public void testMigrateDataIsNotVisibleNormally() {
-        when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mPrimaryInfo);
-        when(mPrimaryInfo.isMountedWritable()).thenReturn(true);
-
-        mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-        mController.onPrepareOptionsMenu(mMenu);
-
-        verify(mMigrateMenuItem).setVisible(false);
-    }
-
-    @Test
-    public void testMigrateDataIsVisibleWhenExternalVolumeIsPrimary() {
-        when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
-
-        mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-        mController.onPrepareOptionsMenu(mMenu);
-
-        verify(mMigrateMenuItem).setVisible(true);
-    }
-
-    @Test
-    public void testMigrateDataIsNotVisibleWhenExternalVolumeIsNotMounted() {
-        when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
-        when(mVolumeInfo.isMountedWritable()).thenReturn(false);
-
-        mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-        mController.onPrepareOptionsMenu(mMenu);
-
-        verify(mMigrateMenuItem).setVisible(false);
-    }
-
-    @Test
-    public void testMigrateDataGoesToMigrateWizard() {
-        when(mPm.getPrimaryStorageCurrentVolume()).thenReturn(mVolumeInfo);
-
-        mController.onCreateOptionsMenu(mMenu, mMenuInflater);
-        mController.onPrepareOptionsMenu(mMenu);
-
-        assertThat(mController.onOptionsItemSelected(mMigrateMenuItem)).isTrue();
-        ShadowApplication shadowApplication = ShadowApplication.getInstance();
-        assertThat(shadowApplication).isNotNull();
-        assertThat(shadowApplication.getNextStartedActivity().getComponent().getClassName())
-                .isEqualTo(StorageWizardMigrateConfirm.class.getName());
-    }
-}
diff --git a/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.java
new file mode 100644
index 0000000..fdf005d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/BasePrivacySettingsPreferenceTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.provider.SearchIndexableResource;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.settings.R;
+import com.android.settings.widget.PreferenceCategoryController;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public abstract class BasePrivacySettingsPreferenceTest {
+
+    protected Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+    }
+
+    protected static void verifyEnterpriseSearchIndexableResources(
+            List<SearchIndexableResource> searchIndexableResources) {
+        assertThat(searchIndexableResources).isNotEmpty();
+        assertThat(searchIndexableResources.size()).isEqualTo(1);
+        assertThat(searchIndexableResources.get(0).xmlResId)
+                .isEqualTo(R.xml.enterprise_privacy_settings);
+    }
+
+    protected static void verifyEnterprisePreferenceControllers(
+            List<AbstractPreferenceController> controllers) {
+        assertThat(controllers).isNotNull();
+        assertThat(controllers.size()).isEqualTo(17);
+        int position = 0;
+        assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                SecurityLogsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                EnterpriseInstalledPackagesPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                AdminGrantedLocationPermissionsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                AdminGrantedMicrophonePermissionPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                AdminGrantedCameraPermissionPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                EnterpriseSetDefaultAppsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                AlwaysOnVpnCurrentUserPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                AlwaysOnVpnManagedProfilePreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                GlobalHttpProxyPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                CaCertsCurrentUserPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                CaCertsManagedProfilePreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                PreferenceCategoryController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                FailedPasswordWipeCurrentUserPreferenceController.class);
+        assertThat(controllers.get(position)).isInstanceOf(
+                FailedPasswordWipeManagedProfilePreferenceController.class);
+    }
+
+    protected static void verifyFinancedSearchIndexableResources(
+            List<SearchIndexableResource> searchIndexableResources) {
+        assertThat(searchIndexableResources).isNotEmpty();
+        assertThat(searchIndexableResources.size()).isEqualTo(1);
+        assertThat(searchIndexableResources.get(0).xmlResId)
+                .isEqualTo(R.xml.financed_privacy_settings);
+    }
+
+    protected static void verifyFinancedPreferenceControllers(
+            List<AbstractPreferenceController> controllers) {
+        assertThat(controllers).isNotNull();
+        assertThat(controllers.size()).isEqualTo(6);
+        int position = 0;
+        assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                SecurityLogsPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                EnterpriseInstalledPackagesPreferenceController.class);
+        assertThat(controllers.get(position++)).isInstanceOf(
+                PreferenceCategoryController.class);
+        assertThat(controllers.get(position)).isInstanceOf(
+                FailedPasswordWipeCurrentUserPreferenceController.class);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
index 2d4ba62..eb70749 100644
--- a/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
+++ b/tests/robotests/src/com/android/settings/enterprise/EnterprisePrivacySettingsTest.java
@@ -16,47 +16,70 @@
 
 package com.android.settings.enterprise;
 
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.provider.SearchIndexableResource;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settings.R;
 import com.android.settings.testutils.FakeFeatureFactory;
-import com.android.settings.widget.PreferenceCategoryController;
 import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.drawer.CategoryKey;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
-public class EnterprisePrivacySettingsTest {
+public class EnterprisePrivacySettingsTest extends BasePrivacySettingsPreferenceTest {
+    private static final ComponentName DEVICE_OWNER_COMPONENT =
+            new ComponentName("com.android.foo", "bar");
 
-    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
-    private Context mContext;
+    @Mock
+    private DevicePolicyManager mDevicePolicyManager;
+    @Mock
+    private PrivacySettingsPreference mPrivacySettingsPreference;
     private FakeFeatureFactory mFeatureFactory;
     private EnterprisePrivacySettings mSettings;
 
+    @Override
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
         mFeatureFactory = FakeFeatureFactory.setupForTest();
         mSettings = new EnterprisePrivacySettings();
+        mSettings.mPrivacySettingsPreference = mPrivacySettingsPreference;
+
+        when(mContext.getSystemService(DevicePolicyManager.class)).thenReturn(mDevicePolicyManager);
+        when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+                .thenReturn(DEVICE_OWNER_COMPONENT);
+        when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+                .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
     }
 
     @Test
     public void verifyConstants() {
+        when(mPrivacySettingsPreference.getPreferenceScreenResId())
+                .thenReturn(R.xml.enterprise_privacy_settings);
+
         assertThat(mSettings.getMetricsCategory())
                 .isEqualTo(MetricsEvent.ENTERPRISE_PRIVACY_SETTINGS);
         assertThat(mSettings.getLogTag()).isEqualTo("EnterprisePrivacySettings");
@@ -76,6 +99,7 @@
 
     @Test
     public void isPageEnabled_noDeviceOwner_shouldReturnFalse() {
+        when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
         when(mFeatureFactory.enterprisePrivacyFeatureProvider.hasDeviceOwner())
                 .thenReturn(false);
 
@@ -85,53 +109,35 @@
 
     @Test
     public void getPreferenceControllers() {
-        final List<AbstractPreferenceController> controllers =
-            mSettings.createPreferenceControllers(RuntimeEnvironment.application);
-        verifyPreferenceControllers(controllers);
+        final List<AbstractPreferenceController> controllers = new ArrayList<>();
+        controllers.add(new NetworkLogsPreferenceController(mContext));
+        when(mPrivacySettingsPreference.createPreferenceControllers(anyBoolean()))
+                .thenReturn(controllers);
+
+        final List<AbstractPreferenceController> privacyControllers =
+                mSettings.createPreferenceControllers(mContext);
+
+        assertThat(privacyControllers).isNotNull();
+        assertThat(privacyControllers.size()).isEqualTo(1);
+        assertThat(controllers.get(0)).isInstanceOf(NetworkLogsPreferenceController.class);
     }
 
     @Test
-    public void getSearchIndexProviderPreferenceControllers() {
+    public void
+            getSearchIndexProviderPreferenceControllers_returnsEnterpriseSearchIndexPreferenceControllers() {
         final List<AbstractPreferenceController> controllers =
             EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
-                .getPreferenceControllers(RuntimeEnvironment.application);
-        verifyPreferenceControllers(controllers);
+                .getPreferenceControllers(mContext);
+
+        verifyEnterprisePreferenceControllers(controllers);
     }
 
-    private void verifyPreferenceControllers(List<AbstractPreferenceController> controllers) {
-        assertThat(controllers).isNotNull();
-        assertThat(controllers.size()).isEqualTo(17);
-        int position = 0;
-        assertThat(controllers.get(position++)).isInstanceOf(NetworkLogsPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(BugReportsPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                SecurityLogsPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                EnterpriseInstalledPackagesPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                AdminGrantedLocationPermissionsPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                AdminGrantedMicrophonePermissionPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                AdminGrantedCameraPermissionPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                EnterpriseSetDefaultAppsPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                AlwaysOnVpnCurrentUserPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                AlwaysOnVpnManagedProfilePreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(ImePreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                GlobalHttpProxyPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                CaCertsCurrentUserPreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                CaCertsManagedProfilePreferenceController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                PreferenceCategoryController.class);
-        assertThat(controllers.get(position++)).isInstanceOf(
-                FailedPasswordWipeCurrentUserPreferenceController.class);
-        assertThat(controllers.get(position)).isInstanceOf(
-                FailedPasswordWipeManagedProfilePreferenceController.class);
+    @Test
+    public void getXmlResourcesToIndex_returnsEnterpriseXmlResources() {
+        final List<SearchIndexableResource> searchIndexableResources =
+                EnterprisePrivacySettings.SEARCH_INDEX_DATA_PROVIDER
+                        .getXmlResourcesToIndex(mContext, true);
+
+        verifyEnterpriseSearchIndexableResources(searchIndexableResources);
     }
 }
diff --git a/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java
new file mode 100644
index 0000000..68e37fc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsEnterprisePreferenceTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class PrivacySettingsEnterprisePreferenceTest extends BasePrivacySettingsPreferenceTest {
+
+    private PrivacySettingsEnterprisePreference mPrivacySettingsEnterprisePreference;
+
+    @Override
+    @Before
+    public void setUp() {
+        super.setUp();
+        mPrivacySettingsEnterprisePreference = new PrivacySettingsEnterprisePreference(mContext);
+    }
+
+    @Test
+    public void getPreferenceScreenResId() {
+        assertThat(mPrivacySettingsEnterprisePreference.getPreferenceScreenResId())
+                .isEqualTo(R.xml.enterprise_privacy_settings);
+    }
+
+    @Test
+    public void getXmlResourcesToIndex() {
+        final List<SearchIndexableResource> searchIndexableResources =
+                mPrivacySettingsEnterprisePreference.getXmlResourcesToIndex();
+
+        verifyEnterpriseSearchIndexableResources(searchIndexableResources);
+    }
+
+    @Test
+    public void getPreferenceControllers() {
+        final List<AbstractPreferenceController> controllers =
+                mPrivacySettingsEnterprisePreference.createPreferenceControllers(true);
+
+        verifyEnterprisePreferenceControllers(controllers);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java
new file mode 100644
index 0000000..fe7b214
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/enterprise/PrivacySettingsFinancedPreferenceTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 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.enterprise;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.SearchIndexableResource;
+
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class PrivacySettingsFinancedPreferenceTest extends BasePrivacySettingsPreferenceTest {
+
+    private PrivacySettingsFinancedPreference mPrivacySettingsFinancedPreference;
+
+    @Override
+    @Before
+    public void setUp() {
+        super.setUp();
+        mPrivacySettingsFinancedPreference = new PrivacySettingsFinancedPreference(mContext);
+    }
+
+    @Test
+    public void getPreferenceScreenResId() {
+        assertThat(mPrivacySettingsFinancedPreference.getPreferenceScreenResId())
+                .isEqualTo(R.xml.financed_privacy_settings);
+    }
+
+    @Test
+    public void getXmlResourcesToIndex() {
+        final List<SearchIndexableResource> searchIndexableResources =
+                mPrivacySettingsFinancedPreference.getXmlResourcesToIndex();
+
+        verifyFinancedSearchIndexableResources(searchIndexableResources);
+    }
+
+    @Test
+    public void getPreferenceControllers() {
+        final List<AbstractPreferenceController> controllers =
+                mPrivacySettingsFinancedPreference.createPreferenceControllers(true);
+
+        verifyFinancedPreferenceControllers(controllers);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
index 775ca40..744db8c 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryUtilsTest.java
@@ -277,7 +277,7 @@
     @Test
     public void testCalculateLastFullChargeTime() {
         final long currentTimeMs = System.currentTimeMillis();
-        when(mBatteryUsageStats.getStatsStartRealtime()).thenReturn(
+        when(mBatteryUsageStats.getStatsStartTimestamp()).thenReturn(
                 currentTimeMs - TIME_SINCE_LAST_FULL_CHARGE_MS);
 
         assertThat(mBatteryUtils.calculateLastFullChargeTime(mBatteryUsageStats, currentTimeMs))
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
index 82448d1..5d7b082 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipLoaderTest.java
@@ -54,7 +54,6 @@
             BatteryTip.TipType.BATTERY_SAVER,
             BatteryTip.TipType.HIGH_DEVICE_USAGE,
             BatteryTip.TipType.LOW_BATTERY,
-            BatteryTip.TipType.SUMMARY,
             BatteryTip.TipType.SMART_BATTERY_MANAGER};
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private BatteryUsageStats mBatteryUsageStats;
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
index b68a8f5..e919288 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/batterytip/BatteryTipPreferenceControllerTest.java
@@ -20,7 +20,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
@@ -30,10 +29,6 @@
 import android.os.Bundle;
 import android.text.format.DateUtils;
 
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
 
 import com.android.internal.logging.nano.MetricsProto;
@@ -49,7 +44,6 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
@@ -104,10 +98,24 @@
     }
 
     @Test
-    public void testDisplayPreference_addSummaryTip() {
+    public void testDisplayPreference_isInvisible() {
         mBatteryTipPreferenceController.displayPreference(mPreferenceScreen);
 
-        assertOnlyContainsSummaryTip(mCardPreference);
+        assertThat(mCardPreference.isVisible()).isFalse();
+    }
+
+    @Test
+    public void testUpdateBatteryTips_tipsStateNew_isVisible() {
+        mBatteryTipPreferenceController.updateBatteryTips(mOldBatteryTips);
+
+        assertThat(mCardPreference.isVisible()).isTrue();
+    }
+
+    @Test
+    public void testUpdateBatteryTips_tipsStateInvisible_isInvisible() {
+        mBatteryTipPreferenceController.updateBatteryTips(mNewBatteryTips);
+
+        assertThat(mCardPreference.isVisible()).isFalse();
     }
 
     @Test
diff --git a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
index a1c0c8f..c6d8233 100644
--- a/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/gestures/SwipeBottomToNotificationPreferenceControllerTest.java
@@ -58,7 +58,7 @@
         mController.setChecked(true);
 
         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
-                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1)).isEqualTo(1);
+                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0)).isEqualTo(1);
     }
 
     @Test
@@ -66,7 +66,7 @@
         mController.setChecked(false);
 
         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
-                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 1)).isEqualTo(0);
+                SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, 0)).isEqualTo(0);
     }
 
     @Test
@@ -109,4 +109,13 @@
         assertThat(mController.getSummary()).isEqualTo(
                 mContext.getText(R.string.gesture_setting_off));
     }
+
+    @Test
+    public void getDefaultConfig_returnsOffState() {
+        SystemProperties.set(OneHandedEnablePreferenceController.SUPPORT_ONE_HANDED_MODE, "false");
+        Settings.Secure.resetToDefaults(mContext.getContentResolver(),
+                Settings.Secure.ONE_HANDED_MODE_ENABLED);
+
+        assertThat(mController.isChecked()).isFalse();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java
new file mode 100644
index 0000000..c707cd6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/privacy/ShowClipAccessNotificationPreferenceControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2021 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.privacy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class ShowClipAccessNotificationPreferenceControllerTest {
+
+    @Mock
+    private PreferenceScreen mScreen;
+
+    private Context mContext;
+    private ShowClipAccessNotificationPreferenceController mController;
+    private Preference mPreference;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = ApplicationProvider.getApplicationContext();
+        mController = new ShowClipAccessNotificationPreferenceController(mContext);
+        mPreference = new Preference(mContext);
+        mPreference.setKey(mController.getPreferenceKey());
+    }
+
+    @Test
+    public void isChecked_settingIsOff_shouldReturnFalse() throws Exception {
+        setProperty(0);
+
+        assertThat(mController.isChecked()).isFalse();
+    }
+
+    @Test
+    public void isChecked_settingIsOn_shouldReturnTrue() throws Exception {
+        setProperty(1);
+
+        assertThat(mController.isChecked()).isTrue();
+    }
+
+    @Test
+    public void onPreferenceChange_turnOn_shouldChangeSettingTo1() throws Exception {
+        setProperty(0);
+
+        mController.onPreferenceChange(mPreference, true);
+
+        assertThat(mController.isChecked()).isTrue();
+        assertProperty(1);
+    }
+
+    @Test
+    public void onPreferenceChange_turnOff_shouldChangeSettingTo0() throws Exception {
+        setProperty(1);
+
+        mController.onPreferenceChange(mPreference, false);
+
+        assertThat(mController.isChecked()).isFalse();
+        assertProperty(0);
+    }
+
+    private void setProperty(int newValue) {
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        Settings.Secure.putInt(
+                contentResolver, Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS, newValue);
+    }
+
+    private void assertProperty(int expectedValue) throws SettingNotFoundException {
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        assertThat(Settings.Secure.getInt(
+                contentResolver, Settings.Secure.CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS))
+                .isEqualTo(expectedValue);
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
index a48b3eb..7bd7813 100644
--- a/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/robotests/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -30,6 +30,7 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
 import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -58,6 +59,7 @@
     public final SupportFeatureProvider supportFeatureProvider;
     public final MetricsFeatureProvider metricsFeatureProvider;
     public final BatteryStatusFeatureProvider batteryStatusFeatureProvider;
+    public final BatterySettingsFeatureProvider batterySettingsFeatureProvider;
     public final PowerUsageFeatureProvider powerUsageFeatureProvider;
     public final DashboardFeatureProvider dashboardFeatureProvider;
     public final DockUpdaterFeatureProvider dockUpdaterFeatureProvider;
@@ -106,6 +108,7 @@
         supportFeatureProvider = mock(SupportFeatureProvider.class);
         metricsFeatureProvider = mock(MetricsFeatureProvider.class);
         batteryStatusFeatureProvider = mock(BatteryStatusFeatureProvider.class);
+        batterySettingsFeatureProvider = mock(BatterySettingsFeatureProvider.class);
         powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
         dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
         dockUpdaterFeatureProvider = mock(DockUpdaterFeatureProvider.class);
@@ -150,6 +153,11 @@
     }
 
     @Override
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+        return batterySettingsFeatureProvider;
+    }
+
+    @Override
     public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
         return powerUsageFeatureProvider;
     }
diff --git a/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java b/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
index e0fb578..66b5bcb 100644
--- a/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
+++ b/tests/robotests/src/com/android/settings/wifi/details2/WifiNetworkDetailsFragment2Test.java
@@ -24,9 +24,9 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.app.settings.SettingsEnums;
 import android.view.Menu;
@@ -36,12 +36,17 @@
 import androidx.preference.Preference;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settings.R;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.wifitrackerlib.NetworkDetailsTracker;
+import com.android.wifitrackerlib.WifiEntry;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
@@ -52,11 +57,22 @@
 
     final String TEST_PREFERENCE_KEY = "TEST_PREFERENCE_KEY";
 
+    @Mock
+    WifiEntry mWifiEntry;
+    @Mock
+    NetworkDetailsTracker mNetworkDetailsTracker;
+    @Mock
+    Menu mMenu;
     private WifiNetworkDetailsFragment2 mFragment;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mWifiEntry).when(mNetworkDetailsTracker).getWifiEntry();
+        doReturn(true).when(mWifiEntry).isSaved();
+
         mFragment = new WifiNetworkDetailsFragment2();
+        mFragment.mNetworkDetailsTracker = mNetworkDetailsTracker;
     }
 
     @Test
@@ -77,16 +93,24 @@
 
     @Test
     public void onCreateOptionsMenu_shouldSetCorrectIcon() {
-        final Menu menu = mock(Menu.class);
         final MenuItem menuItem = mock(MenuItem.class);
-        doReturn(menuItem).when(menu).add(anyInt(), eq(Menu.FIRST), anyInt(), anyInt());
+        doReturn(menuItem).when(mMenu).add(anyInt(), eq(Menu.FIRST), anyInt(), anyInt());
 
-        mFragment.onCreateOptionsMenu(menu, mock(MenuInflater.class));
+        mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class));
 
         verify(menuItem).setIcon(com.android.internal.R.drawable.ic_mode_edit);
     }
 
     @Test
+    public void onCreateOptionsMenu_isNotSavedNetwork_shouldNotAddEditMenu() {
+        doReturn(false).when(mWifiEntry).isSaved();
+
+        mFragment.onCreateOptionsMenu(mMenu, mock(MenuInflater.class));
+
+        verify(mMenu, never()).add(anyInt(), anyInt(), anyInt(), eq(R.string.wifi_modify));
+    }
+
+    @Test
     public void refreshPreferences_controllerShouldUpdateStateAndDisplayPreference() {
         final FakeFragment fragment = spy(new FakeFragment());
         final PreferenceScreen screen = mock(PreferenceScreen.class);
diff --git a/tests/uitests/AndroidManifest.xml b/tests/uitests/AndroidManifest.xml
index dc6fc15..1072754 100644
--- a/tests/uitests/AndroidManifest.xml
+++ b/tests/uitests/AndroidManifest.xml
@@ -26,6 +26,8 @@
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES"/>
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index b55a788..b7ac4b1 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -32,6 +32,8 @@
         "platform-test-annotations",
         "truth-prebuilt",
         "ub-uiautomator",
+        "SettingsLibSettingsSpinner",
+        "SettingsLibUsageProgressBarPreference",
     ],
 
     // Include all test java files.
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 616e6a9..7ef7ae1 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -19,6 +19,8 @@
 
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
+    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
diff --git a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
index 8dac893..22d0b29 100644
--- a/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/ReduceBrightColorsIntensityPreferenceControllerTest.java
@@ -24,7 +24,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.hardware.display.ColorDisplayManager;
 import android.provider.Settings;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -83,8 +82,8 @@
                 Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, 1);
         mPreferenceController.onPreferenceChange(/* preference= */ null, 20);
         assertThat(
-                mContext.getSystemService(
-                        ColorDisplayManager.class).getReduceBrightColorsStrength())
+                Settings.Secure.getInt(mContext.getContentResolver(),
+                        Settings.Secure.REDUCE_BRIGHT_COLORS_LEVEL, 0))
                 .isEqualTo(20);
     }
 
diff --git a/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java
new file mode 100644
index 0000000..314f8c2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/VolumeOptionMenuControllerTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+import android.view.Menu;
+
+import androidx.fragment.app.Fragment;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settings.deviceinfo.storage.StorageEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+public class VolumeOptionMenuControllerTest {
+
+    private static final String INTERNAL_VOLUME_ID = "1";
+    private static final String EXTERNAL_VOLUME_ID = "2";
+    private static final String DISK_ID = "3";
+    private static final String VOLUME_RECORD_FSUUID = "volume_record_fsuuid";
+
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private Menu mMenu;
+    @Mock private PackageManager mPackageManager;
+    @Mock private StorageManager mStorageManager;
+    @Mock private VolumeInfo mExternalVolumeInfo;
+    @Mock private VolumeInfo mInternalVolumeInfo;
+
+    private Context mContext;
+    private VolumeOptionMenuController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+
+        when(mInternalVolumeInfo.getId()).thenReturn(INTERNAL_VOLUME_ID);
+        when(mInternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+        when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+        when(mInternalVolumeInfo.isMountedWritable()).thenReturn(true);
+        when(mExternalVolumeInfo.getId()).thenReturn(EXTERNAL_VOLUME_ID);
+
+        final StorageEntry selectedStorageEntry = new StorageEntry(mContext, mInternalVolumeInfo);
+        mController = new VolumeOptionMenuController(mContext, mock(Fragment.class),
+                selectedStorageEntry);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_unSupportedDiskInfo_formatIsVisible() {
+        final StorageEntry unsupportedStorageEntry =
+                new StorageEntry(new DiskInfo(DISK_ID, 0 /* flags */));
+        mController.setSelectedStorageEntry(unsupportedStorageEntry);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mFormat, atLeastOnce()).setVisible(true);
+        verify(mController.mRename, never()).setVisible(true);
+        verify(mController.mMount, never()).setVisible(true);
+        verify(mController.mUnmount, never()).setVisible(true);
+        verify(mController.mFormatAsPortable, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mMigrate, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+        verify(mController.mForget, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_missingVolumeRecord_forgetIsVisible() {
+        final StorageEntry missingStorageEntry =
+                new StorageEntry(new VolumeRecord(0 /* type */, VOLUME_RECORD_FSUUID));
+        mController.setSelectedStorageEntry(missingStorageEntry);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mForget, atLeastOnce()).setVisible(true);
+        verify(mController.mRename, never()).setVisible(true);
+        verify(mController.mMount, never()).setVisible(true);
+        verify(mController.mUnmount, never()).setVisible(true);
+        verify(mController.mFormat, never()).setVisible(true);
+        verify(mController.mFormatAsPortable, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mMigrate, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_unmountedStorage_mountIsVisible() {
+        when(mInternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTED);
+        mController.setSelectedStorageEntry(new StorageEntry(mContext, mInternalVolumeInfo));
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mMount, atLeastOnce()).setVisible(true);
+        verify(mController.mRename, never()).setVisible(true);
+        verify(mController.mUnmount, never()).setVisible(true);
+        verify(mController.mFormat, never()).setVisible(true);
+        verify(mController.mFormatAsPortable, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mMigrate, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+        verify(mController.mForget, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_privateNotDefaultInternal_someMenusAreVisible() {
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mRename, atLeastOnce()).setVisible(true);
+        verify(mController.mUnmount, atLeastOnce()).setVisible(true);
+        verify(mController.mFormatAsPortable, atLeastOnce()).setVisible(true);
+        verify(mController.mMount, never()).setVisible(true);
+        verify(mController.mFormat, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+        verify(mController.mForget, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_privateDefaultInternal_mostMenusAreNotVisible() {
+        when(mInternalVolumeInfo.getId()).thenReturn(VolumeInfo.ID_PRIVATE_INTERNAL);
+        when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mRename, never()).setVisible(true);
+        verify(mController.mUnmount, never()).setVisible(true);
+        verify(mController.mFormatAsPortable, never()).setVisible(true);
+        verify(mController.mMount, never()).setVisible(true);
+        verify(mController.mFormat, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+        verify(mController.mForget, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_publicStorage_someMenusArcVisible() {
+        when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PUBLIC);
+        when(mExternalVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+        when(mExternalVolumeInfo.getDiskId()).thenReturn(DISK_ID);
+        final DiskInfo externalDiskInfo = mock(DiskInfo.class);
+        when(mStorageManager.findDiskById(DISK_ID)).thenReturn(externalDiskInfo);
+        mController.setSelectedStorageEntry(new StorageEntry(mContext, mExternalVolumeInfo));
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mRename, atLeastOnce()).setVisible(true);
+        verify(mController.mUnmount, atLeastOnce()).setVisible(true);
+        verify(mController.mFormat, atLeastOnce()).setVisible(true);
+        verify(mController.mMount, never()).setVisible(true);
+        verify(mController.mFormatAsPortable, never()).setVisible(true);
+        verify(mController.mFormatAsInternal, never()).setVisible(true);
+        verify(mController.mFree, never()).setVisible(true);
+        verify(mController.mForget, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_noExternalStorage_migrateNotVisible() {
+        when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mInternalVolumeInfo);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mMigrate, atLeastOnce()).setVisible(false);
+        verify(mController.mMigrate, never()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_externalPrimaryStorageAvailable_migrateIsVisible() {
+        when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+        when(mExternalVolumeInfo.isMountedWritable()).thenReturn(true);
+        when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mMigrate, atLeastOnce()).setVisible(true);
+    }
+
+    @Test
+    public void onPrepareOptionsMenu_externalUnmounted_migrateIsVisible() {
+        when(mExternalVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+        when(mExternalVolumeInfo.isMountedWritable()).thenReturn(false);
+        when(mPackageManager.getPrimaryStorageCurrentVolume()).thenReturn(mExternalVolumeInfo);
+
+        mController.onPrepareOptionsMenu(mMenu);
+
+        verify(mController.mMigrate, atLeastOnce()).setVisible(false);
+        verify(mController.mMigrate, never()).setVisible(true);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
new file mode 100644
index 0000000..cf1b6b2
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageEntryTest.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.os.storage.DiskInfo;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.os.storage.VolumeRecord;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageEntryTest {
+
+    private static final String VOLUME_INFO_ID = "volume_info_id";
+    private static final String DISK_INFO_ID = "disk_info_id";
+    private static final String VOLUME_RECORD_UUID = "volume_record_id";
+
+    @Mock
+    private VolumeInfo mVolumeInfo;
+    @Mock
+    private DiskInfo mDiskInfo;
+    @Mock
+    private VolumeRecord mVolumeRecord;
+
+    private Context mContext;
+    @Mock
+    private StorageManager mStorageManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(StorageManager.class)).thenReturn(mStorageManager);
+    }
+
+    @Test
+    public void equals_volumesOfSameId_shouldBeTheSame() {
+        final StorageEntry volumeStorage1 = new StorageEntry(mContext,
+                new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+        final StorageEntry volumeStorage2 = new StorageEntry(mContext,
+                new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+        final StorageEntry diskStorage1 =
+                new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+        final StorageEntry diskStorage2 =
+                new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+        final StorageEntry volumeRecordStorage1 = new StorageEntry(new VolumeRecord(0 /* flags */,
+                VOLUME_RECORD_UUID));
+        final StorageEntry volumeRecordStorage2 = new StorageEntry(new VolumeRecord(0 /* flags */,
+                VOLUME_RECORD_UUID));
+
+        assertThat(Objects.equals(volumeStorage1, volumeStorage2)).isTrue();
+        assertThat(Objects.equals(diskStorage1, diskStorage2)).isTrue();
+        assertThat(Objects.equals(volumeRecordStorage1, volumeRecordStorage2)).isTrue();
+    }
+
+    @Test
+    public void equals_volumesOfDifferentId_shouldBeDifferent() {
+        final StorageEntry volumeStorage1 = new StorageEntry(mContext,
+                new VolumeInfo(VOLUME_INFO_ID, 0 /* type */, null /* disk */, null /* partGuid */));
+        final StorageEntry volumeStorage2 = new StorageEntry(mContext,
+                new VolumeInfo("id2", 0 /* type */, null /* disk */, null /* partGuid */));
+        final StorageEntry diskStorage1 =
+                new StorageEntry(new DiskInfo(DISK_INFO_ID, 0 /* flags */));
+        final StorageEntry diskStorage2 =
+                new StorageEntry(new DiskInfo("id2", 0 /* flags */));
+        final StorageEntry volumeRecordStorage1 = new StorageEntry(new VolumeRecord(0 /* flags */,
+                VOLUME_RECORD_UUID));
+        final StorageEntry volumeRecordStorage2 = new StorageEntry(new VolumeRecord(0 /* flags */,
+                "id2"));
+
+        assertThat(Objects.equals(volumeStorage1, volumeStorage2)).isFalse();
+        assertThat(Objects.equals(diskStorage1, diskStorage2)).isFalse();
+        assertThat(Objects.equals(volumeRecordStorage1, volumeRecordStorage2)).isFalse();
+    }
+
+    @Test
+    public void compareTo_defaultInternalStorage_shouldBeAtTopMost() {
+        final StorageEntry storage1 = mock(StorageEntry.class);
+        when(storage1.isDefaultInternalStorage()).thenReturn(true);
+        final StorageEntry storage2 = mock(StorageEntry.class);
+        when(storage2.isDefaultInternalStorage()).thenReturn(false);
+
+        assertThat(storage1.compareTo(storage2) > 0).isTrue();
+    }
+
+    @Test
+    public void getDefaultInternalStorageEntry_shouldReturnVolumeInfoStorageOfIdPrivateInternal() {
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        when(mStorageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)).thenReturn(volumeInfo);
+
+        assertThat(StorageEntry.getDefaultInternalStorageEntry(mContext))
+                .isEqualTo(new StorageEntry(mContext, volumeInfo));
+    }
+
+    @Test
+    public void isVolumeInfo_shouldReturnTrueForVolumeInfo() {
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        final StorageEntry storage = new StorageEntry(mContext, volumeInfo);
+
+        assertThat(storage.isVolumeInfo()).isTrue();
+        assertThat(storage.isDiskInfoUnsupported()).isFalse();
+        assertThat(storage.isVolumeRecordMissed()).isFalse();
+    }
+
+    @Test
+    public void isDiskInfoUnsupported_shouldReturnTrueForDiskInfo() {
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry storage = new StorageEntry(diskInfo);
+
+        assertThat(storage.isVolumeInfo()).isFalse();
+        assertThat(storage.isDiskInfoUnsupported()).isTrue();
+        assertThat(storage.isVolumeRecordMissed()).isFalse();
+    }
+
+    @Test
+    public void isVolumeRecordMissed_shouldReturnTrueForVolumeRecord() {
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry storage = new StorageEntry(volumeRecord);
+
+        assertThat(storage.isVolumeInfo()).isFalse();
+        assertThat(storage.isDiskInfoUnsupported()).isFalse();
+        assertThat(storage.isVolumeRecordMissed()).isTrue();
+    }
+
+    @Test
+    public void isMounted_mountedOrMountedReadOnly_shouldReturnTrue() {
+        final VolumeInfo mountedVolumeInfo1 = mock(VolumeInfo.class);
+        final StorageEntry mountedStorage1 = new StorageEntry(mContext, mountedVolumeInfo1);
+        when(mountedVolumeInfo1.getState()).thenReturn(VolumeInfo.STATE_MOUNTED);
+        final VolumeInfo mountedVolumeInfo2 = mock(VolumeInfo.class);
+        when(mountedVolumeInfo2.getState()).thenReturn(VolumeInfo.STATE_MOUNTED_READ_ONLY);
+        final StorageEntry mountedStorage2 = new StorageEntry(mContext, mountedVolumeInfo2);
+
+        assertThat(mountedStorage1.isMounted()).isTrue();
+        assertThat(mountedStorage2.isMounted()).isTrue();
+    }
+
+    @Test
+    public void isMounted_nonVolumeInfo_shouldReturnFalse() {
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+        assertThat(diskStorage.isMounted()).isFalse();
+        assertThat(recordStorage2.isMounted()).isFalse();
+    }
+
+    @Test
+    public void isUnmountable_unmountableVolume_shouldReturnTrue() {
+        final VolumeInfo unmountableVolumeInfo = mock(VolumeInfo.class);
+        final StorageEntry mountedStorage = new StorageEntry(mContext, unmountableVolumeInfo);
+        when(unmountableVolumeInfo.getState()).thenReturn(VolumeInfo.STATE_UNMOUNTABLE);
+
+        assertThat(mountedStorage.isUnmountable()).isTrue();
+    }
+
+    @Test
+    public void isUnmountable_nonVolumeInfo_shouldReturnFalse() {
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+        assertThat(diskStorage.isUnmountable()).isFalse();
+        assertThat(recordStorage2.isUnmountable()).isFalse();
+    }
+
+    @Test
+    public void isPrivate_privateVolume_shouldReturnTrue() {
+        final VolumeInfo privateVolumeInfo = mock(VolumeInfo.class);
+        final StorageEntry privateStorage = new StorageEntry(mContext, privateVolumeInfo);
+        when(privateVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_PRIVATE);
+
+        assertThat(privateStorage.isPrivate()).isTrue();
+    }
+
+    @Test
+    public void isPrivate_nonVolumeInfo_shouldReturnFalse() {
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage2 = new StorageEntry(volumeRecord);
+
+        assertThat(diskStorage.isPrivate()).isFalse();
+        assertThat(recordStorage2.isPrivate()).isFalse();
+    }
+
+    @Test
+    public void getDescription_shouldReturnDescription() {
+        final String description = "description";
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        when(mStorageManager.getBestVolumeDescription(volumeInfo)).thenReturn(description);
+        final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        when(diskInfo.getDescription()).thenReturn(description);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+        when(volumeRecord.getNickname()).thenReturn(description);
+
+        assertThat(volumeStorage.getDescription()).isEqualTo(description);
+        assertThat(diskStorage.getDescription()).isEqualTo(description);
+        assertThat(recordStorage.getDescription()).isEqualTo(description);
+    }
+
+    @Test
+    public void getDiskId_shouldReturnDiskId() {
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+        when(volumeInfo.getDiskId()).thenReturn(VOLUME_INFO_ID);
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        when(diskInfo.getId()).thenReturn(DISK_INFO_ID);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+        assertThat(volumeStorage.getDiskId()).isEqualTo(VOLUME_INFO_ID);
+        assertThat(diskStorage.getDiskId()).isEqualTo(DISK_INFO_ID);
+        assertThat(recordStorage.getDiskId()).isEqualTo(null);
+    }
+
+    @Test
+    public void getFsUuid_shouldReturnFsUuid() {
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+        when(volumeInfo.getFsUuid()).thenReturn(VOLUME_INFO_ID);
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+        when(volumeRecord.getFsUuid()).thenReturn(VOLUME_RECORD_UUID);
+
+        assertThat(volumeStorage.getFsUuid()).isEqualTo(VOLUME_INFO_ID);
+        assertThat(diskStorage.getFsUuid()).isEqualTo(null);
+        assertThat(recordStorage.getFsUuid()).isEqualTo(VOLUME_RECORD_UUID);
+    }
+
+    @Test
+    public void getPath_shouldReturnPath() {
+        final File file = new File("fakePath");
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+        when(volumeInfo.getPath()).thenReturn(file);
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+        assertThat(volumeStorage.getPath()).isEqualTo(file);
+        assertThat(diskStorage.getPath()).isEqualTo(null);
+        assertThat(recordStorage.getPath()).isEqualTo(null);
+    }
+
+    @Test
+    public void getVolumeInfo_shouldVolumeInfo() {
+        final VolumeInfo volumeInfo = mock(VolumeInfo.class);
+        final StorageEntry volumeStorage = new StorageEntry(mContext, volumeInfo);
+        final DiskInfo diskInfo = mock(DiskInfo.class);
+        final StorageEntry diskStorage = new StorageEntry(diskInfo);
+        final VolumeRecord volumeRecord = mock(VolumeRecord.class);
+        final StorageEntry recordStorage = new StorageEntry(volumeRecord);
+
+        assertThat(volumeStorage.getVolumeInfo()).isEqualTo(volumeInfo);
+        assertThat(diskStorage.getVolumeInfo()).isEqualTo(null);
+        assertThat(recordStorage.getVolumeInfo()).isEqualTo(null);
+    }
+}
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java
new file mode 100644
index 0000000..86351cb
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceControllerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.os.Looper;
+import android.os.storage.StorageManager;
+
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.widget.SettingsSpinnerPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageSelectionPreferenceControllerTest {
+
+    private static final String PREFERENCE_KEY = "preference_key";
+
+    private Context mContext;
+    private StorageManager mStorageManager;
+    private StorageSelectionPreferenceController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = ApplicationProvider.getApplicationContext();
+        mStorageManager = mContext.getSystemService(StorageManager.class);
+        mController = new StorageSelectionPreferenceController(mContext, PREFERENCE_KEY);
+    }
+
+    @Test
+    public void setStorageEntries_fromStorageManager_correctAdapterItems() {
+        final List<StorageEntry> storageEntries = mStorageManager.getVolumes().stream()
+                .map(volumeInfo -> new StorageEntry(mContext, volumeInfo))
+                .collect(Collectors.toList());
+
+        mController.setStorageEntries(storageEntries);
+
+        final int adapterItemCount = mController.mStorageAdapter.getCount();
+        assertThat(adapterItemCount).isEqualTo(storageEntries.size());
+        for (int i = 0; i < adapterItemCount; i++) {
+            assertThat(storageEntries.get(i).getDescription())
+                    .isEqualTo(mController.mStorageAdapter.getItem(i).getDescription());
+        }
+    }
+
+    @Test
+    public void setSelectedStorageEntry_primaryStorage_correctSelectedAdapterItem() {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        final PreferenceScreen preferenceScreen =
+                preferenceManager.createPreferenceScreen(mContext);
+        final SettingsSpinnerPreference spinnerPreference = new SettingsSpinnerPreference(mContext);
+        spinnerPreference.setKey(PREFERENCE_KEY);
+        preferenceScreen.addPreference(spinnerPreference);
+        mController.displayPreference(preferenceScreen);
+        final StorageEntry primaryStorageEntry =
+                StorageEntry.getDefaultInternalStorageEntry(mContext);
+        mController.setStorageEntries(mStorageManager.getVolumes().stream()
+                .map(volumeInfo -> new StorageEntry(mContext, volumeInfo))
+                .collect(Collectors.toList()));
+
+        mController.setSelectedStorageEntry(primaryStorageEntry);
+
+        assertThat((StorageEntry) mController.mSpinnerPreference.getSelectedItem())
+                .isEqualTo(primaryStorageEntry);
+    }
+}
+
diff --git a/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java
new file mode 100644
index 0000000..6d9155a
--- /dev/null
+++ b/tests/unit/src/com/android/settings/deviceinfo/storage/StorageUsageProgressBarPreferenceControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2021 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.deviceinfo.storage;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.os.Looper;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.settingslib.widget.UsageProgressBarPreference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+public class StorageUsageProgressBarPreferenceControllerTest {
+
+    private static final String FAKE_UUID = "95D9-B3A4";
+    private static final long WAIT_TIMEOUT = 10_000L;
+    private static final long FREE_BYTES = 123L;
+    private static final long TOTAL_BYTES = 456L;
+    private static final long USAGE_BYTES = TOTAL_BYTES - FREE_BYTES;
+
+    private Context mContext;
+    private FakeStorageUsageProgressBarPreferenceController mController;
+    private PreferenceScreen mPreferenceScreen;
+    @Mock
+    private StorageStatsManager mStorageStatsManager;
+
+    @Before
+    public void setUp() throws Exception {
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+        MockitoAnnotations.initMocks(this);
+        mContext = spy(ApplicationProvider.getApplicationContext());
+        when(mContext.getSystemService(StorageStatsManager.class)).thenReturn(mStorageStatsManager);
+        mController = new FakeStorageUsageProgressBarPreferenceController(mContext, "key");
+        final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+        mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext);
+        final UsageProgressBarPreference usageProgressBarPreference =
+                new UsageProgressBarPreference(mContext);
+        usageProgressBarPreference.setKey(mController.getPreferenceKey());
+        mPreferenceScreen.addPreference(usageProgressBarPreference);
+    }
+
+    @Test
+    public void setSelectedStorageEntry_primaryStorage_getPrimaryStorageBytes() throws IOException {
+        final StorageEntry defaultInternalStorageEntry =
+                StorageEntry.getDefaultInternalStorageEntry(mContext);
+        when(mStorageStatsManager.getTotalBytes(defaultInternalStorageEntry.getFsUuid()))
+                .thenReturn(TOTAL_BYTES);
+        when(mStorageStatsManager.getFreeBytes(defaultInternalStorageEntry.getFsUuid()))
+                .thenReturn(FREE_BYTES);
+        mController.displayPreference(mPreferenceScreen);
+
+        synchronized (mController.mLock) {
+            mController.setSelectedStorageEntry(defaultInternalStorageEntry);
+            mController.waitUpdateState(WAIT_TIMEOUT);
+        }
+
+        assertThat(mController.mUsedBytes).isEqualTo(USAGE_BYTES);
+        assertThat(mController.mTotalBytes).isEqualTo(TOTAL_BYTES);
+    }
+
+    private class FakeStorageUsageProgressBarPreferenceController
+            extends StorageUsageProgressBarPreferenceController {
+        private final Object mLock = new Object();
+
+        FakeStorageUsageProgressBarPreferenceController(Context context, String key) {
+            super(context, key);
+        }
+
+        @Override
+        public void updateState(Preference preference) {
+            super.updateState(preference);
+            try {
+                mLock.notifyAll();
+            } catch (IllegalMonitorStateException e) {
+                // Catch it for displayPreference to prevent exception by object not locked by
+                // thread before notify. Do nothing.
+            }
+        }
+
+        public void waitUpdateState(long timeout) {
+            try {
+                mLock.wait(timeout);
+            } catch (InterruptedException e) {
+                // Do nothing.
+            }
+        }
+    }
+}
+
diff --git a/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java b/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
index 4be2ae6..9b42951 100644
--- a/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
+++ b/tests/unit/src/com/android/settings/fuelgauge/batterytip/BatteryTipPolicyTest.java
@@ -94,7 +94,7 @@
         final BatteryTipPolicy batteryTipPolicy = new BatteryTipPolicy(mContext);
 
         assertThat(batteryTipPolicy.batteryTipEnabled).isTrue();
-        assertThat(batteryTipPolicy.summaryEnabled).isTrue();
+        assertThat(batteryTipPolicy.summaryEnabled).isFalse();
         assertThat(batteryTipPolicy.batterySaverTipEnabled).isTrue();
         assertThat(batteryTipPolicy.highUsageEnabled).isTrue();
         assertThat(batteryTipPolicy.highUsageAppCount).isEqualTo(3);
diff --git a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
index ac2e24d..9c5c88b 100644
--- a/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
+++ b/tests/unit/src/com/android/settings/network/ProviderModelSliceHelperTest.java
@@ -33,6 +33,7 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.Uri;
+import android.net.wifi.WifiManager;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
@@ -90,6 +91,8 @@
     private SubscriptionInfo mDefaultDataSubscriptionInfo;
     @Mock
     private Drawable mDrawableWithSignalStrength;
+    @Mock
+    private WifiManager mWifiManager;
 
     @Before
     public void setUp() {
@@ -102,13 +105,23 @@
         when(mContext.getSystemService(CarrierConfigManager.class)).thenReturn(
                 mCarrierConfigManager);
         when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mBundle);
+        mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
         when(mContext.getSystemService(ConnectivityManager.class)).thenReturn(mConnectivityManager);
         when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
         when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
         when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+        when(mContext.getSystemService(WifiManager.class)).thenReturn(mWifiManager);
 
         TestCustomSliceable testCustomSliceable = new TestCustomSliceable();
         mProviderModelSliceHelper = new MockProviderModelSliceHelper(mContext, testCustomSliceable);
+
+        final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
+        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
+                mDefaultDataSubscriptionInfo);
+        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
+                Arrays.asList(mDefaultDataSubscriptionInfo));
     }
 
     @Test
@@ -155,20 +168,9 @@
         String expectDisplayName = "Name1";
         CharSequence expectedSubtitle = Html.fromHtml("5G", Html.FROM_HTML_MODE_LEGACY);
         String networkType = "5G";
-
-        final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
-        when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
-        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
-                mDefaultDataSubscriptionInfo);
-        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                Arrays.asList(mDefaultDataSubscriptionInfo));
-
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-        mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
+                mTelephonyManager.DATA_CONNECTED, true);
         addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-
 
         ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
                 networkType);
@@ -178,7 +180,7 @@
     }
 
     @Test
-    public void createCarrierRow_hasDdsAndActiveNetworkIsCellular_verifyTitleAndSummary() {
+    public void createCarrierRow_wifiOnhasDdsAndActiveNetworkIsCellular_verifyTitleAndSummary() {
         String expectDisplayName = "Name1";
         String networkType = "5G";
         String connectedText = ResourcesUtils.getResourcesString(mContext,
@@ -186,16 +188,27 @@
         CharSequence expectedSubtitle = Html.fromHtml(ResourcesUtils.getResourcesString(mContext,
                 "preference_summary_default_combination", connectedText, networkType),
                 Html.FROM_HTML_MODE_LEGACY);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, expectDisplayName,
+                mTelephonyManager.DATA_CONNECTED, true);
+        addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
-        final int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        when(mDefaultDataSubscriptionInfo.getSubscriptionId()).thenReturn(defaultDataSubId);
-        when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
-        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
-                mDefaultDataSubscriptionInfo);
-        when(mSubscriptionManager.getAvailableSubscriptionInfoList()).thenReturn(
-                Arrays.asList(mDefaultDataSubscriptionInfo));
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-        mBundle.putBoolean(CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL, false);
+        ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
+                networkType);
+
+        assertThat(testRowBuild.getTitle()).isEqualTo(expectDisplayName);
+        assertThat(testRowBuild.getSubtitle()).isEqualTo(expectedSubtitle);
+    }
+
+    @Test
+    public void createCarrierRow_noNetworkAvailable_verifyTitleAndSummary() {
+        String expectDisplayName = "Name1";
+        CharSequence expectedSubtitle = Html.fromHtml(
+                ResourcesUtils.getResourcesString(mContext, "mobile_data_no_connection"),
+                Html.FROM_HTML_MODE_LEGACY);
+        String networkType = "";
+
+        mockConnections(true, ServiceState.STATE_OUT_OF_SERVICE, expectDisplayName,
+                mTelephonyManager.DATA_DISCONNECTED, false);
         addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         ListBuilder.RowBuilder testRowBuild = mProviderModelSliceHelper.createCarrierRow(
@@ -207,53 +220,40 @@
 
     @Test
     public void isNoCarrierData_mobileDataOnAndNoData_returnTrue() {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, "",
+                mTelephonyManager.DATA_DISCONNECTED, true);
 
         assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
     }
 
     @Test
     public void isNoCarrierData_mobileDataOffAndOutOfService_returnTrue() {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+        mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "",
+                mTelephonyManager.DATA_DISCONNECTED, true);
 
         assertThat(mProviderModelSliceHelper.isNoCarrierData()).isTrue();
     }
 
     @Test
     public void isNoCarrierData_mobileDataOnAndDataConnected_returnFalse() {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+                true);
 
         assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
     }
 
     @Test
     public void isNoCarrierData_mobileDataOffAndVoiceIsInService_returnFalse() {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mockConnections(false, ServiceState.STATE_IN_SERVICE, "",
+                mTelephonyManager.DATA_DISCONNECTED, true);
 
         assertThat(mProviderModelSliceHelper.isNoCarrierData()).isFalse();
     }
 
     @Test
     public void getMobileDrawable_noCarrierData_getMobileDrawable() throws Throwable {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(false);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_DISCONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
-        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
-                mDefaultDataSubscriptionInfo);
+        mockConnections(false, ServiceState.STATE_OUT_OF_SERVICE, "",
+                mTelephonyManager.DATA_DISCONNECTED, true);
         when(mConnectivityManager.getActiveNetwork()).thenReturn(null);
         Drawable expectDrawable = mock(Drawable.class);
 
@@ -264,15 +264,10 @@
     @Test
     public void getMobileDrawable_hasCarrierDataAndDataIsOnCellular_getMobileDrawable()
             throws Throwable {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
-        Drawable drawable = mock(Drawable.class);
-        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
-                mDefaultDataSubscriptionInfo);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+                true);
         addNetworkTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+        Drawable drawable = mock(Drawable.class);
 
         assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
                 mDrawableWithSignalStrength);
@@ -283,14 +278,9 @@
     @Test
     public void getMobileDrawable_hasCarrierDataAndDataIsOnWifi_getMobileDrawable()
             throws Throwable {
-        when(mTelephonyManager.isDataEnabled()).thenReturn(true);
-        when(mTelephonyManager.getDataState()).thenReturn(mTelephonyManager.DATA_CONNECTED);
-        when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
-        when(mServiceState.getState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+        mockConnections(true, ServiceState.STATE_IN_SERVICE, "", mTelephonyManager.DATA_CONNECTED,
+                true);
         Drawable drawable = mock(Drawable.class);
-        int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        when(mSubscriptionManager.getActiveSubscriptionInfo(defaultDataSubId)).thenReturn(
-                mDefaultDataSubscriptionInfo);
         addNetworkTransportType(NetworkCapabilities.TRANSPORT_WIFI);
 
         assertThat(mProviderModelSliceHelper.getMobileDrawable(drawable)).isEqualTo(
@@ -303,6 +293,16 @@
                 mNetworkCapabilities);
     }
 
+    private void mockConnections(boolean isDataEnabled, int serviceState, String expectDisplayName,
+            int getDataState, boolean isWifiEnabled) {
+        when(mTelephonyManager.isDataEnabled()).thenReturn(isDataEnabled);
+        when(mWifiManager.isWifiEnabled()).thenReturn(isWifiEnabled);
+        when(mTelephonyManager.getDataState()).thenReturn(getDataState);
+
+        when(mServiceState.getState()).thenReturn(serviceState);
+        when(mDefaultDataSubscriptionInfo.getDisplayName()).thenReturn(expectDisplayName);
+    }
+
     private class TestCustomSliceable implements CustomSliceable {
         TestCustomSliceable() {
         }
diff --git a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
index 6955dc4..a90c9bf 100644
--- a/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
+++ b/tests/unit/src/com/android/settings/testutils/FakeFeatureFactory.java
@@ -28,6 +28,7 @@
 import com.android.settings.dashboard.DashboardFeatureProvider;
 import com.android.settings.dashboard.suggestions.SuggestionFeatureProvider;
 import com.android.settings.enterprise.EnterprisePrivacyFeatureProvider;
+import com.android.settings.fuelgauge.BatterySettingsFeatureProvider;
 import com.android.settings.fuelgauge.BatteryStatusFeatureProvider;
 import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
 import com.android.settings.gestures.AssistGestureFeatureProvider;
@@ -53,6 +54,7 @@
     public final SupportFeatureProvider supportFeatureProvider;
     public final MetricsFeatureProvider metricsFeatureProvider;
     public final BatteryStatusFeatureProvider batteryStatusFeatureProvider;
+    public final BatterySettingsFeatureProvider batterySettingsFeatureProvider;
     public final PowerUsageFeatureProvider powerUsageFeatureProvider;
     public final DashboardFeatureProvider dashboardFeatureProvider;
     public final DockUpdaterFeatureProvider dockUpdaterFeatureProvider;
@@ -92,6 +94,7 @@
         supportFeatureProvider = mock(SupportFeatureProvider.class);
         metricsFeatureProvider = mock(MetricsFeatureProvider.class);
         batteryStatusFeatureProvider = mock(BatteryStatusFeatureProvider.class);
+        batterySettingsFeatureProvider = mock(BatterySettingsFeatureProvider.class);
         powerUsageFeatureProvider = mock(PowerUsageFeatureProvider.class);
         dashboardFeatureProvider = mock(DashboardFeatureProvider.class);
         dockUpdaterFeatureProvider = mock(DockUpdaterFeatureProvider.class);
@@ -136,6 +139,11 @@
     }
 
     @Override
+    public BatterySettingsFeatureProvider getBatterySettingsFeatureProvider(Context context) {
+        return batterySettingsFeatureProvider;
+    }
+
+    @Override
     public PowerUsageFeatureProvider getPowerUsageFeatureProvider(Context context) {
         return powerUsageFeatureProvider;
     }