Merge "Wrap tintable icons with correctly tinted drawable XML"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2484831..af9c3b4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1415,15 +1415,31 @@
                 <category android:name="android.intent.category.VOICE_LAUNCH" />
             </intent-filter>
             <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                android:value="com.android.settings.deviceinfo.Memory" />
+                android:value="com.android.settings.deviceinfo.StorageSettings" />
             <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
                 android:resource="@id/storage_settings" />
             <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                 android:value="true" />
         </activity>
 
-        <activity android:name=".deviceinfo.MiscFilesHandler"
-                android:theme="@style/Theme.SubSettingsDialogWhenLarge"/>
+        <activity
+                android:name="Settings$StorageVolumeSettingsActivity"
+                android:label="@string/storage_settings_title"
+                android:taskAffinity="com.android.settings"
+                android:parentActivityName="Settings$StorageSettingsActivity">
+            <intent-filter>
+                <action android:name="android.provider.action.DOCUMENT_ROOT_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data
+                    android:scheme="content"
+                    android:host="com.android.externalstorage.documents"
+                    android:mimeType="vnd.android.document/root" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+                android:value="com.android.settings.deviceinfo.PublicVolumeSettings" />
+            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
+                android:resource="@id/storage_settings" />
+        </activity>
 
         <activity android:name="ApnEditor"
                 android:label="@string/apn_edit">
@@ -2059,23 +2075,6 @@
                        android:resource="@id/security_settings" />
         </activity>
 
-        <activity android:name="Settings$ConditionProviderSettingsActivity"
-                  android:label="@string/manage_condition_providers"
-                  android:taskAffinity="">
-            <intent-filter android:priority="1">
-                <action android:name="android.settings.ACTION_CONDITION_PROVIDER_SETTINGS" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
-                       android:value="com.android.settings.notification.ConditionProviderSettings" />
-            <meta-data android:name="com.android.settings.TOP_LEVEL_HEADER_ID"
-                       android:resource="@id/notification_settings" />
-        </activity>
-
         <activity android:name="Settings$NotificationSettingsActivity"
                 android:label="@string/notification_settings"
                 android:exported="true"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index be2e221..6ead46e 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -45,6 +45,7 @@
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Settings_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Settings_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/res/layout/preference_storage_action.xml b/res/layout/preference_storage_action.xml
new file mode 100644
index 0000000..10f138b
--- /dev/null
+++ b/res/layout/preference_storage_action.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:orientation="horizontal">
+
+    <TextView
+        android:id="@+id/unmount"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:paddingStart="16dip"
+        android:paddingEnd="16dip"
+        android:contentDescription="@string/storage_menu_unmount"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:textSize="30sp"
+        android:background="?android:attr/selectableItemBackground" />
+
+    <!--
+    <ImageView
+        android:id="@+id/eject"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:paddingStart="16dip"
+        android:paddingEnd="16dip"
+        android:src="@drawable/ic_sync_green_holo"
+        android:contentDescription="@string/storage_menu_eject"
+        android:layout_gravity="center"
+        android:background="?android:attr/selectableItemBackground" />
+    -->
+</LinearLayout>
diff --git a/res/menu/storage_volume.xml b/res/menu/storage_volume.xml
new file mode 100644
index 0000000..093a4bb
--- /dev/null
+++ b/res/menu/storage_volume.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/storage_rename"
+        android:title="@string/storage_menu_rename" />
+    <item
+        android:id="@+id/storage_mount"
+        android:title="@string/storage_menu_mount" />
+    <item
+        android:id="@+id/storage_unmount"
+        android:title="@string/storage_menu_unmount" />
+    <item
+        android:id="@+id/storage_format"
+        android:title="@string/storage_menu_format" />
+    <item
+        android:id="@+id/storage_usb"
+        android:title="@string/storage_menu_usb" />
+</menu>
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index 4f1eb97..16f1970 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -316,6 +316,12 @@
         <item>TTLS</item>
         <!-- Do not translate. -->
         <item>PWD</item>
+        <!-- Do not translate. -->
+        <item>SIM</item>
+        <!-- Do not translate. -->
+        <item>AKA</item>
+        <!-- Do not translate. -->
+        <item>AKA\'</item>
     </string-array>
 
    <!-- Wi-Fi WPS setup for p2p connections.  -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e300d42..a608410 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -593,6 +593,9 @@
     <!-- Button label for generic forget action [CHAR LIMIT=20] -->
     <string name="forget">Forget</string>
 
+    <!-- Button label for generic save action [CHAR LIMIT=20] -->
+    <string name="save">Save</string>
+
     <!-- Title of the Settings activity shown within the application itself. -->
     <string name="settings_label">Settings</string>
     <!-- Title of the Settings activity shown in the Launcher. [CHAR LIMIT=20] -->
@@ -1237,9 +1240,11 @@
     <!-- Message to describe "BLE scan always available feature" when Bluetooth is off. The
       place-holders "LINK_BEGIN" and "LINK_END" must NOT be translated. They mark a link to bring
       the user to "scanning settings" screen. -->
-    <string name="ble_scan_notify_text">To improve location accuracy, Google services will scan for
-        Bluetooth devices, even when Bluetooth is off. You can change this in LINK_BEGINscanning
-        settingsLINK_END.</string>
+    <string name="ble_scan_notify_text">To improve location accuracy, apps and
+        services will scan for Bluetooth devices, even when Bluetooth is off.
+        You can change this in
+        <xliff:g id="link_begin">LINK_BEGIN</xliff:g>scanning
+        settings<xliff:g id="link_end">LINK_END</xliff:g>.</string>
 
 
     <!-- Bluetooth settings.  The title of the screen to pick which profiles to connect to on the device.  For example, headphones may have both A2DP and headset, this allows the user to choose which one he wants to connect to. -->
@@ -1413,9 +1418,11 @@
     <!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off. The
       place-holders "LINK_BEGIN" and "LINK_END" must NOT be translated. They mark a link to bring
       the user to "scanning settings" screen. -->
-    <string name="wifi_scan_notify_text">To improve location accuracy, Google services will scan for
-        Wi\u2011Fi networks, even when Wi\u2011Fi is off. You can change this in LINK_BEGINscanning
-        settingsLINK_END.</string>
+    <string name="wifi_scan_notify_text">To improve location accuracy, apps and
+        services will scan for Wi\u2011Fi networks, even when Wi\u2011Fi is off.
+        You can change this in
+        <xliff:g id="link_begin">LINK_BEGIN</xliff:g>scanning
+        settings<xliff:g id="link_end">LINK_END</xliff:g>.</string>
     <!-- Wifi scan always mode checkbox text -->
     <string name="wifi_scan_notify_remember_choice">Don\'t show again</string>
     <!-- Setting title for setting the wifi sleep policy. Do we keep Wi-Fi active when the screen turns off? -->
@@ -1811,12 +1818,10 @@
     <string-array name="wifi_calling_mode_choices">
         <item>Wi-Fi preferred</item>
         <item>Cellular preferred</item>
-        <item>Wi-Fi only</item>
     </string-array>
     <string-array name="wifi_calling_mode_values">
         <item>"2"</item>
         <item>"1"</item>
-        <item>"0"</item>
     </string-array>
     <!-- Wi-Fi Calling settings. Text displayed when Wi-Fi Calling is off -->
     <string name="wifi_calling_off_explanation">When Wi-Fi calling is on, your phone can route calls via Wi-Fi networks or your carrier\'s network, depending on your preference and which signal is stronger. Before turning on this feature, check with your carrier regarding fees and other details.</string>
@@ -2183,7 +2188,7 @@
     <!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
     <string name="memory_calculating_size">Calculating\u2026</string>
     <!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of applications installed. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
-    <string name="memory_apps_usage">Apps (app data &amp; media content)</string>
+    <string name="memory_apps_usage">Apps &amp; app data</string>
     <!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of media on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
     <string name="memory_media_usage">Media</string>
     <!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of /sdcard/Download on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
@@ -2193,7 +2198,7 @@
     <!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of audio files in /sdcard on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
     <string name="memory_music_usage">Audio (music, ringtones, podcasts, etc.)</string>
     <!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of misc files on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
-    <string name="memory_media_misc_usage">Misc.</string>
+    <string name="memory_media_misc_usage">Other files</string>
     <!-- Storage item representing all cached data on device. [CHAR LIMIT=48] -->
     <string name="memory_media_cache_usage">Cached data</string>
     <!-- SD card & phone storage settings item title that will result in the phone unmounting the SD card.  This will be done before the user phyiscally removes the SD card from the phone.  Kind of like the "Safely remove" on some operating systems.   [CHAR LIMIT=25] -->
@@ -2268,6 +2273,16 @@
     <!-- Settings item summary when storage is running low [CHAR LIMIT=NONE] -->
     <string name="storage_low_summary">Some system functions, such as syncing, may not work correctly. Try to free space by deleting or unpinning items, such as apps or media content.</string>
 
+    <!-- Storage setting.  Menu option for renaming a storage device [CHAR LIMIT=30]-->
+    <string name="storage_menu_rename">Rename</string>
+    <!-- Storage setting.  Menu option for mounting a storage device [CHAR LIMIT=30]-->
+    <string name="storage_menu_mount">Mount</string>
+    <!-- Storage setting.  Menu option for unmounting a storage device [CHAR LIMIT=30]-->
+    <string name="storage_menu_unmount">Eject</string>
+    <!-- Storage setting.  Menu option for erasing and formatting a storage device [CHAR LIMIT=30]-->
+    <string name="storage_menu_format">Erase &amp; format</string>
+    <!-- Storage setting.  Menu option for erasing and formatting a storage device [CHAR LIMIT=30]-->
+    <string name="storage_menu_format_internal">Erase &amp; format as internal storage</string>
     <!-- Storage setting.  Menu option for USB transfer settings [CHAR LIMIT=30]-->
     <string name="storage_menu_usb">USB computer connection</string>
 
@@ -2291,6 +2306,32 @@
     <!-- Section header above list of other users storage [CHAR LIMIT=32] -->
     <string name="storage_other_users">Other users</string>
 
+    <!-- Section header above list of internal storage devices [CHAR LIMIT=30]-->
+    <string name="storage_internal_title">Device storage</string>
+    <!-- Section header above list of external storage devices [CHAR LIMIT=30]-->
+    <string name="storage_external_title">Removable storage</string>
+
+    <!-- Summary of a single storage volume, constrasting available and total storage space. [CHAR LIMIT=48]-->
+    <string name="storage_volume_summary"><xliff:g id="free" example="1.2GB">%1$s</xliff:g> free (Total <xliff:g id="total" example="32GB">%2$s</xliff:g>)</string>
+
+    <!-- Toast informing that storage mount operation was successful. [CHAR LIMIT=64]-->
+    <string name="storage_mount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is mounted</string>
+    <!-- Toast informing that storage unmount operation failed. [CHAR LIMIT=64]-->
+    <string name="storage_mount_failure">Couldn\'t mount <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+    <!-- Toast informing that storage unmount operation was successful. [CHAR LIMIT=64]-->
+    <string name="storage_unmount_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is safely ejected</string>
+    <!-- Toast informing that storage unmount operation failed. [CHAR LIMIT=64]-->
+    <string name="storage_unmount_failure">Couldn\'t safely eject <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+    <!-- Toast informing that storage format operation was successful. [CHAR LIMIT=64]-->
+    <string name="storage_format_success"><xliff:g id="name" example="SD card">%1$s</xliff:g> is formatted</string>
+    <!-- Toast informing that storage format operation failed. [CHAR LIMIT=64]-->
+    <string name="storage_format_failure">Couldn\'t format <xliff:g id="name" example="SD card">%1$s</xliff:g></string>
+
+    <!-- Title of dialog prompting user to rename a storage volume [CHAR LIMIT=32]-->
+    <string name="storage_rename_title">Rename storage</string>
+
     <!-- Phone info screen, section titles: -->
     <string name="battery_status_title">Battery status</string>
     <!-- Phone info screen, section titles: -->
@@ -2608,16 +2649,18 @@
     <string name="location_menu_scanning">Scanning</string>
     <!-- [CHAR LIMIT=30] Wireless background scanning settings screen, screen title -->
     <string name="location_scanning_screen_title">Scanning</string>
-    <!-- [CHAR LIMIT=130] Preference title for Wi-Fi always scanning [DO NOT TRANSLATE] -->
-    <string name="location_scanning_wifi_always_scanning_title">Wi\u2011Fi scanning always available</string>
-    <!-- Preference description text for Wi-Fi always scanning [DO NOT TRANSLATE] -->
-    <string name="location_scanning_wifi_always_scanning_description">The clear history button in
-        your browser has saved more lives than Superman</string>
-    <!-- [CHAR LIMIT=130] Description text for Bluetooth always scanning [DO NOT TRANSLATE] -->
-    <string name="location_scanning_bluetooth_always_scanning_title">Bluetooth scanning always available</string>
-    <!-- Description text for Bluetooth always scanning [DO NOT TRANSLATE] -->
-    <string name="location_scanning_bluetooth_always_scanning_description">If you want to catch a
-        squirrel just climb a tree and act like a nut</string>
+    <!-- [CHAR LIMIT=130] Preference title for Wi-Fi always scanning -->
+    <string name="location_scanning_wifi_always_scanning_title">Wi\u2011Fi scanning</string>
+    <!-- Preference description text for Wi-Fi always scanning -->
+    <string name="location_scanning_wifi_always_scanning_description">Improve
+        location by allowing apps and services to scan for Wi\u2011Fi networks
+        even when Wi\u2011Fi is off</string>
+    <!-- [CHAR LIMIT=130] Description text for Bluetooth always scanning -->
+    <string name="location_scanning_bluetooth_always_scanning_title">Bluetooth scanning</string>
+    <!-- Description text for Bluetooth always scanning -->
+    <string name="location_scanning_bluetooth_always_scanning_description">
+        Improve location by allowing system services to scan for Bluetooth
+        devices, even when Bluetooth is off</string>
 
     <!-- [CHAR LIMIT=30] Security & location settings screen, setting check box label for Google location service (cell ID, wifi, etc.) -->
     <string name="location_network_based">Wi\u2011Fi &amp; cellular network location</string>
@@ -5645,6 +5688,7 @@
     <string name="keywords_emergency_app">emergency ice app default</string>
     <string name="keywords_all_apps">apps download applications system</string>
     <string name="keywords_app_permissions">apps permissions security</string>
+    <string name="keywords_default_apps">apps default</string>
     <!-- Search keywords for different screen unlock modes : slide to unlock, password, pattern and PIN [CHAR LIMIT=none] -->
     <string name="keywords_lockscreen">slide password pattern pin</string>
 
@@ -6180,4 +6224,13 @@
         <item quantity="other"><xliff:g id="count" example="10">%d</xliff:g> apps can open their domain URLs</item>
     </plurals>
 
+    <!-- Title for Default Apps settings [CHAR LIMIT=30] -->
+    <string name="default_apps_title">Default Apps</string>
+
+    <!-- Title for Default Browser settings [CHAR LIMIT=30] -->
+    <string name="default_browser_title">Default Browser</string>
+
+    <!-- Summary for No Default Browser settings [CHAR LIMIT=45] -->
+    <string name="default_browser_title_none">No default Browser</string>
+
 </resources>
diff --git a/res/xml/advanced_apps.xml b/res/xml/advanced_apps.xml
index 682ab2d..0307a60 100644
--- a/res/xml/advanced_apps.xml
+++ b/res/xml/advanced_apps.xml
@@ -20,6 +20,12 @@
     android:key="applications_settings">
 
     <PreferenceScreen
+        android:key="default_apps"
+        android:fragment="com.android.settings.applications.ManageDefaultApps"
+        android:title="@string/default_apps_title"
+        settings:keywords="@string/keywords_default_apps" />
+
+    <PreferenceScreen
         android:key="manage_perms"
         android:fragment="com.android.settings.applications.ManagePermissions"
         android:title="@string/app_permissions"
diff --git a/res/xml/dashboard_categories.xml b/res/xml/dashboard_categories.xml
index 0d8c1ac..78fe0e7 100644
--- a/res/xml/dashboard_categories.xml
+++ b/res/xml/dashboard_categories.xml
@@ -106,7 +106,7 @@
         <dashboard-tile
                 android:id="@+id/storage_settings"
                 android:title="@string/storage_settings"
-                android:fragment="com.android.settings.deviceinfo.Memory"
+                android:fragment="com.android.settings.deviceinfo.StorageSettings"
                 android:icon="@drawable/ic_settings_storage"
                 />
 
diff --git a/res/xml/default_apps.xml b/res/xml/default_apps.xml
new file mode 100644
index 0000000..00c2fc0
--- /dev/null
+++ b/res/xml/default_apps.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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/com.android.settings"
+        android:key="default_apps">
+
+    <com.android.settings.applications.DefaultBrowserPreference
+            android:key="default_browser"
+            android:title="@string/default_browser_title"
+            android:summary="@string/default_browser_title_none"
+            />
+
+</PreferenceScreen>
diff --git a/res/xml/device_info_storage.xml b/res/xml/device_info_storage.xml
new file mode 100644
index 0000000..19c5982
--- /dev/null
+++ b/res/xml/device_info_storage.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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/com.android.settings"
+    android:title="@string/storage_settings"
+    settings:keywords="@string/keywords_storage">
+
+    <PreferenceCategory
+        android:key="storage_internal"
+        android:title="@string/storage_internal_title" />
+
+    <PreferenceCategory
+        android:key="storage_external"
+        android:title="@string/storage_external_title" />
+
+</PreferenceScreen>
diff --git a/res/xml/device_info_memory.xml b/res/xml/device_info_storage_volume.xml
similarity index 64%
rename from res/xml/device_info_memory.xml
rename to res/xml/device_info_storage_volume.xml
index 38cc94a..35435d7 100644
--- a/res/xml/device_info_memory.xml
+++ b/res/xml/device_info_storage_volume.xml
@@ -4,9 +4,9 @@
      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.
@@ -14,11 +14,10 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-                  xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
-        android:title="@string/storage_settings"
-        settings:keywords="@string/keywords_storage">
-
-<!-- Preference categories are dynamically created based on the list of available storage volumes -->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
+    android:title="@string/storage_settings"
+    settings:keywords="@string/keywords_storage">
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/FingerprintEnroll.java b/src/com/android/settings/FingerprintEnroll.java
index 96e612b..a6424e5 100644
--- a/src/com/android/settings/FingerprintEnroll.java
+++ b/src/com/android/settings/FingerprintEnroll.java
@@ -254,7 +254,7 @@
                 case EnrollingStart:
                     mEnrollmentSteps = -1;
                     long challenge = 0x12345; // TODO: get from keyguard confirmation
-                    mFingerprintManager.enroll(challenge, mEnrollmentCallback, mEnrollmentCancel,0);
+                    mFingerprintManager.enroll(challenge, mEnrollmentCancel, mEnrollmentCallback, 0);
                     mProgressBar.setProgress(0);
                     mEnrolling = true;
                     startFingerprintAnimator(); // XXX hack - this should follow fingerprint detection
diff --git a/src/com/android/settings/FingerprintSettings.java b/src/com/android/settings/FingerprintSettings.java
index c134cfb..b4d6a4f 100644
--- a/src/com/android/settings/FingerprintSettings.java
+++ b/src/com/android/settings/FingerprintSettings.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceChangeListener;
@@ -92,8 +93,10 @@
         private FingerprintManager mFingerprintManager;
         private EditText mDialogTextField;
         private PreferenceGroup mManageCategory;
+        private CancellationSignal mFingerprintCancel;
+        private int mMaxFingerprintAttempts;
+
         private AuthenticationCallback mAuthCallback = new AuthenticationCallback() {
-            int mAttempts;
             @Override
             public void onAuthenticationSucceeded(AuthenticationResult result) {
                 mHandler.obtainMessage(MSG_HIGHLIGHT_FINGERPRINT_ITEM,
@@ -101,20 +104,16 @@
                 retryFingerprint(true);
             }
 
+            public void onAuthenticationFailed() {
+                retryFingerprint(true);
+            };
+
             @Override
             public void onAuthenticationError(int errMsgId, CharSequence errString) {
                 Toast.makeText(getActivity(), errString, Toast.LENGTH_SHORT);
-                retryFingerprint(false);
-            }
-
-            private void retryFingerprint(boolean resetAttempts) {
-                if (resetAttempts) {
-                    mAttempts = 0;
+                if (errMsgId != FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+                    retryFingerprint(false);
                 }
-                if (mAttempts < MAX_RETRY_ATTEMPTS) {
-                    mFingerprintManager.authenticate(null, mAuthCallback, null, 0);
-                }
-                mAttempts++;
             }
 
             @Override
@@ -148,6 +147,24 @@
             };
         };
 
+        private void stopFingerprint() {
+            if (mFingerprintCancel != null) {
+                mFingerprintCancel.cancel();
+                mFingerprintCancel = null;
+            }
+        }
+
+        private void retryFingerprint(boolean resetAttempts) {
+            if (resetAttempts) {
+                mMaxFingerprintAttempts = 0;
+            }
+            if (mMaxFingerprintAttempts < MAX_RETRY_ATTEMPTS) {
+                mFingerprintCancel = new CancellationSignal();
+                mFingerprintManager.authenticate(null, mFingerprintCancel, mAuthCallback, 0);
+            }
+            mMaxFingerprintAttempts++;
+        }
+
         @Override
         protected int getMetricsCategory() {
             return MetricsLogger.FINGERPRINT;
@@ -158,7 +175,6 @@
             super.onCreate(savedInstanceState);
             mFingerprintManager = (FingerprintManager) getActivity().getSystemService(
                     Context.FINGERPRINT_SERVICE);
-            mFingerprintManager.authenticate(null, mAuthCallback, null, 0);
         }
 
         protected void removeFingerprintPreference(int fingerprintId) {
@@ -252,6 +268,13 @@
             // Make sure we reload the preference hierarchy since fingerprints may be added,
             // deleted or renamed.
             createPreferenceHierarchy();
+            retryFingerprint(true);
+        }
+
+        @Override
+        public void onPause() {
+            super.onPause();
+            stopFingerprint();
         }
 
         @Override
@@ -260,6 +283,7 @@
             if (KEY_FINGERPRINT_ADD.equals(key)) {
                 Intent intent = new Intent();
                 intent.setClassName("com.android.settings", FingerprintEnroll.class.getName());
+                stopFingerprint();
                 startActivityForResult(intent, ADD_FINGERPRINT_REQUEST);
             } else if (pref instanceof FingerprintPreference) {
                 FingerprintPreference fpref = (FingerprintPreference) pref;
diff --git a/src/com/android/settings/InstrumentedFragment.java b/src/com/android/settings/InstrumentedFragment.java
index 604af18..ff6f219 100644
--- a/src/com/android/settings/InstrumentedFragment.java
+++ b/src/com/android/settings/InstrumentedFragment.java
@@ -23,9 +23,11 @@
  * Instrumented fragment that logs visibility state.
  */
 public abstract class InstrumentedFragment extends PreferenceFragment {
-    // Declare new temproary categories here, starting after this value.
+    // Declare new temporary categories here, starting after this value.
     public static final int VIEW_CATEGORY_UNDECLARED = 100000;
 
+    public static final int VIEW_CATEGORY_DEFAULT_APPS = VIEW_CATEGORY_UNDECLARED + 1;
+
     /**
      * Declare the view of this category.
      *
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index b58159e..fe0df59 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -33,6 +33,7 @@
     public static class VpnSettingsActivity extends SettingsActivity { /* empty */ }
     public static class DateTimeSettingsActivity extends SettingsActivity { /* empty */ }
     public static class StorageSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class StorageVolumeSettingsActivity extends SettingsActivity { /* empty */ }
     public static class WifiSettingsActivity extends SettingsActivity { /* empty */ }
     public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
     public static class InputMethodAndLanguageSettingsActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index 24209b0..7bfd249 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -82,7 +82,8 @@
 import com.android.settings.dashboard.DashboardTile;
 import com.android.settings.dashboard.NoHomeDialogFragment;
 import com.android.settings.dashboard.SearchResultsSummary;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.PublicVolumeSettings;
+import com.android.settings.deviceinfo.StorageSettings;
 import com.android.settings.deviceinfo.UsbSettings;
 import com.android.settings.fuelgauge.BatterySaverSettings;
 import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -305,7 +306,8 @@
             CaptionPropertiesFragment.class.getName(),
             com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment.class.getName(),
             TextToSpeechSettings.class.getName(),
-            Memory.class.getName(),
+            StorageSettings.class.getName(),
+            PublicVolumeSettings.class.getName(),
             DevelopmentSettings.class.getName(),
             UsbSettings.class.getName(),
             AndroidBeam.class.getName(),
diff --git a/src/com/android/settings/applications/DefaultBrowserPreference.java b/src/com/android/settings/applications/DefaultBrowserPreference.java
new file mode 100644
index 0000000..b9ea9c0
--- /dev/null
+++ b/src/com/android/settings/applications/DefaultBrowserPreference.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.preference.ListPreference;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+
+import com.android.settings.R;
+
+import java.util.List;
+
+public class DefaultBrowserPreference extends ListPreference {
+
+    final private PackageManager mPm;
+
+    public DefaultBrowserPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mPm = context.getPackageManager();
+        loadBrowserApps();
+    }
+
+    private void loadBrowserApps() {
+        ArrayMap<String, CharSequence> browsers = resolveBrowserApps();
+
+        setEntries(browsers.values().toArray(new CharSequence[browsers.size()]));
+        setEntryValues(browsers.keySet().toArray(new String[browsers.size()]));
+    }
+
+    private ArrayMap<String, CharSequence> resolveBrowserApps() {
+        ArrayMap<String, CharSequence> result = new ArrayMap<>();
+
+        // Create an Intent that will match ALL Browser Apps
+        Intent intent = new Intent();
+        intent.setAction(Intent.ACTION_VIEW);
+        intent.addCategory(Intent.CATEGORY_BROWSABLE);
+        intent.setData(Uri.parse("http:"));
+
+        // Resolve that intent and check that the handleAllWebDataURI boolean is set
+        PackageManager packageManager = getContext().getPackageManager();
+        List<ResolveInfo> list = packageManager.queryIntentActivitiesAsUser(intent, 0,
+                UserHandle.myUserId());
+
+        final int count = list.size();
+        for (int i=0; i<count; i++) {
+            ResolveInfo info = list.get(i);
+            if (info.activityInfo == null || result.containsKey(info.activityInfo.packageName)
+                    || !info.handleAllWebDataURI) {
+                continue;
+            }
+
+            String packageName = info.activityInfo.packageName;
+            CharSequence label = info.activityInfo.applicationInfo.loadLabel(packageManager);
+
+            result.put(packageName, label);
+        }
+
+        return result;
+    }
+}
diff --git a/src/com/android/settings/applications/ManageDefaultApps.java b/src/com/android/settings/applications/ManageDefaultApps.java
new file mode 100644
index 0000000..87fc78e
--- /dev/null
+++ b/src/com/android/settings/applications/ManageDefaultApps.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.applications;
+
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import com.android.settings.InstrumentedFragment;
+import com.android.settings.SettingsPreferenceFragment;
+
+import com.android.settings.R;
+
+public class ManageDefaultApps extends SettingsPreferenceFragment
+        implements Preference.OnPreferenceClickListener {
+
+    private static final String TAG = ManageDefaultApps.class.getSimpleName();
+
+    private static final String KEY_DEFAULT_BROWSER = "default_browser";
+
+    private DefaultBrowserPreference mDefaultBrowserPreference;
+    private PackageManager mPm;
+    private int myUserId;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferencesFromResource(R.xml.default_apps);
+
+        mPm = getPackageManager();
+        myUserId = UserHandle.myUserId();
+
+        mDefaultBrowserPreference = (DefaultBrowserPreference) findPreference(KEY_DEFAULT_BROWSER);
+        mDefaultBrowserPreference.setOnPreferenceClickListener(
+                new Preference.OnPreferenceClickListener() {
+                    @Override
+                    public boolean onPreferenceClick(Preference preference) {
+                        final CharSequence packageName = mDefaultBrowserPreference.getValue();
+                        if (TextUtils.isEmpty(packageName)) {
+                            return false;
+                        }
+                        return mPm.setDefaultBrowserPackageName(packageName.toString(), myUserId);
+                    }
+        });
+    }
+
+    @Override
+    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        String packageName = getPackageManager().getDefaultBrowserPackageName(
+                UserHandle.myUserId());
+        if (!TextUtils.isEmpty(packageName)) {
+            // Check if the package is still there
+            Intent intent = new Intent();
+            intent.setPackage(packageName);
+            ResolveInfo info = mPm.resolveActivityAsUser(intent, 0, myUserId);
+            if (info != null) {
+                mDefaultBrowserPreference.setValue(packageName);
+            } else {
+                // Otherwise select the first one
+                mDefaultBrowserPreference.setValueIndex(0);
+            }
+        } else {
+            Log.d(TAG, "Cannot set empty default Browser value!");
+        }
+    }
+
+    @Override
+    protected int getMetricsCategory() {
+        return InstrumentedFragment.VIEW_CATEGORY_DEFAULT_APPS;
+    }
+
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        return false;
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java
deleted file mode 100644
index a07f7c2..0000000
--- a/src/com/android/settings/deviceinfo/Memory.java
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * Copyright (C) 2008 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.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserManager;
-import android.os.storage.IMountService;
-import android.os.storage.StorageEventListener;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.widget.Toast;
-
-import com.android.internal.logging.MetricsLogger;
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.Utils;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.search.Indexable;
-import com.android.settings.search.SearchIndexableRaw;
-import com.google.android.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Panel showing storage usage on disk for known {@link StorageVolume} returned
- * by {@link StorageManager}. Calculates and displays usage of data types.
- */
-public class Memory extends SettingsPreferenceFragment implements Indexable {
-    private static final String TAG = "MemorySettings";
-
-    private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
-
-    private static final int DLG_CONFIRM_UNMOUNT = 1;
-    private static final int DLG_ERROR_UNMOUNT = 2;
-
-    // The mountToggle Preference that has last been clicked.
-    // Assumes no two successive unmount event on 2 different volumes are performed before the first
-    // one's preference is disabled
-    private static Preference sLastClickedMountToggle;
-    private static String sClickedMountPoint;
-
-    // Access using getMountService()
-    private IMountService mMountService;
-    private StorageManager mStorageManager;
-    private UsbManager mUsbManager;
-
-    private ArrayList<StorageVolumePreferenceCategory> mCategories = Lists.newArrayList();
-
-    @Override
-    protected int getMetricsCategory() {
-        return MetricsLogger.DEVICEINFO_MEMORY;
-    }
-
-    @Override
-    public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-
-        final Context context = getActivity();
-
-        mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
-
-        mStorageManager = StorageManager.from(context);
-        mStorageManager.registerListener(mStorageListener);
-
-        addPreferencesFromResource(R.xml.device_info_memory);
-
-        addCategory(StorageVolumePreferenceCategory.buildForInternal(context));
-
-        final StorageVolume[] storageVolumes = mStorageManager.getVolumeList();
-        for (StorageVolume volume : storageVolumes) {
-            if (!volume.isEmulated()) {
-                addCategory(StorageVolumePreferenceCategory.buildForPhysical(context, volume));
-            }
-        }
-
-        setHasOptionsMenu(true);
-    }
-
-    private void addCategory(StorageVolumePreferenceCategory category) {
-        mCategories.add(category);
-        getPreferenceScreen().addPreference(category);
-        category.init();
-    }
-
-    private boolean isMassStorageEnabled() {
-        // Mass storage is enabled if primary volume supports it
-        final StorageVolume[] volumes = mStorageManager.getVolumeList();
-        final StorageVolume primary = StorageManager.getPrimaryVolume(volumes);
-        return primary != null && primary.allowMassStorage();
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED);
-        intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
-        intentFilter.addDataScheme("file");
-        getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(UsbManager.ACTION_USB_STATE);
-        getActivity().registerReceiver(mMediaScannerReceiver, intentFilter);
-
-        for (StorageVolumePreferenceCategory category : mCategories) {
-            category.onResume();
-        }
-    }
-
-    StorageEventListener mStorageListener = new StorageEventListener() {
-        @Override
-        public void onStorageStateChanged(String path, String oldState, String newState) {
-            Log.i(TAG, "Received storage state changed notification that " + path +
-                    " changed state from " + oldState + " to " + newState);
-            for (StorageVolumePreferenceCategory category : mCategories) {
-                final StorageVolume volume = category.getStorageVolume();
-                if (volume != null && path.equals(volume.getPath())) {
-                    category.onStorageStateChanged();
-                    break;
-                }
-            }
-        }
-    };
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        getActivity().unregisterReceiver(mMediaScannerReceiver);
-        for (StorageVolumePreferenceCategory category : mCategories) {
-            category.onPause();
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mStorageManager != null && mStorageListener != null) {
-            mStorageManager.unregisterListener(mStorageListener);
-        }
-        super.onDestroy();
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        inflater.inflate(R.menu.storage, menu);
-    }
-
-    @Override
-    public void onPrepareOptionsMenu(Menu menu) {
-        final MenuItem usb = menu.findItem(R.id.storage_usb);
-        UserManager um = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
-        boolean usbItemVisible = !isMassStorageEnabled()
-                && !um.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
-        usb.setVisible(usbItemVisible);
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
-            case R.id.storage_usb:
-                if (getActivity() instanceof SettingsActivity) {
-                    ((SettingsActivity) getActivity()).startPreferencePanel(
-                            UsbSettings.class.getCanonicalName(),
-                            null, R.string.storage_title_usb, null, this, 0);
-                } else {
-                    startFragment(this, UsbSettings.class.getCanonicalName(),
-                            R.string.storage_title_usb, -1, null);
-                }
-                return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
-    private synchronized IMountService getMountService() {
-       if (mMountService == null) {
-           IBinder service = ServiceManager.getService("mount");
-           if (service != null) {
-               mMountService = IMountService.Stub.asInterface(service);
-           } else {
-               Log.e(TAG, "Can't get mount service");
-           }
-       }
-       return mMountService;
-    }
-
-    @Override
-    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
-        if (StorageVolumePreferenceCategory.KEY_CACHE.equals(preference.getKey())) {
-            ConfirmClearCacheFragment.show(this);
-            return true;
-        }
-
-        for (StorageVolumePreferenceCategory category : mCategories) {
-            Intent intent = category.intentForClick(preference);
-            if (intent != null) {
-                // Don't go across app boundary if monkey is running
-                if (!Utils.isMonkeyRunning()) {
-                    try {
-                        startActivity(intent);
-                    } catch (ActivityNotFoundException anfe) {
-                        Log.w(TAG, "No activity found for intent " + intent);
-                    }
-                }
-                return true;
-            }
-
-            final StorageVolume volume = category.getStorageVolume();
-            if (volume != null && category.mountToggleClicked(preference)) {
-                sLastClickedMountToggle = preference;
-                sClickedMountPoint = volume.getPath();
-                String state = mStorageManager.getVolumeState(volume.getPath());
-                if (Environment.MEDIA_MOUNTED.equals(state) ||
-                        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
-                    unmount();
-                } else {
-                    mount();
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    private final BroadcastReceiver mMediaScannerReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action.equals(UsbManager.ACTION_USB_STATE)) {
-               boolean isUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
-               String usbFunction = mUsbManager.getDefaultFunction();
-               for (StorageVolumePreferenceCategory category : mCategories) {
-                   category.onUsbStateChanged(isUsbConnected, usbFunction);
-               }
-            } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_FINISHED)) {
-                for (StorageVolumePreferenceCategory category : mCategories) {
-                    category.onMediaScannerFinished();
-                }
-            }
-        }
-    };
-
-    @Override
-    public Dialog onCreateDialog(int id) {
-        switch (id) {
-        case DLG_CONFIRM_UNMOUNT:
-                return new AlertDialog.Builder(getActivity())
-                    .setTitle(R.string.dlg_confirm_unmount_title)
-                    .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int which) {
-                            doUnmount();
-                        }})
-                    .setNegativeButton(R.string.cancel, null)
-                    .setMessage(R.string.dlg_confirm_unmount_text)
-                    .create();
-        case DLG_ERROR_UNMOUNT:
-                return new AlertDialog.Builder(getActivity())
-            .setTitle(R.string.dlg_error_unmount_title)
-            .setNeutralButton(R.string.dlg_ok, null)
-            .setMessage(R.string.dlg_error_unmount_text)
-            .create();
-        }
-        return null;
-    }
-
-    private void doUnmount() {
-        // Present a toast here
-        Toast.makeText(getActivity(), R.string.unmount_inform_text, Toast.LENGTH_SHORT).show();
-        IMountService mountService = getMountService();
-        try {
-            sLastClickedMountToggle.setEnabled(false);
-            sLastClickedMountToggle.setTitle(getString(R.string.sd_ejecting_title));
-            sLastClickedMountToggle.setSummary(getString(R.string.sd_ejecting_summary));
-            mountService.unmountVolume(sClickedMountPoint, true, false);
-        } catch (RemoteException e) {
-            // Informative dialog to user that unmount failed.
-            showDialogInner(DLG_ERROR_UNMOUNT);
-        }
-    }
-
-    private void showDialogInner(int id) {
-        removeDialog(id);
-        showDialog(id);
-    }
-
-    private boolean hasAppsAccessingStorage() throws RemoteException {
-        IMountService mountService = getMountService();
-        int stUsers[] = mountService.getStorageUsers(sClickedMountPoint);
-        if (stUsers != null && stUsers.length > 0) {
-            return true;
-        }
-        // TODO FIXME Parameterize with mountPoint and uncomment.
-        // On HC-MR2, no apps can be installed on sd and the emulated internal storage is not
-        // removable: application cannot interfere with unmount
-        /*
-        ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
-        List<ApplicationInfo> list = am.getRunningExternalApplications();
-        if (list != null && list.size() > 0) {
-            return true;
-        }
-        */
-        // Better safe than sorry. Assume the storage is used to ask for confirmation.
-        return true;
-    }
-
-    private void unmount() {
-        // Check if external media is in use.
-        try {
-           if (hasAppsAccessingStorage()) {
-               // Present dialog to user
-               showDialogInner(DLG_CONFIRM_UNMOUNT);
-           } else {
-               doUnmount();
-           }
-        } catch (RemoteException e) {
-            // Very unlikely. But present an error dialog anyway
-            Log.e(TAG, "Is MountService running?");
-            showDialogInner(DLG_ERROR_UNMOUNT);
-        }
-    }
-
-    private void mount() {
-        IMountService mountService = getMountService();
-        try {
-            if (mountService != null) {
-                mountService.mountVolume(sClickedMountPoint);
-            } else {
-                Log.e(TAG, "Mount service is null, can't mount");
-            }
-        } catch (RemoteException ex) {
-            // Not much can be done
-        }
-    }
-
-    private void onCacheCleared() {
-        for (StorageVolumePreferenceCategory category : mCategories) {
-            category.onCacheCleared();
-        }
-    }
-
-    private static class ClearCacheObserver extends IPackageDataObserver.Stub {
-        private final Memory mTarget;
-        private int mRemaining;
-
-        public ClearCacheObserver(Memory target, int remaining) {
-            mTarget = target;
-            mRemaining = remaining;
-        }
-
-        @Override
-        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
-            synchronized (this) {
-                if (--mRemaining == 0) {
-                    mTarget.onCacheCleared();
-                }
-            }
-        }
-    }
-
-    /**
-     * Dialog to request user confirmation before clearing all cache data.
-     */
-    public static class ConfirmClearCacheFragment extends DialogFragment {
-        public static void show(Memory parent) {
-            if (!parent.isAdded()) return;
-
-            final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
-            dialog.setTargetFragment(parent, 0);
-            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
-        }
-
-        @Override
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            final Context context = getActivity();
-
-            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-            builder.setTitle(R.string.memory_clear_cache_title);
-            builder.setMessage(getString(R.string.memory_clear_cache_message));
-
-            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    final Memory target = (Memory) getTargetFragment();
-                    final PackageManager pm = context.getPackageManager();
-                    final List<PackageInfo> infos = pm.getInstalledPackages(0);
-                    final ClearCacheObserver observer = new ClearCacheObserver(
-                            target, infos.size());
-                    for (PackageInfo info : infos) {
-                        pm.deleteApplicationCacheFiles(info.packageName, observer);
-                    }
-                }
-            });
-            builder.setNegativeButton(android.R.string.cancel, null);
-
-            return builder.create();
-        }
-    }
-
-    /**
-     * Enable indexing of searchable data
-     */
-    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
-        new BaseSearchIndexProvider() {
-            @Override
-            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
-                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
-
-                SearchIndexableRaw data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.storage_settings);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.internal_storage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                final StorageVolume[] storageVolumes = StorageManager.from(context).getVolumeList();
-                for (StorageVolume volume : storageVolumes) {
-                    if (!volume.isEmulated()) {
-                        data.title = volume.getDescription(context);
-                        data.screenTitle = context.getString(R.string.storage_settings);
-                        result.add(data);
-                    }
-                }
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_size);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_available);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_apps_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_dcim_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_music_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_downloads_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_media_cache_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                data = new SearchIndexableRaw(context);
-                data.title = context.getString(R.string.memory_media_misc_usage);
-                data.screenTitle = context.getString(R.string.storage_settings);
-                result.add(data);
-
-                return result;
-            }
-        };
-
-}
diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java
deleted file mode 100644
index 93e352b..0000000
--- a/src/com/android/settings/deviceinfo/MiscFilesHandler.java
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (C) 2010 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.Activity;
-import android.app.ListActivity;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.storage.StorageVolume;
-import android.text.format.Formatter;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ListView;
-
-import com.android.settings.R;
-import com.android.settings.deviceinfo.StorageMeasurement.FileInfo;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class handles the selection and removal of Misc files.
- */
-public class MiscFilesHandler extends ListActivity {
-    private static final String TAG = "MemorySettings";
-    private String mNumSelectedFormat;
-    private String mNumBytesSelectedFormat;
-    private MemoryMearurementAdapter mAdapter;
-    private LayoutInflater mInflater;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setFinishOnTouchOutside(true);
-        setTitle(R.string.misc_files);
-        mNumSelectedFormat = getString(R.string.misc_files_selected_count);
-        mNumBytesSelectedFormat = getString(R.string.misc_files_selected_count_bytes);
-        mAdapter = new MemoryMearurementAdapter(this);
-        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        setContentView(R.layout.settings_storage_miscfiles_list);
-        ListView lv = getListView();
-        lv.setItemsCanFocus(true);
-        lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
-        lv.setMultiChoiceModeListener(new ModeCallback(this));
-        setListAdapter(mAdapter);
-    } 
-
-    private class ModeCallback implements ListView.MultiChoiceModeListener {
-        private int mDataCount;
-        private final Context mContext;
-
-        public ModeCallback(Context context) {
-            mContext = context;
-            mDataCount = mAdapter.getCount();
-        }
-
-        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-            final MenuInflater inflater = getMenuInflater();
-            inflater.inflate(R.menu.misc_files_menu, menu);
-            return true;
-        }
-
-        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-            return true;
-        }
-
-        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            ListView lv = getListView();
-            switch (item.getItemId()) {
-            case R.id.action_delete:
-                // delete the files selected
-                SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
-                int checkedCount = getListView().getCheckedItemCount();
-                if (checkedCount > mDataCount) {
-                    throw new IllegalStateException("checked item counts do not match. " +
-                            "checkedCount: " + checkedCount + ", dataSize: " + mDataCount);
-                }
-                if (mDataCount > 0) {
-                    ArrayList<Object> toRemove = new ArrayList<Object>();
-                    for (int i = 0; i < mDataCount; i++) {
-                        if (!checkedItems.get(i)) {
-                            //item not selected
-                            continue;
-                        }
-                        if (StorageMeasurement.LOGV) {
-                            Log.i(TAG, "deleting: " + mAdapter.getItem(i));
-                        }
-                        // delete the file
-                        File file = new File(mAdapter.getItem(i).mFileName);
-                        if (file.isDirectory()) {
-                            deleteDir(file);
-                        } else {
-                            file.delete();                            
-                        }
-                        toRemove.add(mAdapter.getItem(i));
-                    }
-                    mAdapter.removeAll(toRemove);
-                    mAdapter.notifyDataSetChanged();
-                    mDataCount = mAdapter.getCount();
-                }
-                mode.finish();
-                break;
-
-            case R.id.action_select_all:
-                // check ALL items
-                for (int i = 0; i < mDataCount; i++) {
-                    lv.setItemChecked(i, true);
-                }
-                // update the title and subtitle with number selected and numberBytes selected
-                onItemCheckedStateChanged(mode, 1, 0, true);
-                break;
-            }
-            return true;
-        }
-
-        // Deletes all files and subdirectories under given dir.
-        // Returns true if all deletions were successful.
-        // If a deletion fails, the method stops attempting to delete and returns false.
-        private boolean deleteDir(File dir) {
-            String[] children = dir.list();
-            if (children != null) {
-                for (int i=0; i < children.length; i++) {
-                    boolean success = deleteDir(new File(dir, children[i]));
-                    if (!success) {
-                        return false;
-                    }
-                }
-            }
-            // The directory is now empty so delete it
-            return dir.delete();
-        }
-
-        public void onDestroyActionMode(ActionMode mode) {
-            // This block intentionally left blank
-        }
-
-        public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
-                boolean checked) {
-            ListView lv = getListView();
-            int numChecked = lv.getCheckedItemCount();
-            mode.setTitle(String.format(mNumSelectedFormat, numChecked, mAdapter.getCount()));
-
-            // total the sizes of all items selected so far
-            SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
-            long selectedDataSize = 0;
-            if (numChecked > 0) {
-                for (int i = 0; i < mDataCount; i++) {
-                    if (checkedItems.get(i)) {
-                        // item is checked
-                        selectedDataSize += mAdapter.getItem(i).mSize;
-                    }
-                }
-            }
-            mode.setSubtitle(String.format(mNumBytesSelectedFormat,
-                    Formatter.formatFileSize(mContext, selectedDataSize),
-                    Formatter.formatFileSize(mContext, mAdapter.getDataSize())));
-        }
-    }
-
-    class MemoryMearurementAdapter extends BaseAdapter {
-        private ArrayList<StorageMeasurement.FileInfo> mData = null;
-        private long mDataSize = 0;
-        private Context mContext;
-
-        public MemoryMearurementAdapter(Activity activity) {
-            mContext = activity;
-            final StorageVolume storageVolume = activity.getIntent().getParcelableExtra(
-                    StorageVolume.EXTRA_STORAGE_VOLUME);
-            StorageMeasurement mMeasurement = StorageMeasurement.getInstance(
-                    activity, storageVolume);
-            if (mMeasurement == null) return;
-            mData = (ArrayList<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
-            if (mData != null) {
-                for (StorageMeasurement.FileInfo info : mData) {
-                    mDataSize += info.mSize;
-                }
-            }
-        }
-
-        @Override
-        public int getCount() {
-            return (mData == null) ? 0 : mData.size();
-        }
-
-        @Override
-        public StorageMeasurement.FileInfo getItem(int position) {
-            if (mData == null || mData.size() <= position) {
-                return null;
-            }
-            return mData.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            if (mData == null || mData.size() <= position) {
-                return 0;
-            }
-            return mData.get(position).mId;
-        }
-
-        public void removeAll(List<Object> objs) {
-            if (mData == null) {
-                return;
-            }
-            for (Object o : objs) {
-                mData.remove(o);
-                mDataSize -= ((StorageMeasurement.FileInfo) o).mSize;
-            }
-        }
-
-        public long getDataSize() {
-            return mDataSize;
-        }
-
-        @Override
-        public void notifyDataSetChanged() {
-            super.notifyDataSetChanged();
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            final FileItemInfoLayout view = (convertView == null) ?
-                    (FileItemInfoLayout) mInflater.inflate(R.layout.settings_storage_miscfiles,
-                            parent, false) : (FileItemInfoLayout) convertView;
-            FileInfo item = getItem(position);
-            view.setFileName(item.mFileName);
-            view.setFileSize(Formatter.formatFileSize(mContext, item.mSize));
-            final ListView listView = (ListView) parent;
-            final int listPosition = position;
-            view.getCheckBox().setOnCheckedChangeListener(new OnCheckedChangeListener() {
-                
-                @Override
-                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-                    listView.setItemChecked(listPosition, isChecked);
-                }
-                
-            });
-            view.setOnLongClickListener(new OnLongClickListener() {
-                @Override
-                public boolean onLongClick(View v) {
-                    if (listView.getCheckedItemCount() > 0) {
-                        return false;
-                    }
-                    listView.setItemChecked(listPosition, !view.isChecked());
-                    return true;
-                }
-            });
-            view.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    if (listView.getCheckedItemCount() > 0) {
-                        listView.setItemChecked(listPosition, !view.isChecked());
-                    }
-                }
-            });
-            return view;
-        }
-    }
-}
diff --git a/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
new file mode 100644
index 0000000..efb9a07
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/PrivateVolumeSettings.java
@@ -0,0 +1,548 @@
+/*
+ * Copyright (C) 2015 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.android.settings.deviceinfo.StorageSettings.EXTRA_VOLUME_ID;
+import static com.android.settings.deviceinfo.StorageSettings.TAG;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.DownloadManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.text.format.Formatter;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+import com.android.settings.Settings;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
+import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
+import com.android.settings.deviceinfo.StorageSettings.FormatTask;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+import com.google.android.collect.Lists;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PRIVATE}
+ * storage volume.
+ */
+public class PrivateVolumeSettings extends SettingsPreferenceFragment {
+    // TODO: disable unmount when providing over MTP/PTP
+
+    private static final String TAG_RENAME = "rename";
+    private static final String TAG_CONFIRM_CLEAR_CACHE = "confirmClearCache";
+
+    private StorageManager mStorageManager;
+    private UserManager mUserManager;
+
+    private VolumeInfo mVolume;
+    private VolumeInfo mSharedVolume;
+
+    private StorageMeasurement mMeasure;
+
+    private UserInfo mCurrentUser;
+
+    private int mNextOrder = 0;
+
+    private UsageBarPreference mGraph;
+    private StorageItemPreference mTotal;
+    private StorageItemPreference mAvailable;
+    private StorageItemPreference mApps;
+    private StorageItemPreference mDcim;
+    private StorageItemPreference mMusic;
+    private StorageItemPreference mDownloads;
+    private StorageItemPreference mCache;
+    private StorageItemPreference mMisc;
+    private List<StorageItemPreference> mUsers = Lists.newArrayList();
+
+    private long mTotalSize;
+    private long mAvailSize;
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.DEVICEINFO_STORAGE;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Context context = getActivity();
+
+        mUserManager = context.getSystemService(UserManager.class);
+        mStorageManager = context.getSystemService(StorageManager.class);
+
+        final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+        mVolume = Preconditions.checkNotNull(mStorageManager.findVolumeById(volId));
+        Preconditions.checkState(mVolume.type == VolumeInfo.TYPE_PRIVATE);
+
+        addPreferencesFromResource(R.xml.device_info_storage_volume);
+
+        // Find the emulated shared storage layered above this private volume
+        mSharedVolume = mStorageManager.findVolumeById(
+                mVolume.id.replace("private", "emulated"));
+
+        mMeasure = new StorageMeasurement(context, mVolume, mSharedVolume);
+        mMeasure.setReceiver(mReceiver);
+
+        mGraph = buildGraph();
+        mTotal = buildItem(R.string.memory_size, 0);
+        mAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
+
+        mApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
+        mDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
+        mMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
+        mDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
+        mCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
+        mMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
+
+        mCurrentUser = mUserManager.getUserInfo(UserHandle.myUserId());
+        final List<UserInfo> otherUsers = getUsersExcluding(mCurrentUser);
+        for (int i = 0; i < otherUsers.size(); i++) {
+            final UserInfo user = otherUsers.get(i);
+            final int colorRes = i % 2 == 0 ? R.color.memory_user_light
+                    : R.color.memory_user_dark;
+            final StorageItemPreference userPref = new StorageItemPreference(
+                    context, user.name, colorRes, user.id);
+            mUsers.add(userPref);
+        }
+
+        setHasOptionsMenu(true);
+    }
+
+    public void refresh() {
+        getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume.id));
+
+        // Valid options may have changed
+        getFragmentManager().invalidateOptionsMenu();
+
+        final Context context = getActivity();
+        final PreferenceScreen screen = getPreferenceScreen();
+
+        screen.removeAll();
+
+        if (mVolume.state != VolumeInfo.STATE_MOUNTED) {
+            return;
+        }
+
+        screen.addPreference(mGraph);
+        screen.addPreference(mTotal);
+        screen.addPreference(mAvailable);
+
+        final boolean showUsers = !mUsers.isEmpty();
+        if (showUsers) {
+            screen.addPreference(new PreferenceHeader(context, mCurrentUser.name));
+        }
+
+        screen.addPreference(mApps);
+        screen.addPreference(mDcim);
+        screen.addPreference(mMusic);
+        screen.addPreference(mDownloads);
+        screen.addPreference(mCache);
+        screen.addPreference(mMisc);
+
+        if (showUsers) {
+            screen.addPreference(new PreferenceHeader(context, R.string.storage_other_users));
+            for (Preference pref : mUsers) {
+                screen.addPreference(pref);
+            }
+        }
+
+        for (int i = 0; i < screen.getPreferenceCount(); i++) {
+            final Preference pref = screen.getPreference(i);
+            if (pref instanceof StorageItemPreference) {
+                ((StorageItemPreference) pref).setLoading();
+            }
+        }
+
+        final File file = new File(mVolume.path);
+        mTotalSize = file.getTotalSpace();
+        mAvailSize = file.getFreeSpace();
+
+        mTotal.setSummary(Formatter.formatFileSize(context, mTotalSize));
+        mAvailable.setSummary(Formatter.formatFileSize(context, mAvailSize));
+
+        mGraph.clear();
+        mGraph.addEntry(0, (mTotalSize - mAvailSize) / (float) mTotalSize,
+                android.graphics.Color.GRAY);
+        mGraph.commit();
+
+        mMeasure.forceMeasure();
+    }
+
+    private UsageBarPreference buildGraph() {
+        final UsageBarPreference pref = new UsageBarPreference(getActivity());
+        pref.setOrder(mNextOrder++);
+        return pref;
+    }
+
+    private StorageItemPreference buildItem(int titleRes, int colorRes) {
+        final StorageItemPreference pref = new StorageItemPreference(getActivity(), titleRes,
+                colorRes);
+        pref.setOrder(mNextOrder++);
+        return pref;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mStorageManager.registerListener(mStorageListener);
+        refresh();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mStorageManager.unregisterListener(mStorageListener);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mMeasure.onDestroy();
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.storage_volume, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        final MenuItem rename = menu.findItem(R.id.storage_rename);
+        final MenuItem mount = menu.findItem(R.id.storage_mount);
+        final MenuItem unmount = menu.findItem(R.id.storage_unmount);
+        final MenuItem format = menu.findItem(R.id.storage_format);
+        final MenuItem usb = menu.findItem(R.id.storage_usb);
+
+        // Actions live in menu for non-internal private volumes; they're shown
+        // as preference items for public volumes.
+        if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolume.id)) {
+            rename.setVisible(false);
+            mount.setVisible(false);
+            unmount.setVisible(false);
+            format.setVisible(false);
+        } else {
+            rename.setVisible(mVolume.type == VolumeInfo.TYPE_PRIVATE);
+            mount.setVisible(mVolume.state == VolumeInfo.STATE_UNMOUNTED);
+            unmount.setVisible(mVolume.state == VolumeInfo.STATE_MOUNTED);
+            format.setVisible(true);
+        }
+
+        // TODO: show usb if we jumped past first screen
+        usb.setVisible(false);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final Context context = getActivity();
+        switch (item.getItemId()) {
+            case R.id.storage_rename:
+                RenameFragment.show(this);
+                return true;
+            case R.id.storage_mount:
+                new MountTask(context, mVolume.id).execute();
+                return true;
+            case R.id.storage_unmount:
+                new UnmountTask(context, mVolume.id).execute();
+                return true;
+            case R.id.storage_format:
+                new FormatTask(context, mVolume.id).execute();
+                return true;
+            case R.id.storage_usb:
+                startFragment(this, UsbSettings.class.getCanonicalName(),
+                        R.string.storage_title_usb, 0, null);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+        // TODO: launch better intents for specific volume
+
+        Intent intent = null;
+        if (pref == mApps) {
+            intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
+            intent.setClass(getActivity(), Settings.ManageApplicationsActivity.class);
+
+        } else if (pref == mDownloads) {
+            intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
+                    DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
+
+        } else if (pref == mMusic) {
+            intent = new Intent(Intent.ACTION_GET_CONTENT);
+            intent.setType("audio/mp3");
+
+        } else if (pref == mDcim) {
+            intent = new Intent(Intent.ACTION_VIEW);
+            intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+            intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
+
+        } else if (pref == mCache) {
+            ConfirmClearCacheFragment.show(this);
+            return true;
+
+        } else if (pref == mMisc) {
+            intent = StorageSettings.buildBrowseIntent(mSharedVolume);
+        }
+
+        if (intent != null) {
+            try {
+                startActivity(intent);
+            } catch (ActivityNotFoundException e) {
+                Log.w(TAG, "No activity found for " + intent);
+            }
+            return true;
+        }
+        return super.onPreferenceTreeClick(preferenceScreen, pref);
+    }
+
+    private final MeasurementReceiver mReceiver = new MeasurementReceiver() {
+        @Override
+        public void onDetailsChanged(MeasurementDetails details) {
+            updateDetails(details);
+        }
+    };
+
+    private void updateDetails(MeasurementDetails details) {
+        mGraph.clear();
+
+        updatePreference(mApps, details.appsSize);
+
+        final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
+                Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
+        updatePreference(mDcim, dcimSize);
+
+        final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
+                Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
+                Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
+        updatePreference(mMusic, musicSize);
+
+        final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
+        updatePreference(mDownloads, downloadsSize);
+
+        updatePreference(mCache, details.cacheSize);
+        updatePreference(mMisc, details.miscSize);
+
+        for (StorageItemPreference userPref : mUsers) {
+            final long userSize = details.usersSize.get(userPref.userHandle);
+            updatePreference(userPref, userSize);
+        }
+
+        mGraph.commit();
+    }
+
+    private void updatePreference(StorageItemPreference pref, long size) {
+        pref.setSummary(Formatter.formatFileSize(getActivity(), size));
+        if (size > 0) {
+            final int order = pref.getOrder();
+            mGraph.addEntry(order, size / (float) mTotalSize, pref.color);
+        }
+    }
+
+    /**
+     * Return list of other users, excluding the current user.
+     */
+    private List<UserInfo> getUsersExcluding(UserInfo excluding) {
+        final List<UserInfo> users = mUserManager.getUsers();
+        final Iterator<UserInfo> i = users.iterator();
+        while (i.hasNext()) {
+            if (i.next().id == excluding.id) {
+                i.remove();
+            }
+        }
+        return users;
+    }
+
+    private static long totalValues(HashMap<String, Long> map, String... keys) {
+        long total = 0;
+        for (String key : keys) {
+            if (map.containsKey(key)) {
+                total += map.get(key);
+            }
+        }
+        return total;
+    }
+
+    private final StorageEventListener mStorageListener = new StorageEventListener() {
+        @Override
+        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+            if (Objects.equals(mVolume.id, vol.id)) {
+                mVolume = vol;
+                refresh();
+            }
+        }
+    };
+
+    /**
+     * Dialog that allows editing of volume nickname.
+     */
+    public static class RenameFragment extends DialogFragment {
+        public static void show(PrivateVolumeSettings parent) {
+            if (!parent.isAdded()) return;
+
+            final RenameFragment dialog = new RenameFragment();
+            dialog.setTargetFragment(parent, 0);
+            dialog.setArguments(parent.getArguments());
+            dialog.show(parent.getFragmentManager(), TAG_RENAME);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+            final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+            final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+            final VolumeInfo vol = storageManager.findVolumeById(volId);
+
+            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);
+
+            if (!TextUtils.isEmpty(vol.nickname)) {
+                nickname.setText(vol.nickname);
+            } else {
+                nickname.setText(storageManager.getBestVolumeDescription(volId));
+            }
+
+            builder.setTitle(R.string.storage_rename_title);
+            builder.setView(view);
+
+            builder.setPositiveButton(R.string.save,
+                    new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int which) {
+                            // TODO: persist the edited nickname!
+                        }
+                    });
+            builder.setNegativeButton(R.string.cancel, null);
+
+            return builder.create();
+        }
+    }
+
+    /**
+     * Dialog to request user confirmation before clearing all cache data.
+     */
+    public static class ConfirmClearCacheFragment extends DialogFragment {
+        public static void show(PrivateVolumeSettings parent) {
+            if (!parent.isAdded()) return;
+
+            final ConfirmClearCacheFragment dialog = new ConfirmClearCacheFragment();
+            dialog.setTargetFragment(parent, 0);
+            dialog.show(parent.getFragmentManager(), TAG_CONFIRM_CLEAR_CACHE);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Context context = getActivity();
+
+            final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+            builder.setTitle(R.string.memory_clear_cache_title);
+            builder.setMessage(getString(R.string.memory_clear_cache_message));
+
+            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialog, int which) {
+                    final PrivateVolumeSettings target = (PrivateVolumeSettings) getTargetFragment();
+                    final PackageManager pm = context.getPackageManager();
+                    final List<PackageInfo> infos = pm.getInstalledPackages(0);
+                    final ClearCacheObserver observer = new ClearCacheObserver(
+                            target, infos.size());
+                    for (PackageInfo info : infos) {
+                        pm.deleteApplicationCacheFiles(info.packageName, observer);
+                    }
+                }
+            });
+            builder.setNegativeButton(android.R.string.cancel, null);
+
+            return builder.create();
+        }
+    }
+
+    private static class ClearCacheObserver extends IPackageDataObserver.Stub {
+        private final PrivateVolumeSettings mTarget;
+        private int mRemaining;
+
+        public ClearCacheObserver(PrivateVolumeSettings target, int remaining) {
+            mTarget = target;
+            mRemaining = remaining;
+        }
+
+        @Override
+        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
+            synchronized (this) {
+                if (--mRemaining == 0) {
+                    mTarget.refresh();
+                }
+            }
+        }
+    }
+
+    public static class PreferenceHeader extends Preference {
+        public PreferenceHeader(Context context, int titleRes) {
+            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
+            setTitle(titleRes);
+        }
+
+        public PreferenceHeader(Context context, CharSequence title) {
+            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
+            setTitle(title);
+        }
+
+        @Override
+        public boolean isEnabled() {
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/PublicVolumeSettings.java b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
new file mode 100644
index 0000000..6edfc92
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/PublicVolumeSettings.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2015 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.android.settings.deviceinfo.StorageSettings.EXTRA_VOLUME_ID;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.provider.DocumentsContract;
+import android.text.format.Formatter;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.Preconditions;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.deviceinfo.StorageSettings.FormatTask;
+import com.android.settings.deviceinfo.StorageSettings.MountTask;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+
+import java.io.File;
+import java.util.Objects;
+
+/**
+ * Panel showing summary and actions for a {@link VolumeInfo#TYPE_PUBLIC}
+ * storage volume.
+ */
+public class PublicVolumeSettings extends SettingsPreferenceFragment {
+    // TODO: disable unmount when providing over MTP/PTP
+
+    private static final String PREF_FORMAT_INTERNAL = "debug.format_internal";
+
+    private StorageManager mStorageManager;
+
+    private VolumeInfo mVolume;
+
+    private int mNextOrder = 0;
+
+    private UsageBarPreference mGraph;
+    private StorageItemPreference mTotal;
+    private StorageItemPreference mAvailable;
+
+    private Preference mMount;
+    private Preference mUnmount;
+    private Preference mFormat;
+    private Preference mFormatInternal;
+
+    private long mTotalSize;
+    private long mAvailSize;
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.DEVICEINFO_STORAGE;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Context context = getActivity();
+
+        mStorageManager = context.getSystemService(StorageManager.class);
+
+        if (DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS.equals(
+                getActivity().getIntent().getAction())) {
+            final Uri rootUri = getActivity().getIntent().getData();
+            final String fsUuid = DocumentsContract.getRootId(rootUri);
+            mVolume = mStorageManager.findVolumeByUuid(fsUuid);
+        } else {
+            final String volId = getArguments().getString(EXTRA_VOLUME_ID);
+            mVolume = mStorageManager.findVolumeById(volId);
+        }
+
+        Preconditions.checkNotNull(mVolume);
+        Preconditions.checkState(mVolume.type == VolumeInfo.TYPE_PUBLIC);
+
+        addPreferencesFromResource(R.xml.device_info_storage_volume);
+
+        mGraph = buildGraph();
+        mTotal = buildItem(R.string.memory_size, 0);
+        mAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
+
+        mMount = buildAction(R.string.storage_menu_mount);
+        mUnmount = buildAction(R.string.storage_menu_unmount);
+        mFormat = buildAction(R.string.storage_menu_format);
+        mFormatInternal = buildAction(R.string.storage_menu_format_internal);
+    }
+
+    public void refresh() {
+        getActivity().setTitle(mStorageManager.getBestVolumeDescription(mVolume.id));
+
+        final Context context = getActivity();
+        final PreferenceScreen screen = getPreferenceScreen();
+
+        screen.removeAll();
+
+        if (mVolume.state == VolumeInfo.STATE_MOUNTED) {
+            screen.addPreference(mGraph);
+            screen.addPreference(mTotal);
+            screen.addPreference(mAvailable);
+        }
+
+        if (mVolume.state == VolumeInfo.STATE_UNMOUNTED) {
+            screen.addPreference(mMount);
+        }
+        if (mVolume.state == VolumeInfo.STATE_MOUNTED) {
+            screen.addPreference(mUnmount);
+        }
+        screen.addPreference(mFormat);
+        if (SystemProperties.getBoolean(PREF_FORMAT_INTERNAL, false)) {
+            screen.addPreference(mFormatInternal);
+        }
+
+        final File file = new File(mVolume.path);
+        mTotalSize = file.getTotalSpace();
+        mAvailSize = file.getFreeSpace();
+
+        mTotal.setSummary(Formatter.formatFileSize(context, mTotalSize));
+        mAvailable.setSummary(Formatter.formatFileSize(context, mAvailSize));
+
+        mGraph.clear();
+        mGraph.addEntry(0, (mTotalSize - mAvailSize) / (float) mTotalSize,
+                android.graphics.Color.GRAY);
+        mGraph.commit();
+    }
+
+    private UsageBarPreference buildGraph() {
+        final UsageBarPreference pref = new UsageBarPreference(getActivity());
+        pref.setOrder(mNextOrder++);
+        return pref;
+    }
+
+    private StorageItemPreference buildItem(int titleRes, int colorRes) {
+        final StorageItemPreference pref = new StorageItemPreference(getActivity(), titleRes,
+                colorRes);
+        pref.setOrder(mNextOrder++);
+        return pref;
+    }
+
+    private Preference buildAction(int titleRes) {
+        final Preference pref = new Preference(getActivity());
+        pref.setTitle(titleRes);
+        pref.setOrder(mNextOrder++);
+        return pref;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mStorageManager.registerListener(mStorageListener);
+        refresh();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mStorageManager.unregisterListener(mStorageListener);
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+        final Context context = getActivity();
+        if (pref == mMount) {
+            new MountTask(context, mVolume.id).execute();
+        } else if (pref == mUnmount) {
+            new UnmountTask(context, mVolume.id).execute();
+        } else if (pref == mFormat) {
+            new FormatTask(context, mVolume.id).execute();
+        } else if (pref == mFormatInternal) {
+            // TODO: implement this
+        }
+
+        return super.onPreferenceTreeClick(preferenceScreen, pref);
+    }
+
+    private final StorageEventListener mStorageListener = new StorageEventListener() {
+        @Override
+        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+            if (Objects.equals(mVolume.id, vol.id)) {
+                mVolume = vol;
+                refresh();
+            }
+        }
+    };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index 87e827e..8d48cf0 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -62,4 +62,8 @@
         shape.getPaint().setColor(color);
         return shape;
     }
+
+    public void setLoading() {
+        setSummary(R.string.memory_calculating_size);
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/StorageMeasurement.java b/src/com/android/settings/deviceinfo/StorageMeasurement.java
index 34ef62b..db91fdb 100644
--- a/src/com/android/settings/deviceinfo/StorageMeasurement.java
+++ b/src/com/android/settings/deviceinfo/StorageMeasurement.java
@@ -27,7 +27,6 @@
 import android.content.pm.PackageStats;
 import android.content.pm.UserInfo;
 import android.os.Environment;
-import android.os.Environment.UserEnvironment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -36,23 +35,22 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
 import android.util.Log;
 import android.util.SparseLongArray;
 
 import com.android.internal.app.IMediaContainerService;
-import com.google.android.collect.Maps;
+import com.android.internal.util.ArrayUtils;
 import com.google.android.collect.Sets;
 
 import java.io.File;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
-import javax.annotation.concurrent.GuardedBy;
-
 /**
  * Utility for measuring the disk usage of internal storage or a physical
  * {@link StorageVolume}. Connects with a remote {@link IMediaContainerService}
@@ -77,28 +75,7 @@
             Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS,
             Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_ANDROID);
 
-    @GuardedBy("sInstances")
-    private static HashMap<StorageVolume, StorageMeasurement> sInstances = Maps.newHashMap();
-
-    /**
-     * Obtain shared instance of {@link StorageMeasurement} for given physical
-     * {@link StorageVolume}, or internal storage if {@code null}.
-     */
-    public static StorageMeasurement getInstance(Context context, StorageVolume volume) {
-        synchronized (sInstances) {
-            StorageMeasurement value = sInstances.get(volume);
-            if (value == null) {
-                value = new StorageMeasurement(context.getApplicationContext(), volume);
-                sInstances.put(volume, value);
-            }
-            return value;
-        }
-    }
-
     public static class MeasurementDetails {
-        public long totalSize;
-        public long availSize;
-
         /**
          * Total apps disk usage.
          * <p>
@@ -128,7 +105,7 @@
          * When measuring a physical {@link StorageVolume}, this reflects media
          * on that volume.
          */
-        public HashMap<String, Long> mediaSize = Maps.newHashMap();
+        public HashMap<String, Long> mediaSize = new HashMap<>();
 
         /**
          * Misc external disk usage for the current user, unaccounted in
@@ -144,34 +121,31 @@
     }
 
     public interface MeasurementReceiver {
-        public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize);
-        public void updateDetails(StorageMeasurement meas, MeasurementDetails details);
+        public void onDetailsChanged(MeasurementDetails details);
     }
 
-    private volatile WeakReference<MeasurementReceiver> mReceiver;
+    private WeakReference<MeasurementReceiver> mReceiver;
 
-    /** Physical volume being measured, or {@code null} for internal. */
-    private final StorageVolume mVolume;
+    private final Context mContext;
 
-    private final boolean mIsInternal;
-    private final boolean mIsPrimary;
+    private final VolumeInfo mVolume;
+    private final VolumeInfo mSharedVolume;
 
-    private final MeasurementHandler mHandler;
+    private final MainHandler mMainHandler;
+    private final MeasurementHandler mMeasurementHandler;
 
-    private long mTotalSize;
-    private long mAvailSize;
+    public StorageMeasurement(Context context, VolumeInfo volume, VolumeInfo sharedVolume) {
+        mContext = context.getApplicationContext();
 
-    List<FileInfo> mFileInfoForMisc;
-
-    private StorageMeasurement(Context context, StorageVolume volume) {
         mVolume = volume;
-        mIsInternal = volume == null;
-        mIsPrimary = volume != null ? volume.isPrimary() : false;
+        mSharedVolume = sharedVolume;
 
         // Start the thread that will measure the disk usage.
         final HandlerThread handlerThread = new HandlerThread("MemoryMeasurement");
         handlerThread.start();
-        mHandler = new MeasurementHandler(context, handlerThread.getLooper());
+
+        mMainHandler = new MainHandler();
+        mMeasurementHandler = new MeasurementHandler(handlerThread.getLooper());
     }
 
     public void setReceiver(MeasurementReceiver receiver) {
@@ -180,52 +154,38 @@
         }
     }
 
+    public void forceMeasure() {
+        invalidate();
+        measure();
+    }
+
     public void measure() {
-        if (!mHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
-            mHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
+        if (!mMeasurementHandler.hasMessages(MeasurementHandler.MSG_MEASURE)) {
+            mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_MEASURE);
         }
     }
 
-    public void cleanUp() {
+    public void onDestroy() {
         mReceiver = null;
-        mHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
-        mHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
+        mMeasurementHandler.removeMessages(MeasurementHandler.MSG_MEASURE);
+        mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_DISCONNECT);
     }
 
-    public void invalidate() {
-        mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
-    }
-
-    private void sendInternalApproximateUpdate() {
-        MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
-        if (receiver == null) {
-            return;
-        }
-        receiver.updateApproximate(this, mTotalSize, mAvailSize);
-    }
-
-    private void sendExactUpdate(MeasurementDetails details) {
-        MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
-        if (receiver == null) {
-            if (LOGV) {
-                Log.i(TAG, "measurements dropped because receiver is null! wasted effort");
-            }
-            return;
-        }
-        receiver.updateDetails(this, details);
+    private void invalidate() {
+        mMeasurementHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE);
     }
 
     private static class StatsObserver extends IPackageStatsObserver.Stub {
-        private final boolean mIsInternal;
+        private final boolean mIsPrivate;
         private final MeasurementDetails mDetails;
         private final int mCurrentUser;
         private final Message mFinished;
 
         private int mRemaining;
 
-        public StatsObserver(boolean isInternal, MeasurementDetails details, int currentUser,
+        public StatsObserver(boolean isPrivate, MeasurementDetails details, int currentUser,
                 Message finished, int remaining) {
-            mIsInternal = isInternal;
+            mIsPrivate = isPrivate;
             mDetails = details;
             mCurrentUser = currentUser;
             mFinished = finished;
@@ -245,7 +205,7 @@
         }
 
         private void addStatsLocked(PackageStats stats) {
-            if (mIsInternal) {
+            if (mIsPrivate) {
                 long codeSize = stats.codeSize;
                 long dataSize = stats.dataSize;
                 long cacheSize = stats.cacheSize;
@@ -279,6 +239,17 @@
         }
     }
 
+    private class MainHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            final MeasurementDetails details = (MeasurementDetails) msg.obj;
+            final MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null;
+            if (receiver != null) {
+                receiver.onDetailsChanged(details);
+            }
+        }
+    }
+
     private class MeasurementHandler extends Handler {
         public static final int MSG_MEASURE = 1;
         public static final int MSG_CONNECTED = 2;
@@ -294,8 +265,6 @@
 
         private MeasurementDetails mCached;
 
-        private final WeakReference<Context> mContext;
-
         private final ServiceConnection mDefContainerConn = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
@@ -313,9 +282,8 @@
             }
         };
 
-        public MeasurementHandler(Context context, Looper looper) {
+        public MeasurementHandler(Looper looper) {
             super(looper);
-            mContext = new WeakReference<Context>(context);
         }
 
         @Override
@@ -323,50 +291,39 @@
             switch (msg.what) {
                 case MSG_MEASURE: {
                     if (mCached != null) {
-                        sendExactUpdate(mCached);
+                        mMainHandler.obtainMessage(0, mCached).sendToTarget();
                         break;
                     }
 
-                    final Context context = (mContext != null) ? mContext.get() : null;
-                    if (context == null) {
-                        return;
-                    }
-
                     synchronized (mLock) {
                         if (mBound) {
                             removeMessages(MSG_DISCONNECT);
                             sendMessage(obtainMessage(MSG_CONNECTED, mDefaultContainer));
                         } else {
                             Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
-                            context.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE,
-                                    UserHandle.OWNER);
+                            mContext.bindServiceAsUser(service, mDefContainerConn,
+                                    Context.BIND_AUTO_CREATE, UserHandle.OWNER);
                         }
                     }
                     break;
                 }
                 case MSG_CONNECTED: {
-                    IMediaContainerService imcs = (IMediaContainerService) msg.obj;
-                    measureApproximateStorage(imcs);
+                    final IMediaContainerService imcs = (IMediaContainerService) msg.obj;
                     measureExactStorage(imcs);
                     break;
                 }
                 case MSG_DISCONNECT: {
                     synchronized (mLock) {
                         if (mBound) {
-                            final Context context = (mContext != null) ? mContext.get() : null;
-                            if (context == null) {
-                                return;
-                            }
-
                             mBound = false;
-                            context.unbindService(mDefContainerConn);
+                            mContext.unbindService(mDefContainerConn);
                         }
                     }
                     break;
                 }
                 case MSG_COMPLETED: {
                     mCached = (MeasurementDetails) msg.obj;
-                    sendExactUpdate(mCached);
+                    mMainHandler.obtainMessage(0, mCached).sendToTarget();
                     break;
                 }
                 case MSG_INVALIDATE: {
@@ -375,88 +332,75 @@
                 }
             }
         }
+    }
 
-        private void measureApproximateStorage(IMediaContainerService imcs) {
-            final String path = mVolume != null ? mVolume.getPath()
-                    : Environment.getDataDirectory().getPath();
-            try {
-                final long[] stats = imcs.getFileSystemStats(path);
-                mTotalSize = stats[0];
-                mAvailSize = stats[1];
-            } catch (Exception e) {
-                Log.w(TAG, "Problem in container service", e);
-            }
+    private void measureExactStorage(IMediaContainerService imcs) {
+        final UserManager userManager = mContext.getSystemService(UserManager.class);
+        final PackageManager packageManager = mContext.getPackageManager();
 
-            sendInternalApproximateUpdate();
-        }
+        final List<UserInfo> users = userManager.getUsers();
+        final int currentUser = ActivityManager.getCurrentUser();
 
-        private void measureExactStorage(IMediaContainerService imcs) {
-            final Context context = mContext != null ? mContext.get() : null;
-            if (context == null) {
-                return;
-            }
+        final MeasurementDetails details = new MeasurementDetails();
+        final Message finished = mMeasurementHandler.obtainMessage(MeasurementHandler.MSG_COMPLETED,
+                details);
 
-            final MeasurementDetails details = new MeasurementDetails();
-            final Message finished = obtainMessage(MSG_COMPLETED, details);
-
-            details.totalSize = mTotalSize;
-            details.availSize = mAvailSize;
-
-            final UserManager userManager = (UserManager) context.getSystemService(
-                    Context.USER_SERVICE);
-            final List<UserInfo> users = userManager.getUsers();
-
-            final int currentUser = ActivityManager.getCurrentUser();
-            final UserEnvironment currentEnv = new UserEnvironment(currentUser);
+        if (mSharedVolume != null && mSharedVolume.state == VolumeInfo.STATE_MOUNTED) {
+            final File basePath = mSharedVolume.getPathForUser(currentUser);
 
             // Measure media types for emulated storage, or for primary physical
             // external volume
-            final boolean measureMedia = (mIsInternal && Environment.isExternalStorageEmulated())
-                    || mIsPrimary;
-            if (measureMedia) {
-                for (String type : sMeasureMediaTypes) {
-                    final File path = currentEnv.getExternalStoragePublicDirectory(type);
-                    final long size = getDirectorySize(imcs, path);
-                    details.mediaSize.put(type, size);
-                }
+            for (String type : sMeasureMediaTypes) {
+                final File path = new File(basePath, type);
+                final long size = getDirectorySize(imcs, path);
+                details.mediaSize.put(type, size);
             }
 
             // Measure misc files not counted under media
-            if (measureMedia) {
-                final File path = mIsInternal ? currentEnv.getExternalStorageDirectory()
-                        : mVolume.getPathFile();
-                details.miscSize = measureMisc(imcs, path);
-            }
+            details.miscSize = measureMisc(imcs, basePath);
 
-            // Measure total emulated storage of all users; internal apps data
-            // will be spliced in later
-            for (UserInfo user : users) {
-                final UserEnvironment userEnv = new UserEnvironment(user.id);
-                final long size = getDirectorySize(imcs, userEnv.getExternalStorageDirectory());
-                addValue(details.usersSize, user.id, size);
-            }
-
-            // Measure all apps for all users
-            final PackageManager pm = context.getPackageManager();
-            if (mIsInternal || mIsPrimary) {
-                final List<ApplicationInfo> apps = pm.getInstalledApplications(
-                        PackageManager.GET_UNINSTALLED_PACKAGES
-                        | PackageManager.GET_DISABLED_COMPONENTS);
-
-                final int count = users.size() * apps.size();
-                final StatsObserver observer = new StatsObserver(
-                        mIsInternal, details, currentUser, finished, count);
-
+            if (mSharedVolume.type == VolumeInfo.TYPE_EMULATED) {
+                // Measure total emulated storage of all users; internal apps data
+                // will be spliced in later
                 for (UserInfo user : users) {
-                    for (ApplicationInfo app : apps) {
-                        pm.getPackageSizeInfo(app.packageName, user.id, observer);
-                    }
+                    final File userPath = mSharedVolume.getPathForUser(user.id);
+                    final long size = getDirectorySize(imcs, userPath);
+                    addValue(details.usersSize, user.id, size);
                 }
-
-            } else {
-                finished.sendToTarget();
             }
         }
+
+        // Measure all apps hosted on this volume for all users
+        if (mVolume.type == VolumeInfo.TYPE_PRIVATE) {
+            final List<ApplicationInfo> apps = packageManager.getInstalledApplications(
+                    PackageManager.GET_UNINSTALLED_PACKAGES
+                    | PackageManager.GET_DISABLED_COMPONENTS);
+
+            final List<ApplicationInfo> volumeApps = new ArrayList<>();
+            for (ApplicationInfo app : apps) {
+                if (Objects.equals(app.volumeUuid, mVolume.fsUuid)) {
+                    volumeApps.add(app);
+                }
+            }
+
+            final int count = users.size() * volumeApps.size();
+            if (count == 0) {
+                finished.sendToTarget();
+                return;
+            }
+
+            final StatsObserver observer = new StatsObserver(
+                    true, details, currentUser, finished, count);
+            for (UserInfo user : users) {
+                for (ApplicationInfo app : volumeApps) {
+                    packageManager.getPackageSizeInfo(app.packageName, user.id, observer);
+                }
+            }
+
+        } else {
+            finished.sendToTarget();
+            return;
+        }
     }
 
     private static long getDirectorySize(IMediaContainerService imcs, File path) {
@@ -471,64 +415,26 @@
     }
 
     private long measureMisc(IMediaContainerService imcs, File dir) {
-        mFileInfoForMisc = new ArrayList<FileInfo>();
-
         final File[] files = dir.listFiles();
-        if (files == null) return 0;
+        if (ArrayUtils.isEmpty(files)) return 0;
 
         // Get sizes of all top level nodes except the ones already computed
-        long counter = 0;
         long miscSize = 0;
-
         for (File file : files) {
-            final String path = file.getAbsolutePath();
             final String name = file.getName();
             if (sMeasureMediaTypes.contains(name)) {
                 continue;
             }
 
             if (file.isFile()) {
-                final long fileSize = file.length();
-                mFileInfoForMisc.add(new FileInfo(path, fileSize, counter++));
-                miscSize += fileSize;
+                miscSize += file.length();
             } else if (file.isDirectory()) {
-                final long dirSize = getDirectorySize(imcs, file);
-                mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++));
-                miscSize += dirSize;
-            } else {
-                // Non directory, non file: not listed
+                miscSize += getDirectorySize(imcs, file);
             }
         }
-
-        // sort the list of FileInfo objects collected above in descending order of their sizes
-        Collections.sort(mFileInfoForMisc);
-
         return miscSize;
     }
 
-    static class FileInfo implements Comparable<FileInfo> {
-        final String mFileName;
-        final long mSize;
-        final long mId;
-
-        FileInfo(String fileName, long size, long id) {
-            mFileName = fileName;
-            mSize = size;
-            mId = id;
-        }
-
-        @Override
-        public int compareTo(FileInfo that) {
-            if (this == that || mSize == that.mSize) return 0;
-            else return (mSize < that.mSize) ? 1 : -1; // for descending sort
-        }
-
-        @Override
-        public String toString() {
-            return mFileName  + " : " + mSize + ", id:" + mId;
-        }
-    }
-
     private static void addValue(SparseLongArray array, int key, long value) {
         array.put(key, array.get(key) + value);
     }
diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
new file mode 100644
index 0000000..001f00d
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/StorageSettings.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2015 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.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.os.storage.StorageEventListener;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.provider.DocumentsContract;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.settings.R;
+import com.android.settings.SettingsPreferenceFragment;
+import com.android.settings.search.BaseSearchIndexProvider;
+import com.android.settings.search.Indexable;
+import com.android.settings.search.SearchIndexableRaw;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Panel showing both internal storage (both built-in storage and private
+ * volumes) and removable storage (public volumes).
+ */
+public class StorageSettings extends SettingsPreferenceFragment implements Indexable {
+    static final String TAG = "StorageSettings";
+
+    // TODO: badging to indicate devices running low on storage
+    // TODO: show currently ejected private volumes
+
+    public static final String EXTRA_VOLUME_ID = "volume_id";
+
+    private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
+    private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
+
+    /**
+     * Build an intent to browse the contents of given {@link VolumeInfo}.
+     */
+    public static Intent buildBrowseIntent(VolumeInfo vol) {
+        final Uri uri;
+        if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+            uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, vol.fsUuid);
+        } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id)) {
+            uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
+                    DOCUMENT_ROOT_PRIMARY_EMULATED);
+        } else if (vol.type == VolumeInfo.TYPE_EMULATED) {
+            // TODO: build intent once supported
+            uri = null;
+        } else {
+            throw new IllegalArgumentException();
+        }
+
+        final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+        intent.addCategory(Intent.CATEGORY_DEFAULT);
+        intent.setData(uri);
+        return intent;
+    }
+
+    private UserManager mUserManager;
+    private StorageManager mStorageManager;
+
+    private PreferenceCategory mInternalCategory;
+    private PreferenceCategory mExternalCategory;
+
+    @Override
+    protected int getMetricsCategory() {
+        return MetricsLogger.DEVICEINFO_STORAGE;
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        final Context context = getActivity();
+
+        mUserManager = context.getSystemService(UserManager.class);
+
+        mStorageManager = context.getSystemService(StorageManager.class);
+        mStorageManager.registerListener(mStorageListener);
+
+        addPreferencesFromResource(R.xml.device_info_storage);
+
+        mInternalCategory = (PreferenceCategory) findPreference("storage_internal");
+        mExternalCategory = (PreferenceCategory) findPreference("storage_external");
+
+        // TODO: if only one volume visible, shortcut into it
+
+        setHasOptionsMenu(true);
+    }
+
+    private static final Comparator<VolumeInfo> sVolumeComparator = new Comparator<VolumeInfo>() {
+        @Override
+        public int compare(VolumeInfo lhs, VolumeInfo rhs) {
+            if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.id)) {
+                return -1;
+            } else if (lhs.getDescription() == null) {
+                return 1;
+            } else {
+                return lhs.getDescription().compareTo(rhs.getDescription());
+            }
+        }
+    };
+
+    private final StorageEventListener mStorageListener = new StorageEventListener() {
+        @Override
+        public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
+            if (isInteresting(vol)) {
+                refresh();
+            }
+        }
+    };
+
+    private static boolean isInteresting(VolumeInfo vol) {
+        return vol.type == VolumeInfo.TYPE_PRIVATE || vol.type == VolumeInfo.TYPE_PUBLIC;
+    }
+
+    private void refresh() {
+        final Context context = getActivity();
+
+        getPreferenceScreen().removeAll();
+        mInternalCategory.removeAll();
+        mExternalCategory.removeAll();
+
+        final List<VolumeInfo> volumes = mStorageManager.getVolumes();
+        Collections.sort(volumes, sVolumeComparator);
+
+        for (VolumeInfo vol : volumes) {
+            if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+                mInternalCategory.addPreference(new StorageVolumePreference(context, vol));
+            } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+                mExternalCategory.addPreference(new StorageVolumePreference(context, vol));
+            }
+        }
+
+        if (mInternalCategory.getPreferenceCount() > 0) {
+            getPreferenceScreen().addPreference(mInternalCategory);
+        }
+        if (mExternalCategory.getPreferenceCount() > 0) {
+            getPreferenceScreen().addPreference(mExternalCategory);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mStorageManager.registerListener(mStorageListener);
+        refresh();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mStorageManager.unregisterListener(mStorageListener);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        inflater.inflate(R.menu.storage, menu);
+    }
+
+    @Override
+    public void onPrepareOptionsMenu(Menu menu) {
+        final MenuItem usb = menu.findItem(R.id.storage_usb);
+
+        usb.setVisible(!mUserManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER));
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.storage_usb:
+                startFragment(this, UsbSettings.class.getCanonicalName(),
+                        R.string.storage_title_usb, 0, null);
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference pref) {
+        final String volId = pref.getKey();
+        final VolumeInfo vol = mStorageManager.findVolumeById(volId);
+        if (vol == null) {
+            return false;
+
+        } else if (vol.type == VolumeInfo.TYPE_PRIVATE) {
+            final Bundle args = new Bundle();
+            args.putString(EXTRA_VOLUME_ID, volId);
+            startFragment(this, PrivateVolumeSettings.class.getCanonicalName(),
+                    -1, 0, args);
+            return true;
+
+        } else if (vol.type == VolumeInfo.TYPE_PUBLIC) {
+            if (vol.state == VolumeInfo.STATE_MOUNTED) {
+                final Intent intent = buildBrowseIntent(vol);
+                startActivity(intent);
+                return true;
+            } else {
+                final Bundle args = new Bundle();
+                args.putString(EXTRA_VOLUME_ID, volId);
+                startFragment(this, PublicVolumeSettings.class.getCanonicalName(),
+                        -1, 0, args);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static class MountTask extends AsyncTask<Void, Void, Exception> {
+        private final Context mContext;
+        private final StorageManager mStorageManager;
+        private final String mVolumeId;
+        private final String mDescription;
+
+        public MountTask(Context context, String volumeId) {
+            mContext = context.getApplicationContext();
+            mStorageManager = mContext.getSystemService(StorageManager.class);
+            mVolumeId = volumeId;
+            mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+        }
+
+        @Override
+        protected Exception doInBackground(Void... params) {
+            try {
+                mStorageManager.mount(mVolumeId);
+                return null;
+            } catch (Exception e) {
+                return e;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Exception e) {
+            if (e == null) {
+                Toast.makeText(mContext, mContext.getString(R.string.storage_mount_success,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            } else {
+                Log.e(TAG, "Failed to mount " + mVolumeId, e);
+                Toast.makeText(mContext, mContext.getString(R.string.storage_mount_failure,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+
+    public static class UnmountTask extends AsyncTask<Void, Void, Exception> {
+        private final Context mContext;
+        private final StorageManager mStorageManager;
+        private final String mVolumeId;
+        private final String mDescription;
+
+        public UnmountTask(Context context, String volumeId) {
+            mContext = context.getApplicationContext();
+            mStorageManager = mContext.getSystemService(StorageManager.class);
+            mVolumeId = volumeId;
+            mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+        }
+
+        @Override
+        protected Exception doInBackground(Void... params) {
+            try {
+                mStorageManager.unmount(mVolumeId);
+                return null;
+            } catch (Exception e) {
+                return e;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Exception e) {
+            if (e == null) {
+                Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_success,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            } else {
+                Log.e(TAG, "Failed to unmount " + mVolumeId, e);
+                Toast.makeText(mContext, mContext.getString(R.string.storage_unmount_failure,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+
+    public static class FormatTask extends AsyncTask<Void, Void, Exception> {
+        private final Context mContext;
+        private final StorageManager mStorageManager;
+        private final String mVolumeId;
+        private final String mDescription;
+
+        public FormatTask(Context context, String volumeId) {
+            mContext = context.getApplicationContext();
+            mStorageManager = mContext.getSystemService(StorageManager.class);
+            mVolumeId = volumeId;
+            mDescription = mStorageManager.getBestVolumeDescription(mVolumeId);
+        }
+
+        @Override
+        protected Exception doInBackground(Void... params) {
+            try {
+                mStorageManager.format(mVolumeId);
+                mStorageManager.mount(mVolumeId);
+                return null;
+            } catch (Exception e) {
+                return e;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Exception e) {
+            if (e == null) {
+                Toast.makeText(mContext, mContext.getString(R.string.storage_format_success,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            } else {
+                Log.e(TAG, "Failed to format " + mVolumeId, e);
+                Toast.makeText(mContext, mContext.getString(R.string.storage_format_failure,
+                        mDescription), Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+
+    /**
+     * Enable indexing of searchable data
+     */
+    public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
+        new BaseSearchIndexProvider() {
+            @Override
+            public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
+                final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
+
+                SearchIndexableRaw data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.storage_settings);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.internal_storage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                final StorageManager storage = context.getSystemService(StorageManager.class);
+                final List<VolumeInfo> vols = storage.getVolumes();
+                for (VolumeInfo vol : vols) {
+                    if (isInteresting(vol)) {
+                        data.title = storage.getBestVolumeDescription(vol.id);
+                        data.screenTitle = context.getString(R.string.storage_settings);
+                        result.add(data);
+                    }
+                }
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_size);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_available);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_apps_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_dcim_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_music_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_downloads_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_media_cache_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                data = new SearchIndexableRaw(context);
+                data.title = context.getString(R.string.memory_media_misc_usage);
+                data.screenTitle = context.getString(R.string.storage_settings);
+                result.add(data);
+
+                return result;
+            }
+        };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
new file mode 100644
index 0000000..fbe34f6
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 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.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.preference.Preference;
+import android.text.format.Formatter;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.TextView;
+
+import com.android.settings.R;
+import com.android.settings.deviceinfo.StorageSettings.UnmountTask;
+
+import java.io.File;
+
+/**
+ * Preference line representing a single {@link VolumeInfo}, possibly including
+ * quick actions like unmounting.
+ */
+public class StorageVolumePreference extends Preference {
+    private final StorageManager mStorageManager;
+    private final VolumeInfo mVolume;
+
+    public StorageVolumePreference(Context context, VolumeInfo volume) {
+        super(context);
+
+        mStorageManager = context.getSystemService(StorageManager.class);
+        mVolume = volume;
+
+        setKey(volume.id);
+        setTitle(mStorageManager.getBestVolumeDescription(volume.id));
+
+        switch (volume.state) {
+            case VolumeInfo.STATE_MOUNTED:
+                // TODO: move statfs() to background thread
+                final File path = new File(volume.path);
+                final String free = Formatter.formatFileSize(context, path.getFreeSpace());
+                final String total = Formatter.formatFileSize(context, path.getTotalSpace());
+                setSummary(context.getString(R.string.storage_volume_summary, free, total));
+                break;
+        }
+
+        // TODO: better icons
+        if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(volume.id)) {
+            setIcon(context.getDrawable(R.drawable.ic_settings_storage));
+        } else {
+            setIcon(context.getDrawable(R.drawable.ic_sim_sd));
+        }
+
+        if (volume.type == VolumeInfo.TYPE_PUBLIC && volume.state == VolumeInfo.STATE_MOUNTED) {
+            setWidgetLayoutResource(R.layout.preference_storage_action);
+        }
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        final TextView unmount = (TextView) view.findViewById(R.id.unmount);
+        if (unmount != null) {
+            unmount.setText("\u23CF");
+            unmount.setOnClickListener(mUnmountListener);
+        }
+
+        super.onBindView(view);
+    }
+
+    private final View.OnClickListener mUnmountListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            new UnmountTask(getContext(), mVolume.id).execute();
+        }
+    };
+}
diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java b/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
deleted file mode 100644
index a98f8d9..0000000
--- a/src/com/android/settings/deviceinfo/StorageVolumePreferenceCategory.java
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
- * Copyright (C) 2011 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.ActivityManagerNative;
-import android.app.ActivityThread;
-import android.app.DownloadManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.IPackageManager;
-import android.content.pm.UserInfo;
-import android.content.res.Resources;
-import android.hardware.usb.UsbManager;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserManager;
-import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
-import android.preference.Preference;
-import android.preference.PreferenceCategory;
-import android.provider.MediaStore;
-import android.text.format.Formatter;
-
-import com.android.settings.R;
-import com.android.settings.Settings;
-import com.android.settings.deviceinfo.StorageMeasurement.MeasurementDetails;
-import com.android.settings.deviceinfo.StorageMeasurement.MeasurementReceiver;
-import com.google.android.collect.Lists;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class StorageVolumePreferenceCategory extends PreferenceCategory {
-    public static final String KEY_CACHE = "cache";
-
-    private static final int ORDER_USAGE_BAR = -2;
-    private static final int ORDER_STORAGE_LOW = -1;
-
-    /** Physical volume being measured, or {@code null} for internal. */
-    private final StorageVolume mVolume;
-    private final StorageMeasurement mMeasure;
-
-    private final Resources mResources;
-    private final StorageManager mStorageManager;
-    private final UserManager mUserManager;
-
-    private UsageBarPreference mUsageBarPreference;
-    private Preference mMountTogglePreference;
-    private Preference mFormatPreference;
-    private Preference mStorageLow;
-
-    private StorageItemPreference mItemTotal;
-    private StorageItemPreference mItemAvailable;
-    private StorageItemPreference mItemApps;
-    private StorageItemPreference mItemDcim;
-    private StorageItemPreference mItemMusic;
-    private StorageItemPreference mItemDownloads;
-    private StorageItemPreference mItemCache;
-    private StorageItemPreference mItemMisc;
-    private List<StorageItemPreference> mItemUsers = Lists.newArrayList();
-
-    private boolean mUsbConnected;
-    private String mUsbFunction;
-
-    private long mTotalSize;
-
-    private static final int MSG_UI_UPDATE_APPROXIMATE = 1;
-    private static final int MSG_UI_UPDATE_DETAILS = 2;
-
-    private Handler mUpdateHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_UI_UPDATE_APPROXIMATE: {
-                    final long[] size = (long[]) msg.obj;
-                    updateApproximate(size[0], size[1]);
-                    break;
-                }
-                case MSG_UI_UPDATE_DETAILS: {
-                    final MeasurementDetails details = (MeasurementDetails) msg.obj;
-                    updateDetails(details);
-                    break;
-                }
-            }
-        }
-    };
-
-    /**
-     * Build category to summarize internal storage, including any emulated
-     * {@link StorageVolume}.
-     */
-    public static StorageVolumePreferenceCategory buildForInternal(Context context) {
-        return new StorageVolumePreferenceCategory(context, null);
-    }
-
-    /**
-     * Build category to summarize specific physical {@link StorageVolume}.
-     */
-    public static StorageVolumePreferenceCategory buildForPhysical(
-            Context context, StorageVolume volume) {
-        return new StorageVolumePreferenceCategory(context, volume);
-    }
-
-    private StorageVolumePreferenceCategory(Context context, StorageVolume volume) {
-        super(context);
-
-        mVolume = volume;
-        mMeasure = StorageMeasurement.getInstance(context, volume);
-
-        mResources = context.getResources();
-        mStorageManager = StorageManager.from(context);
-        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-
-        setTitle(volume != null ? volume.getDescription(context)
-                : context.getText(R.string.internal_storage));
-    }
-
-    private StorageItemPreference buildItem(int titleRes, int colorRes) {
-        return new StorageItemPreference(getContext(), titleRes, colorRes);
-    }
-
-    public void init() {
-        final Context context = getContext();
-
-        removeAll();
-
-        final UserInfo currentUser;
-        try {
-            currentUser = ActivityManagerNative.getDefault().getCurrentUser();
-        } catch (RemoteException e) {
-            throw new RuntimeException("Failed to get current user");
-        }
-
-        final List<UserInfo> otherUsers = getUsersExcluding(currentUser);
-        final boolean showUsers = mVolume == null && otherUsers.size() > 0;
-
-        mUsageBarPreference = new UsageBarPreference(context);
-        mUsageBarPreference.setOrder(ORDER_USAGE_BAR);
-        addPreference(mUsageBarPreference);
-
-        mItemTotal = buildItem(R.string.memory_size, 0);
-        mItemAvailable = buildItem(R.string.memory_available, R.color.memory_avail);
-        addPreference(mItemTotal);
-        addPreference(mItemAvailable);
-
-        mItemApps = buildItem(R.string.memory_apps_usage, R.color.memory_apps_usage);
-        mItemDcim = buildItem(R.string.memory_dcim_usage, R.color.memory_dcim);
-        mItemMusic = buildItem(R.string.memory_music_usage, R.color.memory_music);
-        mItemDownloads = buildItem(R.string.memory_downloads_usage, R.color.memory_downloads);
-        mItemCache = buildItem(R.string.memory_media_cache_usage, R.color.memory_cache);
-        mItemMisc = buildItem(R.string.memory_media_misc_usage, R.color.memory_misc);
-
-        mItemCache.setKey(KEY_CACHE);
-
-        final boolean showDetails = mVolume == null || mVolume.isPrimary();
-        if (showDetails) {
-            if (showUsers) {
-                addPreference(new PreferenceHeader(context, currentUser.name));
-            }
-
-            addPreference(mItemApps);
-            addPreference(mItemDcim);
-            addPreference(mItemMusic);
-            addPreference(mItemDownloads);
-            addPreference(mItemCache);
-            addPreference(mItemMisc);
-
-            if (showUsers) {
-                addPreference(new PreferenceHeader(context, R.string.storage_other_users));
-
-                int count = 0;
-                for (UserInfo info : otherUsers) {
-                    final int colorRes = count++ % 2 == 0 ? R.color.memory_user_light
-                            : R.color.memory_user_dark;
-                    final StorageItemPreference userPref = new StorageItemPreference(
-                            getContext(), info.name, colorRes, info.id);
-                    mItemUsers.add(userPref);
-                    addPreference(userPref);
-                }
-            }
-        }
-
-        final boolean isRemovable = mVolume != null ? mVolume.isRemovable() : false;
-        // Always create the preference since many code rely on it existing
-        mMountTogglePreference = new Preference(context);
-        if (isRemovable) {
-            mMountTogglePreference.setTitle(R.string.sd_eject);
-            mMountTogglePreference.setSummary(R.string.sd_eject_summary);
-            addPreference(mMountTogglePreference);
-        }
-
-        final boolean allowFormat = mVolume != null;
-        if (allowFormat) {
-            mFormatPreference = new Preference(context);
-            mFormatPreference.setTitle(R.string.sd_format);
-            mFormatPreference.setSummary(R.string.sd_format_summary);
-            addPreference(mFormatPreference);
-        }
-
-        final IPackageManager pm = ActivityThread.getPackageManager();
-        try {
-            if (pm.isStorageLow()) {
-                mStorageLow = new Preference(context);
-                mStorageLow.setOrder(ORDER_STORAGE_LOW);
-                mStorageLow.setTitle(R.string.storage_low_title);
-                mStorageLow.setSummary(R.string.storage_low_summary);
-                addPreference(mStorageLow);
-            } else if (mStorageLow != null) {
-                removePreference(mStorageLow);
-                mStorageLow = null;
-            }
-        } catch (RemoteException e) {
-        }
-    }
-
-    public StorageVolume getStorageVolume() {
-        return mVolume;
-    }
-
-    private void updatePreferencesFromState() {
-        // Only update for physical volumes
-        if (mVolume == null) return;
-
-        mMountTogglePreference.setEnabled(true);
-
-        final String state = mStorageManager.getVolumeState(mVolume.getPath());
-
-        if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
-            mItemAvailable.setTitle(R.string.memory_available_read_only);
-        } else {
-            mItemAvailable.setTitle(R.string.memory_available);
-        }
-
-        if (Environment.MEDIA_MOUNTED.equals(state)
-                || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
-            mMountTogglePreference.setEnabled(true);
-            mMountTogglePreference.setTitle(mResources.getString(R.string.sd_eject));
-            mMountTogglePreference.setSummary(mResources.getString(R.string.sd_eject_summary));
-            addPreference(mUsageBarPreference);
-            addPreference(mItemTotal);
-            addPreference(mItemAvailable);
-        } else {
-            if (Environment.MEDIA_UNMOUNTED.equals(state) || Environment.MEDIA_NOFS.equals(state)
-                    || Environment.MEDIA_UNMOUNTABLE.equals(state)) {
-                mMountTogglePreference.setEnabled(true);
-                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
-                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_mount_summary));
-            } else {
-                mMountTogglePreference.setEnabled(false);
-                mMountTogglePreference.setTitle(mResources.getString(R.string.sd_mount));
-                mMountTogglePreference.setSummary(mResources.getString(R.string.sd_insert_summary));
-            }
-
-            removePreference(mUsageBarPreference);
-            removePreference(mItemTotal);
-            removePreference(mItemAvailable);
-        }
-
-        if (mUsbConnected && (UsbManager.USB_FUNCTION_MTP.equals(mUsbFunction) ||
-                UsbManager.USB_FUNCTION_PTP.equals(mUsbFunction))) {
-            mMountTogglePreference.setEnabled(false);
-            if (Environment.MEDIA_MOUNTED.equals(state)
-                    || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
-                mMountTogglePreference.setSummary(
-                        mResources.getString(R.string.mtp_ptp_mode_summary));
-            }
-
-            if (mFormatPreference != null) {
-                mFormatPreference.setEnabled(false);
-                mFormatPreference.setSummary(mResources.getString(R.string.mtp_ptp_mode_summary));
-            }
-        } else if (mFormatPreference != null) {
-            mFormatPreference.setEnabled(mMountTogglePreference.isEnabled());
-            mFormatPreference.setSummary(mResources.getString(R.string.sd_format_summary));
-        }
-    }
-
-    public void updateApproximate(long totalSize, long availSize) {
-        mItemTotal.setSummary(formatSize(totalSize));
-        mItemAvailable.setSummary(formatSize(availSize));
-
-        mTotalSize = totalSize;
-
-        final long usedSize = totalSize - availSize;
-
-        mUsageBarPreference.clear();
-        mUsageBarPreference.addEntry(0, usedSize / (float) totalSize, android.graphics.Color.GRAY);
-        mUsageBarPreference.commit();
-
-        updatePreferencesFromState();
-    }
-
-    private static long totalValues(HashMap<String, Long> map, String... keys) {
-        long total = 0;
-        for (String key : keys) {
-            if (map.containsKey(key)) {
-                total += map.get(key);
-            }
-        }
-        return total;
-    }
-
-    public void updateDetails(MeasurementDetails details) {
-        final boolean showDetails = mVolume == null || mVolume.isPrimary();
-        if (!showDetails) return;
-
-        // Count caches as available space, since system manages them
-        mItemTotal.setSummary(formatSize(details.totalSize));
-        mItemAvailable.setSummary(formatSize(details.availSize));
-
-        mUsageBarPreference.clear();
-
-        updatePreference(mItemApps, details.appsSize);
-
-        final long dcimSize = totalValues(details.mediaSize, Environment.DIRECTORY_DCIM,
-                Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
-        updatePreference(mItemDcim, dcimSize);
-
-        final long musicSize = totalValues(details.mediaSize, Environment.DIRECTORY_MUSIC,
-                Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
-                Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
-        updatePreference(mItemMusic, musicSize);
-
-        final long downloadsSize = totalValues(details.mediaSize, Environment.DIRECTORY_DOWNLOADS);
-        updatePreference(mItemDownloads, downloadsSize);
-
-        updatePreference(mItemCache, details.cacheSize);
-        updatePreference(mItemMisc, details.miscSize);
-
-        for (StorageItemPreference userPref : mItemUsers) {
-            final long userSize = details.usersSize.get(userPref.userHandle);
-            updatePreference(userPref, userSize);
-        }
-
-        mUsageBarPreference.commit();
-    }
-
-    private void updatePreference(StorageItemPreference pref, long size) {
-        if (size > 0) {
-            pref.setSummary(formatSize(size));
-            final int order = pref.getOrder();
-            mUsageBarPreference.addEntry(order, size / (float) mTotalSize, pref.color);
-        } else {
-            removePreference(pref);
-        }
-    }
-
-    private void measure() {
-        mMeasure.invalidate();
-        mMeasure.measure();
-    }
-
-    public void onResume() {
-        mMeasure.setReceiver(mReceiver);
-        measure();
-    }
-
-    public void onStorageStateChanged() {
-        init();
-        measure();
-    }
-
-    public void onUsbStateChanged(boolean isUsbConnected, String usbFunction) {
-        mUsbConnected = isUsbConnected;
-        mUsbFunction = usbFunction;
-        measure();
-    }
-
-    public void onMediaScannerFinished() {
-        measure();
-    }
-
-    public void onCacheCleared() {
-        measure();
-    }
-
-    public void onPause() {
-        mMeasure.cleanUp();
-    }
-
-    private String formatSize(long size) {
-        return Formatter.formatFileSize(getContext(), size);
-    }
-
-    private MeasurementReceiver mReceiver = new MeasurementReceiver() {
-        @Override
-        public void updateApproximate(StorageMeasurement meas, long totalSize, long availSize) {
-            mUpdateHandler.obtainMessage(MSG_UI_UPDATE_APPROXIMATE, new long[] {
-                    totalSize, availSize }).sendToTarget();
-        }
-
-        @Override
-        public void updateDetails(StorageMeasurement meas, MeasurementDetails details) {
-            mUpdateHandler.obtainMessage(MSG_UI_UPDATE_DETAILS, details).sendToTarget();
-        }
-    };
-
-    public boolean mountToggleClicked(Preference preference) {
-        return preference == mMountTogglePreference;
-    }
-
-    public Intent intentForClick(Preference pref) {
-        Intent intent = null;
-
-        // TODO The current "delete" story is not fully handled by the respective applications.
-        // When it is done, make sure the intent types below are correct.
-        // If that cannot be done, remove these intents.
-        final String key = pref.getKey();
-        if (pref == mFormatPreference) {
-            intent = new Intent(Intent.ACTION_VIEW);
-            intent.setClass(getContext(), com.android.settings.MediaFormat.class);
-            intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
-        } else if (pref == mItemApps) {
-            intent = new Intent(Intent.ACTION_MANAGE_PACKAGE_STORAGE);
-            intent.setClass(getContext(), Settings.ManageApplicationsActivity.class);
-        } else if (pref == mItemDownloads) {
-            intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).putExtra(
-                    DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true);
-        } else if (pref == mItemMusic) {
-            intent = new Intent(Intent.ACTION_GET_CONTENT);
-            intent.setType("audio/mp3");
-        } else if (pref == mItemDcim) {
-            intent = new Intent(Intent.ACTION_VIEW);
-            intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
-            // TODO Create a Videos category, MediaStore.Video.Media.EXTERNAL_CONTENT_URI
-            intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
-        } else if (pref == mItemMisc) {
-            Context context = getContext().getApplicationContext();
-            intent = new Intent(context, MiscFilesHandler.class);
-            intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolume);
-        }
-
-        return intent;
-    }
-
-    public static class PreferenceHeader extends Preference {
-        public PreferenceHeader(Context context, int titleRes) {
-            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
-            setTitle(titleRes);
-        }
-
-        public PreferenceHeader(Context context, CharSequence title) {
-            super(context, null, com.android.internal.R.attr.preferenceCategoryStyle);
-            setTitle(title);
-        }
-
-        @Override
-        public boolean isEnabled() {
-            return false;
-        }
-    }
-
-    /**
-     * Return list of other users, excluding the current user.
-     */
-    private List<UserInfo> getUsersExcluding(UserInfo excluding) {
-        final List<UserInfo> users = mUserManager.getUsers();
-        final Iterator<UserInfo> i = users.iterator();
-        while (i.hasNext()) {
-            if (i.next().id == excluding.id) {
-                i.remove();
-            }
-        }
-        return users;
-    }
-}
diff --git a/src/com/android/settings/notification/ConditionProviderSettings.java b/src/com/android/settings/notification/ConditionProviderSettings.java
index 259e53c..76576ab 100644
--- a/src/com/android/settings/notification/ConditionProviderSettings.java
+++ b/src/com/android/settings/notification/ConditionProviderSettings.java
@@ -21,8 +21,6 @@
 import android.provider.Settings;
 import android.service.notification.ConditionProviderService;
 
-import com.android.settings.R;
-
 public class ConditionProviderSettings extends ManagedServiceSettings {
     private static final String TAG = ConditionProviderSettings.class.getSimpleName();
     private static final Config CONFIG = getConditionProviderConfig();
@@ -34,9 +32,6 @@
         c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
         c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
         c.noun = "condition provider";
-        c.warningDialogTitle = R.string.condition_provider_security_warning_title;
-        c.warningDialogSummary = R.string.condition_provider_security_warning_summary;
-        c.emptyText = R.string.no_condition_providers;
         return c;
     }
 
diff --git a/src/com/android/settings/search/Ranking.java b/src/com/android/settings/search/Ranking.java
index f37e1fc..2143d0d 100644
--- a/src/com/android/settings/search/Ranking.java
+++ b/src/com/android/settings/search/Ranking.java
@@ -32,7 +32,7 @@
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.StorageSettings;
 import com.android.settings.deviceinfo.UsbSettings;
 import com.android.settings.fuelgauge.BatterySaverSettings;
 import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -129,7 +129,7 @@
         sRankMap.put(ZenModeAutomationSettings.class.getName(), RANK_NOTIFICATIONS);
 
         // Storage
-        sRankMap.put(Memory.class.getName(), RANK_STORAGE);
+        sRankMap.put(StorageSettings.class.getName(), RANK_STORAGE);
         sRankMap.put(UsbSettings.class.getName(), RANK_STORAGE);
 
         // Battery
diff --git a/src/com/android/settings/search/SearchIndexableResources.java b/src/com/android/settings/search/SearchIndexableResources.java
index dae8860..b7bb062 100644
--- a/src/com/android/settings/search/SearchIndexableResources.java
+++ b/src/com/android/settings/search/SearchIndexableResources.java
@@ -34,7 +34,7 @@
 import com.android.settings.accessibility.AccessibilitySettings;
 import com.android.settings.applications.AdvancedAppSettings;
 import com.android.settings.bluetooth.BluetoothSettings;
-import com.android.settings.deviceinfo.Memory;
+import com.android.settings.deviceinfo.StorageSettings;
 import com.android.settings.deviceinfo.UsbSettings;
 import com.android.settings.fuelgauge.BatterySaverSettings;
 import com.android.settings.fuelgauge.PowerUsageSummary;
@@ -170,11 +170,11 @@
                         ZenModePrioritySettings.class.getName(),
                         R.drawable.ic_settings_notifications));
 
-        sResMap.put(Memory.class.getName(),
+        sResMap.put(StorageSettings.class.getName(),
                 new SearchIndexableResource(
-                        Ranking.getRankForClassName(Memory.class.getName()),
+                        Ranking.getRankForClassName(StorageSettings.class.getName()),
                         NO_DATA_RES_ID,
-                        Memory.class.getName(),
+                        StorageSettings.class.getName(),
                         R.drawable.ic_settings_storage));
 
         sResMap.put(UsbSettings.class.getName(),
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index cc8481d..1503c63 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -88,6 +88,9 @@
     public static final int WIFI_EAP_METHOD_TLS  = 1;
     public static final int WIFI_EAP_METHOD_TTLS = 2;
     public static final int WIFI_EAP_METHOD_PWD  = 3;
+    public static final int WIFI_EAP_METHOD_SIM  = 4;
+    public static final int WIFI_EAP_METHOD_AKA  = 5;
+    public static final int WIFI_EAP_METHOD_AKA_PRIME  = 6;
 
     /* These values come from "wifi_peap_phase2_entries" resource array */
     public static final int WIFI_PEAP_PHASE2_NONE 	    = 0;
@@ -734,9 +737,23 @@
                 mView.findViewById(R.id.l_anonymous).setVisibility(View.VISIBLE);
                 setUserCertInvisible();
                 break;
+            case WIFI_EAP_METHOD_SIM:
+            case WIFI_EAP_METHOD_AKA:
+            case WIFI_EAP_METHOD_AKA_PRIME:
+                setPhase2Invisible();
+                setAnonymousIdentInvisible();
+                setUserCertInvisible();
+                setPasswordInvisible();
+                setIdentityInvisible();
+                break;
         }
     }
 
+    private void setIdentityInvisible() {
+        mView.findViewById(R.id.l_identity).setVisibility(View.GONE);
+        mPhase2Spinner.setSelection(Phase2.NONE);
+    }
+
     private void setPhase2Invisible() {
         mView.findViewById(R.id.l_phase2).setVisibility(View.GONE);
         mPhase2Spinner.setSelection(Phase2.NONE);