resolved conflicts for merge of d8eb423a to klp-dev

Change-Id: I44c7c7d0e1da8fd169a0bf57696e308631fa9f25
diff --git a/Android.mk b/Android.mk
index fb8cb6e..eb20588 100644
--- a/Android.mk
+++ b/Android.mk
@@ -10,5 +10,6 @@
 
 LOCAL_PACKAGE_NAME := MediaProvider
 LOCAL_CERTIFICATE := media
+LOCAL_PRIVILEGED_MODULE := true
 
 include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 693d20c..7d48a4a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -2,7 +2,7 @@
         package="com.android.providers.media"
         android:sharedUserId="android.media"
         android:sharedUserLabel="@string/uid_label"
-        android:versionCode="601">
+        android:versionCode="700">
         
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
@@ -25,6 +25,17 @@
                     android:writePermission="android.permission.WRITE_EXTERNAL_STORAGE" />
         </provider>
 
+        <provider
+            android:name="MediaDocumentsProvider"
+            android:authorities="com.android.providers.media.documents"
+            android:grantUriPermissions="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DOCUMENTS">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+        </provider>
+
         <!-- Handles database upgrades after OTAs, then disables itself -->
         <receiver android:name="MediaUpgradeReceiver">
             <!-- This broadcast is sent after the core system has finished
diff --git a/res/mipmap-hdpi/ic_launcher_gallery.png b/res/mipmap-hdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..23ea998
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_gallery.png b/res/mipmap-mdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..e1a9949
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_gallery.png b/res/mipmap-xhdpi/ic_launcher_gallery.png
new file mode 100644
index 0000000..79544a2
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher_gallery.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 9deaea5..f37a5bb 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Mediaberging"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Gradeer mediadatabasis op."</string>
     <string name="artist_label" msgid="1181678850424271227">"Kunstenaar"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Verstek luitoon"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Verstek kennisgewingklank"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Verstek wekkerklank"</string>
+    <string name="root_images" msgid="7098113056247445324">"Prente"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video\'s"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Oudio"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d9f30d9..561e6c5 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -16,8 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="uid_label" msgid="999888504096344278">"ማህደረመረጃ"</string>
-    <string name="app_label" msgid="581696352855930028">"ማህደረመረጃ ማከማቻ"</string>
+    <string name="uid_label" msgid="999888504096344278">" ማህደረ መረጃ"</string>
+    <string name="app_label" msgid="581696352855930028">" ማህደረ መረጃ  ማከማቻ"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"የሚዲያ ውሂብ ጎታ በማሻሻል ላይ፡፡"</string>
     <string name="artist_label" msgid="1181678850424271227">"አርቲስት"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"ነባሪ የደወል ቅላጼ"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"ነባሪ የማሳወቂያ ድምጽ"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"ነባሪ የማንቂያ ድምጽ"</string>
+    <string name="root_images" msgid="7098113056247445324">"ምስሎች"</string>
+    <string name="root_videos" msgid="3304457332406057833">"ቪዲዮዎች"</string>
+    <string name="root_audio" msgid="7177565505195715659">"ድምጽ"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index c990466..d396b58 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"تخزين الوسائط"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"جارٍ ترقية قاعدة بيانات الوسائط."</string>
     <string name="artist_label" msgid="1181678850424271227">"الفنان"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"نغمة الرنين الافتراضية"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"صوت الإشعار الافتراضي"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"صوت التنبيه الافتراضي"</string>
+    <string name="root_images" msgid="7098113056247445324">"صور"</string>
+    <string name="root_videos" msgid="3304457332406057833">"مقاطع فيديو"</string>
+    <string name="root_audio" msgid="7177565505195715659">"الصوت"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 1e12072..05908e8 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Медыянакапляльнік"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Абнаўленне базы дадзеных мультымедыя."</string>
     <string name="artist_label" msgid="1181678850424271227">"Выканаўца"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Рынгтон па змаўчанні"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Сігнал паведамлення па змаўчаннi"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Сігнал будзільніка па змаўчанні"</string>
+    <string name="root_images" msgid="7098113056247445324">"Выявы"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Відэа"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Аўдыё"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index d29ce4f..50ad9d6 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Хранилище на медии"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Базата от данни на носителите се надстройва."</string>
     <string name="artist_label" msgid="1181678850424271227">"Изпълнител"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Стандартна мелодия"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Стандартен звук за известяване"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Стандартен звук за будилника"</string>
+    <string name="root_images" msgid="7098113056247445324">"Изображения"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Видеоклипове"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Аудио"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 1cdc28e..aecc22b 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Emmagatzematge de mitjans"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"S\'està actualitzant la base de dades multimèdia."</string>
     <string name="artist_label" msgid="1181678850424271227">"Intèrpret"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"To predeterminat"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"So de notificació predeterminat"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"So d\'alarma predeterminat"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imatges"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Àudio"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index f87691c..d248cd8 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Úložiště médií"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Aktualizace databáze médií."</string>
     <string name="artist_label" msgid="1181678850424271227">"Interpret"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Výchozí vyzváněcí tón"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Výchozí zvuk oznámení"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Výchozí zvuk budíku"</string>
+    <string name="root_images" msgid="7098113056247445324">"Obrázky"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videa"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Zvuk"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e6b57ce..439cd6c 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medielagring"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Opgraderer mediedatabasen."</string>
     <string name="artist_label" msgid="1181678850424271227">"Kunstner"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Standardringetone"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Standardlyd for underretninger"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Standardlyd for alarmer"</string>
+    <string name="root_images" msgid="7098113056247445324">"Billeder"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videoer"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Lyd"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8b9da85..d4a2a2f 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medienspeicher"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Medien-Datenbank wird aktualisiert."</string>
     <string name="artist_label" msgid="1181678850424271227">"Interpret"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Standard-Klingelton"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Standard-Benachrichtigungston"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Standard-Weckerton"</string>
+    <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index da973fd..dc84490 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Χώρος αποθήκευσης μέσων"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Αναβάθμιση βάσης δεδομένων μέσων"</string>
     <string name="artist_label" msgid="1181678850424271227">"Καλλιτέχνης"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Προεπιλεγμένος ήχος κλήσης"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Προεπιλεγμένος ήχος ειδοποίησης"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Προεπιλεγμένος ήχος ειδοποίησης"</string>
+    <string name="root_images" msgid="7098113056247445324">"Εικόνες"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Βίντεο"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Ήχος"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 0b28a1e..6945912 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Media Storage"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Upgrading Media database."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Default ringtone"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Default notification sound"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Default alarm sound"</string>
+    <string name="root_images" msgid="7098113056247445324">"Images"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..6945912
--- /dev/null
+++ b/res/values-en-rIN/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2009 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="uid_label" msgid="999888504096344278">"Media"</string>
+    <string name="app_label" msgid="581696352855930028">"Media Storage"</string>
+    <string name="upgrade_msg" msgid="4093462661265175619">"Upgrading Media database."</string>
+    <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Default ringtone"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Default notification sound"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Default alarm sound"</string>
+    <string name="root_images" msgid="7098113056247445324">"Images"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d2a38f6..7edb0eb 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Almacenamiento de medios"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Actualizando la base de datos de los medios"</string>
     <string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Tono predeterminado"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Sonido de notificación predeterminado"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Sonido de alarma predeterminado"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imágenes"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index e1382eb..c2aa127 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Almacenamiento de medios"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Actualizando base de datos multimedia"</string>
     <string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Tono predeterminado"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Sonido de notificación predeterminado"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Sonido de alarma predeterminado"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imágenes"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..f36166a
--- /dev/null
+++ b/res/values-et-rEE/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2009 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="uid_label" msgid="999888504096344278">"Meedium"</string>
+    <string name="app_label" msgid="581696352855930028">"Salvestusmeedium"</string>
+    <string name="upgrade_msg" msgid="4093462661265175619">"Meediumi andmebaasi uuendamine."</string>
+    <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Vaikehelin"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Märguande vaikeheli"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Äratuse vaikeheli"</string>
+    <string name="root_images" msgid="7098113056247445324">"Pildid"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videod"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Heli"</string>
+</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 4b9dcdb..593a2c7 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -20,4 +20,9 @@
     <string name="app_label" msgid="581696352855930028">"Salvestusmeedium"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Meediumi andmebaasi uuendamine."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Vaikehelin"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Märguande vaikeheli"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Äratuse vaikeheli"</string>
+    <string name="root_images" msgid="7098113056247445324">"Pildid"</string>
+    <string name="root_music" msgid="4663573634162145060">"Muusika"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1caae4a..56a0526 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"حافظه رسانه"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"ارتقا پایگاه داده رسانه."</string>
     <string name="artist_label" msgid="1181678850424271227">"هنرمند"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"آهنگ زنگ پیش‌فرض"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"صدای اعلان پیش‌فرض"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"آهنگ زنگ پیش‌فرض"</string>
+    <string name="root_images" msgid="7098113056247445324">"تصاویر"</string>
+    <string name="root_videos" msgid="3304457332406057833">"ویدیوها"</string>
+    <string name="root_audio" msgid="7177565505195715659">"صدا"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index bcdd937..6453c3f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Median tallennustila"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Päivitetään Median tietokantaa."</string>
     <string name="artist_label" msgid="1181678850424271227">"Esittäjä"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Oletussoittoääni"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Ilmoituksen oletusääni"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Hälytyksen oletusääni"</string>
+    <string name="root_images" msgid="7098113056247445324">"Kuvat"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videot"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Ääni"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..0da5059
--- /dev/null
+++ b/res/values-fr-rCA/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2009 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="uid_label" msgid="999888504096344278">"Médias"</string>
+    <string name="app_label" msgid="581696352855930028">"Stockage multimédia"</string>
+    <string name="upgrade_msg" msgid="4093462661265175619">"Mise à jour de la base de données multimédia."</string>
+    <string name="artist_label" msgid="1181678850424271227">"Artiste"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Sonnerie par défaut"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Son de notification par défaut"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Son de l\'alarme par défaut"</string>
+    <string name="root_images" msgid="7098113056247445324">"Images"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vidéos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 65e3b86..c1f9c04 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Stockage multimédia"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Mise à jour de la base de données multimédia."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artiste"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Sonnerie par défaut"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Son de notification par défaut"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Son de l\'alarme par défaut"</string>
+    <string name="root_images" msgid="7098113056247445324">"Images"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vidéos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index a7736bb..ad4d9ad 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"मीडिया संग्रहण"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"मीडिया डेटाबेस अपग्रेड हो रहा है."</string>
     <string name="artist_label" msgid="1181678850424271227">"कलाकार"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"डिफ़ॉल्‍ट रिंगटोन"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"सामान्य सूचना ध्वनी"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"डिफ़ॉल्ट अलार्म ध्वनि"</string>
+    <string name="root_images" msgid="7098113056247445324">"चित्र"</string>
+    <string name="root_videos" msgid="3304457332406057833">"वीडियो"</string>
+    <string name="root_audio" msgid="7177565505195715659">"ऑडियो"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 45fe3f8..be7b286 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Prostor za pohranjivanje na mediju"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Nadogradnja baze podataka medija."</string>
     <string name="artist_label" msgid="1181678850424271227">"Izvođač"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Zadana melodija zvona"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Zadani zvuk obavijesti"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Zadani zvuk alarma"</string>
+    <string name="root_images" msgid="7098113056247445324">"Slike"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videozapisi"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index b57a400..804cab5 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Médiatároló"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Médiaszolgáltató adatbázisának frissítése"</string>
     <string name="artist_label" msgid="1181678850424271227">"Előadó"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Alapértelmezett csengőhang"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Alapértelmezett értesítési hang"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Alapértelmezett ébresztési hang"</string>
+    <string name="root_images" msgid="7098113056247445324">"Képek"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videók"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Hang"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 7bda4a2..11a86e7 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Penyimpanan Media"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Meningkatkan versi basis data Media."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Nada dering default"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Suara pemberitahuan default"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Suara alarm default"</string>
+    <string name="root_images" msgid="7098113056247445324">"Gambar"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 848aa63..5d53d08 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Media Storage"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Upgrade del database di Media."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Suoneria predefinita"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Suono di notifica predefinito"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Suono allarme predefinito"</string>
+    <string name="root_images" msgid="7098113056247445324">"Immagini"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index f8502dc..0905702 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"אחסון מדיה"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"משדרג את מסד הנתונים של המדיה."</string>
     <string name="artist_label" msgid="1181678850424271227">"אמן"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"רינגטון ברירת מחדל"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"צליל ברירת מחדל להתראה"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"צליל ברירת מחדל להתרעה"</string>
+    <string name="root_images" msgid="7098113056247445324">"תמונות"</string>
+    <string name="root_videos" msgid="3304457332406057833">"סרטונים"</string>
+    <string name="root_audio" msgid="7177565505195715659">"אודיו"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 852df60..ac58c3f 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"メディアストレージ"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"メディアデータベースをアップグレードしています。"</string>
     <string name="artist_label" msgid="1181678850424271227">"アーティスト"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"デフォルトの着信音"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"デフォルトの通知音"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"デフォルトの警告音"</string>
+    <string name="root_images" msgid="7098113056247445324">"画像"</string>
+    <string name="root_videos" msgid="3304457332406057833">"動画"</string>
+    <string name="root_audio" msgid="7177565505195715659">"音声"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index a97c30a..ffb9664 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"미디어 저장소"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"미디어 데이터베이스 업그레이드 중"</string>
     <string name="artist_label" msgid="1181678850424271227">"아티스트"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"기본 벨소리"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"기본 알림 소리"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"기본 알람 소리"</string>
+    <string name="root_images" msgid="7098113056247445324">"이미지"</string>
+    <string name="root_videos" msgid="3304457332406057833">"동영상"</string>
+    <string name="root_audio" msgid="7177565505195715659">"오디오"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 41c67be..7b5be76 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medijos saugykla"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Naujovinama medijos duomenų bazė."</string>
     <string name="artist_label" msgid="1181678850424271227">"Atlikėjas"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Numatytasis skambėjimo tonas"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Numatytasis pranešimo garsas"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Numatytasis signalo garsas"</string>
+    <string name="root_images" msgid="7098113056247445324">"Vaizdai"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vaizdo įrašai"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Garso įrašai"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 5da36c7..93cd9e4 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Multivides krātuve"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Tiek jaunināta multivides datu bāze."</string>
     <string name="artist_label" msgid="1181678850424271227">"Izpildītājs"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Noklusējuma zvana signāls"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Paziņojuma noklusējuma skaņa"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Signāla noklusējuma skaņa"</string>
+    <string name="root_images" msgid="7098113056247445324">"Attēli"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videoklipi"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..cef695d
--- /dev/null
+++ b/res/values-ms-rMY/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2009 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="uid_label" msgid="999888504096344278">"Media"</string>
+    <string name="app_label" msgid="581696352855930028">"Storan Media"</string>
+    <string name="upgrade_msg" msgid="4093462661265175619">"Menaiktaraf pangkalan data Media."</string>
+    <string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Nada dering lalai"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Bunyi pemberitahuan lalai"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Bunyi penggera lalai"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imej"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
+</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index ad1b139..7f2ae5b 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -20,4 +20,9 @@
     <string name="app_label" msgid="581696352855930028">"Storan Media"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Menaiktaraf pangkalan data Media."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artis"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Nada dering lalai"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Bunyi pemberitahuan lalai"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Bunyi penggera lalai"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imej"</string>
+    <string name="root_music" msgid="4663573634162145060">"Muzik"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 9e6b81c..e566c21 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medielagring"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Oppgraderer mediedatabase."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Standard ringetone"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Standard varsellyd"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Standard alarmlyd"</string>
+    <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videoer"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Lyd"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index b31a15d..f2e1fde 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Mediaopslag"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Mediadatabase bijwerken."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artiest"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Standaardbeltoon"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Standaardmeldingsgeluid"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Standaardalarmgeluid"</string>
+    <string name="root_images" msgid="7098113056247445324">"Afbeeldingen"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video\'s"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 420c2e0..43b9041 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Przechowywanie multimediów"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Uaktualnianie bazy danych mediów."</string>
     <string name="artist_label" msgid="1181678850424271227">"Wykonawca"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Dzwonek domyślny"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Domyślny dźwięk powiadomienia"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Domyślny dźwięk alarmu"</string>
+    <string name="root_images" msgid="7098113056247445324">"Obrazy"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Filmy"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Dźwięk"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 2aa595d..daea25a 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Armazenamento de multimédia"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"A atualizar a base de dados de Multimédia."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Toque predefinido"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Som de notificação predefinido"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Som de alarme predefinido"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imagens"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Áudio"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index eb464c9..9362c9a 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Armazenamento de mídia"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Atualizando o banco de dados de mídia."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artista"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Toque padrão"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Som de notificação padrão"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Som de alarme padrão"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imagens"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Vídeos"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Áudio"</string>
 </resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index e1298b4..75fec6d 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -21,4 +21,16 @@
     <!-- no translation found for upgrade_msg (4093462661265175619) -->
     <skip />
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <!-- no translation found for ringtone_default (3332469542084549546) -->
+    <skip />
+    <!-- no translation found for notification_sound_default (5309766396223094324) -->
+    <skip />
+    <!-- no translation found for alarm_sound_default (6854264112790616066) -->
+    <skip />
+    <!-- no translation found for root_images (7098113056247445324) -->
+    <skip />
+    <!-- no translation found for root_videos (3304457332406057833) -->
+    <skip />
+    <!-- no translation found for root_audio (7177565505195715659) -->
+    <skip />
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 2fecf5c..4f724f3 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Mediu de stocare"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Se actualizează baza de date media."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Ton de sonerie prestabilit"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Sunet de notificare prestabilit"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Sunet de alarmă prestabilit"</string>
+    <string name="root_images" msgid="7098113056247445324">"Imagini"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videoclipuri"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index ce89639..ea61c07 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Хранилище мультимедиа"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Обновление базы данных мультимедиа..."</string>
     <string name="artist_label" msgid="1181678850424271227">"Исполнитель"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Рингтон по умолчанию"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Звук уведомлений по умолчанию"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Звуковой сигнал по умолчанию"</string>
+    <string name="root_images" msgid="7098113056247445324">"Изображения"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Видео"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Аудио"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 9b3f90d..2dcc7e2 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Ukladací priestor médií"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Prebieha inovácia databázy médií"</string>
     <string name="artist_label" msgid="1181678850424271227">"Umelec"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Predvolený tón zvonenia"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Predvolený zvuk upozornenia"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Predvolený zvuk budíka"</string>
+    <string name="root_images" msgid="7098113056247445324">"Obrázky"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videá"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Zvuk"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index fef7ee7..61a525f 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Prostor za shranjevanje predstavnosti"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Nadgradnja večpredstavnostne zbirke podatkov."</string>
     <string name="artist_label" msgid="1181678850424271227">"Izvajalec"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Privzeti ton zvonjenja"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Privzeti zvok obvestila"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Privzeti zvok alarma"</string>
+    <string name="root_images" msgid="7098113056247445324">"Slike"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videoposnetki"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Zvok"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bacfcb1..1a1024c 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Складиште за медије"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Надограђивање базе података медија."</string>
     <string name="artist_label" msgid="1181678850424271227">"Извођач"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Подразумевана мелодија звона"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Подразумевани звук обавештења"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Подразумевани звук аларма"</string>
+    <string name="root_images" msgid="7098113056247445324">"Слике"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Видео снимци"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Звук"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7930730..fb0af08 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medialagring"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Uppgraderar mediedatabas."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Standardringsignal"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Standardljud för meddelanden"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Standardljud för alarm"</string>
+    <string name="root_images" msgid="7098113056247445324">"Bilder"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videor"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Ljud"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 7095b78..9374a8b 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -16,8 +16,14 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="uid_label" msgid="999888504096344278">"Midia"</string>
+    <string name="uid_label" msgid="999888504096344278">"Media"</string>
     <string name="app_label" msgid="581696352855930028">"Hifadhi ya media"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Inapandisha daraja la hifadhidata ya Midia."</string>
     <string name="artist_label" msgid="1181678850424271227">"Msanii"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Mlio wa simu chaguo-msingi"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Sauti chaguo-msingi ya arifa"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Sauti chaguo-msingi ya kengele"</string>
+    <string name="root_images" msgid="7098113056247445324">"Picha"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Sauti"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index b962aa1..a227e3e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"พื้นที่เก็บข้อมูลสื่อ"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"กำลังอัปเกรดฐานข้อมูลสื่อ"</string>
     <string name="artist_label" msgid="1181678850424271227">"ศิลปิน"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"เสียงเรียกเข้าเริ่มต้น"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"เสียงแจ้งเตือนเริ่มต้น"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"เสียงปลุกเริ่มต้น"</string>
+    <string name="root_images" msgid="7098113056247445324">"รูปภาพ"</string>
+    <string name="root_videos" msgid="3304457332406057833">"วิดีโอ"</string>
+    <string name="root_audio" msgid="7177565505195715659">"เสียง"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 026a8ad..4d381bd 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Imbakan ng Media"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Ina-upgrade ang database ng Media."</string>
     <string name="artist_label" msgid="1181678850424271227">"Artist"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Default na ringtone"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Default na tunog ng notification"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Default na tunog ng alarma"</string>
+    <string name="root_images" msgid="7098113056247445324">"Mga Larawan"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Mga Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Audio"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 196822d..7f62bf2 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Medya Deposu"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Medya veritabanı yeni sürüme geçiriliyor."</string>
     <string name="artist_label" msgid="1181678850424271227">"Sanatçı"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Varsayılan zil sesi"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Varsayılan bildirim sesi"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Varsayılan alarm sesi"</string>
+    <string name="root_images" msgid="7098113056247445324">"Resimler"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Videolar"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Ses"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index ce61d15..bb458d8 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Збер. медіа-файл."</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Оновлення бази даних медіа-файлів."</string>
     <string name="artist_label" msgid="1181678850424271227">"Виконавець"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Сигнал дзвінка за умовчанням"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Звук сповіщення за умовчанням"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Звук сигналу за умовчанням"</string>
+    <string name="root_images" msgid="7098113056247445324">"Зображення"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Відео"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Звук"</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a501f3e..79fdf74 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Bộ nhớ Phương tiện"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Đang nâng cấp cơ sở dữ liệu Phương tiện."</string>
     <string name="artist_label" msgid="1181678850424271227">"Nghệ sĩ"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Nhạc chuông mặc định"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Âm thanh thông báo mặc định"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Âm thanh báo thức mặc định"</string>
+    <string name="root_images" msgid="7098113056247445324">"Hình ảnh"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Video"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Âm thanh"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index d29ddad..9bf46c9 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,5 +19,11 @@
     <string name="uid_label" msgid="999888504096344278">"媒体"</string>
     <string name="app_label" msgid="581696352855930028">"媒体存储"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"正在升级媒体数据库。"</string>
-    <string name="artist_label" msgid="1181678850424271227">"艺术家"</string>
+    <string name="artist_label" msgid="1181678850424271227">"音乐人"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"默认铃声"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"默认通知提示音"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"默认闹钟提示音"</string>
+    <string name="root_images" msgid="7098113056247445324">"图片"</string>
+    <string name="root_videos" msgid="3304457332406057833">"视频"</string>
+    <string name="root_audio" msgid="7177565505195715659">"音频"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..6da7650
--- /dev/null
+++ b/res/values-zh-rHK/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2009 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="uid_label" msgid="999888504096344278">"媒體"</string>
+    <string name="app_label" msgid="581696352855930028">"媒體儲存空間"</string>
+    <string name="upgrade_msg" msgid="4093462661265175619">"正在升級媒體資料庫。"</string>
+    <string name="artist_label" msgid="1181678850424271227">"歌手/創作人"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"預設鈴聲"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"預設通知音效"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"預設鬧鐘音效"</string>
+    <string name="root_images" msgid="7098113056247445324">"圖片"</string>
+    <string name="root_videos" msgid="3304457332406057833">"影片"</string>
+    <string name="root_audio" msgid="7177565505195715659">"音頻"</string>
+</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 8a88124..bbc4769 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"媒體儲存空間"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"正在升級媒體資料庫。"</string>
     <string name="artist_label" msgid="1181678850424271227">"演出者"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"預設鈴聲"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"預設通知音效"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"預設鬧鐘音效"</string>
+    <string name="root_images" msgid="7098113056247445324">"圖片"</string>
+    <string name="root_videos" msgid="3304457332406057833">"影片"</string>
+    <string name="root_audio" msgid="7177565505195715659">"音訊"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 8a68cf7..e76085c 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -20,4 +20,10 @@
     <string name="app_label" msgid="581696352855930028">"Isitoreji Semidiya"</string>
     <string name="upgrade_msg" msgid="4093462661265175619">"Ukufaka ezakamuva kwimininingo egciniwe yeMediya."</string>
     <string name="artist_label" msgid="1181678850424271227">"Umculi"</string>
+    <string name="ringtone_default" msgid="3332469542084549546">"Ithoni yokukhala ezenzakalelayo"</string>
+    <string name="notification_sound_default" msgid="5309766396223094324">"Umsindo wesaziso ozenzakalelayo"</string>
+    <string name="alarm_sound_default" msgid="6854264112790616066">"Umsindo we-alamu ozenzakalelayo"</string>
+    <string name="root_images" msgid="7098113056247445324">"Izithombe"</string>
+    <string name="root_videos" msgid="3304457332406057833">"Amavidiyo"</string>
+    <string name="root_audio" msgid="7177565505195715659">"Umsindo"</string>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1c6f3ab..8e8c541 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -36,4 +36,12 @@
 
     <!-- Choice in the alarm sound picker.  If chosen, the default alarm sound will be used. -->
     <string name="alarm_sound_default">Default alarm sound</string>
+
+    <!-- Title for documents backend that offers images. [CHAR LIMIT=24] -->
+    <string name="root_images">Images</string>
+    <!-- Title for documents backend that offers videos. [CHAR LIMIT=24] -->
+    <string name="root_videos">Videos</string>
+    <!-- Title for documents backend that offers audio. [CHAR LIMIT=24] -->
+    <string name="root_audio">Audio</string>
+
 </resources>
diff --git a/src/com/android/providers/media/MediaDocumentsProvider.java b/src/com/android/providers/media/MediaDocumentsProvider.java
new file mode 100644
index 0000000..a887a83
--- /dev/null
+++ b/src/com/android/providers/media/MediaDocumentsProvider.java
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2013 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.providers.media;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.provider.MediaStore.Audio;
+import android.provider.MediaStore.Audio.AlbumColumns;
+import android.provider.MediaStore.Audio.Albums;
+import android.provider.MediaStore.Audio.ArtistColumns;
+import android.provider.MediaStore.Audio.Artists;
+import android.provider.MediaStore.Audio.AudioColumns;
+import android.provider.MediaStore.Files.FileColumns;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Images.ImageColumns;
+import android.provider.MediaStore.Video;
+import android.provider.MediaStore.Video.VideoColumns;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Presents a {@link DocumentsContract} view of {@link MediaProvider} external
+ * contents.
+ */
+public class MediaDocumentsProvider extends DocumentsProvider {
+    private static final String TAG = "MediaDocumentsProvider";
+
+    private static final String AUTHORITY = "com.android.providers.media.documents";
+
+    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
+            Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_MIME_TYPES
+    };
+
+    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+    };
+
+    private static final String IMAGE_MIME_TYPES = joinNewline("image/*");
+
+    private static final String VIDEO_MIME_TYPES = joinNewline("video/*");
+
+    private static final String AUDIO_MIME_TYPES = joinNewline(
+            "audio/*", "application/ogg", "application/x-flac");
+
+    private static final String TYPE_IMAGES_ROOT = "images_root";
+    private static final String TYPE_IMAGES_BUCKET = "images_bucket";
+    private static final String TYPE_IMAGE = "image";
+
+    private static final String TYPE_VIDEOS_ROOT = "videos_root";
+    private static final String TYPE_VIDEOS_BUCKET = "videos_bucket";
+    private static final String TYPE_VIDEO = "video";
+
+    private static final String TYPE_AUDIO_ROOT = "audio_root";
+    private static final String TYPE_AUDIO = "audio";
+    private static final String TYPE_ARTIST = "artist";
+    private static final String TYPE_ALBUM = "album";
+
+    private static boolean sReturnedImagesEmpty = false;
+    private static boolean sReturnedVideosEmpty = false;
+    private static boolean sReturnedAudioEmpty = false;
+
+    private static String joinNewline(String... args) {
+        return TextUtils.join("\n", args);
+    }
+
+    private void copyNotificationUri(MatrixCursor result, Cursor cursor) {
+        result.setNotificationUri(getContext().getContentResolver(), cursor.getNotificationUri());
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    private static void notifyRootsChanged(Context context) {
+        context.getContentResolver()
+                .notifyChange(DocumentsContract.buildRootsUri(AUTHORITY), null, false);
+    }
+
+    /**
+     * When inserting the first item of each type, we need to trigger a roots
+     * refresh to clear a previously reported {@link Root#FLAG_EMPTY}.
+     */
+    static void onMediaStoreInsert(Context context, String volumeName, int type, long id) {
+        if (!"external".equals(volumeName)) return;
+
+        if (type == FileColumns.MEDIA_TYPE_IMAGE && sReturnedImagesEmpty) {
+            sReturnedImagesEmpty = false;
+            notifyRootsChanged(context);
+        } else if (type == FileColumns.MEDIA_TYPE_VIDEO && sReturnedVideosEmpty) {
+            sReturnedVideosEmpty = false;
+            notifyRootsChanged(context);
+        } else if (type == FileColumns.MEDIA_TYPE_AUDIO && sReturnedAudioEmpty) {
+            sReturnedAudioEmpty = false;
+            notifyRootsChanged(context);
+        }
+    }
+
+    /**
+     * When deleting an item, we need to revoke any outstanding Uri grants.
+     */
+    static void onMediaStoreDelete(Context context, String volumeName, int type, long id) {
+        if (!"external".equals(volumeName)) return;
+
+        if (type == FileColumns.MEDIA_TYPE_IMAGE) {
+            final Uri uri = DocumentsContract.buildDocumentUri(
+                    AUTHORITY, getDocIdForIdent(TYPE_IMAGE, id));
+            context.revokeUriPermission(uri, ~0);
+        } else if (type == FileColumns.MEDIA_TYPE_VIDEO) {
+            final Uri uri = DocumentsContract.buildDocumentUri(
+                    AUTHORITY, getDocIdForIdent(TYPE_VIDEO, id));
+            context.revokeUriPermission(uri, ~0);
+        } else if (type == FileColumns.MEDIA_TYPE_AUDIO) {
+            final Uri uri = DocumentsContract.buildDocumentUri(
+                    AUTHORITY, getDocIdForIdent(TYPE_AUDIO, id));
+            context.revokeUriPermission(uri, ~0);
+        }
+    }
+
+    private static class Ident {
+        public String type;
+        public long id;
+    }
+
+    private static Ident getIdentForDocId(String docId) {
+        final Ident ident = new Ident();
+        final int split = docId.indexOf(':');
+        if (split == -1) {
+            ident.type = docId;
+            ident.id = -1;
+        } else {
+            ident.type = docId.substring(0, split);
+            ident.id = Long.parseLong(docId.substring(split + 1));
+        }
+        return ident;
+    }
+
+    private static String getDocIdForIdent(String type, long id) {
+        return type + ":" + id;
+    }
+
+    private static String[] resolveRootProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
+    }
+
+    private static String[] resolveDocumentProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
+    }
+
+    @Override
+    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
+        includeImagesRoot(result);
+        includeVideosRoot(result);
+        includeAudioRoot(result);
+        return result;
+    }
+
+    @Override
+    public Cursor queryDocument(String docId, String[] projection) throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        final Ident ident = getIdentForDocId(docId);
+
+        final long token = Binder.clearCallingIdentity();
+        Cursor cursor = null;
+        try {
+            if (TYPE_IMAGES_ROOT.equals(ident.type)) {
+                // single root
+                includeImagesRootDocument(result);
+            } else if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+                // single bucket
+                cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                        ImagesBucketQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + ident.id,
+                        null, ImagesBucketQuery.SORT_ORDER);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeImagesBucket(result, cursor);
+                }
+            } else if (TYPE_IMAGE.equals(ident.type)) {
+                // single image
+                cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                        ImageQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+                        null);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeImage(result, cursor);
+                }
+            } else if (TYPE_VIDEOS_ROOT.equals(ident.type)) {
+                // single root
+                includeVideosRootDocument(result);
+            } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+                // single bucket
+                cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                        VideosBucketQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + ident.id,
+                        null, VideosBucketQuery.SORT_ORDER);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeVideosBucket(result, cursor);
+                }
+            } else if (TYPE_VIDEO.equals(ident.type)) {
+                // single video
+                cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                        VideoQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+                        null);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeVideo(result, cursor);
+                }
+            } else if (TYPE_AUDIO_ROOT.equals(ident.type)) {
+                // single root
+                includeAudioRootDocument(result);
+            } else if (TYPE_ARTIST.equals(ident.type)) {
+                // single artist
+                cursor = resolver.query(Artists.EXTERNAL_CONTENT_URI,
+                        ArtistQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+                        null);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeArtist(result, cursor);
+                }
+            } else if (TYPE_ALBUM.equals(ident.type)) {
+                // single album
+                cursor = resolver.query(Albums.EXTERNAL_CONTENT_URI,
+                        AlbumQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+                        null);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeAlbum(result, cursor);
+                }
+            } else if (TYPE_AUDIO.equals(ident.type)) {
+                // single song
+                cursor = resolver.query(Audio.Media.EXTERNAL_CONTENT_URI,
+                        SongQuery.PROJECTION, BaseColumns._ID + "=" + ident.id, null,
+                        null);
+                copyNotificationUri(result, cursor);
+                if (cursor.moveToFirst()) {
+                    includeAudio(result, cursor);
+                }
+            } else {
+                throw new UnsupportedOperationException("Unsupported document " + docId);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+            Binder.restoreCallingIdentity(token);
+        }
+        return result;
+    }
+
+    @Override
+    public Cursor queryChildDocuments(String docId, String[] projection, String sortOrder)
+            throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        final Ident ident = getIdentForDocId(docId);
+
+        final long token = Binder.clearCallingIdentity();
+        Cursor cursor = null;
+        try {
+            if (TYPE_IMAGES_ROOT.equals(ident.type)) {
+                // include all unique buckets
+                cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                        ImagesBucketQuery.PROJECTION, null, null, ImagesBucketQuery.SORT_ORDER);
+                // multiple orders
+                copyNotificationUri(result, cursor);
+                long lastId = Long.MIN_VALUE;
+                while (cursor.moveToNext()) {
+                    final long id = cursor.getLong(ImagesBucketQuery.BUCKET_ID);
+                    if (lastId != id) {
+                        includeImagesBucket(result, cursor);
+                        lastId = id;
+                    }
+                }
+            } else if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+                // include images under bucket
+                cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                        ImageQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + ident.id,
+                        null, null);
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext()) {
+                    includeImage(result, cursor);
+                }
+            } else if (TYPE_VIDEOS_ROOT.equals(ident.type)) {
+                // include all unique buckets
+                cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                        VideosBucketQuery.PROJECTION, null, null, VideosBucketQuery.SORT_ORDER);
+                copyNotificationUri(result, cursor);
+                long lastId = Long.MIN_VALUE;
+                while (cursor.moveToNext()) {
+                    final long id = cursor.getLong(VideosBucketQuery.BUCKET_ID);
+                    if (lastId != id) {
+                        includeVideosBucket(result, cursor);
+                        lastId = id;
+                    }
+                }
+            } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+                // include videos under bucket
+                cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                        VideoQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + ident.id,
+                        null, null);
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext()) {
+                    includeVideo(result, cursor);
+                }
+            } else if (TYPE_AUDIO_ROOT.equals(ident.type)) {
+                // include all artists
+                cursor = resolver.query(Audio.Artists.EXTERNAL_CONTENT_URI,
+                        ArtistQuery.PROJECTION, null, null, null);
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext()) {
+                    includeArtist(result, cursor);
+                }
+            } else if (TYPE_ARTIST.equals(ident.type)) {
+                // include all albums under artist
+                cursor = resolver.query(Artists.Albums.getContentUri("external", ident.id),
+                        AlbumQuery.PROJECTION, null, null, null);
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext()) {
+                    includeAlbum(result, cursor);
+                }
+            } else if (TYPE_ALBUM.equals(ident.type)) {
+                // include all songs under album
+                cursor = resolver.query(Audio.Media.EXTERNAL_CONTENT_URI,
+                        SongQuery.PROJECTION, AudioColumns.ALBUM_ID + "=" + ident.id,
+                        null, null);
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext()) {
+                    includeAudio(result, cursor);
+                }
+            } else {
+                throw new UnsupportedOperationException("Unsupported document " + docId);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+            Binder.restoreCallingIdentity(token);
+        }
+        return result;
+    }
+
+    @Override
+    public Cursor queryRecentDocuments(String rootId, String[] projection)
+            throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+
+        final long token = Binder.clearCallingIdentity();
+        Cursor cursor = null;
+        try {
+            if (TYPE_IMAGES_ROOT.equals(rootId)) {
+                // include all unique buckets
+                cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                        ImageQuery.PROJECTION, null, null, ImageColumns.DATE_MODIFIED + " DESC");
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext() && result.getCount() < 64) {
+                    includeImage(result, cursor);
+                }
+            } else if (TYPE_VIDEOS_ROOT.equals(rootId)) {
+                // include all unique buckets
+                cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                        VideoQuery.PROJECTION, null, null, VideoColumns.DATE_MODIFIED + " DESC");
+                copyNotificationUri(result, cursor);
+                while (cursor.moveToNext() && result.getCount() < 64) {
+                    includeVideo(result, cursor);
+                }
+            } else {
+                throw new UnsupportedOperationException("Unsupported root " + rootId);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+            Binder.restoreCallingIdentity(token);
+        }
+        return result;
+    }
+
+    @Override
+    public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+            throws FileNotFoundException {
+        final Ident ident = getIdentForDocId(docId);
+
+        if (!"r".equals(mode)) {
+            throw new IllegalArgumentException("Media is read-only");
+        }
+
+        final Uri target;
+        if (TYPE_IMAGE.equals(ident.type) && ident.id != -1) {
+            target = ContentUris.withAppendedId(
+                    Images.Media.EXTERNAL_CONTENT_URI, ident.id);
+        } else if (TYPE_VIDEO.equals(ident.type) && ident.id != -1) {
+            target = ContentUris.withAppendedId(
+                    Video.Media.EXTERNAL_CONTENT_URI, ident.id);
+        } else if (TYPE_AUDIO.equals(ident.type) && ident.id != -1) {
+            target = ContentUris.withAppendedId(
+                    Audio.Media.EXTERNAL_CONTENT_URI, ident.id);
+        } else {
+            throw new UnsupportedOperationException("Unsupported document " + docId);
+        }
+
+        // Delegate to real provider
+        final long token = Binder.clearCallingIdentity();
+        try {
+            return getContext().getContentResolver().openFileDescriptor(target, mode);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public AssetFileDescriptor openDocumentThumbnail(
+            String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        final Ident ident = getIdentForDocId(docId);
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            if (TYPE_IMAGES_BUCKET.equals(ident.type)) {
+                final long id = getImageForBucketCleared(ident.id);
+                return openOrCreateImageThumbnailCleared(id, signal);
+            } else if (TYPE_IMAGE.equals(ident.type)) {
+                return openOrCreateImageThumbnailCleared(ident.id, signal);
+            } else if (TYPE_VIDEOS_BUCKET.equals(ident.type)) {
+                final long id = getVideoForBucketCleared(ident.id);
+                return openOrCreateVideoThumbnailCleared(id, signal);
+            } else if (TYPE_VIDEO.equals(ident.type)) {
+                return openOrCreateVideoThumbnailCleared(ident.id, signal);
+            } else {
+                throw new UnsupportedOperationException("Unsupported document " + docId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private boolean isEmpty(Uri uri) {
+        final ContentResolver resolver = getContext().getContentResolver();
+        final long token = Binder.clearCallingIdentity();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(uri, new String[] {
+                    BaseColumns._ID }, null, null, null);
+            return (cursor == null) || (cursor.getCount() == 0);
+        } finally {
+            IoUtils.closeQuietly(cursor);
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    private void includeImagesRoot(MatrixCursor result) {
+        int flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS;
+        if (isEmpty(Images.Media.EXTERNAL_CONTENT_URI)) {
+            flags |= Root.FLAG_EMPTY;
+            sReturnedImagesEmpty = true;
+        }
+
+        final RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, TYPE_IMAGES_ROOT);
+        row.add(Root.COLUMN_FLAGS, flags);
+        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_images));
+        row.add(Root.COLUMN_DOCUMENT_ID, TYPE_IMAGES_ROOT);
+        row.add(Root.COLUMN_MIME_TYPES, IMAGE_MIME_TYPES);
+    }
+
+    private void includeVideosRoot(MatrixCursor result) {
+        int flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_RECENTS;
+        if (isEmpty(Video.Media.EXTERNAL_CONTENT_URI)) {
+            flags |= Root.FLAG_EMPTY;
+            sReturnedVideosEmpty = true;
+        }
+
+        final RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, TYPE_VIDEOS_ROOT);
+        row.add(Root.COLUMN_FLAGS, flags);
+        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_videos));
+        row.add(Root.COLUMN_DOCUMENT_ID, TYPE_VIDEOS_ROOT);
+        row.add(Root.COLUMN_MIME_TYPES, VIDEO_MIME_TYPES);
+    }
+
+    private void includeAudioRoot(MatrixCursor result) {
+        int flags = Root.FLAG_LOCAL_ONLY;
+        if (isEmpty(Audio.Media.EXTERNAL_CONTENT_URI)) {
+            flags |= Root.FLAG_EMPTY;
+            sReturnedAudioEmpty = true;
+        }
+
+        final RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, TYPE_AUDIO_ROOT);
+        row.add(Root.COLUMN_FLAGS, flags);
+        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.root_audio));
+        row.add(Root.COLUMN_DOCUMENT_ID, TYPE_AUDIO_ROOT);
+        row.add(Root.COLUMN_MIME_TYPES, AUDIO_MIME_TYPES);
+    }
+
+    private void includeImagesRootDocument(MatrixCursor result) {
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, TYPE_IMAGES_ROOT);
+        row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_images));
+        row.add(Document.COLUMN_FLAGS,
+                Document.FLAG_DIR_PREFERS_GRID | Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+    }
+
+    private void includeVideosRootDocument(MatrixCursor result) {
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, TYPE_VIDEOS_ROOT);
+        row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_videos));
+        row.add(Document.COLUMN_FLAGS,
+                Document.FLAG_DIR_PREFERS_GRID | Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+    }
+
+    private void includeAudioRootDocument(MatrixCursor result) {
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, TYPE_AUDIO_ROOT);
+        row.add(Document.COLUMN_DISPLAY_NAME, getContext().getString(R.string.root_audio));
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+    }
+
+    private interface ImagesBucketQuery {
+        final String[] PROJECTION = new String[] {
+                ImageColumns.BUCKET_ID,
+                ImageColumns.BUCKET_DISPLAY_NAME,
+                ImageColumns.DATE_MODIFIED };
+        final String SORT_ORDER = ImageColumns.BUCKET_ID + ", " + ImageColumns.DATE_MODIFIED
+                + " DESC";
+
+        final int BUCKET_ID = 0;
+        final int BUCKET_DISPLAY_NAME = 1;
+        final int DATE_MODIFIED = 2;
+    }
+
+    private void includeImagesBucket(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(ImagesBucketQuery.BUCKET_ID);
+        final String docId = getDocIdForIdent(TYPE_IMAGES_BUCKET, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME,
+                cursor.getString(ImagesBucketQuery.BUCKET_DISPLAY_NAME));
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+        row.add(Document.COLUMN_LAST_MODIFIED,
+                cursor.getLong(ImagesBucketQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+        row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_GRID
+                | Document.FLAG_SUPPORTS_THUMBNAIL | Document.FLAG_DIR_PREFERS_LAST_MODIFIED
+                | Document.FLAG_DIR_HIDE_GRID_TITLES);
+    }
+
+    private interface ImageQuery {
+        final String[] PROJECTION = new String[] {
+                ImageColumns._ID,
+                ImageColumns.DISPLAY_NAME,
+                ImageColumns.MIME_TYPE,
+                ImageColumns.SIZE,
+                ImageColumns.DATE_MODIFIED };
+
+        final int _ID = 0;
+        final int DISPLAY_NAME = 1;
+        final int MIME_TYPE = 2;
+        final int SIZE = 3;
+        final int DATE_MODIFIED = 4;
+    }
+
+    private void includeImage(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(ImageQuery._ID);
+        final String docId = getDocIdForIdent(TYPE_IMAGE, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(ImageQuery.DISPLAY_NAME));
+        row.add(Document.COLUMN_SIZE, cursor.getLong(ImageQuery.SIZE));
+        row.add(Document.COLUMN_MIME_TYPE, cursor.getString(ImageQuery.MIME_TYPE));
+        row.add(Document.COLUMN_LAST_MODIFIED,
+                cursor.getLong(ImageQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+        row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_THUMBNAIL);
+    }
+
+    private interface VideosBucketQuery {
+        final String[] PROJECTION = new String[] {
+                VideoColumns.BUCKET_ID,
+                VideoColumns.BUCKET_DISPLAY_NAME,
+                VideoColumns.DATE_MODIFIED };
+        final String SORT_ORDER = VideoColumns.BUCKET_ID + ", " + VideoColumns.DATE_MODIFIED
+                + " DESC";
+
+        final int BUCKET_ID = 0;
+        final int BUCKET_DISPLAY_NAME = 1;
+        final int DATE_MODIFIED = 2;
+    }
+
+    private void includeVideosBucket(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(VideosBucketQuery.BUCKET_ID);
+        final String docId = getDocIdForIdent(TYPE_VIDEOS_BUCKET, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME,
+                cursor.getString(VideosBucketQuery.BUCKET_DISPLAY_NAME));
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+        row.add(Document.COLUMN_LAST_MODIFIED,
+                cursor.getLong(VideosBucketQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+        row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_GRID
+                | Document.FLAG_SUPPORTS_THUMBNAIL | Document.FLAG_DIR_PREFERS_LAST_MODIFIED
+                | Document.FLAG_DIR_HIDE_GRID_TITLES);
+    }
+
+    private interface VideoQuery {
+        final String[] PROJECTION = new String[] {
+                VideoColumns._ID,
+                VideoColumns.DISPLAY_NAME,
+                VideoColumns.MIME_TYPE,
+                VideoColumns.SIZE,
+                VideoColumns.DATE_MODIFIED };
+
+        final int _ID = 0;
+        final int DISPLAY_NAME = 1;
+        final int MIME_TYPE = 2;
+        final int SIZE = 3;
+        final int DATE_MODIFIED = 4;
+    }
+
+    private void includeVideo(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(VideoQuery._ID);
+        final String docId = getDocIdForIdent(TYPE_VIDEO, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(VideoQuery.DISPLAY_NAME));
+        row.add(Document.COLUMN_SIZE, cursor.getLong(VideoQuery.SIZE));
+        row.add(Document.COLUMN_MIME_TYPE, cursor.getString(VideoQuery.MIME_TYPE));
+        row.add(Document.COLUMN_LAST_MODIFIED,
+                cursor.getLong(VideoQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+        row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_THUMBNAIL);
+    }
+
+    private interface ArtistQuery {
+        final String[] PROJECTION = new String[] {
+                BaseColumns._ID,
+                ArtistColumns.ARTIST };
+
+        final int _ID = 0;
+        final int ARTIST = 1;
+    }
+
+    private void includeArtist(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(ArtistQuery._ID);
+        final String docId = getDocIdForIdent(TYPE_ARTIST, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(ArtistQuery.ARTIST));
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+    }
+
+    private interface AlbumQuery {
+        final String[] PROJECTION = new String[] {
+                BaseColumns._ID,
+                AlbumColumns.ALBUM };
+
+        final int _ID = 0;
+        final int ALBUM = 1;
+    }
+
+    private void includeAlbum(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(AlbumQuery._ID);
+        final String docId = getDocIdForIdent(TYPE_ALBUM, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(AlbumQuery.ALBUM));
+        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+    }
+
+    private interface SongQuery {
+        final String[] PROJECTION = new String[] {
+                AudioColumns._ID,
+                AudioColumns.TITLE,
+                AudioColumns.MIME_TYPE,
+                AudioColumns.SIZE,
+                AudioColumns.DATE_MODIFIED };
+
+        final int _ID = 0;
+        final int TITLE = 1;
+        final int MIME_TYPE = 2;
+        final int SIZE = 3;
+        final int DATE_MODIFIED = 4;
+    }
+
+    private void includeAudio(MatrixCursor result, Cursor cursor) {
+        final long id = cursor.getLong(SongQuery._ID);
+        final String docId = getDocIdForIdent(TYPE_AUDIO, id);
+
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, docId);
+        row.add(Document.COLUMN_DISPLAY_NAME, cursor.getString(SongQuery.TITLE));
+        row.add(Document.COLUMN_SIZE, cursor.getLong(SongQuery.SIZE));
+        row.add(Document.COLUMN_MIME_TYPE, cursor.getString(SongQuery.MIME_TYPE));
+        row.add(Document.COLUMN_LAST_MODIFIED,
+                cursor.getLong(SongQuery.DATE_MODIFIED) * DateUtils.SECOND_IN_MILLIS);
+    }
+
+    private interface ImagesBucketThumbnailQuery {
+        final String[] PROJECTION = new String[] {
+                ImageColumns._ID,
+                ImageColumns.BUCKET_ID,
+                ImageColumns.DATE_MODIFIED };
+
+        final int _ID = 0;
+        final int BUCKET_ID = 1;
+        final int DATE_MODIFIED = 2;
+    }
+
+    private long getImageForBucketCleared(long bucketId) throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                    ImagesBucketThumbnailQuery.PROJECTION, ImageColumns.BUCKET_ID + "=" + bucketId,
+                    null, ImageColumns.DATE_MODIFIED + " DESC");
+            if (cursor.moveToFirst()) {
+                return cursor.getLong(ImagesBucketThumbnailQuery._ID);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+        throw new FileNotFoundException("No video found for bucket");
+    }
+
+    private interface ImageThumbnailQuery {
+        final String[] PROJECTION = new String[] {
+                Images.Thumbnails.DATA };
+
+        final int _DATA = 0;
+    }
+
+    private ParcelFileDescriptor openImageThumbnailCleared(long id, CancellationSignal signal)
+            throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Images.Thumbnails.EXTERNAL_CONTENT_URI,
+                    ImageThumbnailQuery.PROJECTION, Images.Thumbnails.IMAGE_ID + "=" + id, null,
+                    null, signal);
+            if (cursor.moveToFirst()) {
+                final String data = cursor.getString(ImageThumbnailQuery._DATA);
+                return ParcelFileDescriptor.open(
+                        new File(data), ParcelFileDescriptor.MODE_READ_ONLY);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+        return null;
+    }
+
+    private AssetFileDescriptor openOrCreateImageThumbnailCleared(
+            long id, CancellationSignal signal) throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        ParcelFileDescriptor pfd = openImageThumbnailCleared(id, signal);
+        if (pfd == null) {
+            // No thumbnail yet, so generate. This is messy, since we drop the
+            // Bitmap on the floor, but its the least-complicated way.
+            final BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inJustDecodeBounds = true;
+            Images.Thumbnails.getThumbnail(resolver, id, Images.Thumbnails.MINI_KIND, opts);
+
+            pfd = openImageThumbnailCleared(id, signal);
+        }
+
+        if (pfd == null) {
+            // Phoey, fallback to full image
+            final Uri fullUri = ContentUris.withAppendedId(Images.Media.EXTERNAL_CONTENT_URI, id);
+            pfd = resolver.openFileDescriptor(fullUri, "r", signal);
+        }
+
+        final int orientation = queryOrientationForImage(id, signal);
+        final Bundle extras;
+        if (orientation != 0) {
+            extras = new Bundle(1);
+            extras.putInt(DocumentsContract.EXTRA_ORIENTATION, orientation);
+        } else {
+            extras = null;
+        }
+
+        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, extras);
+    }
+
+    private interface VideosBucketThumbnailQuery {
+        final String[] PROJECTION = new String[] {
+                VideoColumns._ID,
+                VideoColumns.BUCKET_ID,
+                VideoColumns.DATE_MODIFIED };
+
+        final int _ID = 0;
+        final int BUCKET_ID = 1;
+        final int DATE_MODIFIED = 2;
+    }
+
+    private long getVideoForBucketCleared(long bucketId)
+            throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Video.Media.EXTERNAL_CONTENT_URI,
+                    VideosBucketThumbnailQuery.PROJECTION, VideoColumns.BUCKET_ID + "=" + bucketId,
+                    null, VideoColumns.DATE_MODIFIED + " DESC");
+            if (cursor.moveToFirst()) {
+                return cursor.getLong(VideosBucketThumbnailQuery._ID);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+        throw new FileNotFoundException("No video found for bucket");
+    }
+
+    private interface VideoThumbnailQuery {
+        final String[] PROJECTION = new String[] {
+                Video.Thumbnails.DATA };
+
+        final int _DATA = 0;
+    }
+
+    private AssetFileDescriptor openVideoThumbnailCleared(long id, CancellationSignal signal)
+            throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Video.Thumbnails.EXTERNAL_CONTENT_URI,
+                    VideoThumbnailQuery.PROJECTION, Video.Thumbnails.VIDEO_ID + "=" + id, null,
+                    null, signal);
+            if (cursor.moveToFirst()) {
+                final String data = cursor.getString(VideoThumbnailQuery._DATA);
+                return new AssetFileDescriptor(ParcelFileDescriptor.open(
+                        new File(data), ParcelFileDescriptor.MODE_READ_ONLY), 0,
+                        AssetFileDescriptor.UNKNOWN_LENGTH);
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+        return null;
+    }
+
+    private AssetFileDescriptor openOrCreateVideoThumbnailCleared(
+            long id, CancellationSignal signal) throws FileNotFoundException {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        AssetFileDescriptor afd = openVideoThumbnailCleared(id, signal);
+        if (afd == null) {
+            // No thumbnail yet, so generate. This is messy, since we drop the
+            // Bitmap on the floor, but its the least-complicated way.
+            final BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inJustDecodeBounds = true;
+            Video.Thumbnails.getThumbnail(resolver, id, Video.Thumbnails.MINI_KIND, opts);
+
+            afd = openVideoThumbnailCleared(id, signal);
+        }
+
+        return afd;
+    }
+
+    private interface ImageOrientationQuery {
+        final String[] PROJECTION = new String[] {
+                ImageColumns.ORIENTATION };
+
+        final int ORIENTATION = 0;
+    }
+
+    private int queryOrientationForImage(long id, CancellationSignal signal) {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        Cursor cursor = null;
+        try {
+            cursor = resolver.query(Images.Media.EXTERNAL_CONTENT_URI,
+                    ImageOrientationQuery.PROJECTION, ImageColumns._ID + "=" + id, null, null,
+                    signal);
+            if (cursor.moveToFirst()) {
+                return cursor.getInt(ImageOrientationQuery.ORIENTATION);
+            } else {
+                Log.w(TAG, "Missing orientation data for " + id);
+                return 0;
+            }
+        } finally {
+            IoUtils.closeQuietly(cursor);
+        }
+    }
+}
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 3e7ee05..9739893 100755
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -38,6 +38,7 @@
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.UriMatcher;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -59,7 +60,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
@@ -84,6 +84,12 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+import libcore.io.StructStat;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -101,12 +107,6 @@
 import java.util.PriorityQueue;
 import java.util.Stack;
 
-import libcore.io.ErrnoException;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
-import libcore.io.OsConstants;
-import libcore.io.StructStat;
-
 /**
  * Media content provider. See {@link android.provider.MediaStore} for details.
  * Separate databases are kept for each external storage card we see (using the
@@ -139,6 +139,8 @@
         }
     }
 
+    private StorageManager mStorageManager;
+
     // In memory cache of path<->id mappings, to speed up inserts during media scan
     HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
 
@@ -232,6 +234,8 @@
 
     private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
 
+    private static final String CANONICAL = "canonical";
+
     private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -265,7 +269,7 @@
                                 // We do this to avoid deleting files if the volume is remounted while
                                 // we are still processing the unmount event.
                                 ContentValues values = new ContentValues();
-                                values.put(Files.FileColumns.DATA, "");
+                                values.putNull(Files.FileColumns.DATA);
                                 String where = FileColumns.STORAGE_ID + "=?";
                                 String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
                                 database.mNumUpdates++;
@@ -546,6 +550,8 @@
     public boolean onCreate() {
         final Context context = getContext();
 
+        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+
         sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
                 MediaStore.Audio.Albums._ID);
         sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
@@ -635,7 +641,7 @@
                         d = (ThumbData)mThumbRequestStack.pop();
                     }
 
-                    makeThumbInternal(d);
+                    IoUtils.closeQuietly(makeThumbInternal(d));
                     synchronized (mPendingThumbs) {
                         mPendingThumbs.remove(d.path);
                     }
@@ -1821,6 +1827,19 @@
             db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
         }
 
+        if (fromVersion < 700) {
+            // fix datetaken fields that were added with an incorrect timestamp
+            // datetaken needs to be in milliseconds, so should generally be a few orders of
+            // magnitude larger than date_modified. If it's within the same order of magnitude, it
+            // is probably wrong.
+            // (this could do the wrong thing if your picture was actually taken before ~3/21/1970)
+            db.execSQL("UPDATE files set datetaken=date_modified*1000"
+                    + " WHERE date_modified IS NOT NULL"
+                    + " AND datetaken IS NOT NULL"
+                    + " AND datetaken<date_modified*5;");
+        }
+
+
         sanityCheck(db, fromVersion);
         long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
         logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
@@ -2117,10 +2136,100 @@
         }
         return true;
     }
+
+    @Override
+    public Uri canonicalize(Uri uri) {
+        int match = URI_MATCHER.match(uri);
+
+        // only support canonicalizing specific audio Uris
+        if (match != AUDIO_MEDIA_ID) {
+            return null;
+        }
+
+        Cursor c = query(uri, null, null, null, null);
+        if (c == null) {
+            return null;
+        }
+        if (c.getCount() != 1 || !c.moveToNext()) {
+            c.close();
+            return null;
+        }
+
+        // Construct a canonical Uri by tacking on some query parameters
+        Uri.Builder builder = uri.buildUpon();
+        builder.appendQueryParameter(CANONICAL, "1");
+        String title = c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE));
+        c.close();
+        if (TextUtils.isEmpty(title)) {
+            return null;
+        }
+        builder.appendQueryParameter(MediaStore.Audio.Media.TITLE, title);
+        Uri newUri = builder.build();
+        return newUri;
+    }
+
+    @Override
+    public Uri uncanonicalize(Uri uri) {
+        if (uri != null && "1".equals(uri.getQueryParameter(CANONICAL))) {
+            int match = URI_MATCHER.match(uri);
+            if (match != AUDIO_MEDIA_ID) {
+                // this type of canonical Uri is not supported
+                return null;
+            }
+            String titleFromUri = uri.getQueryParameter(MediaStore.Audio.Media.TITLE);
+            if (titleFromUri == null) {
+                // the required parameter is missing
+                return null;
+            }
+            // clear the query parameters, we don't need them anymore
+            uri = uri.buildUpon().clearQuery().build();
+
+            Cursor c = query(uri, null, null, null, null);
+
+            int titleIdx = c.getColumnIndex(MediaStore.Audio.Media.TITLE);
+            if (c != null && c.getCount() == 1 && c.moveToNext() &&
+                    titleFromUri.equals(c.getString(titleIdx))) {
+                // the result matched perfectly
+                c.close();
+                return uri;
+            }
+
+            c.close();
+            // do a lookup by title
+            Uri newUri = MediaStore.Audio.Media.getContentUri(uri.getPathSegments().get(0));
+
+            c = query(newUri, null, MediaStore.Audio.Media.TITLE + "=?",
+                    new String[] {titleFromUri}, null);
+            if (c == null) {
+                return null;
+            }
+            if (!c.moveToNext()) {
+                c.close();
+                return null;
+            }
+            // get the first matching entry and return a Uri for it
+            long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID));
+            c.close();
+            return ContentUris.withAppendedId(newUri, id);
+        }
+        return uri;
+    }
+
+    private Uri safeUncanonicalize(Uri uri) {
+        Uri newUri = uncanonicalize(uri);
+        if (newUri != null) {
+            return newUri;
+        }
+        return uri;
+    }
+
     @SuppressWarnings("fallthrough")
     @Override
     public Cursor query(Uri uri, String[] projectionIn, String selection,
             String[] selectionArgs, String sort) {
+
+        uri = safeUncanonicalize(uri);
+
         int table = URI_MATCHER.match(uri);
         List<String> prependArgs = new ArrayList<String>();
 
@@ -3250,6 +3359,8 @@
 
     private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
                                ArrayList<Long> notifyRowIds) {
+        final String volumeName = getVolumeName(uri);
+
         long rowId;
 
         if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
@@ -3290,8 +3401,10 @@
                 rowId = insertFile(helper, uri, initialValues,
                         FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
                 if (rowId > 0) {
+                    MediaDocumentsProvider.onMediaStoreInsert(
+                            getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId);
                     newUri = ContentUris.withAppendedId(
-                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
+                            Images.Media.getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3304,7 +3417,7 @@
                 rowId = db.insert("thumbnails", "name", values);
                 if (rowId > 0) {
                     newUri = ContentUris.withAppendedId(Images.Thumbnails.
-                            getContentUri(uri.getPathSegments().get(0)), rowId);
+                            getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3317,7 +3430,7 @@
                 rowId = db.insert("videothumbnails", "name", values);
                 if (rowId > 0) {
                     newUri = ContentUris.withAppendedId(Video.Thumbnails.
-                            getContentUri(uri.getPathSegments().get(0)), rowId);
+                            getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3326,7 +3439,10 @@
                 rowId = insertFile(helper, uri, initialValues,
                         FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
                 if (rowId > 0) {
-                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
+                    MediaDocumentsProvider.onMediaStoreInsert(
+                            getContext(), volumeName, FileColumns.MEDIA_TYPE_AUDIO, rowId);
+                    newUri = ContentUris.withAppendedId(
+                            Audio.Media.getContentUri(volumeName), rowId);
                     if (genre != null) {
                         updateGenre(rowId, genre);
                     }
@@ -3363,7 +3479,8 @@
                 helper.mNumInserts++;
                 rowId = db.insert("audio_genres", "audio_id", initialValues);
                 if (rowId > 0) {
-                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
+                    newUri = ContentUris.withAppendedId(
+                            Audio.Genres.getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3386,7 +3503,8 @@
                 rowId = insertFile(helper, uri, values,
                         FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
                 if (rowId > 0) {
-                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
+                    newUri = ContentUris.withAppendedId(
+                            Audio.Playlists.getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3408,8 +3526,10 @@
                 rowId = insertFile(helper, uri, initialValues,
                         FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
                 if (rowId > 0) {
-                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
-                            uri.getPathSegments().get(0)), rowId);
+                    MediaDocumentsProvider.onMediaStoreInsert(
+                            getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId);
+                    newUri = ContentUris.withAppendedId(
+                            Video.Media.getContentUri(volumeName), rowId);
                 }
                 break;
             }
@@ -3463,7 +3583,7 @@
                 rowId = insertFile(helper, uri, initialValues,
                         FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
                 if (rowId > 0) {
-                    newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
+                    newUri = Files.getContentUri(volumeName, rowId);
                 }
                 break;
 
@@ -3472,7 +3592,7 @@
                 rowId = insertFile(helper, uri, initialValues,
                         FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
                 if (rowId > 0) {
-                    newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
+                    newUri = Files.getMtpObjectsUri(volumeName, rowId);
                 }
                 break;
 
@@ -3812,6 +3932,7 @@
 
     @Override
     public int delete(Uri uri, String userWhere, String[] whereArgs) {
+        uri = safeUncanonicalize(uri);
         int count;
         int match = URI_MATCHER.match(uri);
 
@@ -3850,6 +3971,8 @@
                 }
             }
         } else {
+            final String volumeName = getVolumeName(uri);
+
             DatabaseHelper database = getDatabaseForUri(uri);
             if (database == null) {
                 throw new UnsupportedOperationException(
@@ -3860,7 +3983,6 @@
 
             synchronized (sGetTableAndWhereParam) {
                 getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
-
                 if (sGetTableAndWhereParam.table.equals("files")) {
                     String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
                     if (deleteparam == null || ! deleteparam.equals("false")) {
@@ -3871,30 +3993,37 @@
                         String [] idvalue = new String[] { "" };
                         String [] playlistvalues = new String[] { "", "" };
                         while (c.moveToNext()) {
-                            int mediatype = c.getInt(0);
-                            if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) {
-                                try {
-                                    Libcore.os.remove(c.getString(1));
-                                    idvalue[0] =  "" + c.getLong(2);
-                                    database.mNumQueries++;
-                                    Cursor cc = db.query("thumbnails", sDataOnlyColumn,
-                                            "image_id=?", idvalue, null, null, null);
-                                    while (cc.moveToNext()) {
-                                        Libcore.os.remove(cc.getString(0));
-                                    }
-                                    cc.close();
-                                    database.mNumDeletes++;
-                                    db.delete("thumbnails", "image_id=?", idvalue);
-                                } catch (ErrnoException e) {
+                            final int mediaType = c.getInt(0);
+                            final String data = c.getString(1);
+                            final long id = c.getLong(2);
+
+                            if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
+                                deleteIfAllowed(uri, data);
+                                MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
+                                        FileColumns.MEDIA_TYPE_IMAGE, id);
+
+                                idvalue[0] = String.valueOf(id);
+                                database.mNumQueries++;
+                                Cursor cc = db.query("thumbnails", sDataOnlyColumn,
+                                        "image_id=?", idvalue, null, null, null);
+                                while (cc.moveToNext()) {
+                                    deleteIfAllowed(uri, cc.getString(0));
                                 }
-                            } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) {
-                                try {
-                                    Libcore.os.remove(c.getString(1));
-                                } catch (ErrnoException e) {
-                                }
-                            } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) {
+                                cc.close();
+                                database.mNumDeletes++;
+                                db.delete("thumbnails", "image_id=?", idvalue);
+
+                            } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
+                                deleteIfAllowed(uri, data);
+                                MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
+                                        FileColumns.MEDIA_TYPE_VIDEO, id);
+
+                            } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) {
                                 if (!database.mInternal) {
-                                    idvalue[0] =  "" + c.getLong(2);
+                                    MediaDocumentsProvider.onMediaStoreDelete(getContext(),
+                                            volumeName, FileColumns.MEDIA_TYPE_AUDIO, id);
+
+                                    idvalue[0] = String.valueOf(id);
                                     database.mNumDeletes += 2; // also count the one below
                                     db.delete("audio_genres_map", "audio_id=?", idvalue);
                                     // for each playlist that the item appears in, move
@@ -3914,7 +4043,7 @@
                                     cc.close();
                                     db.delete("audio_playlists_map", "audio_id=?", idvalue);
                                 }
-                            } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) {
+                            } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
                                 // TODO, maybe: remove the audio_playlists_cleanup trigger and implement
                                 // it functionality here (clean up the playlist map)
                             }
@@ -3951,10 +4080,7 @@
                                 sGetTableAndWhereParam.where, whereArgs, null, null, null);
                         if (c != null) {
                             while (c.moveToNext()) {
-                                try {
-                                    Libcore.os.remove(c.getString(0));
-                                } catch (ErrnoException e) {
-                                }
+                                deleteIfAllowed(uri, c.getString(0));
                             }
                             c.close();
                         }
@@ -3969,12 +4095,12 @@
                                 sGetTableAndWhereParam.where, whereArgs);
                         break;
                 }
+
                 // Since there are multiple Uris that can refer to the same files
                 // and deletes can affect other objects in storage (like subdirectories
                 // or playlists) we will notify a change on the entire volume to make
                 // sure no listeners miss the notification.
-                String volume = uri.getPathSegments().get(0);
-                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
+                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName);
                 getContext().getContentResolver().notifyChange(notifyUri, null);
             }
         }
@@ -3994,6 +4120,7 @@
     @Override
     public int update(Uri uri, ContentValues initialValues, String userWhere,
             String[] whereArgs) {
+        uri = safeUncanonicalize(uri);
         int count;
         // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
         int match = URI_MATCHER.match(uri);
@@ -4328,6 +4455,7 @@
     public ParcelFileDescriptor openFile(Uri uri, String mode)
             throws FileNotFoundException {
 
+        uri = safeUncanonicalize(uri);
         ParcelFileDescriptor pfd = null;
 
         if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
@@ -4440,10 +4568,33 @@
      */
     private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
             throws FileNotFoundException {
-        final int modeBits = ContentResolver.modeToMode(uri, mode);
-        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
+        final int modeBits = ParcelFileDescriptor.parseMode(mode);
 
         File file = queryForDataFile(uri);
+
+        checkAccess(uri, file, modeBits);
+
+        // Bypass emulation layer when file is opened for reading, but only
+        // when opening read-only and we have an exact match.
+        if (modeBits == MODE_READ_ONLY) {
+            file = Environment.maybeTranslateEmulatedPathToInternal(file);
+        }
+
+        return ParcelFileDescriptor.open(file, modeBits);
+    }
+
+    private void deleteIfAllowed(Uri uri, String path) {
+        try {
+            File file = new File(path);
+            checkAccess(uri, file, ParcelFileDescriptor.MODE_WRITE_ONLY);
+            file.delete();
+        } catch (Exception e) {
+            Log.e(TAG, "Couldn't delete " + path);
+        }
+    }
+
+    private void checkAccess(Uri uri, File file, int modeBits) throws FileNotFoundException {
+        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
         final String path;
         try {
             path = file.getCanonicalPath();
@@ -4452,19 +4603,21 @@
         }
 
         if (path.startsWith(sExternalPath) || path.startsWith(sLegacyPath)) {
-            getContext().enforceCallingOrSelfPermission(
-                    READ_EXTERNAL_STORAGE, "External path: " + path);
+            Context c = getContext();
+            if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                    != PackageManager.PERMISSION_GRANTED) {
+                c.enforceCallingOrSelfPermission(
+                        READ_EXTERNAL_STORAGE, "External path: " + path);
+            }
 
             if (isWrite) {
-                getContext().enforceCallingOrSelfPermission(
-                        WRITE_EXTERNAL_STORAGE, "External path: " + path);
+                if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    c.enforceCallingOrSelfPermission(
+                            WRITE_EXTERNAL_STORAGE, "External path: " + path);
+                }
             }
 
-            // Bypass emulation layer when file is opened for reading, but only
-            // when opening read-only and we have an exact match.
-            if (modeBits == MODE_READ_ONLY) {
-                file = Environment.maybeTranslateEmulatedPathToInternal(file);
-            }
         } else if (path.startsWith(sCachePath)) {
             getContext().enforceCallingOrSelfPermission(
                     ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
@@ -4478,8 +4631,6 @@
         } else {
             checkWorldReadAccess(path);
         }
-
-        return ParcelFileDescriptor.open(file, modeBits);
     }
 
     private boolean isSecondaryExternalPath(String path) {
@@ -5080,16 +5231,15 @@
                         false, mObjectRemovedCallback);
             } else if (EXTERNAL_VOLUME.equals(volume)) {
                 if (Environment.isExternalStorageRemovable()) {
-                    String path = mExternalStoragePaths[0];
-                    int volumeID = FileUtils.getFatVolumeId(path);
-                    if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
+                    final StorageVolume actualVolume = mStorageManager.getPrimaryVolume();
+                    final int volumeId = actualVolume.getFatVolumeId();
 
                     // Must check for failure!
                     // If the volume is not (yet) mounted, this will create a new
                     // external-ffffffff.db database instead of the one we expect.  Then, if
                     // android.process.media is later killed and respawned, the real external
                     // database will be attached, containing stale records, or worse, be empty.
-                    if (volumeID == -1) {
+                    if (volumeId == -1) {
                         String state = Environment.getExternalStorageState();
                         if (Environment.MEDIA_MOUNTED.equals(state) ||
                                 Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
@@ -5108,10 +5258,10 @@
                     }
 
                     // generate database name based on volume ID
-                    String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
+                    String dbName = "external-" + Integer.toHexString(volumeId) + ".db";
                     helper = new DatabaseHelper(context, dbName, false,
                             false, mObjectRemovedCallback);
-                    mVolumeId = volumeID;
+                    mVolumeId = volumeId;
                 } else {
                     // external database name should be EXTERNAL_DATABASE_NAME
                     // however earlier releases used the external-XXXXXXXX.db naming
@@ -5423,6 +5573,15 @@
         URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
     }
 
+    private static String getVolumeName(Uri uri) {
+        final List<String> segments = uri.getPathSegments();
+        if (segments != null && segments.size() > 0) {
+            return segments.get(0);
+        } else {
+            return null;
+        }
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         Collection<DatabaseHelper> foo = mDatabases.values();
diff --git a/src/com/android/providers/media/MediaScannerReceiver.java b/src/com/android/providers/media/MediaScannerReceiver.java
index 0e0d321..89635b9 100644
--- a/src/com/android/providers/media/MediaScannerReceiver.java
+++ b/src/com/android/providers/media/MediaScannerReceiver.java
@@ -25,6 +25,9 @@
 import android.os.Environment;
 import android.util.Log;
 
+import java.io.File;
+import java.io.IOException;
+
 public class MediaScannerReceiver extends BroadcastReceiver {
     private final static String TAG = "MediaScannerReceiver";
 
@@ -42,6 +45,17 @@
                 // handle intents related to external storage
                 String path = uri.getPath();
                 String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
+                String legacyPath = Environment.getLegacyExternalStorageDirectory().getPath();
+
+                try {
+                    path = new File(path).getCanonicalPath();
+                } catch (IOException e) {
+                    Log.e(TAG, "couldn't canonicalize " + path);
+                    return;
+                }
+                if (path.startsWith(legacyPath)) {
+                    path = externalStoragePath + path.substring(legacyPath.length());
+                }
 
                 Log.d(TAG, "action: " + action + " path: " + path);
                 if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
diff --git a/src/com/android/providers/media/RingtonePickerActivity.java b/src/com/android/providers/media/RingtonePickerActivity.java
index c5118e7..3075bbb 100644
--- a/src/com/android/providers/media/RingtonePickerActivity.java
+++ b/src/com/android/providers/media/RingtonePickerActivity.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.util.Log;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ListView;
@@ -44,6 +45,8 @@
         AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener,
         AlertController.AlertParams.OnPrepareListViewListener {
 
+    private static final int POS_UNKNOWN = -1;
+
     private static final String TAG = "RingtonePickerActivity";
 
     private static final int DELAY_MS_SELECTION_PLAYED = 300;
@@ -57,16 +60,16 @@
     private Handler mHandler;
 
     /** The position in the list of the 'Silent' item. */
-    private int mSilentPos = -1;
+    private int mSilentPos = POS_UNKNOWN;
 
     /** The position in the list of the 'Default' item. */
-    private int mDefaultRingtonePos = -1;
+    private int mDefaultRingtonePos = POS_UNKNOWN;
 
     /** The position in the list of the last clicked item. */
-    private int mClickedPos = -1;
+    private int mClickedPos = POS_UNKNOWN;
 
     /** The position in the list of the ringtone to sample. */
-    private int mSampleRingtonePos = -1;
+    private int mSampleRingtonePos = POS_UNKNOWN;
 
     /** Whether this list has the 'Silent' item. */
     private boolean mHasSilentItem;
@@ -90,6 +93,18 @@
      */
     private Ringtone mDefaultRingtone;
 
+    /**
+     * The ringtone that's currently playing, unless the currently playing one is the default
+     * ringtone.
+     */
+    private Ringtone mCurrentRingtone;
+
+    /**
+     * Keep the currently playing ringtone around when changing orientation, so that it
+     * can be stopped later, after the activity is recreated.
+     */
+    private static Ringtone sPlayingRingtone;
+
     private DialogInterface.OnClickListener mRingtoneClickListener =
             new DialogInterface.OnClickListener() {
 
@@ -125,7 +140,7 @@
         }
 
         if (savedInstanceState != null) {
-            mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, -1);
+            mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, POS_UNKNOWN);
         }
         // Get whether to show the 'Silent' item
         mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
@@ -133,11 +148,6 @@
         // Give the Activity so it can do managed queries
         mRingtoneManager = new RingtoneManager(this);
 
-        // Get whether to include DRM ringtones
-        final boolean includeDrm = intent.getBooleanExtra(
-                RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, true);
-        mRingtoneManager.setIncludeDrm(includeDrm);
-
         // Get the types of ringtones to show
         mType = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1);
         if (mType != -1) {
@@ -172,7 +182,6 @@
 
         setupAlert();
     }
-
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -184,7 +193,7 @@
         if (mHasDefaultItem) {
             mDefaultRingtonePos = addDefaultRingtoneItem(listView);
 
-            if (RingtoneManager.isDefault(mExistingUri)) {
+            if (mClickedPos == POS_UNKNOWN && RingtoneManager.isDefault(mExistingUri)) {
                 mClickedPos = mDefaultRingtonePos;
             }
         }
@@ -193,12 +202,12 @@
             mSilentPos = addSilentItem(listView);
 
             // The 'Silent' item should use a null Uri
-            if (mExistingUri == null) {
+            if (mClickedPos == POS_UNKNOWN && mExistingUri == null) {
                 mClickedPos = mSilentPos;
             }
         }
 
-        if (mClickedPos == -1) {
+        if (mClickedPos == POS_UNKNOWN) {
             mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri));
         }
 
@@ -292,21 +301,11 @@
     }
 
     public void run() {
-
+        stopAnyPlayingRingtone();
         if (mSampleRingtonePos == mSilentPos) {
-            mRingtoneManager.stopPreviousRingtone();
             return;
         }
 
-        /*
-         * Stop the default ringtone, if it's playing (other ringtones will be
-         * stopped by the RingtoneManager when we get another Ringtone from it.
-         */
-        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
-            mDefaultRingtone.stop();
-            mDefaultRingtone = null;
-        }
-
         Ringtone ringtone;
         if (mSampleRingtonePos == mDefaultRingtonePos) {
             if (mDefaultRingtone == null) {
@@ -320,17 +319,10 @@
                 mDefaultRingtone.setStreamType(mRingtoneManager.inferStreamType());
             }
             ringtone = mDefaultRingtone;
-
-            /*
-             * Normally the non-static RingtoneManager.getRingtone stops the
-             * previous ringtone, but we're getting the default ringtone outside
-             * of the RingtoneManager instance, so let's stop the previous
-             * ringtone manually.
-             */
-            mRingtoneManager.stopPreviousRingtone();
-
+            mCurrentRingtone = null;
         } else {
             ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos));
+            mCurrentRingtone = ringtone;
         }
 
         if (ringtone != null) {
@@ -341,16 +333,34 @@
     @Override
     protected void onStop() {
         super.onStop();
-        stopAnyPlayingRingtone();
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        } else {
+            saveAnyPlayingRingtone();
+        }
     }
 
     @Override
     protected void onPause() {
         super.onPause();
-        stopAnyPlayingRingtone();
+        if (!isChangingConfigurations()) {
+            stopAnyPlayingRingtone();
+        }
+    }
+
+    private void saveAnyPlayingRingtone() {
+        if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
+            sPlayingRingtone = mDefaultRingtone;
+        } else if (mCurrentRingtone != null && mCurrentRingtone.isPlaying()) {
+            sPlayingRingtone = mCurrentRingtone;
+        }
     }
 
     private void stopAnyPlayingRingtone() {
+        if (sPlayingRingtone != null && sPlayingRingtone.isPlaying()) {
+            sPlayingRingtone.stop();
+        }
+        sPlayingRingtone = null;
 
         if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) {
             mDefaultRingtone.stop();
diff --git a/tools/genfiles/genfiles.sh b/tools/genfiles/genfiles.sh
index 2a139a5..0df984a 100755
--- a/tools/genfiles/genfiles.sh
+++ b/tools/genfiles/genfiles.sh
@@ -29,12 +29,6 @@
 fi
 
 
-if [ "$ANDROID_HOST_OUT" == "" ]
-then
-    echo "Couldn't find sqlite3. Please run envsetup/lunch and build."
-    exit 1
-fi
-
 if [ "$1" == "" ]
 then
     echo "Usage: $0 <file.db> [external storage root]"
@@ -48,11 +42,14 @@
 fi
 
 # generate script to generate directory structure and content
-$ANDROID_HOST_OUT/bin/sqlite3 $1 "select format, media_type, mime_type, _data from files where _data like '"$EXTERNAL"/%';" | {
+sqlite3 $1 "select format, media_type, mime_type, case when substr(_data,-1) is '\' then substr(_data,1,length(_data)-1) else _data end from files where _data like '"$EXTERNAL"/%';" | {
 
 MKDIRS=/tmp/mkdirs$$
 CPFILES=/tmp/cpfiles$$
 
+echo "# create directories" > $MKDIRS
+echo "# copy files" > $CPFILES
+
 IFS="|"
 while read format mediatype mimetype data;
 do
@@ -100,15 +97,19 @@
     then
         # 3gp
         echo "cat /storage/sdcard0/proto.3gp > \"$data\"" >> $CPFILES
-    elif [ "$format" == "47362" -a "$mediatype" == "2" ]
+    elif [ "$format" == "47362" ]
     then
         # ogg
         echo "cat /storage/sdcard0/proto.ogg > \"$data\"" >> $CPFILES
+    elif [ "$format" == "47747" ]
+    then
+        # doc
+        echo "cat /storage/sdcard0/proto.doc > \"$data\"" >> $CPFILES
     elif [ "$format" == "12288" -a "$mediatype" == "0" ]
     then
         # unknown type
         echo "cat /storage/sdcard0/proto.dat > \"$data\"" >> $CPFILES
-    elif [ "$format" == "12289" -a "$mediatype" == "0" ]
+    elif [ "$format" == "12289" ]
     then
         # directory, ignore
         true
@@ -119,7 +120,7 @@
     else
         echo ignored: $format '|' $mediatype '|' $mimetype '|' $data
     fi
-    echo mkdir -p \"$(dirname $data)\" >> $MKDIRS
+    echo mkdir -p \"$(dirname "$data")\" >> $MKDIRS
 done
 
 sort -u $MKDIRS > mkfiles.sh
@@ -129,7 +130,7 @@
 }
 
 # generate playlist files
-$ANDROID_HOST_OUT/bin/sqlite3 $1 "select audio_playlists._data, audio._data from audio_playlists left outer join audio_playlists_map on audio_playlists._id=audio_playlists_map.playlist_id left outer join audio on audio_playlists_map.audio_id=audio._id order by audio_playlists_map.playlist_id,audio_playlists_map.play_order;" | {
+sqlite3 $1 "select audio_playlists._data, audio._data from audio_playlists left outer join audio_playlists_map on audio_playlists._id=audio_playlists_map.playlist_id left outer join audio on audio_playlists_map.audio_id=audio._id order by audio_playlists_map.playlist_id,audio_playlists_map.play_order;" | {
 
 IFS="|"
 while read plist entry
diff --git a/tools/genfiles/protos/proto.doc b/tools/genfiles/protos/proto.doc
new file mode 100644
index 0000000..75bc601
--- /dev/null
+++ b/tools/genfiles/protos/proto.doc
Binary files differ