Merge "Fixing the CursorWindow error log for cloud albums." into udc-mainline-prod
diff --git a/Android.bp b/Android.bp
index 412541a..785c8ee 100644
--- a/Android.bp
+++ b/Android.bp
@@ -20,6 +20,7 @@
         "modules-utils-build",
         "modules-utils-uieventlogger-interface",
         "glide-prebuilt",
+        "glide-integration-recyclerview-prebuilt",
         "glide-gifdecoder-prebuilt",
         "glide-disklrucache-prebuilt",
         "glide-annotation-and-compiler-prebuilt",
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index f10ca96..ce76d52 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -325,6 +325,20 @@
      * {@hide}
      */
     @VisibleForTesting
+    public static final String GET_RECOVERY_DATA = "get_recovery_data";
+
+    /**
+     * Only used for testing.
+     * {@hide}
+     */
+    @VisibleForTesting
+    public static final String REMOVE_RECOVERY_DATA = "remove_recovery_data";
+
+    /**
+     * Only used for testing.
+     * {@hide}
+     */
+    @VisibleForTesting
     public static final String DELETE_BACKED_UP_FILE_PATHS = "delete_backed_up_file_paths";
 
     /** {@hide} */
@@ -4782,6 +4796,25 @@
     }
 
     /**
+     * Only used for testing.
+     * {@hide}
+     */
+    @VisibleForTesting
+    public static String[] getRecoveryData(@NonNull ContentResolver resolver) {
+        Bundle bundle = resolver.call(AUTHORITY, GET_RECOVERY_DATA, null, null);
+        return bundle.getStringArray(GET_RECOVERY_DATA);
+    }
+
+    /**
+     * Only used for testing.
+     * {@hide}
+     */
+    @VisibleForTesting
+    public static void removeRecoveryData(@NonNull ContentResolver resolver) {
+        resolver.call(AUTHORITY, REMOVE_RECOVERY_DATA, null, null);
+    }
+
+    /**
      * Block until any pending operations have finished, such as
      * {@link #scanFile} or {@link #scanVolume} requests.
      *
diff --git a/mediaproviderutils.sh b/mediaproviderutils.sh
index f25640d..f0b3dc6 100644
--- a/mediaproviderutils.sh
+++ b/mediaproviderutils.sh
@@ -1,5 +1,6 @@
 # Shell utility functions for mediaprovider developers.
 # sudo apt-get install rlwrap to have a more fully featured sqlite CLI
+# sudo apt-get install sqlitebrowser to navigate the database with a GUI
 set -x # enable debugging
 
 function add-media-grant () {
@@ -63,14 +64,9 @@
   fi
 }
 
-function sqlite3-pull () {
-    adb root
-    if [ -z "$1" ]
-    then
-        dir=$(pwd)
-    else
-        dir=$1
-    fi
+function media-pull () {
+    adb root && adb wait-for-device
+    dir=$(get-dir $1)
     package=$(get-package)
 
     if [ -f "$dir/external.db" ]; then
@@ -86,10 +82,21 @@
     sqlite3 $dir/external.db "drop trigger files_insert"
     sqlite3 $dir/external.db "drop trigger files_update"
     sqlite3 $dir/external.db "drop trigger files_delete"
-
-    rlwrap sqlite3 $dir/external.db
 }
 
+function sqlite3-pull () {
+      dir="$(get-dir $1)"
+      media-pull "$dir"
+      rlwrap sqlite3 "$dir"/external.db
+}
+
+function sqlitebrowser-pull () {
+    dir="$(get-dir "$1")"
+    media-pull "$dir"
+    sqlitebrowser "$dir"/external.db
+}
+
+
 function sqlite3-push () {
     adb root
     if [ -z "$1" ]
@@ -145,6 +152,16 @@
     adb shell sqlite3 $dir $clause
 }
 
+function get-dir (){
+    if [ -z "$1" ]
+    then
+        dir=$(pwd)
+    else
+        dir=$1
+    fi
+    echo "$dir"
+}
+
 function get-package() {
     if [ -z "$(adb shell pm list package com.android.providers.media.module)" ]
     then
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 707f517..e9f0466 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Voeg by (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Laat toe (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Laat geen toe nie"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Aflaaie"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Gunstelinge"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 70c3843..cf08f5c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ንጥል}one{<xliff:g id="COUNT_1">^1</xliff:g> ንጥል}other{<xliff:g id="COUNT_1">^1</xliff:g> ንጥሎች}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) አክል"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"ለ(<xliff:g id="COUNT">^1</xliff:g>) ፍቀድ"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ምንም አትፍቀድ"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ካሜራ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ውርዶች"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ተወዳጆች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 56157d8..1cba3df 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{صورة واحدة (<xliff:g id="COUNT_0">^1</xliff:g>)}zero{<xliff:g id="COUNT_1">^1</xliff:g> صورة}two{صورتان (<xliff:g id="COUNT_1">^1</xliff:g>)}few{<xliff:g id="COUNT_1">^1</xliff:g> صور}many{<xliff:g id="COUNT_1">^1</xliff:g> صورة}other{<xliff:g id="COUNT_1">^1</xliff:g> صورة}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"إضافة (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"السماح (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"لم يتم اختيار أي صورة"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"الكاميرا"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"العناصر التي تم تنزيلها"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"العناصر المفضّلة"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 8f48c1a..ec19082 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> টা বস্তু}one{<xliff:g id="COUNT_1">^1</xliff:g> টা বস্তু}other{<xliff:g id="COUNT_1">^1</xliff:g> টা বস্তু}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g> টা) যোগ দিয়ক"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"অনুমতি দিয়ক (<xliff:g id="COUNT">^1</xliff:g> টা)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"এখনৰো অনুমতি নিদিব"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"কেমেৰা"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ডাউনল’ড"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"প্ৰিয়"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 9277b3b..5fc20b0 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> element}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Əlavə edin (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"İcazə verin (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Heç birinə icazə verməyin"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Endirmələr"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Sevimlilər"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index a9499ad..495ab4b 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Dozvoli (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ne dozvoli nijednu"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Preuzeto"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 75fff53..c20866c 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> элемент}one{<xliff:g id="COUNT_1">^1</xliff:g> элемент}few{<xliff:g id="COUNT_1">^1</xliff:g> элементы}many{<xliff:g id="COUNT_1">^1</xliff:g> элементаў}other{<xliff:g id="COUNT_1">^1</xliff:g> элемента}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Дадаць (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Дазволіць (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Не дазваляць ніякія"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Спампоўкі"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Абранае"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index f67db6d..356cc1a 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> елемент}other{<xliff:g id="COUNT_1">^1</xliff:g> елемента}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Добавяне (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Разрешаване (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Забраняване на всички"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Изтегляния"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Любими"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index f4569a7..0f60019 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>টি আইটেম}one{<xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম}other{<xliff:g id="COUNT_1">^1</xliff:g>টি আইটেম}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>)টি যোগ করুন"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"অনুমতি দিন (<xliff:g id="COUNT">^1</xliff:g>টি)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"কারও অনুমতি নেই"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ক্যামেরা"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ডাউনলোড করা আইটেম"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"পছন্দসই আইটেম"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 480b0a6..f6da691 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Dozvoli (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nemoj dozvoliti ništa"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Preuzimanja"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 79279ed..ed89418 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}many{<xliff:g id="COUNT_1">^1</xliff:g> elements}other{<xliff:g id="COUNT_1">^1</xliff:g> elements}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Afegeix (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permet (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"No en permetis cap"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Càmera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Baixades"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Preferits"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 051c300..0b0773a 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> položka}few{<xliff:g id="COUNT_1">^1</xliff:g> položky}many{<xliff:g id="COUNT_1">^1</xliff:g> položky}other{<xliff:g id="COUNT_1">^1</xliff:g> položek}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Přidat (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Povolit (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nepovolit nic"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Fotoaparát"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Stažené soubory"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Oblíbené"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9826ae5..cea19ca 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}one{<xliff:g id="COUNT_1">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tilføj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Tillad (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Tillad ingen"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritter"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index e49ab53..37a6bc6 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> Element}other{<xliff:g id="COUNT_1">^1</xliff:g> Elemente}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Hinzufügen (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Erlauben (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Keines zulassen"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriten"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 24717c4..2a44df5 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> στοιχείο}other{<xliff:g id="COUNT_1">^1</xliff:g> στοιχεία}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Προσθήκη (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Αποδοχή (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Δεν επιτρέπεται καμία"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Κάμερα"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Λήψεις"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Αγαπημένα"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 7ac8e5d..a719437 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Allow (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Allow none"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a3f9cd3..c6b7563 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Allow (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Allow none"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favorites"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 7ac8e5d..a719437 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Allow (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Allow none"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 7ac8e5d..a719437 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Add (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Allow (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Allow none"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favourites"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index 0060917..dbc133f 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_0">^1</xliff:g>‎‏‎‎‏‏‏‎ item‎‏‎‎‏‎}other{‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="COUNT_1">^1</xliff:g>‎‏‎‎‏‏‏‎ items‎‏‎‎‏‎}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‏‏‏‎Add (‎‏‎‎‏‏‎<xliff:g id="COUNT">^1</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎Allow (‎‏‎‎‏‏‎<xliff:g id="COUNT">^1</xliff:g>‎‏‎‎‏‏‏‎)‎‏‎‎‏‎"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎Allow none‎‏‎‎‏‎"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‎‎‎Camera‎‏‎‎‏‎"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‏‎‏‏‎‏‏‎‎‎Downloads‎‏‎‎‏‎"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎Favorites‎‏‎‎‏‎"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index adfd307..11b0976 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> elementos}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Agregar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"No permitir ninguna"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5b89460..d7f2b64 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> elementos}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Añadir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"No permitir ninguna"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index c2b6e68..4aa4017 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> üksus}other{<xliff:g id="COUNT_1">^1</xliff:g> üksust}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lisa (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Luba (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ära luba ühtki"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kaamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Allalaadimised"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Lemmikud"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 51d180a..49fb67a 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elementu}other{<xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Gehitu (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Eman baimena (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ez eman baimenik"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Deskargak"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Gogokoak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index b059945..6209611 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> مورد}one{<xliff:g id="COUNT_1">^1</xliff:g> مورد}other{<xliff:g id="COUNT_1">^1</xliff:g> مورد}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"افزودن (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"اجازه دادن (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"مجاز کردن عدم بارگذاری"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"دوربین"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"بارگیری‌ها"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"موارد دلخواه"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 3edc4bc..3f4101b 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> kohde}other{<xliff:g id="COUNT_1">^1</xliff:g> kohdetta}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lisää (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Salli (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Älä salli mitään"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Lataukset"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Suosikit"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 9b92d6a..1d46b00 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> élément}one{<xliff:g id="COUNT_1">^1</xliff:g> élément}many{<xliff:g id="COUNT_1">^1</xliff:g> éléments}other{<xliff:g id="COUNT_1">^1</xliff:g> éléments}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ajouter (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Autoriser (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ne rien autoriser"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Appareil photo"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Téléchargements"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoris"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8d482cb..1122665 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> élément}one{<xliff:g id="COUNT_1">^1</xliff:g> élément}many{<xliff:g id="COUNT_1">^1</xliff:g> éléments}other{<xliff:g id="COUNT_1">^1</xliff:g> éléments}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ajouter (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Autoriser (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ne rien autoriser"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Appareil photo"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Téléchargements"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoris"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 38793c5..5b90bca 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}other{<xliff:g id="COUNT_1">^1</xliff:g> elementos}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Engadir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Non permitir ningunha"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Cámara"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Descargas"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index ddb547c..cbaf312 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> આઇટમ}one{<xliff:g id="COUNT_1">^1</xliff:g> આઇટમ}other{<xliff:g id="COUNT_1">^1</xliff:g> આઇટમ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ઉમેરો (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"મંજૂરી આપો (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"કોઈને મંજૂરી આપશો નહીં"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"કૅમેરા"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ડાઉનલોડ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"મનપસંદ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index a2f5528..070b0f9 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> आइटम}one{<xliff:g id="COUNT_1">^1</xliff:g> आइटम}other{<xliff:g id="COUNT_1">^1</xliff:g> आइटम}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) जोड़ें"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"अनुमति दें (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"कोई फ़ोटो न चुनें"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"कैमरा"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोड किए गए आइटम"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"पसंदीदा"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 9ce8a92..bd32b54 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> stavka}one{<xliff:g id="COUNT_1">^1</xliff:g> stavka}few{<xliff:g id="COUNT_1">^1</xliff:g> stavke}other{<xliff:g id="COUNT_1">^1</xliff:g> stavki}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Dopusti (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nemoj dopustiti prijenos"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Preuzimanja"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Omiljeno"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index f79bd96..0132ca5 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elem}other{<xliff:g id="COUNT_1">^1</xliff:g> elem}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Hozzáadás (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Engedélyezés (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Egy se legyen engedélyezve"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Letöltések"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Kedvencek"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 559bba5..df9771b 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> տարր}one{<xliff:g id="COUNT_1">^1</xliff:g> տարր}other{<xliff:g id="COUNT_1">^1</xliff:g> տարր}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ավելացնել (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Թույլատրել (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Արգելել բոլորը"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Տեսախցիկ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Ներբեռնումներ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Ընտրանի"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index efc0042..5431783 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tambahkan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Izinkan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Tidak ada"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Hasil download"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favorit"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index a6f1f44..211f051 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> atriði}one{<xliff:g id="COUNT_1">^1</xliff:g> atriði}other{<xliff:g id="COUNT_1">^1</xliff:g> atriði}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Bæta við (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Leyfa (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ekki leyfa neitt"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Myndavél"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Niðurhal"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Uppáhald"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 45a8f88..c03968a 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elemento}many{<xliff:g id="COUNT_1">^1</xliff:g> elementi}other{<xliff:g id="COUNT_1">^1</xliff:g> elementi}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Aggiungi (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Consenti (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Non consentire foto"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Fotocamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Download"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Preferiti"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 67b3268..4e76d6b 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{פריט אחד (<xliff:g id="COUNT_0">^1</xliff:g>)}one{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}two{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}other{<xliff:g id="COUNT_1">^1</xliff:g> פריטים}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"הוספה (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"אישור (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"לא להוסיף"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"מצלמה"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"הורדות"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"מועדפים"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 2080159..1524313 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 件のアイテム}other{<xliff:g id="COUNT_1">^1</xliff:g> 件のアイテム}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"追加(<xliff:g id="COUNT">^1</xliff:g> 件)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"許可(<xliff:g id="COUNT">^1</xliff:g> 件)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"すべて許可しない"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"カメラ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ダウンロード"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"お気に入り"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7e6b8ce..d6700f3 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ერთეული}other{<xliff:g id="COUNT_1">^1</xliff:g> ერთეული}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"დამატება (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"დაშვება (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"არცერთის დაშვება"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"კამერა"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ჩამოტვირთვები"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"რჩეულები"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ebde170..263ff2d 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> элемент}other{<xliff:g id="COUNT_1">^1</xliff:g> элемент}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Қосу (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Рұқсат (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ешқайсысына рұқсат етпеу"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Жүктеп алынғандар"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Таңдаулылар"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 740c09f..a72a992 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{ធាតុ <xliff:g id="COUNT_0">^1</xliff:g>}other{ធាតុ <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"បញ្ចូល (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"អនុញ្ញាត (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"អនុញ្ញាត \"គ្មាន\""</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"កាមេរ៉ា"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ការទាញយក"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"សំណព្វ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index c3dee90..6baafe4 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ಐಟಂ}one{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳು}other{<xliff:g id="COUNT_1">^1</xliff:g> ಐಟಂಗಳು}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ಸೇರಿಸಿ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"(<xliff:g id="COUNT">^1</xliff:g>) ಅನುಮತಿಸಿ"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ಯಾವುದನ್ನೂ ಅನುಮತಿಸಬೇಡಿ"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ಕ್ಯಾಮರಾ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ಡೌನ್‌ಲೋಡ್‌ಗಳು"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ಮೆಚ್ಚಿನವುಗಳು"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 7344101..60ac833 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{항목 <xliff:g id="COUNT_0">^1</xliff:g>개}other{항목 <xliff:g id="COUNT_1">^1</xliff:g>개}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"추가(<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"허용(<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"허용 안 함"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"카메라"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"다운로드"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"즐겨찾기"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 91428e4..50f3f03 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> нерсе}other{<xliff:g id="COUNT_1">^1</xliff:g> нерсе}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Кошуу (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Уруксат берүү (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Уруксат берилбейт"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Жүктөлүп алынгандар"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Тандалмалар"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index bdac330..f70953a 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ລາຍການ}other{<xliff:g id="COUNT_1">^1</xliff:g> ລາຍການ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"ເພີ່ມ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"ອະນຸຍາດ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ບໍ່ອະນຸຍາດ"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ກ້ອງຖ່າຍຮູບ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ດາວໂຫຼດ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ລາຍການທີ່ມັກ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d8b74a2..e8e06f0 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> elementas}one{<xliff:g id="COUNT_1">^1</xliff:g> elementas}few{<xliff:g id="COUNT_1">^1</xliff:g> elementai}many{<xliff:g id="COUNT_1">^1</xliff:g> elemento}other{<xliff:g id="COUNT_1">^1</xliff:g> elementų}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pridėti (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Leisti (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Niekas neleidžiama"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Vaizdo kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Atsisiuntimai"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Mėgstamiausi"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 353b431..4331651 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> vienums}zero{<xliff:g id="COUNT_1">^1</xliff:g> vienumu}one{<xliff:g id="COUNT_1">^1</xliff:g> vienums}other{<xliff:g id="COUNT_1">^1</xliff:g> vienumi}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pievienot <xliff:g id="COUNT">^1</xliff:g>"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Atļaut (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Neatļaut nevienu"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Lejupielādes"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Izlase"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 165246c..3fdc1c0 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ставка}one{<xliff:g id="COUNT_1">^1</xliff:g> ставка}other{<xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додај (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Дозволи (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ниедна"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Преземања"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Омилени"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 0e06e43..d02f7e7 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ഇനം}other{<xliff:g id="COUNT_1">^1</xliff:g> ഇനങ്ങൾ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ചേർക്കുക"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"(<xliff:g id="COUNT">^1</xliff:g>) എണ്ണത്തെ അനുവദിക്കുക"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ഒന്നും അനുവദിക്കരുത്"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ക്യാമറ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ഡൗൺലോഡുകൾ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"പ്രിയപ്പെട്ടവ"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 743bf69..38d6f15 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> зүйл}other{<xliff:g id="COUNT_1">^1</xliff:g> зүйл}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Нэмэх (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Зөвшөөрөх (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Юуг ч бүү зөвшөөр"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камер"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Таталтууд"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Дуртай зүйлс"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index f6d0126..3fb8729 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> आयटम}other{<xliff:g id="COUNT_1">^1</xliff:g> आयटम}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) जोडा"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"(<xliff:g id="COUNT">^1</xliff:g>) ला अनुमती द्या"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"काहीही नाही याला अनुमती द्या"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"कॅमेरा"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोड"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"आवडते"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 04725d1..f96d6e4 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> item}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Tambah (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Benarkan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Tiada yang dibenarkan"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Muat turun"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Kegemaran"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 7ae3f6c..9a1013f 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{ဖိုင် <xliff:g id="COUNT_0">^1</xliff:g> ခု}other{ဖိုင် <xliff:g id="COUNT_1">^1</xliff:g> ခု}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ခု ထည့်ရန်"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"(<xliff:g id="COUNT">^1</xliff:g>) ခု ခွင့်ပြုရန်"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"သုည ခွင့်ပြုရန်"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ကင်မရာ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ဒေါင်းလုဒ်များ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"စိတ်ကြိုက်များ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ad68b4b..901f7ed 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}other{<xliff:g id="COUNT_1">^1</xliff:g> elementer}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Legg til (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Tillat (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Tillat ingen"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Nedlastinger"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritter"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 2f15afa..eed492e 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> वटा वस्तु}other{<xliff:g id="COUNT_1">^1</xliff:g> वटा वस्तु}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"थप्नुहोस् (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"अनुमति दिनुहोस् (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"कुनै पनि फोटो प्रयोग गर्न नदिनुहोस्"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"क्यामेरा"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"डाउनलोडहरू"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"मन पर्ने कुराहरू"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 7c2726f..de4fcc2 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> items}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Toevoegen (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Toestaan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Geen toestaan"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favorieten"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 0c07414..afcb014 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g>ଟି ଆଇଟମ}other{<xliff:g id="COUNT_1">^1</xliff:g>ଟି ଆଇଟମ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>)ଟି ଯୋଗ କରନ୍ତୁ"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"ଅନୁମତି ଦିଅନ୍ତୁ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"କାହାରିକୁ ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"କେମେରା"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ଡାଉନଲୋଡଗୁଡ଼ିକ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ପସନ୍ଦଗୁଡ଼ିକ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index db4e165..c17c1d0 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ਆਈਟਮ}one{<xliff:g id="COUNT_1">^1</xliff:g> ਆਈਟਮ}other{<xliff:g id="COUNT_1">^1</xliff:g> ਆਈਟਮਾਂ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"ਆਗਿਆ ਦਿਓ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ਕੋਈ ਵੀ ਆਗਿਆ ਨਾ ਦਿਓ"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"ਕੈਮਰਾ"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ਡਾਊਨਲੋਡ"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ਮਨਪਸੰਦ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 7d38cdc..cc3b1c5 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}few{<xliff:g id="COUNT_1">^1</xliff:g> elementy}many{<xliff:g id="COUNT_1">^1</xliff:g> elementów}other{<xliff:g id="COUNT_1">^1</xliff:g> elementu}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Zezwól (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nie zezwalaj na żadne"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Aparat"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Pobrane"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Ulubione"</string>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
index cb7b017..bef70c1 100644
--- a/res/values-pt-rBR/strings.xml
+++ b/res/values-pt-rBR/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> itens}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Não autorizar"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Câmera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a153742..a049d92 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> itens}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Não permitir nenhuma"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Câmara"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Transferências"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index cb7b017..bef70c1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}many{<xliff:g id="COUNT_1">^1</xliff:g> itens}other{<xliff:g id="COUNT_1">^1</xliff:g> itens}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adicionar (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permitir (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Não autorizar"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Câmera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Downloads"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoritos"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index ebde095..d9d0df9 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}few{<xliff:g id="COUNT_1">^1</xliff:g> elemente}other{<xliff:g id="COUNT_1">^1</xliff:g> de elemente}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Adaugă (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Permite (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nu permite nimic"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Cameră"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Descărcări"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Preferate"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index c996b2c..b8ff7b3 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> объект}one{<xliff:g id="COUNT_1">^1</xliff:g> объект}few{<xliff:g id="COUNT_1">^1</xliff:g> объекта}many{<xliff:g id="COUNT_1">^1</xliff:g> объектов}other{<xliff:g id="COUNT_1">^1</xliff:g> объекта}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Добавить (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Открыть доступ (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Запретить доступ всем"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Скачанные"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Избранное"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 1211c73..e972106 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{අයිතම <xliff:g id="COUNT_0">^1</xliff:g>}one{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}other{අයිතම <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"එක් කරන්න (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"ඉඩ දෙන්න (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"කිසිවකට ඉඩ නොදෙන්න"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"කැමරාව"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"බාගැනීම්"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ප්‍රියතමයන්"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index a359609..3a8f9fb 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> položka}few{<xliff:g id="COUNT_1">^1</xliff:g> položky}many{<xliff:g id="COUNT_1">^1</xliff:g> items}other{<xliff:g id="COUNT_1">^1</xliff:g> položiek}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Pridať (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Povoliť (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Nepovoliť žiadne"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Stiahnuté"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Obľúbené"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index e49c7dd..08cb6a1 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> element}one{<xliff:g id="COUNT_1">^1</xliff:g> element}two{<xliff:g id="COUNT_1">^1</xliff:g> elementa}few{<xliff:g id="COUNT_1">^1</xliff:g> elementi}other{<xliff:g id="COUNT_1">^1</xliff:g> elementov}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Dodaj (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Dovoli (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Dovoli brez izbire"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Fotoaparat"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Prenosi"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Priljubljeno"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 9e47b0e..0b60fde 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> artikull}other{<xliff:g id="COUNT_1">^1</xliff:g> artikuj}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Shto (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Lejo (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Mos lejo asnjë"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Shkarkimet"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Të preferuarat"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a0cf7e2..726459e 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ставка}one{<xliff:g id="COUNT_1">^1</xliff:g> ставка}few{<xliff:g id="COUNT_1">^1</xliff:g> ставке}other{<xliff:g id="COUNT_1">^1</xliff:g> ставки}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додај (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Дозволи (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Не дозволи ниједну"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Преузето"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Омиљено"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index c2bcd79..d725331 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> objekt}other{<xliff:g id="COUNT_1">^1</xliff:g> objekt}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Lägg till (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Tillåt (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Tillåt inga"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Nedladdningar"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriter"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 1a59554..020b5a2 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{Kipengee <xliff:g id="COUNT_0">^1</xliff:g>}other{Vipengee <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Weka (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Ruhusu (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Usiruhusu yoyote"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Vipakuliwa"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Vipendwa"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index bd9e8a1..309ba9d 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ஆவணம்}other{<xliff:g id="COUNT_1">^1</xliff:g> ஆவணங்கள்}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) படங்களைச் சேர்"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"அனுமதி (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"எதையும் அனுமதிக்காதே"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"கேமரா"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"பதிவிறக்கங்கள்"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"பிடித்தவை"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 628443b..2621a48 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ఐటెమ్}other{<xliff:g id="COUNT_1">^1</xliff:g> ఐటెమ్‌లు}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"జోడించండి (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"అనుమతించండి (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"వేటినీ అనుమతించవద్దు"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"కెమెరా"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"డౌన్‌లోడ్‌లు"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"ఫేవరెట్స్"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 1d0c192..538d732 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> รายการ}other{<xliff:g id="COUNT_1">^1</xliff:g> รายการ}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"เพิ่ม (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"อนุญาต (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"ไม่อนุญาต"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"กล้อง"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"การดาวน์โหลด"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"รายการโปรด"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 22c2151..ce60a92 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> item}one{<xliff:g id="COUNT_1">^1</xliff:g> item}other{<xliff:g id="COUNT_1">^1</xliff:g> na item}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Magdagdag (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Payagan (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Walang papayagan"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Camera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Mga Download"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Mga Paborito"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 12b3693..c959485 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> öğe}other{<xliff:g id="COUNT_1">^1</xliff:g> öğe}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Ekle (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"İzin ver (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Hiçbirine izin verme"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"İndirilenler"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Favoriler"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 1eeee66..8ec4cfb 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> об’єкт}one{<xliff:g id="COUNT_1">^1</xliff:g> об’єкт}few{<xliff:g id="COUNT_1">^1</xliff:g> об’єкти}many{<xliff:g id="COUNT_1">^1</xliff:g> об’єктів}other{<xliff:g id="COUNT_1">^1</xliff:g> об’єкта}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Додати (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Дозволити (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Не дозволяти жодної фотографії"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Камера"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Завантаження"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Вибране"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index caa4195..dea4d10 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> آئٹم}other{<xliff:g id="COUNT_1">^1</xliff:g> آئٹمز}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"(<xliff:g id="COUNT">^1</xliff:g>) شامل کریں"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"(<xliff:g id="COUNT">^1</xliff:g>) کو اجازت دیں"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"کسی کو اجازت نہ دیں"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"کیمرا"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"ڈاؤن لوڈز"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"پسندیدہ"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index c496bd8..b97c5df 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> ta narsa}other{<xliff:g id="COUNT_1">^1</xliff:g> ta narsa}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Kiritish (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Ruxsat (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Hech qanday ruxsat berilmasin"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Kamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Yuklanmalar"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Sevimlilar"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index d8afa9f..c0dd41d 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> mục}other{<xliff:g id="COUNT_1">^1</xliff:g> mục}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Thêm (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Cho phép (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Không cho phép"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Máy ảnh"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Tệp đã tải xuống"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Mục yêu thích"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 056a809..28c400c 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 个}other{<xliff:g id="COUNT_1">^1</xliff:g> 个}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"添加(<xliff:g id="COUNT">^1</xliff:g> 项)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"允许 (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"全部不允许"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"相机"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"下载内容"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"收藏"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 55eeedf..870d569 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{<xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"新增 (<xliff:g id="COUNT">^1</xliff:g> 個)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"允許 (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"全部禁止"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"相機"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"下載"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"我的最愛"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 24756f6..4d2b5d6 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{<xliff:g id="COUNT_0">^1</xliff:g> 個項目}other{<xliff:g id="COUNT_1">^1</xliff:g> 個項目}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"新增 (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"允許 (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"全部禁止"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"相機"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"下載的內容"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"收藏的內容"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 94fc4e2..fd42bc1 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -74,6 +74,7 @@
     <string name="picker_album_item_count" msgid="4420723302534177596">"{count,plural, =1{into <xliff:g id="COUNT_0">^1</xliff:g>}one{izinto <xliff:g id="COUNT_1">^1</xliff:g>}other{izinto <xliff:g id="COUNT_1">^1</xliff:g>}}"</string>
     <string name="picker_add_button_multi_select" msgid="4005164092275518399">"Engeza (<xliff:g id="COUNT">^1</xliff:g>)"</string>
     <string name="picker_add_button_multi_select_permissions" msgid="5138751105800138838">"Vumela (<xliff:g id="COUNT">^1</xliff:g>)"</string>
+    <string name="picker_add_button_allow_none_option" msgid="9183772732922241035">"Ungavumeli lutho"</string>
     <string name="picker_category_camera" msgid="4857367052026843664">"Ikhamera"</string>
     <string name="picker_category_downloads" msgid="793866660287361900">"Okulandiwe"</string>
     <string name="picker_category_favorites" msgid="7008495397818966088">"Izintandokazi"</string>
diff --git a/src/com/android/providers/media/AccessChecker.java b/src/com/android/providers/media/AccessChecker.java
index 986b444..adde626 100644
--- a/src/com/android/providers/media/AccessChecker.java
+++ b/src/com/android/providers/media/AccessChecker.java
@@ -363,16 +363,6 @@
         return PACKAGE_USER_ID_COLUMN + "=" + callingIdentity.uid / MediaStore.PER_USER_RANGE;
     }
 
-    /**
-     * Returns true if redaction is needed for openFile calls on picker uri by checking calling
-     * package permission
-     *
-     * @param callingIdentity - the current caller
-     */
-    public static boolean isRedactionNeededForPickerUri(LocalCallingIdentity callingIdentity) {
-        return callingIdentity.hasPermission(LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED);
-    }
-
     @VisibleForTesting
     static String getWhereForMediaTypeMatch(int mediaType) {
         return bindSelection("media_type=?", mediaType);
diff --git a/src/com/android/providers/media/DatabaseBackupAndRecovery.java b/src/com/android/providers/media/DatabaseBackupAndRecovery.java
index 5cfec37..ec864cd 100644
--- a/src/com/android/providers/media/DatabaseBackupAndRecovery.java
+++ b/src/com/android/providers/media/DatabaseBackupAndRecovery.java
@@ -980,7 +980,9 @@
         List<String> invalidUsers = getInvalidUsersList(xattrList, validUsers);
         Log.i(TAG, "Invalid users list is " + invalidUsers);
         for (String userIdToBeRemoved : invalidUsers) {
-            removeRecoveryDataForUserId(Integer.parseInt(userIdToBeRemoved));
+            if (userIdToBeRemoved != null && !userIdToBeRemoved.trim().isEmpty()) {
+                removeRecoveryDataForUserId(Integer.parseInt(userIdToBeRemoved));
+            }
         }
     }
 
diff --git a/src/com/android/providers/media/LocalUriMatcher.java b/src/com/android/providers/media/LocalUriMatcher.java
index 9beebdb..888a619 100644
--- a/src/com/android/providers/media/LocalUriMatcher.java
+++ b/src/com/android/providers/media/LocalUriMatcher.java
@@ -72,12 +72,11 @@
     static final int DOWNLOADS_ID = 801;
 
     static final int PICKER = 900;
-    public static final int PICKER_ID = 901;
+    static final int PICKER_ID = 901;
     static final int PICKER_INTERNAL_MEDIA_ALL = 902;
     static final int PICKER_INTERNAL_MEDIA_LOCAL = 903;
     static final int PICKER_INTERNAL_ALBUMS_ALL = 904;
     static final int PICKER_INTERNAL_ALBUMS_LOCAL = 905;
-    public static final int PICKER_GET_CONTENT_ID = 906;
 
     public static final int MEDIA_GRANTS = 1000;
 
@@ -122,11 +121,6 @@
         // content://media/picker/<user-id>/<authority>/media/<media-id>
         mPublic.addURI(auth, "picker/#/*/media/*", PICKER_ID);
 
-        // content://media/picker_get_content/<user-id>/<media-id>
-        mPublic.addURI(auth, "picker_get_content/#/#", PICKER_GET_CONTENT_ID);
-        // content://media/picker_get_content/<user-id>/<authority>/media/<media-id>
-        mPublic.addURI(auth, "picker_get_content/#/*/media/*", PICKER_GET_CONTENT_ID);
-
         mPublic.addURI(auth, "cli", CLI);
 
         mPublic.addURI(auth, "*/images/media", IMAGES_MEDIA);
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 4fbb46b..7954565 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -61,7 +61,6 @@
 import static com.android.providers.media.AccessChecker.getWhereForUserSelectedAccess;
 import static com.android.providers.media.AccessChecker.hasAccessToCollection;
 import static com.android.providers.media.AccessChecker.hasUserSelectedAccess;
-import static com.android.providers.media.AccessChecker.isRedactionNeededForPickerUri;
 import static com.android.providers.media.DatabaseHelper.EXTERNAL_DATABASE_NAME;
 import static com.android.providers.media.DatabaseHelper.INTERNAL_DATABASE_NAME;
 import static com.android.providers.media.LocalCallingIdentity.APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID;
@@ -110,7 +109,6 @@
 import static com.android.providers.media.LocalUriMatcher.IMAGES_THUMBNAILS_ID;
 import static com.android.providers.media.LocalUriMatcher.MEDIA_GRANTS;
 import static com.android.providers.media.LocalUriMatcher.MEDIA_SCANNER;
-import static com.android.providers.media.LocalUriMatcher.PICKER_GET_CONTENT_ID;
 import static com.android.providers.media.LocalUriMatcher.PICKER_ID;
 import static com.android.providers.media.LocalUriMatcher.PICKER_INTERNAL_ALBUMS_ALL;
 import static com.android.providers.media.LocalUriMatcher.PICKER_INTERNAL_ALBUMS_LOCAL;
@@ -124,10 +122,8 @@
 import static com.android.providers.media.LocalUriMatcher.VIDEO_THUMBNAILS_ID;
 import static com.android.providers.media.LocalUriMatcher.VOLUMES;
 import static com.android.providers.media.LocalUriMatcher.VOLUMES_ID;
-import static com.android.providers.media.PickerUriResolver.PICKER_GET_CONTENT_SEGMENT;
-import static com.android.providers.media.PickerUriResolver.PICKER_SEGMENT;
 import static com.android.providers.media.PickerUriResolver.getMediaUri;
-import static com.android.providers.media.photopicker.data.MediaGrantsProvider.EXTRA_MIME_TYPE_SELECTION;
+import static com.android.providers.media.photopicker.data.ItemsProvider.EXTRA_MIME_TYPE_SELECTION;
 import static com.android.providers.media.scan.MediaScanner.REASON_DEMAND;
 import static com.android.providers.media.scan.MediaScanner.REASON_IDLE;
 import static com.android.providers.media.util.DatabaseUtils.bindList;
@@ -254,6 +250,7 @@
 import android.provider.MediaStore.Images.ImageColumns;
 import android.provider.MediaStore.MediaColumns;
 import android.provider.MediaStore.Video;
+import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
@@ -291,6 +288,7 @@
 import com.android.providers.media.photopicker.data.ExternalDbFacade;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
 import com.android.providers.media.photopicker.data.PickerSyncRequestExtras;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 import com.android.providers.media.playlist.Playlist;
 import com.android.providers.media.scan.MediaScanner;
 import com.android.providers.media.scan.MediaScanner.ScanReason;
@@ -853,6 +851,23 @@
                             editor.commit();
                         }
                     }
+
+                    boolean isDeviceInDemoMode = false;
+                    try {
+                        isDeviceInDemoMode = Settings.Global.getInt(
+                                getContext().getContentResolver(), Settings.Global.DEVICE_DEMO_MODE)
+                                > 0;
+                    } catch (Settings.SettingNotFoundException e) {
+                        Log.w(TAG, "Exception in reading DEVICE_DEMO_MODE setting", e);
+                    }
+
+                    Log.i(TAG, "isDeviceInDemoMode: " + isDeviceInDemoMode);
+                    // Only allow default system user 0 to update xattrs on /data/media/0 and
+                    // only on retail demo devices
+                    if (sUserId == UserHandle.SYSTEM.getIdentifier() && isDeviceInDemoMode) {
+                        mDatabaseBackupAndRecovery.removeRecoveryDataForUserId(
+                                userToBeRemoved.getIdentifier());
+                    }
                     break;
             }
         }
@@ -1310,16 +1325,16 @@
                 mProjectionHelper, Metrics::logSchemaChange, mFilesListener,
                 MIGRATION_LISTENER, mIdGenerator, true, mDatabaseBackupAndRecovery);
         mExternalDbFacade = new ExternalDbFacade(getContext(), mExternalDatabase, mVolumeCache);
-        mPickerDbFacade = new PickerDbFacade(context);
 
         mMediaGrants = new MediaGrants(mExternalDatabase);
 
+        PickerSyncLockManager pickerSyncLockManager = new PickerSyncLockManager();
+        mPickerDbFacade = new PickerDbFacade(context, pickerSyncLockManager);
         mPickerSyncController = PickerSyncController.initialize(context, mPickerDbFacade,
-                mConfigStore);
+                mConfigStore, pickerSyncLockManager);
         mPickerDataLayer = PickerDataLayer.create(context, mPickerDbFacade, mPickerSyncController,
                 mConfigStore);
-        mPickerUriResolver = new PickerUriResolver(context, mPickerDbFacade, mProjectionHelper,
-                mUriMatcher);
+        mPickerUriResolver = new PickerUriResolver(context, mPickerDbFacade, mProjectionHelper);
 
         if (SdkLevel.isAtLeastS()) {
             mTranscodeHelper = new TranscodeHelperImpl(context, this, mConfigStore);
@@ -1613,12 +1628,12 @@
         // removing calling userId
         userIds.remove(String.valueOf(sUserId));
 
-        List<String> validUsers = mUserManager.getEnabledProfiles().stream()
+        List<String> validUserProfiles = mUserManager.getEnabledProfiles().stream()
                 .map(userHandle -> String.valueOf(userHandle.getIdentifier())).collect(
                         Collectors.toList());
         // removing all the valid/existing user, remaining userIds would be users who would have
         // been removed
-        userIds.removeAll(validUsers);
+        userIds.removeAll(validUserProfiles);
 
         // Cleaning media files of users who have been removed
         mExternalDatabase.runWithTransaction((db) -> {
@@ -1629,6 +1644,25 @@
             });
             return null ;
         });
+
+        boolean isDeviceInDemoMode = false;
+        try {
+            isDeviceInDemoMode = Settings.Global.getInt(getContext().getContentResolver(),
+                    Settings.Global.DEVICE_DEMO_MODE) > 0;
+        } catch (Settings.SettingNotFoundException e) {
+            Log.w(TAG, "Exception in reading DEVICE_DEMO_MODE setting", e);
+        }
+
+        Log.i(TAG, "isDeviceInDemoMode: " + isDeviceInDemoMode);
+        // Only allow default system user 0 to update xattrs on /data/media/0 and only when
+        // device is in retail mode
+        if (sUserId == UserHandle.SYSTEM.getIdentifier() && isDeviceInDemoMode) {
+            List<String> validUsers = mUserManager.getUserHandles(/* excludeDying */ true).stream()
+                    .map(userHandle -> String.valueOf(userHandle.getIdentifier())).collect(
+                            Collectors.toList());
+            Log.i(TAG, "Active user ids are:" + validUsers);
+            mDatabaseBackupAndRecovery.removeRecoveryDataExceptValidUsers(validUsers);
+        }
     }
 
     private void pruneStalePackages(CancellationSignal signal) {
@@ -2322,14 +2356,13 @@
         boolean result = false;
         switch (segmentCount) {
             case 1:
-                // .../picker or .../picker_get_content
-                if (lastSegment.equals(PICKER_SEGMENT) || lastSegment.equals(
-                        PICKER_GET_CONTENT_SEGMENT)) {
+                // .../picker
+                if (lastSegment.equals("picker")) {
                     result = file.exists() || file.mkdir();
                 }
                 break;
             case 2:
-                // .../picker/<user-id> or .../picker_get_content/<user-id>
+                // .../picker/<user-id>
                 try {
                     Integer.parseInt(lastSegment);
                     result = file.exists() || file.mkdir();
@@ -2339,24 +2372,21 @@
                 }
                 break;
             case 3:
-                // .../picker/<user-id>/<authority> or .../picker_get_content/<user-id>/<authority>
+                // .../picker/<user-id>/<authority>
                 result = preparePickerAuthorityPathSegment(file, lastSegment, uid);
                 break;
             case 4:
-                // .../picker/<user-id>/<authority>/media or
-                // .../picker_get_content/<user-id>/<authority>/media
+                // .../picker/<user-id>/<authority>/media
                 if (lastSegment.equals("media")) {
                     result = file.exists() || file.mkdir();
                 }
                 break;
             case 5:
-                // .../picker/<user-id>/<authority>/media/<media-id.extension> or
-                // .../picker_get_content/<user-id>/<authority>/media/<media-id.extension>
-                final String pickerSegmentType = syntheticRelativePathSegments.get(0);
+                // .../picker/<user-id>/<authority>/media/<media-id.extension>
                 final String fileUserId = syntheticRelativePathSegments.get(1);
                 final String authority = syntheticRelativePathSegments.get(2);
-                result = preparePickerMediaIdPathSegment(file, pickerSegmentType, authority,
-                        lastSegment, fileUserId, uid);
+                result = preparePickerMediaIdPathSegment(file, authority, lastSegment, fileUserId,
+                        uid);
                 break;
         }
 
@@ -2374,9 +2404,8 @@
                     new long[0]);
         }
 
-        // ['', 'storage', 'emulated', '0', 'transforms', 'synthetic',
-        // 'picker' or 'picker_get_content', '<user-id>', '<host>', 'media', '<fileName>']
-        final String pickerSegmentType = segments[6];
+        // ['', 'storage', 'emulated', '0', 'transforms', 'synthetic', 'picker', '<user-id>',
+        // '<host>', 'media', '<fileName>']
         final String userId = segments[7];
         final String fileName = segments[10];
         final String host = segments[8];
@@ -2412,19 +2441,7 @@
 
         try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) {
             final String mimeType = MimeUtils.resolveMimeType(new File(path));
-            // Picker segment indicates we need to force redact location metadata.
-            // Picker_get_content indicates that we need to check A_M_L permission to decide if the
-            // metadata needs to be redacted
-            LocalCallingIdentity callingIdentityForOriginalUid = getCachedCallingIdentityForFuse(
-                    uid);
-            final boolean isRedactionNeeded = pickerSegmentType.equalsIgnoreCase(PICKER_SEGMENT)
-                    || callingIdentityForOriginalUid == null
-                    || isRedactionNeededForPickerUri(callingIdentityForOriginalUid);
-            Log.v(TAG, "Redaction needed for file open: " + isRedactionNeeded);
-            long[] redactionRanges = new long[0];
-            if (isRedactionNeeded) {
-                redactionRanges = getRedactionRanges(fis, mimeType).redactionRanges;
-            }
+            final long[] redactionRanges = getRedactionRanges(fis, mimeType).redactionRanges;
             return new FileOpenResult(0 /* status */, uid, /* transformsUid */ 0,
                     /* nativeFd */ pfd.detachFd(), redactionRanges);
         } catch (IOException e) {
@@ -2441,14 +2458,13 @@
         return false;
     }
 
-    private boolean preparePickerMediaIdPathSegment(File file, String pickerSegmentType,
-            String authority, String fileName, String userId, int uid) {
+    private boolean preparePickerMediaIdPathSegment(File file, String authority, String fileName,
+            String userId, int uid) {
         final String mediaId = extractFileName(fileName);
-        final String[] projection = new String[]{MediaStore.PickerMediaColumns.SIZE};
+        final String[] projection = new String[] { MediaStore.PickerMediaColumns.SIZE };
 
-        final Uri uri = Uri.parse(
-                "content://media/" + pickerSegmentType + "/" + userId + "/" + authority + "/media/"
-                        + mediaId);
+        final Uri uri = Uri.parse("content://media/picker/" + userId + "/" + authority + "/media/"
+                + mediaId);
         try (Cursor cursor = mPickerUriResolver.query(uri, projection, /* callingPid */0, uid,
                 mCallingIdentity.get().getPackageName())) {
             if (cursor != null && cursor.moveToFirst()) {
@@ -4025,7 +4041,6 @@
                 return Downloads.CONTENT_TYPE;
 
             case PICKER_ID:
-            case PICKER_GET_CONTENT_ID:
                 return mPickerUriResolver.getType(url, Binder.getCallingPid(),
                         Binder.getCallingUid());
         }
@@ -6639,6 +6654,13 @@
             case MediaStore.GET_BACKUP_FILES: {
                 return getResultForGetBackupFiles();
             }
+            case MediaStore.GET_RECOVERY_DATA: {
+                return getResultForGetRecoveryData();
+            }
+            case MediaStore.REMOVE_RECOVERY_DATA: {
+                removeRecoveryData();
+                return new Bundle();
+            }
             default:
                 throw new UnsupportedOperationException("Unsupported call: " + method);
         }
@@ -7168,6 +7190,36 @@
         return bundle;
     }
 
+    @NotNull
+    private Bundle getResultForGetRecoveryData() {
+        getContext().enforceCallingPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
+                "Permission missing to call GET_RECOVERY_DATA by "
+                        + "uid:" + Binder.getCallingUid());
+
+        String[] xattrs = null;
+        try {
+           xattrs = Os.listxattr("/data/media/0");
+        } catch (ErrnoException e) {
+            Log.w(TAG, "Error in getting xattr list ", e);
+        }
+
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(MediaStore.GET_RECOVERY_DATA, xattrs);
+        return bundle;
+    }
+
+    private void removeRecoveryData() {
+        getContext().enforceCallingPermission(Manifest.permission.WRITE_MEDIA_STORAGE,
+                "Permission missing to call REMOVE_RECOVERY_DATA by "
+                        + "uid:" + Binder.getCallingUid());
+
+        List<String> validUsers = mUserManager.getUserHandles(/* excludeDying */ true).stream()
+                .map(userHandle -> String.valueOf(userHandle.getIdentifier())).collect(
+                        Collectors.toList());
+        Log.i(TAG, "Active user ids are:" + validUsers);
+        mDatabaseBackupAndRecovery.removeRecoveryDataExceptValidUsers(validUsers);
+    }
+
     private String getSecurityExceptionMessage(String method) {
         int callingUid = Binder.getCallingUid();
         return String.format("%s not allowed. Calling app ID: %d, Calling UID %d. "
@@ -8589,7 +8641,7 @@
 
     private boolean isPickerUri(Uri uri) {
         final int match = matchUri(uri, /* allowHidden */ isCallingPackageAllowedHidden());
-        return match == PICKER_ID || match == PICKER_GET_CONTENT_ID;
+        return match == PICKER_ID;
     }
 
     @Override
@@ -8616,20 +8668,9 @@
         uri = safeUncanonicalize(uri);
 
         if (isPickerUri(uri)) {
-            int tid = Process.myTid();
-            synchronized (mPendingOpenInfo) {
-                mPendingOpenInfo.put(tid, new PendingOpenInfo(
-                        Binder.getCallingUid(), /* mediaCapabilitiesUid */ 0, /* shouldRedact */
-                        false, /* transcodeReason */ 0));
-            }
-
-            try {
-                return mPickerUriResolver.openFile(uri, mode, signal, mCallingIdentity.get());
-            } finally {
-                synchronized (mPendingOpenInfo) {
-                    mPendingOpenInfo.remove(tid);
-                }
-            }
+            final int callingPid = mCallingIdentity.get().pid;
+            final int callingUid = mCallingIdentity.get().uid;
+            return mPickerUriResolver.openFile(uri, mode, signal, callingPid, callingUid);
         }
 
         final boolean allowHidden = isCallingPackageAllowedHidden();
@@ -8762,21 +8803,10 @@
 
         // This is needed for thumbnail resolution as it doesn't go through openFileCommon
         if (isPickerUri(uri)) {
-            int tid = Process.myTid();
-            synchronized (mPendingOpenInfo) {
-                mPendingOpenInfo.put(tid, new PendingOpenInfo(
-                        Binder.getCallingUid(), /* mediaCapabilitiesUid */ 0, /* shouldRedact */
-                        false, /* transcodeReason */ 0));
-            }
-
-            try {
-                return mPickerUriResolver.openTypedAssetFile(uri, mimeTypeFilter, opts, signal,
-                        mCallingIdentity.get());
-            } finally {
-                synchronized (mPendingOpenInfo) {
-                    mPendingOpenInfo.remove(tid);
-                }
-            }
+            final int callingPid = mCallingIdentity.get().pid;
+            final int callingUid = mCallingIdentity.get().uid;
+            return mPickerUriResolver.openTypedAssetFile(uri, mimeTypeFilter, opts, signal,
+                    callingPid, callingUid);
         }
 
         // TODO: enforce that caller has access to this uri
diff --git a/src/com/android/providers/media/MediaProviderShellCommand.java b/src/com/android/providers/media/MediaProviderShellCommand.java
index ddeef00..3886160 100644
--- a/src/com/android/providers/media/MediaProviderShellCommand.java
+++ b/src/com/android/providers/media/MediaProviderShellCommand.java
@@ -34,6 +34,7 @@
 import com.android.providers.media.photopicker.PickerSyncController;
 import com.android.providers.media.photopicker.data.CloudProviderInfo;
 import com.android.providers.media.photopicker.data.PickerDatabaseHelper;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
 
 import java.io.OutputStream;
 import java.io.PrintWriter;
@@ -187,7 +188,12 @@
 
         // TODO(b/242550131): add PickerSyncController's API to make it possible to reset just one
         //  provider's library at a time (i.e. either CMP or local).
-        mPickerSyncController.resetAllMedia();
+        try {
+            mPickerSyncController.resetAllMedia();
+        } catch (UnableToAcquireLockException e) {
+            pw.print("Could not reset all media" + e.getMessage());
+            return 1;
+        }
 
         pw.println("Done.");
         return 0;
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
index 3a37c9a..b56b694 100644
--- a/src/com/android/providers/media/PickerUriResolver.java
+++ b/src/com/android/providers/media/PickerUriResolver.java
@@ -19,9 +19,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 
-import static com.android.providers.media.AccessChecker.isRedactionNeededForPickerUri;
-import static com.android.providers.media.LocalUriMatcher.PICKER_GET_CONTENT_ID;
-import static com.android.providers.media.LocalUriMatcher.PICKER_ID;
 import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
 import static com.android.providers.media.util.FileUtils.toFuseFile;
 
@@ -33,7 +30,6 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.ParcelFileDescriptor;
@@ -62,9 +58,7 @@
 public class PickerUriResolver {
     private static final String TAG = "PickerUriResolver";
 
-    public static final String PICKER_SEGMENT = "picker";
-
-    public static final String PICKER_GET_CONTENT_SEGMENT = "picker_get_content";
+    private static final String PICKER_SEGMENT = "picker";
     private static final String PICKER_INTERNAL_SEGMENT = "picker_internal";
     /** A uri with prefix "content://media/picker" is considered as a picker uri */
     public static final Uri PICKER_URI = MediaStore.AUTHORITY_URI.buildUpon().
@@ -90,28 +84,23 @@
     private final PickerDbFacade mDbFacade;
     private final Set<String> mAllValidProjectionColumns;
     private final String[] mAllValidProjectionColumnsArray;
-    private final LocalUriMatcher mLocalUriMatcher;
 
-    PickerUriResolver(Context context, PickerDbFacade dbFacade, ProjectionHelper projectionHelper,
-            LocalUriMatcher localUriMatcher) {
+    PickerUriResolver(Context context, PickerDbFacade dbFacade, ProjectionHelper projectionHelper) {
         mContext = context;
         mDbFacade = dbFacade;
         mAllValidProjectionColumns = projectionHelper.getProjectionMap(
                 MediaStore.PickerMediaColumns.class).keySet();
         mAllValidProjectionColumnsArray = mAllValidProjectionColumns.toArray(new String[0]);
-        mLocalUriMatcher = localUriMatcher;
     }
 
     public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal,
-            LocalCallingIdentity localCallingIdentity)
-            throws FileNotFoundException {
+            int callingPid, int callingUid) throws FileNotFoundException {
         if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
             throw new SecurityException("PhotoPicker Uris can only be accessed to read."
                     + " Uri: " + uri);
         }
 
-        checkPermissionForRequireOriginalQueryParam(uri, localCallingIdentity);
-        checkUriPermission(uri, localCallingIdentity.pid, localCallingIdentity.uid);
+        checkUriPermission(uri, callingPid, callingUid);
 
         final ContentResolver resolver;
         try {
@@ -128,10 +117,9 @@
     }
 
     public AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts,
-            CancellationSignal signal, LocalCallingIdentity localCallingIdentity)
+            CancellationSignal signal, int callingPid, int callingUid)
             throws FileNotFoundException {
-        checkPermissionForRequireOriginalQueryParam(uri, localCallingIdentity);
-        checkUriPermission(uri, localCallingIdentity.pid, localCallingIdentity.uid);
+        checkUriPermission(uri, callingPid, callingUid);
 
         final ContentResolver resolver;
         try {
@@ -222,8 +210,7 @@
                 + CloudMediaProviderContract.URI_PATH_SURFACE_CONTROLLER);
     }
 
-    private ParcelFileDescriptor openPickerFile(Uri uri)
-            throws FileNotFoundException {
+    private ParcelFileDescriptor openPickerFile(Uri uri) throws FileNotFoundException {
         final File file = getPickerFileFromUri(uri);
         if (file == null) {
             throw new FileNotFoundException("File not found for uri: " + uri);
@@ -248,38 +235,19 @@
 
     @VisibleForTesting
     Cursor queryPickerUri(Uri uri, String[] projection) {
-        String pickerSegmentType = getPickerSegmentType(uri);
         uri = unwrapProviderUri(uri);
-        return mDbFacade.queryMediaIdForApps(pickerSegmentType, uri.getHost(),
-                uri.getLastPathSegment(), projection);
+        return mDbFacade.queryMediaIdForApps(uri.getHost(), uri.getLastPathSegment(),
+                projection);
     }
 
-    private String getPickerSegmentType(Uri uri) {
-        switch (mLocalUriMatcher.matchUri(uri, /* allowHidden */ false)) {
-            case PICKER_ID:
-                return PICKER_SEGMENT;
-            case PICKER_GET_CONTENT_ID:
-                return PICKER_GET_CONTENT_SEGMENT;
-        }
-
-        return null;
-    }
-
-    /**
-     * Creates a picker uri incorporating authority, user id and cloud provider.
-     */
-    public static Uri wrapProviderUri(Uri uri, String action, int userId) {
+    public static Uri wrapProviderUri(Uri uri, int userId) {
         final List<String> segments = uri.getPathSegments();
         if (segments.size() != 2) {
             throw new IllegalArgumentException("Unexpected provider URI: " + uri);
         }
 
         Uri.Builder builder = initializeUriBuilder(MediaStore.AUTHORITY);
-        if (action.equalsIgnoreCase(Intent.ACTION_GET_CONTENT)) {
-            builder.appendPath(PICKER_GET_CONTENT_SEGMENT);
-        } else {
-            builder.appendPath(PICKER_SEGMENT);
-        }
+        builder.appendPath(PICKER_SEGMENT);
         builder.appendPath(String.valueOf(userId));
         builder.appendPath(uri.getHost());
 
@@ -325,36 +293,10 @@
     }
 
     private void checkUriPermission(Uri uri, int pid, int uid) {
-        // Clear query parameters to check for URI permissions, apps can add requireOriginal
-        // query parameter to URI, URI grants will not be present in that case.
-        Uri uriWithoutQueryParams = uri.buildUpon().clearQuery().build();
-        if (!isSelf(uid) && mContext.checkUriPermission(uriWithoutQueryParams, pid, uid,
+        if (!isSelf(uid) && mContext.checkUriPermission(uri, pid, uid,
                 Intent.FLAG_GRANT_READ_URI_PERMISSION) != PERMISSION_GRANTED) {
             throw new SecurityException("Calling uid ( " + uid + " ) does not have permission to " +
-                    "access picker uri: " + uriWithoutQueryParams);
-        }
-    }
-
-    private void checkPermissionForRequireOriginalQueryParam(Uri uri,
-            LocalCallingIdentity localCallingIdentity) {
-        String value = uri.getQueryParameter(MediaStore.PARAM_REQUIRE_ORIGINAL);
-        if (value == null || value.isEmpty()) {
-            return;
-        }
-
-        // Check if requireOriginal is set
-        if (Integer.parseInt(value) == 1) {
-            if (mLocalUriMatcher.matchUri(uri, /* allowHidden */ false) == PICKER_ID) {
-                throw new UnsupportedOperationException(
-                        "Require Original is not supported for Picker URI " + uri);
-            }
-
-            if (mLocalUriMatcher.matchUri(uri, /* allowHidden */ false) == PICKER_GET_CONTENT_ID
-                    && isRedactionNeededForPickerUri(localCallingIdentity)) {
-                throw new UnsupportedOperationException("Calling uid ( " + Binder.getCallingUid()
-                        + " ) does not have ACCESS_MEDIA_LOCATION permission for requesting "
-                        + "original file");
-            }
+                    "access picker uri: " + uri);
         }
     }
 
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
index 4fe7556..2b33403 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerActivity.java
@@ -543,7 +543,7 @@
         logPickerSelectionConfirmed(mSelection.getSelectedItems().size());
         if (shouldPreloadSelectedItems()) {
             final var uris = PickerResult.getPickerUrisForItems(
-                    getIntent().getAction(), mSelection.getSelectedItems());
+                    mSelection.getSelectedItems());
             mPreloaderInstanceHolder.preloader =
                     SelectedMediaPreloader.preload(/* activity */ this, uris);
             deSelectUnavailableMedia(mPreloaderInstanceHolder.preloader);
@@ -571,8 +571,7 @@
         // The permission controller will pass the requesting package's UID here
         final Bundle extras = getIntent().getExtras();
         final int uid = extras.getInt(Intent.EXTRA_UID);
-        final List<Uri> uris = getPickerUrisForItems(getIntent().getAction(),
-                mSelection.getSelectedItemsWithoutGrants());
+        final List<Uri> uris = getPickerUrisForItems(mSelection.getSelectedItemsWithoutGrants());
         ForegroundThread.getExecutor().execute(() -> {
             // Handle grants in another thread to not block the UI.
             grantMediaReadForPackage(getApplicationContext(), uid, uris);
@@ -580,10 +579,9 @@
 
         // Revoke READ_GRANT for items that were pre-granted but now in the current session user has
         // deselected them.
-        if (isUserSelectImagesForAppAction()
-                && mPickerViewModel.getConfigStore().isPickerChoiceManagedSelectionEnabled()) {
+        if (mPickerViewModel.isManagedSelectionEnabled()) {
             final List<Uri> urisForItemsWhoseGrantsNeedsToBeRevoked = getPickerUrisForItems(
-                    getIntent().getAction(), mSelection.getPreGrantedItemsToBeRevoked());
+                    mSelection.getPreGrantedItemsToBeRevoked());
             if (!urisForItemsWhoseGrantsNeedsToBeRevoked.isEmpty()) {
                 ForegroundThread.getExecutor().execute(() -> {
                     // Handle grants in another thread to not block the UI.
@@ -595,8 +593,9 @@
     }
 
     private void setResultForPickImagesOrGetContentAction() {
-        final Intent resultData = getPickerResponseIntent(getIntent().getAction(),
-                mSelection.canSelectMultiple(), mSelection.getSelectedItems());
+        final Intent resultData = getPickerResponseIntent(
+                mSelection.canSelectMultiple(),
+                mSelection.getSelectedItems());
         setResult(RESULT_OK, resultData);
     }
 
@@ -912,8 +911,7 @@
     /**
      * Reset to Photo Picker initial launch state (Photos grid tab) in the current profile mode.
      */
-    @VisibleForTesting
-    public void resetInCurrentProfile() {
+    private void resetInCurrentProfile() {
         // Clear all the fragments in the FragmentManager
         final FragmentManager fragmentManager = getSupportFragmentManager();
         fragmentManager.popBackStackImmediate(/* name */ null,
diff --git a/src/com/android/providers/media/photopicker/PickerSyncController.java b/src/com/android/providers/media/photopicker/PickerSyncController.java
index 52f85ff..67909d5 100644
--- a/src/com/android/providers/media/photopicker/PickerSyncController.java
+++ b/src/com/android/providers/media/photopicker/PickerSyncController.java
@@ -54,7 +54,6 @@
 import android.util.ArraySet;
 import android.util.Log;
 
-import androidx.annotation.GuardedBy;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -66,8 +65,11 @@
 import com.android.providers.media.photopicker.data.CloudProviderInfo;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
 import com.android.providers.media.photopicker.metrics.NonUiEventLogger;
+import com.android.providers.media.photopicker.sync.CloseableReentrantLock;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 import com.android.providers.media.photopicker.util.CloudProviderUtils;
 import com.android.providers.media.photopicker.util.exceptions.RequestObsoleteException;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -136,16 +138,9 @@
     private final PickerDbFacade mDbFacade;
     private final SharedPreferences mSyncPrefs;
     private final SharedPreferences mUserPrefs;
+    private final PickerSyncLockManager mPickerSyncLockManager;
     private final String mLocalProvider;
 
-    private final Object mCloudSyncLock = new Object();
-    private final Object mCloudAlbumSyncLock = new Object();
-
-
-    // TODO(b/278562157): If there is a dependency on the sync process, always acquire the
-    //  {@link mCloudSyncLock} before {@link mCloudProviderLock} to avoid deadlock.
-    private final Object mCloudProviderLock = new Object();
-    @GuardedBy("mCloudProviderLock")
     private CloudProviderInfo mCloudProviderInfo;
     @Nullable
     private static PickerSyncController sInstance;
@@ -161,8 +156,10 @@
      */
     @NonNull
     public static PickerSyncController initialize(@NonNull Context context,
-            @NonNull PickerDbFacade dbFacade, @NonNull ConfigStore configStore) {
-        return initialize(context, dbFacade, configStore, LOCAL_PICKER_PROVIDER_AUTHORITY);
+            @NonNull PickerDbFacade dbFacade, @NonNull ConfigStore configStore, @NonNull
+            PickerSyncLockManager pickerSyncLockManager) {
+        return initialize(context, dbFacade, configStore, pickerSyncLockManager,
+                LOCAL_PICKER_PROVIDER_AUTHORITY);
     }
 
     /**
@@ -180,8 +177,8 @@
     @VisibleForTesting
     public static PickerSyncController initialize(@NonNull Context context,
             @NonNull PickerDbFacade dbFacade, @NonNull ConfigStore configStore,
-            @NonNull String localProvider) {
-        sInstance = new PickerSyncController(context, dbFacade, configStore,
+            @NonNull PickerSyncLockManager pickerSyncLockManager, @NonNull String localProvider) {
+        sInstance = new PickerSyncController(context, dbFacade, configStore, pickerSyncLockManager,
                 localProvider);
         return sInstance;
     }
@@ -209,7 +206,8 @@
     }
 
     private PickerSyncController(@NonNull Context context, @NonNull PickerDbFacade dbFacade,
-            @NonNull ConfigStore configStore, @NonNull String localProvider) {
+            @NonNull ConfigStore configStore, @NonNull PickerSyncLockManager pickerSyncLockManager,
+            @NonNull String localProvider) {
         mContext = context;
         mConfigStore = configStore;
         mSyncPrefs = mContext.getSharedPreferences(PICKER_SYNC_PREFS_FILE_NAME,
@@ -217,6 +215,7 @@
         mUserPrefs = mContext.getSharedPreferences(PICKER_USER_PREFS_FILE_NAME,
                 Context.MODE_PRIVATE);
         mDbFacade = dbFacade;
+        mPickerSyncLockManager = pickerSyncLockManager;
         mLocalProvider = localProvider;
 
         // Listen to the device config, and try to enable cloud features when the config changes.
@@ -224,8 +223,14 @@
         initCloudProvider();
     }
 
+    @NonNull
+    public PickerSyncLockManager getPickerSyncLockManager() {
+        return mPickerSyncLockManager;
+    }
+
     private void initCloudProvider() {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             if (!mConfigStore.isCloudMediaInPhotoPickerEnabled()) {
                 Log.d(TAG, "Cloud-Media-in-Photo-Picker feature is disabled during " + TAG
                         + " construction.");
@@ -262,14 +267,6 @@
     }
 
     /**
-     * Returns the sync lock object for Cloud albums.
-     * @return the lock object for protecting synchronized code related to cloud albums.
-     */
-    public Object getCloudAlbumSyncLock() {
-        return mCloudAlbumSyncLock;
-    }
-
-    /**
      * Syncs the local and currently enabled cloud {@link CloudMediaProvider} instances
      */
     public void syncAllMedia() {
@@ -305,8 +302,9 @@
      */
     public void syncAllMediaFromCloudProvider(@Nullable CancellationSignal cancellationSignal) {
 
-        synchronized (mCloudSyncLock) {
-            final String cloudProvider = getCloudProvider();
+        try (CloseableReentrantLock ignored =
+                     mPickerSyncLockManager.tryLock(PickerSyncLockManager.CLOUD_SYNC_LOCK)) {
+            final String cloudProvider = getCloudProviderWithTimeout();
 
             // Trigger a sync.
             final InstanceId instanceId = NonUiEventLogger.generateInstanceId();
@@ -320,6 +318,8 @@
                         + ". The cloud provider may have changed during the sync, or only a"
                         + " partial sync was completed.");
             }
+        } catch (UnableToAcquireLockException e) {
+            Log.e(TAG, "Could not sync with the cloud provider", e);
         }
     }
 
@@ -332,8 +332,13 @@
             executeSyncAlbumReset(getLocalProvider(), isLocal, albumId);
             syncAlbumMediaFromLocalProvider(albumId, /* cancellationSignal=*/ null);
         } else {
-            synchronized (mCloudAlbumSyncLock) {
-                executeSyncAlbumReset(getCloudProvider(), isLocal, albumId);
+            try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                    .tryLock(PickerSyncLockManager.CLOUD_ALBUM_SYNC_LOCK)) {
+                executeSyncAlbumReset(getCloudProviderWithTimeout(), isLocal, albumId);
+            } catch (UnableToAcquireLockException e) {
+                Log.e(TAG, "Unable to reset cloud album media " + albumId, e);
+                // Continue to attempt cloud album sync. This may show deleted album media on
+                // the album view.
             }
             syncAlbumMediaFromCloudProvider(albumId, /*cancellationSignal=*/ null);
         }
@@ -349,9 +354,12 @@
     /** Syncs album media from the currently enabled cloud {@link CloudMediaProvider}. */
     public void syncAlbumMediaFromCloudProvider(
             @NonNull String albumId, @Nullable CancellationSignal cancellationSignal) {
-        synchronized (mCloudAlbumSyncLock) {
-            syncAlbumMediaFromProvider(getCloudProvider(), /* isLocal */ false, albumId,
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .tryLock(PickerSyncLockManager.CLOUD_ALBUM_SYNC_LOCK)) {
+            syncAlbumMediaFromProvider(getCloudProviderWithTimeout(), /* isLocal */ false, albumId,
                     /* enablePagedSync= */ true, cancellationSignal);
+        } catch (UnableToAcquireLockException e) {
+            Log.e(TAG, "Unable to sync cloud album media " + albumId, e);
         }
     }
 
@@ -359,14 +367,20 @@
      * Resets media library previously synced from the current {@link CloudMediaProvider} as well
      * as the {@link #mLocalProvider local provider}.
      */
-    public void resetAllMedia() {
+    public void resetAllMedia() throws UnableToAcquireLockException {
+        // No need to acquire cloud lock for local reset.
         resetAllMedia(mLocalProvider, /* isLocal */ true);
-        synchronized (mCloudSyncLock) {
+
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_SYNC_LOCK)) {
+
+            // This does not fall in any sync path. Try to acquire the lock indefinitely.
             resetAllMedia(getCloudProvider(), /* isLocal */ false);
         }
     }
 
-    private boolean resetAllMedia(@Nullable String authority, boolean isLocal) {
+    private boolean resetAllMedia(@Nullable String authority, boolean isLocal)
+            throws UnableToAcquireLockException {
         Trace.beginSection(traceSectionName("resetAllMedia", isLocal));
         try {
             executeSyncReset(authority, isLocal);
@@ -445,7 +459,8 @@
             Log.v(TAG, "Thread=" + Thread.currentThread() + "; Stacktrace:", new Throwable());
         }
 
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             if (Objects.equals(mCloudProviderInfo.authority, authority)) {
                 Log.w(TAG, "Cloud provider already set: " + authority);
                 return true;
@@ -454,12 +469,13 @@
 
         final CloudProviderInfo newProviderInfo = getCloudProviderInfo(authority, ignoreAllowList);
         if (authority == null || !newProviderInfo.isEmpty()) {
-            synchronized (mCloudProviderLock) {
+            try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                    .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
                 // Disable cloud provider queries on the db until next sync
                 // This will temporarily *clear* the cloud provider on the db facade and prevent
                 // any queries from seeing cloud media until a sync where the cloud provider will be
                 // reset on the facade
-                disablePickerCloudMediaQueries(/* isLocal */ false);
+                mDbFacade.setCloudProvider(null);
 
                 final String oldAuthority = mCloudProviderInfo.authority;
                 persistCloudProviderInfo(newProviderInfo, /* shouldUnset */ true);
@@ -485,7 +501,8 @@
      */
     @NonNull
     public CloudProviderInfo getCurrentCloudProviderInfo() {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             return mCloudProviderInfo;
         }
     }
@@ -496,19 +513,38 @@
      *         disabled by the user.
      */
     private void setCurrentCloudProviderInfo(@NonNull CloudProviderInfo cloudProviderInfo) {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             mCloudProviderInfo = cloudProviderInfo;
         }
     }
 
     /**
+     * This should not be used in picker sync paths because we should not wait on a lock
+     * indefinitely during the picker sync process.
+     * Use {@link this#getCloudProviderWithTimeout()} instead.
      * @return {@link android.content.pm.ProviderInfo#authority authority} of the current
      *         {@link CloudMediaProvider} or {@code null} if the {@link CloudMediaProvider}
      *         integration is not enabled.
      */
     @Nullable
     public String getCloudProvider() {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
+            return mCloudProviderInfo.authority;
+        }
+    }
+
+    /**
+     * @return {@link android.content.pm.ProviderInfo#authority authority} of the current
+     *         {@link CloudMediaProvider} or {@code null} if the {@link CloudMediaProvider}
+     *         integration is not enabled. This operation acquires a lock internally with a timeout.
+     * @throws UnableToAcquireLockException if the lock was not acquired within the given timeout.
+     */
+    @Nullable
+    public String getCloudProviderWithTimeout() throws UnableToAcquireLockException {
+        try (CloseableReentrantLock ignored  = mPickerSyncLockManager
+                .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             return mCloudProviderInfo.authority;
         }
     }
@@ -526,7 +562,8 @@
             return true;
         }
 
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             if (!mCloudProviderInfo.isEmpty()
                     && Objects.equals(mCloudProviderInfo.authority, authority)) {
                 return true;
@@ -541,7 +578,8 @@
             return true;
         }
 
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             if (!mCloudProviderInfo.isEmpty() && uid == mCloudProviderInfo.uid
                     && Objects.equals(mCloudProviderInfo.authority, authority)) {
                 return true;
@@ -574,7 +612,8 @@
      * Notifies about package removal
      */
     public void notifyPackageRemoval(String packageName) {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             if (mCloudProviderInfo.matches(packageName)) {
                 Log.i(TAG, "Package " + packageName
                         + " is the current cloud provider and got removed");
@@ -584,7 +623,8 @@
     }
 
     private void resetCloudProvider() {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             setCloudProvider(/* authority */ null);
 
             /**
@@ -621,7 +661,7 @@
                 executeSyncAddAlbum(
                         authority, isLocal, albumId, queryArgs, instanceId, cancellationSignal);
             }
-        } catch (RuntimeException e) {
+        } catch (RuntimeException | UnableToAcquireLockException e) {
             // Unlike syncAllMediaFromProvider, we don't retry here because any errors would have
             // occurred in fetching all the album_media since incremental sync is not supported.
             // A full sync is therefore unlikely to resolve any issue
@@ -717,13 +757,17 @@
             Log.e(TAG, "Failed to sync all media because authority has changed: ", e);
         } catch (IllegalStateException e) {
             // If we're in an illegal state, reset and start a full sync again.
-            resetAllMedia(authority, isLocal);
             Log.e(TAG, "Failed to sync all media. Reset media and retry: " + retryOnFailure, e);
-            if (retryOnFailure) {
-                return syncAllMediaFromProvider(authority, isLocal, /* retryOnFailure */ false,
-                        enablePagedSync, instanceId, cancellationSignal);
+            try {
+                resetAllMedia(authority, isLocal);
+                if (retryOnFailure) {
+                    return syncAllMediaFromProvider(authority, isLocal, /* retryOnFailure */ false,
+                            enablePagedSync, instanceId, cancellationSignal);
+                }
+            } catch (UnableToAcquireLockException ex) {
+                Log.e(TAG, "Could not reset media", e);
             }
-        } catch (RuntimeException e) {
+        } catch (RuntimeException | UnableToAcquireLockException e) {
             // Retry the failed operation to see if it was an intermittent problem. If this fails,
             // the database will be in a partial state until the sync resumes from this point
             // on next run.
@@ -742,9 +786,10 @@
      * Disable cloud media queries from Picker database. After disabling cloud media queries, when a
      * media query will run on Picker database, only local media items will be returned.
      */
-    private void disablePickerCloudMediaQueries(boolean isLocal) {
+    private void disablePickerCloudMediaQueries(boolean isLocal)
+            throws UnableToAcquireLockException {
         if (!isLocal) {
-            mDbFacade.setCloudProvider(null);
+            mDbFacade.setCloudProviderWithTimeout(null);
         }
     }
 
@@ -752,11 +797,13 @@
      * Enable cloud media queries from Picker database. After enabling cloud media queries, when a
      * media query will run on Picker database, both local and cloud media items will be returned.
      */
-    private void enablePickerCloudMediaQueries(String authority, boolean isLocal) {
+    private void enablePickerCloudMediaQueries(String authority, boolean isLocal)
+            throws UnableToAcquireLockException {
         if (!isLocal) {
-            synchronized (mCloudProviderLock) {
+            try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                    .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
                 if (Objects.equals(mCloudProviderInfo.authority, authority)) {
-                    mDbFacade.setCloudProvider(authority);
+                    mDbFacade.setCloudProviderWithTimeout(authority);
                 }
             }
         }
@@ -817,7 +864,7 @@
             Bundle queryArgs,
             InstanceId instanceId,
             @Nullable CancellationSignal cancellationSignal)
-            throws RequestObsoleteException {
+            throws RequestObsoleteException, UnableToAcquireLockException {
         final Uri uri = getMediaUri(authority);
         final List<String> expectedHonoredArgs = new ArrayList<>();
         if (isIncrementalSync) {
@@ -867,7 +914,7 @@
             Bundle queryArgs,
             InstanceId instanceId,
             @Nullable CancellationSignal cancellationSignal)
-            throws RequestObsoleteException {
+            throws RequestObsoleteException, UnableToAcquireLockException {
         final Uri uri = getMediaUri(authority);
 
         Log.i(TAG, "Executing SyncAddAlbum. "
@@ -918,7 +965,7 @@
             Bundle queryArgs,
             InstanceId instanceId,
             @Nullable CancellationSignal cancellationSignal)
-            throws RequestObsoleteException {
+            throws RequestObsoleteException, UnableToAcquireLockException {
         final Uri uri = getDeletedMediaUri(authority);
 
         Log.i(TAG, "Executing SyncRemove. isLocal: " + isLocal + ". authority: " + authority);
@@ -949,7 +996,8 @@
      * Persist cloud provider info and send a sync request to the background thread.
      */
     private void persistCloudProviderInfo(@NonNull CloudProviderInfo info, boolean shouldUnset) {
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             setCurrentCloudProviderInfo(info);
 
             final String authority = info.authority;
@@ -981,7 +1029,11 @@
 
             Log.d(TAG, "Updated cloud provider to: " + authority);
 
-            resetCachedMediaCollectionInfo(info.authority, /* isLocal */ false);
+            try {
+                resetCachedMediaCollectionInfo(info.authority, /* isLocal */ false);
+            } catch (UnableToAcquireLockException e) {
+                Log.wtf(TAG, "CLOUD_PROVIDER_LOCK is already held by this thread.");
+            }
 
             sendPickerUiRefreshNotification();
         }
@@ -1010,7 +1062,7 @@
      * Commit the latest media collection info when a sync operation is completed.
      */
     private boolean cacheMediaCollectionInfo(@Nullable String authority, boolean isLocal,
-            @Nullable Bundle bundle) {
+            @Nullable Bundle bundle) throws UnableToAcquireLockException {
         if (authority == null) {
             Log.d(TAG, "Ignoring cache media info for null authority with bundle: " + bundle);
             return true;
@@ -1023,7 +1075,8 @@
                 cacheMediaCollectionInfoInternal(isLocal, bundle);
                 return true;
             } else {
-                synchronized (mCloudProviderLock) {
+                try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                        .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
                     // Check if the media collection info belongs to the current cloud provider
                     // authority.
                     if (Objects.equals(authority, mCloudProviderInfo.authority)) {
@@ -1072,9 +1125,11 @@
      * @param token The token to remember. A null value will clear the preference.
      * @param resumeKey The operation's key in sync preferences.
      */
-    private void rememberNextPageToken(@Nullable String token, String resumeKey) {
+    private void rememberNextPageToken(@Nullable String token, String resumeKey)
+            throws UnableToAcquireLockException {
 
-        synchronized (mCloudProviderLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             final SharedPreferences.Editor editor = mSyncPrefs.edit();
             if (token == null) {
                 Log.d(TAG, String.format("Clearing next page token for key: %s", resumeKey));
@@ -1096,13 +1151,15 @@
      * @return The PageToken to resume from, or {@code null} if there is no operation to resume.
      */
     @Nullable
-    public String getPageTokenFromResumeKey(String resumeKey) {
-        synchronized (mCloudProviderLock) {
+    private String getPageTokenFromResumeKey(String resumeKey) throws UnableToAcquireLockException {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
             return mSyncPrefs.getString(resumeKey, /* defValue= */ null);
         }
     }
 
-    private boolean resetCachedMediaCollectionInfo(@Nullable String authority, boolean isLocal) {
+    private boolean resetCachedMediaCollectionInfo(@Nullable String authority, boolean isLocal)
+            throws UnableToAcquireLockException {
         return cacheMediaCollectionInfo(authority, isLocal, /* bundle */ null);
     }
 
@@ -1136,12 +1193,13 @@
 
     @NonNull
     private SyncRequestParams getSyncRequestParams(@Nullable String authority,
-            boolean isLocal) throws RequestObsoleteException {
+            boolean isLocal) throws RequestObsoleteException, UnableToAcquireLockException {
         if (isLocal) {
             return getSyncRequestParamsInternal(authority, isLocal);
         } else {
             // Ensure that we are fetching sync request params for the current cloud provider.
-            synchronized (mCloudProviderLock) {
+            try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                    .tryLock(PickerSyncLockManager.CLOUD_PROVIDER_LOCK)) {
                 if (Objects.equals(mCloudProviderInfo.authority, authority)) {
                     return getSyncRequestParamsInternal(authority, isLocal);
                 } else {
@@ -1264,7 +1322,8 @@
             @OperationType int op,
             String authority,
             Boolean isLocal,
-            @Nullable CancellationSignal cancellationSignal) throws RequestObsoleteException {
+            @Nullable CancellationSignal cancellationSignal)
+            throws RequestObsoleteException, UnableToAcquireLockException {
         return executePagedSync(
                 uri,
                 expectedMediaCollectionId,
@@ -1308,7 +1367,8 @@
             String authority,
             Boolean isLocal,
             @Nullable String albumId,
-            @Nullable CancellationSignal cancellationSignal) throws RequestObsoleteException {
+            @Nullable CancellationSignal cancellationSignal)
+            throws RequestObsoleteException, UnableToAcquireLockException {
         Trace.beginSection(traceSectionName("executePagedSync"));
 
         try {
@@ -1350,7 +1410,7 @@
                         if (!isLocal) {
                             // Ensure the cloud provider hasn't change out from underneath the
                             // running sync. If it has, we need to stop syncing.
-                            String currentCloudProvider = getCloudProvider();
+                            String currentCloudProvider = getCloudProviderWithTimeout();
                             if (TextUtils.isEmpty(currentCloudProvider)
                                     || !currentCloudProvider.equals(authority)) {
 
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
index 1c6f2c0..6e58395 100644
--- a/src/com/android/providers/media/photopicker/data/ItemsProvider.java
+++ b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
@@ -19,17 +19,21 @@
 import static android.content.ContentResolver.QUERY_ARG_LIMIT;
 import static android.database.DatabaseUtils.dumpCursorToString;
 import static android.provider.MediaStore.AUTHORITY;
+import static android.provider.MediaStore.MediaColumns.DATA;
 
+import static com.android.providers.media.MediaGrants.FILE_ID_COLUMN;
 import static com.android.providers.media.PickerUriResolver.PICKER_INTERNAL_URI;
 import static com.android.providers.media.photopicker.PickerDataLayer.QUERY_DATE_TAKEN_BEFORE_MS;
 import static com.android.providers.media.photopicker.PickerDataLayer.QUERY_LOCAL_ID_SELECTION;
 import static com.android.providers.media.photopicker.PickerDataLayer.QUERY_ROW_ID;
 import static com.android.providers.media.photopicker.util.CloudProviderUtils.sendInitPhotoPickerDataNotification;
+import static com.android.providers.media.util.FileUtils.getContentUriForPath;
 
 import android.content.ContentProvider;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.net.Uri;
@@ -75,6 +79,10 @@
     private static final Uri URI_ALBUMS_ALL;
     private static final Uri URI_ALBUMS_LOCAL;
 
+    private static final String MEDIA_GRANTS_URI_PATH = "content://media/media_grants";
+    public static final String EXTRA_MIME_TYPE_SELECTION = "media_grant_mime_type_selection";
+
+
     static {
         final Uri media = PICKER_INTERNAL_URI.buildUpon()
                 .appendPath(PickerUriResolver.MEDIA_PATH).build();
@@ -406,6 +414,36 @@
     }
 
     /**
+     * Fetches file Uris for items having {@link com.android.providers.media.MediaGrants} for the
+     * given package. Returns an empty list if no grants are present.
+     */
+    @NonNull
+    public List<Uri> fetchReadGrantedItemsUrisForPackage(int packageUid, String[] mimeTypes) {
+        final ContentResolver resolver = mContext.getContentResolver();
+        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+            assert client != null;
+            final Bundle extras = new Bundle();
+            extras.putInt(Intent.EXTRA_UID, packageUid);
+            extras.putStringArray(EXTRA_MIME_TYPE_SELECTION, mimeTypes);
+            List<Uri> filesUriList = new ArrayList<>();
+            try (Cursor c = client.query(Uri.parse(MEDIA_GRANTS_URI_PATH),
+                    /* projection= */ null,
+                    /* queryArgs= */ extras,
+                    null)) {
+                while (c.moveToNext()) {
+                    final String file_path = c.getString(c.getColumnIndexOrThrow(DATA));
+                    final Integer file_id = c.getInt(c.getColumnIndexOrThrow(FILE_ID_COLUMN));
+                    filesUriList.add(getContentUriForPath(
+                            file_path).buildUpon().appendPath(String.valueOf(file_id)).build());
+                }
+            }
+            return filesUriList;
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
      * Sends a data init notification to the MP process.
      */
     public void initPhotoPickerData(@Nullable String albumId,
diff --git a/src/com/android/providers/media/photopicker/data/MediaGrantsProvider.java b/src/com/android/providers/media/photopicker/data/MediaGrantsProvider.java
deleted file mode 100644
index 67718d7..0000000
--- a/src/com/android/providers/media/photopicker/data/MediaGrantsProvider.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.providers.media.photopicker.data;
-
-import static android.provider.MediaStore.AUTHORITY;
-import static android.provider.MediaStore.MediaColumns.DATA;
-
-import static com.android.providers.media.MediaGrants.FILE_ID_COLUMN;
-import static com.android.providers.media.util.FileUtils.getContentUriForPath;
-
-import android.annotation.NonNull;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Provider grants for items selected in previous sessions.
- */
-public final class MediaGrantsProvider {
-    private static final String MEDIA_GRANTS_URI_PATH = "content://media/media_grants";
-    public static final String EXTRA_MIME_TYPE_SELECTION = "media_grant_mime_type_selection";
-
-    /**
-     * Fetches file Uris for items having {@link com.android.providers.media.MediaGrants} for the
-     * given package. Returns an empty list if no grants are present.
-     *
-     * @hide
-     */
-    @NonNull
-    public static List<Uri> fetchReadGrantedItemsUrisForPackage(
-            @NonNull Context context, int packageUid, String[] mimeTypes) {
-        final ContentResolver resolver = context.getContentResolver();
-        try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
-            assert client != null;
-            final Bundle extras = new Bundle();
-            extras.putInt(Intent.EXTRA_UID, packageUid);
-            extras.putStringArray(EXTRA_MIME_TYPE_SELECTION, mimeTypes);
-            final Cursor c = client.query(Uri.parse(MEDIA_GRANTS_URI_PATH),
-                    /* projection= */ null,
-                    /* queryArgs= */ extras,
-                    null);
-            List<Uri> filesUriList = new ArrayList<>(0);
-            while (c.moveToNext()) {
-                final String file_path = c.getString(c.getColumnIndexOrThrow(DATA));
-                final Integer file_id = c.getInt(c.getColumnIndexOrThrow(FILE_ID_COLUMN));
-                filesUriList.add(getContentUriForPath(
-                        file_path).buildUpon().appendPath(String.valueOf(file_id)).build());
-            }
-            c.close();
-            return filesUriList;
-        } catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-    }
-}
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
index acd60ca..cd51b9b 100644
--- a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
@@ -47,10 +47,12 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.providers.media.PickerUriResolver;
 import com.android.providers.media.photopicker.PickerSyncController;
 import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.sync.CloseableReentrantLock;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 import com.android.providers.media.photopicker.sync.SyncTrackerRegistry;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
 import com.android.providers.media.util.MimeUtils;
 
 import java.io.PrintWriter;
@@ -66,34 +68,32 @@
  */
 public class PickerDbFacade {
     private static final String VIDEO_MIME_TYPES = "video/%";
-
-    // TODO(b/278562157): If there is a dependency on
-    //  {@link PickerSyncController#mCloudProviderLock}, always acquire
-    //  {@link PickerSyncController#mCloudProviderLock} before {@link mLock} to avoid deadlock.
-    @NonNull
-    private final Object mLock = new Object();
     private final Context mContext;
     private final SQLiteDatabase mDatabase;
+    private final PickerSyncLockManager mPickerSyncLockManager;
     private final String mLocalProvider;
     // This is the cloud provider the database is synced with. It can be set as null to disable
     // cloud queries when database is not in sync with the current cloud provider.
     @Nullable
     private String mCloudProvider;
 
-    public PickerDbFacade(Context context) {
-        this(context, PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
+    public PickerDbFacade(Context context, PickerSyncLockManager pickerSyncLockManager) {
+        this(context, pickerSyncLockManager, PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
     }
 
     @VisibleForTesting
-    public PickerDbFacade(Context context, String localProvider) {
-        this(context, localProvider, new PickerDatabaseHelper(context));
+    public PickerDbFacade(Context context, PickerSyncLockManager pickerSyncLockManager,
+            String localProvider) {
+        this(context, pickerSyncLockManager, localProvider, new PickerDatabaseHelper(context));
     }
 
     @VisibleForTesting
-    public PickerDbFacade(Context context, String localProvider, PickerDatabaseHelper dbHelper) {
+    public PickerDbFacade(Context context, PickerSyncLockManager pickerSyncLockManager,
+            String localProvider, PickerDatabaseHelper dbHelper) {
         mContext = context;
         mLocalProvider = localProvider;
         mDatabase = dbHelper.getWritableDatabase();
+        mPickerSyncLockManager = pickerSyncLockManager;
     }
 
     private static final String TAG = "PickerDbFacade";
@@ -103,7 +103,10 @@
     private static final int FAIL = -1;
 
     private static final String TABLE_MEDIA = "media";
-
+    // Intentionally use /sdcard path so that the receiving app resolves it to it's per-user
+    // external storage path, e.g. /storage/emulated/<userid>. That way FUSE cross-user access is
+    // not required for picker paths sent across users
+    private static final String PICKER_PATH = "/sdcard/" + getPickerRelativePath();
     private static final String TABLE_ALBUM_MEDIA = "album_media";
 
     @VisibleForTesting
@@ -225,19 +228,39 @@
     /**
      * Sets the cloud provider to be returned after querying the picker db
      * If null, cloud media will be excluded from all queries.
+     * This should not be used in picker sync paths because we should not wait on a lock
+     * indefinitely during the picker sync process.
+     * Use {@link this#setCloudProviderWithTimeout} instead.
      */
     public void setCloudProvider(String authority) {
-        synchronized (mLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.DB_CLOUD_LOCK)) {
             mCloudProvider = authority;
         }
     }
 
     /**
-     * Returns the cloud provider that will be returned after querying the picker db
+     * Sets the cloud provider to be returned after querying the picker db
+     * If null, cloud media will be excluded from all queries.
+     * This should be used in picker sync paths because we should not wait on a lock
+     * indefinitely during the picker sync process
+     */
+    public void setCloudProviderWithTimeout(String authority) throws UnableToAcquireLockException {
+        try (CloseableReentrantLock ignored =
+                     mPickerSyncLockManager.tryLock(PickerSyncLockManager.DB_CLOUD_LOCK)) {
+            mCloudProvider = authority;
+        }
+    }
+
+    /**
+     * Returns the cloud provider that will be returned after querying the picker db.
+     * This should not be used in picker sync paths because we should not wait on a lock
+     * indefinitely during the picker sync process.
      */
     @VisibleForTesting
     public String getCloudProvider() {
-        synchronized (mLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.DB_CLOUD_LOCK)) {
             return mCloudProvider;
         }
     }
@@ -869,13 +892,10 @@
                     TABLE_MEDIA, /* cloudProvider*/ null);
         }
 
-        final String cloudProvider;
-        synchronized (mLock) {
-            // If the cloud sync is in progress or the cloud provider has changed but a sync has not
-            // been completed and committed, {@link PickerDBFacade.mCloudProvider} will be
-            // {@code null}.
-            cloudProvider = mCloudProvider;
-        }
+        // If the cloud sync is in progress or the cloud provider has changed but a sync has not
+        // been completed and committed, {@link PickerDBFacade.mCloudProvider} will be
+        // {@code null}.
+        final String cloudProvider = getCloudProvider();
 
         return queryMediaForUi(qb, selectionArgs, query.mLimit, query.mIsLocalOnly,
                 TABLE_MEDIA, cloudProvider);
@@ -907,7 +927,7 @@
      * Returns a {@link Cursor} containing picker db media rows with columns as {@code projection},
      * a subset of {@link PickerMediaColumns}.
      */
-    public Cursor queryMediaIdForApps(String pickerSegmentType, String authority, String mediaId,
+    public Cursor queryMediaIdForApps(String authority, String mediaId,
             @NonNull String[] projection) {
         final String[] selectionArgs = new String[] { mediaId };
         final SQLiteQueryBuilder qb = createVisibleMediaQueryBuilder();
@@ -918,12 +938,13 @@
         }
 
         if (authority.equals(mLocalProvider)) {
-            return queryMediaIdForAppsLocked(qb, projection, selectionArgs, pickerSegmentType);
+            return queryMediaIdForAppsLocked(qb, projection, selectionArgs);
         }
 
-        synchronized (mLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.DB_CLOUD_LOCK)) {
             if (authority.equals(mCloudProvider)) {
-                return queryMediaIdForAppsLocked(qb, projection, selectionArgs, pickerSegmentType);
+                return queryMediaIdForAppsLocked(qb, projection, selectionArgs);
             }
         }
 
@@ -931,9 +952,8 @@
     }
 
     private Cursor queryMediaIdForAppsLocked(@NonNull SQLiteQueryBuilder qb,
-            @NonNull String[] projection, @NonNull String[] selectionArgs,
-            String pickerSegmentType) {
-        return qb.query(mDatabase, getMediaStoreProjectionLocked(projection, pickerSegmentType),
+            @NonNull String[] projection, @NonNull String[] selectionArgs) {
+        return qb.query(mDatabase, getMediaStoreProjectionLocked(projection),
                 /* selection */ null, selectionArgs, /* groupBy */ null, /* having */ null,
                 /* orderBy */ null, /* limitStr */ null);
     }
@@ -1048,7 +1068,8 @@
         // Hold lock while checking the cloud provider and querying so that cursor extras containing
         // the cloud provider is consistent with the cursor results and doesn't race with
         // #setCloudProvider
-        synchronized (mLock) {
+        try (CloseableReentrantLock ignored = mPickerSyncLockManager
+                .lock(PickerSyncLockManager.DB_CLOUD_LOCK)) {
             if (mCloudProvider == null || !Objects.equals(mCloudProvider, authority)) {
                 // TODO(b/278086344): If cloud provider is null or has changed from what we received
                 //  from the UI, skip all cloud items in the picker db.
@@ -1071,7 +1092,7 @@
     private String[] getCloudMediaProjectionLocked() {
         return new String[] {
             getProjectionAuthorityLocked(),
-            getProjectionDataLocked(MediaColumns.DATA, PickerUriResolver.PICKER_SEGMENT),
+            getProjectionDataLocked(MediaColumns.DATA),
             getProjectionId(MediaColumns.ID),
             // The id in the picker.db table represents the row id. This is used in UI pagination.
             getProjectionSimple(KEY_ID, Item.ROW_ID),
@@ -1085,14 +1106,13 @@
         };
     }
 
-    private String[] getMediaStoreProjectionLocked(String[] columns, String pickerSegmentType) {
+    private String[] getMediaStoreProjectionLocked(String[] columns) {
         final String[] projection = new String[columns.length];
 
         for (int i = 0; i < projection.length; i++) {
             switch (columns[i]) {
                 case PickerMediaColumns.DATA:
-                    projection[i] = getProjectionDataLocked(PickerMediaColumns.DATA,
-                            pickerSegmentType);
+                    projection[i] = getProjectionDataLocked(PickerMediaColumns.DATA);
                     break;
                 case PickerMediaColumns.DISPLAY_NAME:
                     projection[i] =
@@ -1147,13 +1167,13 @@
                 KEY_CLOUD_ID, mLocalProvider, mCloudProvider, MediaColumns.AUTHORITY);
     }
 
-    private String getProjectionDataLocked(String asColumn, String pickerSegmentType) {
+    private String getProjectionDataLocked(String asColumn) {
         // _data format:
         // /sdcard/.transforms/synthetic/picker/<user-id>/<authority>/media/<display-name>
         // See PickerUriResolver#getMediaUri
         final String authority = String.format("CASE WHEN %s IS NULL THEN '%s' ELSE '%s' END",
                 KEY_CLOUD_ID, mLocalProvider, mCloudProvider);
-        final String fullPath = "'" + getPickerPath(pickerSegmentType) + "/'"
+        final String fullPath = "'" + PICKER_PATH + "/'"
                 + "||" + "'" + MediaStore.MY_USER_ID + "/'"
                 + "||" + authority
                 + "||" + "'/" + CloudMediaProviderContract.URI_PATH_MEDIA + "/'"
@@ -1161,13 +1181,6 @@
         return String.format("%s AS %s", fullPath, asColumn);
     }
 
-    private String getPickerPath(String pickerSegmentType) {
-        // Intentionally use /sdcard path so that the receiving app resolves it to its per-user
-        // external storage path, e.g. /storage/emulated/<userid>. That way FUSE cross-user
-        // access is not required for picker paths sent across users
-        return "/sdcard/" + getPickerRelativePath(pickerSegmentType);
-    }
-
     private String getProjectionId(String asColumn) {
         // We prefer cloud_id first and it only matters for cloud+local items. For those, the row
         // will already be associated with a cloud authority, see #getProjectionAuthorityLocked.
diff --git a/src/com/android/providers/media/photopicker/data/PickerResult.java b/src/com/android/providers/media/photopicker/data/PickerResult.java
index 8108afa..7bea27c 100644
--- a/src/com/android/providers/media/photopicker/data/PickerResult.java
+++ b/src/com/android/providers/media/photopicker/data/PickerResult.java
@@ -40,10 +40,10 @@
      * @return {@code Intent} which contains Uri that has been granted access on.
      */
     @NonNull
-    public static Intent getPickerResponseIntent(String action, boolean canSelectMultiple,
+    public static Intent getPickerResponseIntent(boolean canSelectMultiple,
             @NonNull List<Item> selectedItems) {
         // 1. Get Picker Uris corresponding to the selected items
-        List<Uri> selectedUris = getPickerUrisForItems(action, selectedItems);
+        List<Uri> selectedUris = getPickerUrisForItems(selectedItems);
 
         // 2. Grant read access to picker Uris and return
         Intent intent = new Intent();
@@ -71,23 +71,22 @@
     }
 
     @VisibleForTesting
-    static Uri getPickerUri(String action, Uri uri) {
+    static Uri getPickerUri(Uri uri) {
         final String userInfo = uri.getUserInfo();
         final String userId = userInfo == null ? UserId.CURRENT_USER.toString() : userInfo;
-        return PickerUriResolver.wrapProviderUri(uri, action, Integer.parseInt(userId));
+        return PickerUriResolver.wrapProviderUri(uri, Integer.parseInt(userId));
     }
 
     /**
      * Returns list of PhotoPicker Uris corresponding to each {@link Item}
      *
-     * @param action action name which opened PhotoPicker
      * @param items list of Item for which we return uri list.
      */
     @NonNull
-    public static List<Uri> getPickerUrisForItems(String action, @NonNull List<Item> items) {
+    public static List<Uri> getPickerUrisForItems(@NonNull List<Item> items) {
         List<Uri> uris = new ArrayList<>();
         for (Item item : items) {
-            uris.add(getPickerUri(action, item.getContentUri()));
+            uris.add(getPickerUri(item.getContentUri()));
         }
 
         return uris;
diff --git a/src/com/android/providers/media/photopicker/data/glide/GlideLoadable.java b/src/com/android/providers/media/photopicker/data/glide/GlideLoadable.java
new file mode 100644
index 0000000..7b536a4
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/GlideLoadable.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.data.glide;
+
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.Item;
+
+import com.bumptech.glide.signature.ObjectKey;
+
+import java.util.Optional;
+
+/**
+ * A data class to coalesce {@link Item} and {@link Category} into a common loadable glide object,
+ * with the relevant data required for Glide loading.
+ */
+public class GlideLoadable {
+
+    private final Optional<String> mCacheKey;
+
+    @NonNull private final Uri mUri;
+
+    public GlideLoadable(@NonNull Uri uri) {
+        this(uri, /* cacheKey= */ null);
+    }
+
+    public GlideLoadable(@NonNull Uri uri, @Nullable String cacheKey) {
+        this.mUri = uri;
+        this.mCacheKey = Optional.ofNullable(cacheKey);
+    }
+
+    /**
+     * Get a signature string to represent this item in the Glide cache.
+     *
+     * @param prefix Optional prefix to prepend to this item's signature.
+     * @return A glide cache signature string.
+     */
+    @Nullable
+    public ObjectKey getLoadableSignature(@Nullable String prefix) {
+        return new ObjectKey(
+                Optional.ofNullable(prefix).orElse("") + mUri.toString() + mCacheKey.orElse(""));
+    }
+    ;
+
+    /**
+     * @return A {@link Uri} object to locate the media for this loadable.
+     */
+    public Uri getLoadableUri() {
+        return mUri;
+    }
+    ;
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java b/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java
index cc48670..8ef68d9 100644
--- a/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerGlideModule.java
@@ -17,7 +17,6 @@
 package com.android.providers.media.photopicker.data.glide;
 
 import android.content.Context;
-import android.net.Uri;
 
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.Registry;
@@ -34,6 +33,7 @@
 
     @Override
     public void registerComponents(Context context, Glide glide, Registry registry) {
-        registry.prepend(Uri.class, InputStream.class, new PickerModelLoaderFactory(context));
+        registry.append(
+                GlideLoadable.class, InputStream.class, new PickerModelLoaderFactory(context));
     }
 }
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java
index 1f3bb4c..f381630 100644
--- a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoader.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.content.UriMatcher;
-import android.net.Uri;
 import android.provider.CloudMediaProviderContract;
 
 import com.bumptech.glide.load.Options;
@@ -29,10 +28,8 @@
 
 import java.io.InputStream;
 
-/**
- * Custom {@link ModelLoader} to load thumbnails from cloud media provider.
- */
-public final class PickerModelLoader implements ModelLoader<Uri, InputStream> {
+/** Custom {@link ModelLoader} to load thumbnails from cloud media provider. */
+public final class PickerModelLoader implements ModelLoader<GlideLoadable, InputStream> {
     private final Context mContext;
 
     PickerModelLoader(Context context) {
@@ -40,21 +37,24 @@
     }
 
     @Override
-    public LoadData<InputStream> buildLoadData(Uri model, int width, int height,
-            Options options) {
+    public LoadData<InputStream> buildLoadData(
+            GlideLoadable model, int width, int height, Options options) {
         final boolean isThumbRequest = Boolean.TRUE.equals(options.get(THUMBNAIL_REQUEST));
-        return new LoadData<>(new ObjectKey(model),
+        return new LoadData<>(
+                new ObjectKey(model.getLoadableSignature(/* prefix= */ null)),
                 new PickerThumbnailFetcher(mContext, model, width, height, isThumbRequest));
     }
 
     @Override
-    public boolean handles(Uri model) {
+    public boolean handles(GlideLoadable model) {
         final int pickerId = 1;
         final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
-        matcher.addURI(model.getAuthority(),
-                CloudMediaProviderContract.URI_PATH_MEDIA + "/*", pickerId);
+        matcher.addURI(
+                model.getLoadableUri().getAuthority(),
+                CloudMediaProviderContract.URI_PATH_MEDIA + "/*",
+                pickerId);
 
         // Matches picker URIs of the form content://<authority>/media
-        return matcher.match(model) == pickerId;
+        return matcher.match(model.getLoadableUri()) == pickerId;
     }
 }
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java
index 938ef88..d7086d4 100644
--- a/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerModelLoaderFactory.java
@@ -17,7 +17,6 @@
 package com.android.providers.media.photopicker.data.glide;
 
 import android.content.Context;
-import android.net.Uri;
 
 import com.bumptech.glide.load.model.ModelLoader;
 import com.bumptech.glide.load.model.ModelLoaderFactory;
@@ -29,7 +28,7 @@
  * Custom {@link ModelLoaderFactory} which provides a {@link ModelLoader} for loading thumbnails
  * from cloud media provider.
  */
-public class PickerModelLoaderFactory implements ModelLoaderFactory<Uri, InputStream> {
+public class PickerModelLoaderFactory implements ModelLoaderFactory<GlideLoadable, InputStream> {
 
     private final Context mContext;
 
@@ -38,7 +37,7 @@
     }
 
     @Override
-    public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory unused) {
+    public ModelLoader<GlideLoadable, InputStream> build(MultiModelLoaderFactory unused) {
         return new PickerModelLoader(mContext);
     }
 
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerPreloadModelProvider.java b/src/com/android/providers/media/photopicker/data/glide/PickerPreloadModelProvider.java
new file mode 100644
index 0000000..9e5b4dd
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerPreloadModelProvider.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.data.glide;
+
+import static com.android.providers.media.photopicker.ui.ImageLoader.THUMBNAIL_REQUEST;
+
+import static com.bumptech.glide.load.resource.bitmap.Downsampler.PREFERRED_COLOR_SPACE;
+
+import android.content.Context;
+import android.provider.MediaStore;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.providers.media.photopicker.data.model.Item;
+import com.android.providers.media.photopicker.ui.PhotosTabAdapter;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.ListPreloader.PreloadModelProvider;
+import com.bumptech.glide.RequestBuilder;
+import com.bumptech.glide.load.PreferredColorSpace;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.signature.ObjectKey;
+
+import java.util.Collections;
+import java.util.List;
+
+/** Custom glide module to enable the loading of thumbnails from CloudMediaProvider. */
+public class PickerPreloadModelProvider implements PreloadModelProvider<GlideLoadable> {
+
+    private final Context mContext;
+    private final PreferredColorSpace mPreferredColorSpace;
+    private final PhotosTabAdapter mAdapter;
+
+    public PickerPreloadModelProvider(Context context, PhotosTabAdapter adapter) {
+        mContext = context;
+        mAdapter = adapter;
+
+        final boolean isScreenWideColorGamut =
+                mContext.getResources().getConfiguration().isScreenWideColorGamut();
+        mPreferredColorSpace =
+                isScreenWideColorGamut ? PreferredColorSpace.DISPLAY_P3 : PreferredColorSpace.SRGB;
+    }
+
+    /**
+     * Return a list of items that should be preloaded for the given RecyclerView adapter position.
+     *
+     * @param position the current position of the RecyclerView's adapter.
+     * @return A list of items to begin preloading.
+     */
+    @Override
+    @NonNull
+    public List<GlideLoadable> getPreloadItems(int position) {
+        if (mAdapter.isItemTypeMediaItem(position)) {
+            Object adapterItem = mAdapter.getAdapterItem(position);
+            if (adapterItem instanceof Item) {
+                Item item = (Item) adapterItem;
+                return Collections.singletonList(item.toGlideLoadable());
+            }
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * This should generate a load request identical to the load request generated by the
+     * RecyclerView itself. This ensures that there are not inadvertent cache misses because the
+     * preload succeeded, but the actual RecyclerView request didn't match what was in the cache
+     * already.
+     *
+     * @param loadable The {@link GlideLoadable} model for the thumbnail.
+     * @return An identical glide RequestBuilder to what the RecyclerView will generate when it
+     *     attempts to load this item.
+     */
+    @Override
+    @Nullable
+    public RequestBuilder getPreloadRequestBuilder(GlideLoadable loadable) {
+        RequestOptions options =
+                RequestOptions.option(THUMBNAIL_REQUEST, true)
+                        .set(PREFERRED_COLOR_SPACE, mPreferredColorSpace);
+        // TODO(b/224725723): Remove media store version from key once MP ids are
+        // stable.
+        ObjectKey signature =
+                loadable.getLoadableSignature(/* prefix= */ MediaStore.getVersion(mContext));
+
+        return Glide.with(mContext).asBitmap().apply(options).signature(signature).load(loadable);
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java b/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java
index 0d85196..b4ed01a 100644
--- a/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java
+++ b/src/com/android/providers/media/photopicker/data/glide/PickerThumbnailFetcher.java
@@ -20,43 +20,46 @@
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.graphics.Point;
-import android.net.Uri;
 import android.os.Bundle;
+import android.os.CancellationSignal;
 import android.provider.CloudMediaProviderContract;
+import android.provider.MediaStore;
 import android.util.Log;
 
-import com.bumptech.glide.Glide;
+import androidx.annotation.Nullable;
+
 import com.bumptech.glide.Priority;
 import com.bumptech.glide.load.DataSource;
-import com.bumptech.glide.load.ImageHeaderParserUtils;
 import com.bumptech.glide.load.data.DataFetcher;
-import com.bumptech.glide.load.data.ExifOrientationStream;
-
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
 /**
- * Custom {@link DataFetcher} to fetch a {@link InputStream} for a thumbnail from a cloud
- * media provider.
+ * Custom {@link DataFetcher} to fetch a {@link InputStream} for a thumbnail from a cloud media
+ * provider.
  */
 public class PickerThumbnailFetcher implements DataFetcher<InputStream> {
 
     private static final String TAG = "PickerThumbnailFetcher";
     private final Context mContext;
-    private final Uri mModel;
+    private final GlideLoadable mModel;
     private final int mWidth;
     private final int mHeight;
     private final boolean mIsThumbRequest;
+    private final CancellationSignal mCancellationSignal;
+    @Nullable private AssetFileDescriptor mAssetFileDescriptor = null;
+    @Nullable private InputStream mInputStream = null;
 
-    PickerThumbnailFetcher(Context context, Uri model, int width, int height,
-            boolean isThumbRequest) {
+    PickerThumbnailFetcher(
+            Context context, GlideLoadable model, int width, int height, boolean isThumbRequest) {
         mContext = context;
         mModel = model;
         mWidth = width;
         mHeight = height;
         mIsThumbRequest = isThumbRequest;
+        mCancellationSignal = new CancellationSignal();
     }
 
     @Override
@@ -70,57 +73,50 @@
             opts.putBoolean(CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB, true);
         }
 
-        try (AssetFileDescriptor afd = contentResolver.openTypedAssetFileDescriptor(mModel,
-                /* mimeType */ "image/*", opts, /* cancellationSignal */ null)) {
-            if (afd == null) {
+        try {
+            // Do not close the afd or InputStream as it will close the input stream. The
+            // afd needs to be closed when cleanup is called, so save a reference so it can
+            // be closed when Glide is done with it.
+            mAssetFileDescriptor =
+                    contentResolver.openTypedAssetFileDescriptor(
+                            mModel.getLoadableUri(),
+                            /* mimeType= */ "image/*",
+                            opts,
+                            /* cancellationSignal= */ mCancellationSignal);
+            if (mAssetFileDescriptor == null) {
                 final String err = "Failed to load data for " + mModel;
                 callback.onLoadFailed(new FileNotFoundException(err));
                 return;
             }
-
-            final InputStream inputStream;
-            if (mIsThumbRequest) {
-                inputStream = getOrientationInputStream(afd);
-            } else {
-                // We don't need to handle orientation for preview requests. Glide load takes care
-                // of loading the image in the right orientation.
-                inputStream = afd.createInputStream();
-            }
-            callback.onDataReady(inputStream);
+            mInputStream = mAssetFileDescriptor.createInputStream();
+            callback.onDataReady(mInputStream);
         } catch (IOException e) {
             callback.onLoadFailed(e);
         }
     }
 
-    private InputStream getOrientationInputStream(AssetFileDescriptor afd) throws IOException {
-        InputStream inputStream = afd.createInputStream();
-
-        int orientation = -1;
-        if (inputStream != null) {
-            try {
-                orientation = ImageHeaderParserUtils.getOrientation(
-                        Glide.get(mContext).getRegistry().getImageHeaderParsers(), inputStream,
-                        Glide.get(mContext).getArrayPool());
-            } catch (IOException | NullPointerException ignored) {
-                Log.d(TAG, "Unable to fetch orientation for " + mModel, ignored);
-            }
-        }
-
-        if (orientation != -1) {
-            inputStream = new ExifOrientationStream(inputStream, orientation);
-        }
-        return inputStream;
-    }
-
+    /**
+     * Cleanup is called after Glide is done with this Fetcher instance, and it is now safe to close
+     * the remembered AssetFileDescriptor.
+     */
     @Override
     public void cleanup() {
-        // Intentionally empty only because we're not opening an InputStream or another I/O
-        // resource.
+        try {
+            if (mInputStream != null) {
+                mInputStream.close();
+            }
+
+            if (mAssetFileDescriptor != null) {
+                mAssetFileDescriptor.close();
+            }
+        } catch (IOException e) {
+            Log.d(TAG, "Unexpected error during thumbnail request cleanup.", e);
+        }
     }
 
     @Override
     public void cancel() {
-        // Intentionally empty.
+        mCancellationSignal.cancel();
     }
 
     @Override
@@ -130,6 +126,13 @@
 
     @Override
     public DataSource getDataSource() {
-        return DataSource.LOCAL;
+        // If the authority belongs to MediaProvider, we can consider this a local load.
+        if (mModel.getLoadableUri().getAuthority().equals(MediaStore.AUTHORITY)) {
+            return DataSource.LOCAL;
+        } else {
+            // Otherwise, let's assume it's a Remote data source so that Glide will cache
+            // the raw return value rather than manipulated bytes.
+            return DataSource.REMOTE;
+        }
     }
 }
diff --git a/src/com/android/providers/media/photopicker/data/model/Category.java b/src/com/android/providers/media/photopicker/data/model/Category.java
index 487a39c..927a031 100644
--- a/src/com/android/providers/media/photopicker/data/model/Category.java
+++ b/src/com/android/providers/media/photopicker/data/model/Category.java
@@ -39,6 +39,7 @@
 
 import com.android.providers.media.R;
 import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.data.glide.GlideLoadable;
 
 import java.util.List;
 import java.util.Locale;
@@ -187,4 +188,13 @@
                 return albumId;
         }
     }
-}
\ No newline at end of file
+
+    /**
+     * Convert this category into a loadable object for Glide.
+     *
+     * @return {@link GlideLoadable} that represents the relevant loadable data for this item.
+     */
+    public GlideLoadable toGlideLoadable() {
+        return new GlideLoadable(getCoverUri());
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/data/model/Item.java b/src/com/android/providers/media/photopicker/data/model/Item.java
index a98f03c..83b0bdb 100644
--- a/src/com/android/providers/media/photopicker/data/model/Item.java
+++ b/src/com/android/providers/media/photopicker/data/model/Item.java
@@ -38,6 +38,7 @@
 
 import com.android.providers.media.R;
 import com.android.providers.media.photopicker.data.ItemsProvider;
+import com.android.providers.media.photopicker.data.glide.GlideLoadable;
 import com.android.providers.media.photopicker.util.DateTimeUtils;
 import com.android.providers.media.util.MimeUtils;
 
@@ -252,4 +253,14 @@
     @Override public int hashCode() {
         return Objects.hash(mUri);
     }
-}
\ No newline at end of file
+
+    /**
+     * Convert this item into a loadable object for Glide.
+     *
+     * @return {@link GlideLoadable} that represents the relevant loadable data for this item.
+     */
+    public GlideLoadable toGlideLoadable() {
+        return new GlideLoadable(mUri, String.valueOf(getGenerationModified()));
+    }
+
+}
diff --git a/src/com/android/providers/media/photopicker/sync/CloseableReentrantLock.java b/src/com/android/providers/media/photopicker/sync/CloseableReentrantLock.java
new file mode 100644
index 0000000..bcd54e4
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/sync/CloseableReentrantLock.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.sync;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * A Reentrant lock that implements AutoCloseable interface.
+ */
+public class CloseableReentrantLock extends ReentrantLock implements AutoCloseable {
+    private static final String TAG = CloseableReentrantLock.class.getSimpleName();
+    private final String mLockName;
+
+    public CloseableReentrantLock(@NonNull String lockName) {
+        super();
+        mLockName = lockName;
+    }
+
+    /**
+     * Try to acquire lock with a timeout after running some validations.
+     */
+    public CloseableReentrantLock lockWithTimeout(long timeout, TimeUnit unit)
+            throws UnableToAcquireLockException {
+        try {
+            final boolean success =
+                    this.tryLock(timeout, unit);
+            if (!success) {
+                throw new UnableToAcquireLockException(
+                        "Could not acquire the lock within timeout " + this);
+            }
+            Log.d(TAG, "Successfully acquired lock " + this);
+            return this;
+        } catch (InterruptedException e) {
+            throw new UnableToAcquireLockException(
+                    "Interrupted while waiting for lock " + this, e);
+        }
+    }
+
+    @Override
+    public void close() {
+        unlock();
+    }
+
+    /**
+     * Attempt to release the lock and swallow IllegalMonitorStateException, if thrown.
+     */
+    @Override
+    public void lock() {
+        super.lock();
+        Log.d(TAG, "Successfully acquired lock " + this);
+    }
+
+    /**
+     * Attempt to release the lock and swallow IllegalMonitorStateException, if thrown.
+     */
+    @Override
+    public void unlock() {
+        try {
+            super.unlock();
+            Log.d(TAG, "Successfully released lock " + this);
+        } catch (IllegalMonitorStateException e) {
+            Log.e(TAG, "Tried to release a lock that is not held by this thread - " + this);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + ". Lock Name = " + mLockName
+                + ". Threads that may be waiting to acquire this lock = " + getQueuedThreads();
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/sync/MediaResetWorker.java b/src/com/android/providers/media/photopicker/sync/MediaResetWorker.java
index 47c7455..1558b9f 100644
--- a/src/com/android/providers/media/photopicker/sync/MediaResetWorker.java
+++ b/src/com/android/providers/media/photopicker/sync/MediaResetWorker.java
@@ -43,6 +43,7 @@
 import com.android.providers.media.photopicker.PickerSyncController;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
 import com.android.providers.media.photopicker.sync.PickerSyncManager.SyncResetType;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
 
 /**
  * This is a {@link Worker} class responsible for handling table reset operations in the picker
@@ -80,9 +81,11 @@
 
         PickerSyncController controller;
         PickerDbFacade dBFacade;
+        PickerSyncLockManager pickerSyncLockManager;
         try {
             controller = PickerSyncController.getInstanceOrThrow();
-            dBFacade = new PickerDbFacade(mContext);
+            pickerSyncLockManager = controller.getPickerSyncLockManager();
+            dBFacade = new PickerDbFacade(mContext, pickerSyncLockManager);
         } catch (IllegalStateException ex) {
             Log.e(TAG, "Unable to obtain PickerSyncController", ex);
             return ListenableWorker.Result.failure();
@@ -91,39 +94,42 @@
             return ListenableWorker.Result.failure();
         }
 
-        if (getTags().contains(SYNC_WORKER_TAG_IS_PERIODIC)) {
-            // If this worker is being run as part of periodic work, it needs to register
-            // its own sync with the sync tracker.
-            trackNewAlbumMediaSyncRequests(mSyncSource, getId());
-
-            // Since this is a periodic worker, we'll use the cloud authority, if it exists.
-            // Using the cloud authority will reset files for all providers. If the local
-            // authority is used, it will limit the query to only files with a local_id, but
-            // the cloud authority does not have such a limitation.
-            // (This is not intuitive, it's just how it works.)
-            mAuthority = controller.getCloudProvider();
-            if (mAuthority == null) {
-                mAuthority = controller.getLocalProvider();
-            }
-            // If the authority is still null, end the operation.
-            if (mAuthority == null) {
-                Log.e(TAG, "Unable to set authority for periodic worker");
-                return ListenableWorker.Result.failure();
-            }
-        }
 
         try {
+            if (getTags().contains(SYNC_WORKER_TAG_IS_PERIODIC)) {
+                // If this worker is being run as part of periodic work, it needs to register
+                // its own sync with the sync tracker.
+                trackNewAlbumMediaSyncRequests(mSyncSource, getId());
+
+                // Since this is a periodic worker, we'll use the cloud authority, if it exists.
+                // Using the cloud authority will reset files for all providers. If the local
+                // authority is used, it will limit the query to only files with a local_id, but
+                // the cloud authority does not have such a limitation.
+                // (This is not intuitive, it's just how it works.)
+                mAuthority = controller.getCloudProviderWithTimeout();
+                if (mAuthority == null) {
+                    mAuthority = controller.getLocalProvider();
+                }
+                // If the authority is still null, end the operation.
+                if (mAuthority == null) {
+                    Log.e(TAG, "Unable to set authority for periodic worker");
+                    return ListenableWorker.Result.failure();
+                }
+            }
 
             if (mSyncSource == SYNC_LOCAL_ONLY) {
                 return start(dBFacade);
             } else {
                 // SyncSource is either CLOUD_ONLY or LOCAL_AND_CLOUD, either way we need the
                 // cloud lock.
-                final Object cloudAlbumSyncLock = controller.getCloudAlbumSyncLock();
-                synchronized (cloudAlbumSyncLock) {
+                try (CloseableReentrantLock ignored = pickerSyncLockManager
+                        .tryLock(PickerSyncLockManager.CLOUD_ALBUM_SYNC_LOCK)) {
                     return start(dBFacade);
                 }
             }
+        } catch (UnableToAcquireLockException e) {
+            Log.e(TAG, "Could not acquire lock", e);
+            return ListenableWorker.Result.failure();
         } finally {
             markAlbumMediaSyncAsComplete(mSyncSource, getId());
         }
diff --git a/src/com/android/providers/media/photopicker/sync/PickerSyncLockManager.java b/src/com/android/providers/media/photopicker/sync/PickerSyncLockManager.java
new file mode 100644
index 0000000..f01026f
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/sync/PickerSyncLockManager.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.sync;
+
+import android.annotation.IntDef;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Manages Java locks acquired during the sync process to ensure that the cloud sync is thread safe.
+ */
+public class PickerSyncLockManager {
+    private static final String TAG = PickerSyncLockManager.class.getSimpleName();
+    private static final Integer LOCK_ACQUIRE_TIMEOUT_MINS = 4;
+    private static final TimeUnit LOCK_ACQUIRE_TIMEOUT_UNIT = TimeUnit.MINUTES;
+
+    @IntDef(value = {CLOUD_SYNC_LOCK, CLOUD_ALBUM_SYNC_LOCK, CLOUD_PROVIDER_LOCK, DB_CLOUD_LOCK})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface LockType {}
+    public static final int CLOUD_SYNC_LOCK = 0;
+    public static final int CLOUD_ALBUM_SYNC_LOCK = 1;
+    public static final int CLOUD_PROVIDER_LOCK = 2;
+    public static final int DB_CLOUD_LOCK = 3;
+
+    private final CloseableReentrantLock mCloudSyncLock =
+            new CloseableReentrantLock("CLOUD_SYNC_LOCK");
+    private final CloseableReentrantLock mCloudAlbumSyncLock =
+            new CloseableReentrantLock("CLOUD_ALBUM_SYNC_LOCK");
+    private final CloseableReentrantLock mCloudProviderLock =
+            new CloseableReentrantLock("CLOUD_PROVIDER_LOCK");
+    private final CloseableReentrantLock mDbCloudLock =
+            new CloseableReentrantLock("DB_CLOUD_LOCK");
+
+    /**
+     * Try to acquire lock with a default timeout after running some validations.
+     */
+    public CloseableReentrantLock tryLock(@LockType int lockType)
+            throws UnableToAcquireLockException {
+        return tryLock(lockType, LOCK_ACQUIRE_TIMEOUT_MINS, LOCK_ACQUIRE_TIMEOUT_UNIT);
+    }
+
+    /**
+     * Try to acquire lock with the provided timeout after running some validations.
+     */
+    public CloseableReentrantLock tryLock(@LockType int lockType, long timeout, TimeUnit unit)
+            throws UnableToAcquireLockException {
+        return tryLock(getLock(lockType), timeout, unit);
+    }
+
+    /**
+     * Try to acquire the given lock with the provided timeout after running some validations.
+     */
+    @VisibleForTesting
+    public CloseableReentrantLock tryLock(@NonNull CloseableReentrantLock lock,
+            long timeout, TimeUnit unit) throws UnableToAcquireLockException {
+        Log.d(TAG, "Trying to acquire lock " + lock + " with timeout.");
+        validateLockOrder(lock);
+        return lock.lockWithTimeout(timeout, unit);
+    }
+
+    /**
+     * Try to acquire the lock after running some validations.
+     */
+    public CloseableReentrantLock lock(@LockType int lockType) {
+        final CloseableReentrantLock reentrantLock = getLock(lockType);
+        Log.d(TAG, "Trying to acquire lock " + reentrantLock);
+        validateLockOrder(reentrantLock);
+        reentrantLock.lock();
+        return reentrantLock;
+    }
+
+    /**
+     * Return the {@link CloseableReentrantLock} corresponding to the given {@link LockType}.
+     * Throws a {@link RuntimeException} if the lock is not recognized.
+     */
+    @VisibleForTesting
+    public CloseableReentrantLock getLock(@LockType int lockType) {
+        switch (lockType) {
+            case CLOUD_SYNC_LOCK:
+                return mCloudSyncLock;
+            case CLOUD_ALBUM_SYNC_LOCK:
+                return mCloudAlbumSyncLock;
+            case CLOUD_PROVIDER_LOCK:
+                return mCloudProviderLock;
+            case DB_CLOUD_LOCK:
+                return mDbCloudLock;
+            default:
+                throw new RuntimeException("Unrecognizable lock type " + lockType);
+        }
+    }
+
+    private void validateLockOrder(@NonNull ReentrantLock lockToBeAcquired) {
+        if (lockToBeAcquired.equals(mCloudSyncLock)) {
+            validateLockOrder(lockToBeAcquired, mCloudAlbumSyncLock);
+            validateLockOrder(lockToBeAcquired, mCloudProviderLock);
+            validateLockOrder(lockToBeAcquired, mDbCloudLock);
+        } else if (lockToBeAcquired.equals(mCloudAlbumSyncLock)) {
+            validateLockOrder(lockToBeAcquired, mCloudSyncLock);
+            validateLockOrder(lockToBeAcquired, mCloudProviderLock);
+            validateLockOrder(lockToBeAcquired, mDbCloudLock);
+        } else if (lockToBeAcquired.equals(mCloudProviderLock)) {
+            validateLockOrder(lockToBeAcquired, mDbCloudLock);
+        }
+    }
+
+    private void validateLockOrder(@NonNull ReentrantLock lockToBeAcquired,
+            @NonNull ReentrantLock lockThatShouldNotBeHeld) {
+        if (lockThatShouldNotBeHeld.isHeldByCurrentThread()) {
+            Log.e(TAG, String.format("Lock {%s} should not be held before acquiring lock {%s}"
+                            + " This could lead to a deadlock.",
+                    lockThatShouldNotBeHeld, lockToBeAcquired));
+        }
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/ui/ImageLoader.java b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
index 08f25d9..6c44f4d 100644
--- a/src/com/android/providers/media/photopicker/ui/ImageLoader.java
+++ b/src/com/android/providers/media/photopicker/ui/ImageLoader.java
@@ -32,6 +32,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.providers.media.photopicker.data.glide.GlideLoadable;
 import com.android.providers.media.photopicker.data.model.Category;
 import com.android.providers.media.photopicker.data.model.Item;
 
@@ -43,6 +44,8 @@
 import com.bumptech.glide.request.RequestOptions;
 import com.bumptech.glide.signature.ObjectKey;
 
+import java.util.Optional;
+
 /**
  * A class to assist with loading and managing the Images (i.e. thumbnails and preview) associated
  * with item.
@@ -81,7 +84,7 @@
             defaultIcon.setVisibility(View.GONE);
             imageView.setVisibility(View.VISIBLE);
 
-            loadWithGlide(getBitmapRequestBuilder(category.getCoverUri()), THUMBNAIL_OPTION,
+            loadWithGlide(getBitmapRequestBuilder(category.toGlideLoadable()), THUMBNAIL_OPTION,
                     /* signature */ null, imageView);
         } else {
             imageView.setVisibility(View.INVISIBLE);
@@ -99,11 +102,12 @@
      * @param imageView the imageView shows the thumbnail
      */
     public void loadPhotoThumbnail(@NonNull Item item, @NonNull ImageView imageView) {
+        final GlideLoadable loadable = item.toGlideLoadable();
         // Always show all thumbnails as bitmap images instead of drawables
         // This is to ensure that we do not animate any thumbnail (for eg GIF)
         // TODO(b/194285082): Use drawable instead of bitmap, as it saves memory.
-        loadWithGlide(getBitmapRequestBuilder(item.getContentUri()), THUMBNAIL_OPTION,
-                getGlideSignature(item, /* prefix */ ""), imageView);
+        loadWithGlide(getBitmapRequestBuilder(loadable), THUMBNAIL_OPTION,
+                getGlideSignature(loadable, /* prefix */ null), imageView);
     }
 
     /**
@@ -113,26 +117,34 @@
      * @param imageView the imageView shows the image
      */
     public void loadImagePreview(@NonNull Item item, @NonNull ImageView imageView)  {
+        final GlideLoadable loadable = item.toGlideLoadable();
         if (item.isGif()) {
-            loadWithGlide(getGifRequestBuilder(item.getContentUri()), /* requestOptions */ null,
-                    getGlideSignature(item, /* prefix */ ""), imageView);
+            loadWithGlide(
+                    getGifRequestBuilder(loadable),
+                    /* requestOptions */ null,
+                    getGlideSignature(loadable, /* prefix= */ null),
+                    imageView);
             return;
         }
 
         if (item.isAnimatedWebp()) {
-            loadAnimatedWebpPreview(item, imageView);
+            loadAnimatedWebpPreview(loadable, imageView);
             return;
         }
 
         // Preview as bitmap image for all other image types
-        loadWithGlide(getBitmapRequestBuilder(item.getContentUri()), /* requestOptions */ null,
-                getGlideSignature(item, /* prefix */ ""), imageView);
+        loadWithGlide(
+                getBitmapRequestBuilder(loadable),
+                /* requestOptions */ null,
+                getGlideSignature(loadable, /* prefix= */ null),
+                imageView);
     }
 
-    private void loadAnimatedWebpPreview(@NonNull Item item, @NonNull ImageView imageView) {
-        final Uri uri = item.getContentUri();
-        final ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(),
-                uri);
+    private void loadAnimatedWebpPreview(
+            @NonNull GlideLoadable loadable, @NonNull ImageView imageView) {
+        final Uri uri = loadable.getLoadableUri();
+        final ImageDecoder.Source source =
+                ImageDecoder.createSource(mContext.getContentResolver(), uri);
         Drawable drawable = null;
         try {
             drawable = ImageDecoder.decodeDrawable(source);
@@ -140,38 +152,47 @@
             Log.d(TAG, "Failed to decode drawable for uri: " + uri, e);
         }
 
-        // If we failed to decode drawable for a source using ImageDecoder, then try using uri
-        // directly. Glide will show static image for an animated webp. That is okay as we tried our
-        // best to load animated webp but couldn't, and we anyway show the GIF badge in preview.
-        loadWithGlide(getDrawableRequestBuilder(drawable == null ? uri : drawable),
-                /* requestOptions */ null, getGlideSignature(item, /* prefix */ ""), imageView);
+        // If we failed to decode drawable for a source using ImageDecoder, then try
+        // using uri directly. Glide will show static image for an animated webp. That
+        // is okay as we tried our best to load animated webp but couldn't, and we
+        // anyway show the GIF badge in preview.
+        loadWithGlide(
+                getDrawableRequestBuilder(drawable == null ? loadable : drawable),
+                /* requestOptions */ null,
+                getGlideSignature(loadable, null),
+                imageView);
     }
 
     /**
      * Loads the image from first frame of the given video item
      */
     public void loadImageFromVideoForPreview(@NonNull Item item, @NonNull ImageView imageView) {
-        loadWithGlide(getBitmapRequestBuilder(item.getContentUri()),
-                new RequestOptions().frame(1000), getGlideSignature(item, "Preview"), imageView);
+        final GlideLoadable loadable = item.toGlideLoadable();
+        loadWithGlide(
+                getBitmapRequestBuilder(loadable),
+                new RequestOptions().frame(1000),
+                getGlideSignature(loadable, "Preview"),
+                imageView);
     }
 
-    private ObjectKey getGlideSignature(Item item, String prefix) {
-        // TODO(b/224725723): Remove media store version from key once MP ids are stable.
-        return new ObjectKey(
-                MediaStore.getVersion(mContext) + prefix + item.getContentUri().toString() +
-                        item.getGenerationModified());
+    private ObjectKey getGlideSignature(GlideLoadable loadable, @Nullable String prefix) {
+        // TODO(b/224725723): Remove media store version from key once MP ids are
+        // stable.
+        return loadable.getLoadableSignature(
+                /* prefix= */ MediaStore.getVersion(mContext)
+                        + Optional.ofNullable(prefix).orElse(""));
     }
 
-    private RequestBuilder<Bitmap> getBitmapRequestBuilder(Uri uri) {
+    private RequestBuilder<Bitmap> getBitmapRequestBuilder(GlideLoadable loadable) {
         return Glide.with(mContext)
                 .asBitmap()
-                .load(uri);
+                .load(loadable);
     }
 
-    private RequestBuilder<GifDrawable> getGifRequestBuilder(Uri uri) {
+    private RequestBuilder<GifDrawable> getGifRequestBuilder(GlideLoadable loadable) {
         return Glide.with(mContext)
                 .asGif()
-                .load(uri);
+                .load(loadable);
     }
 
     private RequestBuilder<Drawable> getDrawableRequestBuilder(Object model) {
diff --git a/src/com/android/providers/media/photopicker/ui/MediaItemGridViewHolder.java b/src/com/android/providers/media/photopicker/ui/MediaItemGridViewHolder.java
index f8d3400..c6aa300 100644
--- a/src/com/android/providers/media/photopicker/ui/MediaItemGridViewHolder.java
+++ b/src/com/android/providers/media/photopicker/ui/MediaItemGridViewHolder.java
@@ -108,6 +108,14 @@
         return itemView.getContext();
     }
 
+    /**
+     * Get the {@link ImageView} for the thumbnail image representing this MediaItem.
+     * @return the image view for the thumbnail.
+     */
+    public ImageView getThumbnailImageView() {
+        return mIconThumb;
+    }
+
     private boolean showShowOverlayGradient(@NonNull Item item) {
         return mCanSelectMultiple
                 || item.isGifOrAnimatedWebp()
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java b/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
index e734a4a..38d2fc9 100644
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabAdapter.java
@@ -33,6 +33,8 @@
 import com.android.providers.media.photopicker.data.model.Item;
 import com.android.providers.media.photopicker.util.DateTimeUtils;
 
+import com.bumptech.glide.util.ViewPreloadSizeProvider;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -40,13 +42,14 @@
 /**
  * Adapts from model to something RecyclerView understands.
  */
-class PhotosTabAdapter extends TabAdapter {
+public class PhotosTabAdapter extends TabAdapter {
 
     private static final int RECENT_MINIMUM_COUNT = 12;
 
     private final boolean mShowRecentSection;
     private final OnMediaItemClickListener mOnMediaItemClickListener;
     private final Selection mSelection;
+    private final ViewPreloadSizeProvider mPreloadSizeProvider;
 
     private final View.OnHoverListener mOnMediaItemHoverListener;
 
@@ -65,7 +68,8 @@
             @NonNull OnBannerEventListener onCloudMediaAvailableBannerEventListener,
             @NonNull OnBannerEventListener onAccountUpdatedBannerEventListener,
             @NonNull OnBannerEventListener onChooseAccountBannerEventListener,
-            @NonNull View.OnHoverListener onMediaItemHoverListener) {
+            @NonNull View.OnHoverListener onMediaItemHoverListener,
+            @NonNull ViewPreloadSizeProvider preloadSizeProvider) {
         super(imageLoader, lifecycleOwner, cloudMediaProviderAppTitle, cloudMediaAccountName,
                 shouldShowChooseAppBanner, shouldShowCloudMediaAvailableBanner,
                 shouldShowAccountUpdatedBanner, shouldShowChooseAccountBanner,
@@ -75,6 +79,7 @@
         mSelection = selection;
         mOnMediaItemClickListener = onMediaItemClickListener;
         mOnMediaItemHoverListener = onMediaItemHoverListener;
+        mPreloadSizeProvider = preloadSizeProvider;
     }
 
     @NonNull
@@ -88,8 +93,15 @@
     @Override
     RecyclerView.ViewHolder createMediaItemViewHolder(@NonNull ViewGroup viewGroup) {
         final View view = getView(viewGroup, R.layout.item_photo_grid);
-        return new MediaItemGridViewHolder(view, mImageLoader, mOnMediaItemClickListener,
-                mOnMediaItemHoverListener, mSelection.canSelectMultiple());
+        final MediaItemGridViewHolder viewHolder =
+                new MediaItemGridViewHolder(
+                        view,
+                        mImageLoader,
+                        mOnMediaItemClickListener,
+                        mOnMediaItemHoverListener,
+                        mSelection.canSelectMultiple());
+        mPreloadSizeProvider.setView(viewHolder.getThumbnailImageView());
+        return viewHolder;
     }
 
     @Override
@@ -124,7 +136,7 @@
     }
 
     @Override
-    boolean isItemTypeMediaItem(int position) {
+    public boolean isItemTypeMediaItem(int position) {
         return getAdapterItem(position) instanceof Item;
     }
 
@@ -199,4 +211,4 @@
 
         boolean onItemLongClick(@NonNull View view, int position);
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
index 0822fcb..f329a44 100644
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
@@ -18,6 +18,7 @@
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_LOAD_NEXT_PAGE;
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_REFRESH_ITEMS;
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_VIEW_CREATED;
+import static com.android.providers.media.photopicker.ui.TabAdapter.ITEM_TYPE_MEDIA_ITEM;
 import static com.android.providers.media.photopicker.util.LayoutModeUtils.MODE_ALBUM_PHOTOS_TAB;
 import static com.android.providers.media.photopicker.util.LayoutModeUtils.MODE_PHOTOS_TAB;
 
@@ -46,6 +47,7 @@
 
 import com.android.providers.media.R;
 import com.android.providers.media.photopicker.data.PaginationParameters;
+import com.android.providers.media.photopicker.data.glide.PickerPreloadModelProvider;
 import com.android.providers.media.photopicker.data.model.Category;
 import com.android.providers.media.photopicker.data.model.Item;
 import com.android.providers.media.photopicker.util.LayoutModeUtils;
@@ -53,6 +55,10 @@
 import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
 import com.android.providers.media.util.StringUtils;
 
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestManager;
+import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader;
+import com.bumptech.glide.util.ViewPreloadSizeProvider;
 import com.google.android.material.snackbar.Snackbar;
 
 import org.jetbrains.annotations.NotNull;
@@ -80,6 +86,10 @@
     private boolean mIsCloudMediaInPhotoPickerEnabled;
 
     private int mPageSize;
+    private PickerPreloadModelProvider mPreloadModelProvider;
+
+    @Nullable
+    private RequestManager mGlideRequestManager = null;
 
     private ProgressBar mProgressBar;
     private TextView mLoadingTextView;
@@ -136,14 +146,39 @@
         }
         mSelection.clearCheckedItemList();
 
-        final PhotosTabAdapter adapter = new PhotosTabAdapter(showRecentSection, mSelection,
-                mImageLoader, mOnMediaItemClickListener, /* lifecycleOwner */ this,
-                mPickerViewModel.getCloudMediaProviderAppTitleLiveData(),
-                mPickerViewModel.getCloudMediaAccountNameLiveData(), showChooseAppBanner,
-                showCloudMediaAvailableBanner, showAccountUpdatedBanner, showChooseAccountBanner,
-                mOnChooseAppBannerEventListener, mOnCloudMediaAvailableBannerEventListener,
-                mOnAccountUpdatedBannerEventListener, mOnChooseAccountBannerEventListener,
-                mOnMediaItemHoverListener);
+        ViewPreloadSizeProvider viewSizeProvider = new ViewPreloadSizeProvider();
+
+        final PhotosTabAdapter adapter =
+                new PhotosTabAdapter(
+                        showRecentSection,
+                        mSelection,
+                        mImageLoader,
+                        mOnMediaItemClickListener, /* lifecycleOwner */
+                        this,
+                        mPickerViewModel.getCloudMediaProviderAppTitleLiveData(),
+                        mPickerViewModel.getCloudMediaAccountNameLiveData(),
+                        showChooseAppBanner,
+                        showCloudMediaAvailableBanner,
+                        showAccountUpdatedBanner,
+                        showChooseAccountBanner,
+                        mOnChooseAppBannerEventListener,
+                        mOnCloudMediaAvailableBannerEventListener,
+                        mOnAccountUpdatedBannerEventListener,
+                        mOnChooseAccountBannerEventListener,
+                        mOnMediaItemHoverListener,
+                        viewSizeProvider);
+
+        mPreloadModelProvider = new PickerPreloadModelProvider(getContext(), adapter);
+        mGlideRequestManager = Glide.with(this);
+
+        RecyclerViewPreloader<Item> preloader =
+                new RecyclerViewPreloader<>(
+                        Glide.with(getContext()),
+                        mPreloadModelProvider,
+                        viewSizeProvider,
+                        /* maxPreload= */ 8);
+        mRecyclerView.addOnScrollListener(preloader);
+
 
         // initialise pre-granted items is necessary.
         Intent activityIntent = requireActivity().getIntent();
@@ -195,6 +230,19 @@
         setLayoutManager(context, adapter, GRID_COLUMN_COUNT);
         mRecyclerView.setAdapter(adapter);
         mRecyclerView.addItemDecoration(itemDecoration);
+
+        // Listen for views as they are being recycled and attempt to cancel any pending glide load
+        // requests to prevent a large backlog of requests building up in the event of really
+        // large scrolls.
+        mRecyclerView.addRecyclerListener(
+                new RecyclerView.RecyclerListener() {
+                    @Override
+                    public void onViewRecycled(RecyclerView.ViewHolder holder) {
+                        cancelGlideLoadForViewHolder(holder);
+                    }
+                });
+        mRecyclerView.setItemViewCacheSize(10);
+
         if (mIsCloudMediaInPhotoPickerEnabled) {
             setOnScrollListenerForRecyclerView();
         }
@@ -543,9 +591,26 @@
         }
     }
 
+    /**
+     * Attempts to cancel any outstanding Glide requests for the given ViewHolder.
+     *
+     * @param holder The View holder in the RecyclerView to cancel requests for.
+     */
+    private void cancelGlideLoadForViewHolder(RecyclerView.ViewHolder holder) {
+
+        if (mGlideRequestManager != null && holder.getItemViewType() == ITEM_TYPE_MEDIA_ITEM) {
+            // This cast is safe as we've already checked the view type is
+            MediaItemGridViewHolder vh = (MediaItemGridViewHolder) holder;
+            // Attempt to clear the potential pending load out of glide's request
+            // manager.
+            mGlideRequestManager.clear(vh.getThumbnailImageView());
+        }
+    }
+
     @Override
     public void onDestroy() {
         super.onDestroy();
         mMainThreadHandler.removeCallbacksAndMessages(mHideProgressBarToken);
+        mGlideRequestManager = null;
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/providers/media/photopicker/ui/TabAdapter.java b/src/com/android/providers/media/photopicker/ui/TabAdapter.java
index 35db580..3295941 100644
--- a/src/com/android/providers/media/photopicker/ui/TabAdapter.java
+++ b/src/com/android/providers/media/photopicker/ui/TabAdapter.java
@@ -47,15 +47,14 @@
 /**
  * Adapts from model to something RecyclerView understands.
  */
-@VisibleForTesting
 public abstract class TabAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 
     @VisibleForTesting
     public static final int ITEM_TYPE_BANNER = 0;
     // Date header sections for "Photos" tab
-    static final int ITEM_TYPE_SECTION = 1;
+    public static final int ITEM_TYPE_SECTION = 1;
     // Media items (a.k.a. Items) for "Photos" tab, Albums (a.k.a. Categories) for "Albums" tab
-    private static final int ITEM_TYPE_MEDIA_ITEM = 2;
+    public static final int ITEM_TYPE_MEDIA_ITEM = 2;
 
     @NonNull final ImageLoader mImageLoader;
     @NonNull private final LiveData<String> mCloudMediaProviderAppTitle;
@@ -273,7 +272,7 @@
     }
 
     @NonNull
-    final Object getAdapterItem(int position) {
+    public final Object getAdapterItem(int position) {
         if (position < 0) {
             throw new IllegalStateException("Get adapter item for negative position " + position);
         }
diff --git a/src/com/android/providers/media/photopicker/ui/TabFragment.java b/src/com/android/providers/media/photopicker/ui/TabFragment.java
index 269edb7..9d0b107 100644
--- a/src/com/android/providers/media/photopicker/ui/TabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/TabFragment.java
@@ -83,6 +83,8 @@
     private boolean mIsAccessibilityEnabled;
 
     private Button mAddButton;
+
+    private Button mViewSelectedButton;
     private View mBottomBar;
     private Animation mSlideUpAnimation;
     private Animation mSlideDownAnimation;
@@ -157,6 +159,7 @@
         final boolean canSelectMultiple = mSelection.canSelectMultiple();
         if (canSelectMultiple) {
             mAddButton = activity.findViewById(R.id.button_add);
+            mViewSelectedButton = activity.findViewById(R.id.button_view_selected);
             mAddButton.setOnClickListener(v -> {
                 try {
                     requirePickerActivity().setResultAndFinishSelf();
@@ -164,10 +167,8 @@
                     Log.e(TAG, "Fragment is likely not attached to an activity. ", e);
                 }
             });
-
-            final Button viewSelectedButton = activity.findViewById(R.id.button_view_selected);
             // Transition to PreviewFragment on clicking "View Selected".
-            viewSelectedButton.setOnClickListener(v -> {
+            mViewSelectedButton.setOnClickListener(v -> {
                 // Load items for preview that are pre granted but not yet loaded for UI.
                 mPickerViewModel.getRemainingPreGrantedItems();
                 mSelection.prepareSelectedItemsForPreviewAll();
@@ -184,6 +185,8 @@
             });
 
             mBottomBar = activity.findViewById(R.id.picker_bottom_bar);
+            // consume the event so that it doesn't get passed through to the next view b/287661737
+            mBottomBar.setOnClickListener(v -> {});
             mSlideUpAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_up);
             mSlideDownAnimation = AnimationUtils.loadAnimation(context, R.anim.slide_down);
 
@@ -250,19 +253,38 @@
             return;
         }
 
-        if (selectedItemListSize == 0) {
-            if (mBottomBar.getVisibility() == View.VISIBLE) {
-                mBottomBar.setVisibility(View.GONE);
-                mBottomBar.startAnimation(mSlideDownAnimation);
+        if (mPickerViewModel.isManagedSelectionEnabled()) {
+            animateAndShowBottomBar(context, selectedItemListSize);
+            if (selectedItemListSize == 0) {
+                mViewSelectedButton.setVisibility(View.GONE);
+                // Update the add button to show "Allow none".
+                mAddButton.setText(R.string.picker_add_button_allow_none_option);
             }
         } else {
-            if (mBottomBar.getVisibility() == View.GONE) {
-                mBottomBar.setVisibility(View.VISIBLE);
-                mBottomBar.startAnimation(mSlideUpAnimation);
+            if (selectedItemListSize == 0) {
+                animateAndHideBottomBar();
+            } else {
+                animateAndShowBottomBar(context, selectedItemListSize);
             }
-            mAddButton.setText(generateAddButtonString(context, selectedItemListSize));
         }
-        mIsBottomBarVisible.setValue(selectedItemListSize > 0);
+        mIsBottomBarVisible.setValue(
+                mPickerViewModel.isManagedSelectionEnabled() || selectedItemListSize > 0);
+    }
+
+    private void animateAndShowBottomBar(Context context, int selectedItemListSize) {
+        if (mBottomBar.getVisibility() == View.GONE) {
+            mBottomBar.setVisibility(View.VISIBLE);
+            mBottomBar.startAnimation(mSlideUpAnimation);
+        }
+        mViewSelectedButton.setVisibility(View.VISIBLE);
+        mAddButton.setText(generateAddButtonString(context, selectedItemListSize));
+    }
+
+    private void animateAndHideBottomBar() {
+        if (mBottomBar.getVisibility() == View.VISIBLE) {
+            mBottomBar.setVisibility(View.GONE);
+            mBottomBar.startAnimation(mSlideDownAnimation);
+        }
     }
 
     private void setUpListenersForProfileButton() {
diff --git a/src/com/android/providers/media/photopicker/util/exceptions/UnableToAcquireLockException.java b/src/com/android/providers/media/photopicker/util/exceptions/UnableToAcquireLockException.java
new file mode 100644
index 0000000..fad0c01
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/exceptions/UnableToAcquireLockException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.util.exceptions;
+
+/**
+ * Exception thrown when the current thread tries to acquire a lock but fails. The failure could be
+ * because of a timeout or thread interruption.
+ */
+public class UnableToAcquireLockException extends Exception {
+    public UnableToAcquireLockException(String message) {
+        super(message);
+    }
+
+    public UnableToAcquireLockException(String message, Exception e) {
+        super(message, e);
+    }
+}
diff --git a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
index 1a004b2..5b65155 100644
--- a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
+++ b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
@@ -27,7 +27,6 @@
 import static com.android.providers.media.PickerUriResolver.REFRESH_UI_PICKER_INTERNAL_OBSERVABLE_URI;
 import static com.android.providers.media.photopicker.DataLoaderThread.TOKEN;
 import static com.android.providers.media.photopicker.PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY;
-import static com.android.providers.media.photopicker.data.MediaGrantsProvider.fetchReadGrantedItemsUrisForPackage;
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_CLEAR_AND_UPDATE_LIST;
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_CLEAR_GRID;
 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_DEFAULT;
@@ -165,6 +164,8 @@
 
     // Note - Must init banner manager on mIsUserSelectForApp / mIsLocalOnly updates
     private boolean mIsUserSelectForApp;
+
+    private boolean mIsManagedSelectionEnabled;
     private boolean mIsLocalOnly;
     private boolean mIsAllCategoryItemsLoaded = false;
     private boolean mIsNotificationForUpdateReceived = false;
@@ -180,6 +181,7 @@
         mInstanceId = new InstanceIdSequence(INSTANCE_ID_MAX).newInstanceId();
         mLogger = new PhotoPickerUiEventLogger();
         mIsUserSelectForApp = false;
+        mIsManagedSelectionEnabled = false;
         mIsLocalOnly = false;
 
         initConfigStore();
@@ -299,6 +301,15 @@
     }
 
     /**
+     * @return {@code mIsManagedSelectionEnabled} if the picker is currently being used
+     * for the {@link MediaStore#ACTION_USER_SELECT_IMAGES_FOR_APP} action and flag
+     * pickerChoiceManagedSelection is enabled..
+     */
+    public boolean isManagedSelectionEnabled() {
+        return mIsManagedSelectionEnabled;
+    }
+
+    /**
      * @return a {@link LiveData} that holds the value (once it's fetched) of the
      * {@link android.content.ContentProvider#mAuthority authority} of the current
      * {@link android.provider.CloudMediaProvider}.
@@ -394,10 +405,9 @@
      */
     public void initialisePreGrantsIfNecessary(Selection selection, Bundle intentExtras,
             String[] mimeTypeFilters) {
-        if (getConfigStore().isPickerChoiceManagedSelectionEnabled() && isUserSelectForApp()
-                && selection.getPreGrantedItems() == null) {
+        if (isManagedSelectionEnabled() && selection.getPreGrantedItems() == null) {
             DataLoaderThread.getHandler().postDelayed(() -> {
-                selection.setPreGrantedItemSet(fetchReadGrantedItemsUrisForPackage(mAppContext,
+                selection.setPreGrantedItemSet(mItemsProvider.fetchReadGrantedItemsUrisForPackage(
                         intentExtras.getInt(Intent.EXTRA_UID), mimeTypeFilters)
                         .stream().map((Uri uri) -> String.valueOf(ContentUris.parseId(uri)))
                         .collect(Collectors.toSet()));
@@ -526,8 +536,7 @@
 
             Set<String> preGrantedItems = new HashSet<>(0);
             Set<String> deSelectedPreGrantedItems = new HashSet<>(0);
-            if (isUserSelectForApp() && getConfigStore().isPickerChoiceManagedSelectionEnabled()
-                    && mSelection.getPreGrantedItems() != null) {
+            if (isManagedSelectionEnabled() && mSelection.getPreGrantedItems() != null) {
                 preGrantedItems = mSelection.getPreGrantedItems();
                 deSelectedPreGrantedItems = new HashSet<>(
                         mSelection.getPreGrantedItemIdsToBeRevoked());
@@ -573,9 +582,7 @@
      * issue by selectively loading those items and adding them to the selection list.</p>
      */
     public void getRemainingPreGrantedItems() {
-        if (isUserSelectForApp()
-                && getConfigStore().isPickerChoiceManagedSelectionEnabled()
-                && mSelection.getPreGrantedItems() != null) {
+        if (isManagedSelectionEnabled() && mSelection.getPreGrantedItems() != null) {
             List<String> idsForItemsToBeFetched = new ArrayList<>(mSelection.getPreGrantedItems());
             idsForItemsToBeFetched.removeAll(mSelection.getSelectedItemsIds());
             idsForItemsToBeFetched.removeAll(mSelection.getPreGrantedItemIdsToBeRevoked());
@@ -847,6 +854,8 @@
 
         mIsUserSelectForApp =
                 MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP.equals(intent.getAction());
+        mIsManagedSelectionEnabled = mIsUserSelectForApp
+                && getConfigStore().isPickerChoiceManagedSelectionEnabled();
         if (!SdkLevel.isAtLeastU() && mIsUserSelectForApp) {
             throw new IllegalArgumentException("ACTION_USER_SELECT_IMAGES_FOR_APP is not enabled "
                     + " for this OS version");
diff --git a/src/com/android/providers/media/util/SyntheticPathUtils.java b/src/com/android/providers/media/util/SyntheticPathUtils.java
index 6d73802..aa0db93 100644
--- a/src/com/android/providers/media/util/SyntheticPathUtils.java
+++ b/src/com/android/providers/media/util/SyntheticPathUtils.java
@@ -16,17 +16,14 @@
 
 package com.android.providers.media.util;
 
-import static com.android.providers.media.PickerUriResolver.PICKER_GET_CONTENT_SEGMENT;
-import static com.android.providers.media.PickerUriResolver.PICKER_SEGMENT;
 import static com.android.providers.media.util.FileUtils.buildPath;
 import static com.android.providers.media.util.FileUtils.buildPrimaryVolumeFile;
 import static com.android.providers.media.util.FileUtils.extractFileName;
 
+import androidx.annotation.VisibleForTesting;
 import android.text.TextUtils;
 import android.util.Log;
 
-import androidx.annotation.VisibleForTesting;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.RandomAccessFile;
@@ -40,6 +37,7 @@
     private static final String TRANSFORMS_DIR = ".transforms";
     private static final String SYNTHETIC_DIR = "synthetic";
     private static final String REDACTED_DIR = "redacted";
+    private static final String PICKER_DIR = "picker";
 
     public static final String REDACTED_URI_ID_PREFIX = "RUID";
     public static final int REDACTED_URI_ID_SIZE = 36;
@@ -50,12 +48,8 @@
         return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, REDACTED_DIR).getPath();
     }
 
-    /**
-     * Returns picker synthetic path directory.
-     */
-    public static String getPickerRelativePath(String pickerSegmentType) {
-        return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR,
-                pickerSegmentType).getPath();
+    public static String getPickerRelativePath() {
+        return buildPath(/* base */ null, TRANSFORMS_DIR, SYNTHETIC_DIR, PICKER_DIR).getPath();
     }
 
     public static boolean isRedactedPath(String path, int userId) {
@@ -72,13 +66,10 @@
     }
 
     public static boolean isPickerPath(String path, int userId) {
-        final String pickerDir = buildPrimaryVolumeFile(userId, getPickerRelativePath(
-                PICKER_SEGMENT)).getAbsolutePath();
-        final String pickerGetContentDir = buildPrimaryVolumeFile(userId,
-                getPickerRelativePath(PICKER_GET_CONTENT_SEGMENT)).getAbsolutePath();
+        final String pickerDir = buildPrimaryVolumeFile(userId, getPickerRelativePath())
+                .getAbsolutePath();
 
-        return path != null && (startsWith(path, pickerDir) || startsWith(path,
-                pickerGetContentDir));
+        return path != null && startsWith(path, pickerDir);
     }
 
     public static boolean isSyntheticPath(String path, int userId) {
diff --git a/tests/Android.bp b/tests/Android.bp
index 4dbaf16..e3b267c 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -190,6 +190,7 @@
         "glide-gifdecoder-prebuilt",
         "glide-disklrucache-prebuilt",
         "glide-annotation-and-compiler-prebuilt",
+        "glide-integration-recyclerview-prebuilt",
         "androidx.fragment_fragment",
         "androidx.vectordrawable_vectordrawable-animated",
         "androidx.exifinterface_exifinterface",
diff --git a/tests/src/com/android/providers/media/AccessCheckerTest.java b/tests/src/com/android/providers/media/AccessCheckerTest.java
index ef6c963..49a870e 100644
--- a/tests/src/com/android/providers/media/AccessCheckerTest.java
+++ b/tests/src/com/android/providers/media/AccessCheckerTest.java
@@ -29,7 +29,6 @@
 import static com.android.providers.media.AccessChecker.getWhereForUserSelectedAccess;
 import static com.android.providers.media.AccessChecker.hasAccessToCollection;
 import static com.android.providers.media.AccessChecker.hasUserSelectedAccess;
-import static com.android.providers.media.AccessChecker.isRedactionNeededForPickerUri;
 import static com.android.providers.media.LocalUriMatcher.AUDIO_MEDIA;
 import static com.android.providers.media.LocalUriMatcher.DOWNLOADS;
 import static com.android.providers.media.LocalUriMatcher.DOWNLOADS_ID;
@@ -46,9 +45,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
 
 import android.os.Bundle;
 import android.system.Os;
@@ -401,28 +398,6 @@
     }
 
     @Test
-    public void testIsRedactionNeededForPickerUri_returnsFalse_withNoRedactPerms() {
-        LocalCallingIdentity callingIdentityWithRedactionNotNeededPermission =
-                LocalCallingIdentity.forTest(
-                        InstrumentationRegistry.getTargetContext(), Os.getuid(),
-                        ~LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED);
-
-        assertFalse("App with write perms should get non redacted data",
-                isRedactionNeededForPickerUri(callingIdentityWithRedactionNotNeededPermission));
-    }
-
-    @Test
-    public void testIsRedactionNeededForPickerUri_returnsTrue_withRedactPerms() {
-        LocalCallingIdentity callingIdentityWithRedactionNeededPermission =
-                LocalCallingIdentity.forTest(
-                        InstrumentationRegistry.getTargetContext(), Os.getuid(),
-                        LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED);
-
-        assertTrue("App with no perms should get redacted data",
-                isRedactionNeededForPickerUri(callingIdentityWithRedactionNeededPermission));
-    }
-
-    @Test
     public void testGetWhereForConstrainedAccess_forWrite_hasLegacyWrite() {
         LocalCallingIdentity hasLegacyWrite = LocalCallingIdentity.forTest(
                 InstrumentationRegistry.getTargetContext(), Os.getuid(),
diff --git a/tests/src/com/android/providers/media/IdleServiceTest.java b/tests/src/com/android/providers/media/IdleServiceTest.java
index 9dd2eb6..bd0e7bd 100644
--- a/tests/src/com/android/providers/media/IdleServiceTest.java
+++ b/tests/src/com/android/providers/media/IdleServiceTest.java
@@ -32,8 +32,11 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import android.Manifest;
+import android.app.Instrumentation;
 import android.app.job.JobScheduler;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
@@ -45,14 +48,21 @@
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Environment;
+import android.os.NewUserRequest;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.MediaStore;
 import android.text.format.DateUtils;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SdkSuppress;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.providers.media.library.RunOnlyOnPostsubmit;
 import com.android.providers.media.scan.MediaScannerTest;
 import com.android.providers.media.util.FileUtils;
 
@@ -68,7 +78,10 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 public class IdleServiceTest {
@@ -85,6 +98,8 @@
                         android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
                         android.Manifest.permission.READ_DEVICE_CONFIG,
                         Manifest.permission.INTERACT_ACROSS_USERS,
+                        Manifest.permission.MANAGE_USERS,
+                        Manifest.permission.WRITE_MEDIA_STORAGE,
                         android.Manifest.permission.DUMP);
 
         mDir = new File(context.getExternalMediaDirs()[0], "test_" + System.nanoTime());
@@ -294,6 +309,119 @@
         }
     }
 
+    /**
+     * Idle maintenance run on non-demo devices should not remove xattr data stored for different
+     * users on /data/media/0. This is not done due to b/305658663.
+     */
+    @Test
+    @RunOnlyOnPostsubmit
+    @LargeTest
+    @SdkSuppress(minSdkVersion = 33, codeName = "T")
+    public void test_idle_maintenance_nonDemoDevice() throws IOException {
+        assumeTrue(UserManager.supportsMultipleUsers());
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(
+                        Manifest.permission.INTERACT_ACROSS_USERS,
+                        Manifest.permission.CREATE_USERS,
+                        Manifest.permission.MANAGE_USERS,
+                        Manifest.permission.WRITE_MEDIA_STORAGE,
+                        android.Manifest.permission.DUMP);
+        SystemClock.sleep(3000);
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Context context = instrumentation.getContext();
+        UserManager userManager = context.getSystemService(UserManager.class);
+        Integer secondaryUser = -1;
+        boolean secondaryUserPresent = false;
+
+        try {
+            secondaryUser = createNewUser(userManager);
+            secondaryUserPresent = true;
+            startUser(secondaryUser);
+            SystemClock.sleep(3000);
+
+            // Verify presence of recovery data
+            String[] recoveryData = MediaStore.getRecoveryData(context.getContentResolver());
+            assertThat(getUserIdsForUsersFromRecoveryData(recoveryData)).containsAtLeastElementsIn(
+                    Arrays.asList(UserHandle.SYSTEM.getIdentifier(), secondaryUser));
+
+            executeShellCommand(
+                    "content call --uri content://media/external/file --method "
+                            + "run_idle_maintenance --user "
+                            + UserHandle.SYSTEM.getIdentifier());
+            executeShellCommand(
+                    "content call --uri content://media/external/file --method "
+                            + "run_idle_maintenance --user " + secondaryUser);
+
+            // Verify presence of recovery data even after running idle maintenance
+            recoveryData = MediaStore.getRecoveryData(context.getContentResolver());
+            assertThat(getUserIdsForUsersFromRecoveryData(recoveryData)).containsAtLeastElementsIn(
+                    Arrays.asList(UserHandle.SYSTEM.getIdentifier(), secondaryUser));
+
+            // Remove secondary user
+            removeUser(secondaryUser);
+            secondaryUserPresent = false;
+            SystemClock.sleep(3000);
+
+            // Run idle maintenance for user 0
+            executeShellCommand(
+                    "content call --uri content://media/external/file --method "
+                            + "run_idle_maintenance --user "
+                            + UserHandle.SYSTEM.getIdentifier());
+
+            // Verify presence of recovery data
+            recoveryData = MediaStore.getRecoveryData(context.getContentResolver());
+            assertThat(getUserIdsForUsersFromRecoveryData(recoveryData)).containsAtLeastElementsIn(
+                    Arrays.asList(UserHandle.SYSTEM.getIdentifier(), secondaryUser));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (secondaryUserPresent) {
+                removeUser(secondaryUser);
+            }
+            MediaStore.removeRecoveryData(context.getContentResolver());
+        }
+    }
+
+    private Set<Integer> getUserIdsForUsersFromRecoveryData(String[] recoveryData) {
+        Set<Integer> userIdSet = new HashSet<>();
+        for (String data : recoveryData) {
+            if (data.startsWith("user.extdbnextrowid")) {
+                userIdSet.add(Integer.valueOf(data.substring("user.extdbnextrowid".length())));
+            } else if (data.startsWith("user.extdbsessionid")) {
+                userIdSet.add(Integer.valueOf(data.substring("user.extdbsessionid".length())));
+            }
+        }
+
+        return userIdSet;
+    }
+
+
+    private int createNewUser(UserManager userManager) {
+        final NewUserRequest newUserRequest = new NewUserRequest.Builder().setName(
+                "test_user" + System.currentTimeMillis()).setUserType(
+                UserManager.USER_TYPE_FULL_SECONDARY).build();
+        final UserHandle newUser = userManager.createUser(newUserRequest).getUser();
+        if (newUser == null) {
+            fail("Error while creating a new user");
+        }
+        return newUser.getIdentifier();
+    }
+
+    private void startUser(int userId) throws IOException {
+        Log.i(TAG, "Starting user " + userId);
+        String output = executeShellCommand("am start-user -w " + userId);
+        if (output.startsWith("Error")) {
+            fail(String.format("Failed to start user %d: %s", userId, output));
+        }
+    }
+
+    private void removeUser(int userId) throws IOException {
+        final String output = executeShellCommand("cmd package remove-user " + userId);
+        if (output.startsWith("Error")) {
+            fail("Error removing the user #" + userId + ": " + output);
+        }
+    }
+
     private void assertExpiredItemIsExtended(ContentResolver resolver, Uri uri,
             long lastExpiredDate) {
         final String[] projection = new String[]{DATE_EXPIRES};
diff --git a/tests/src/com/android/providers/media/LocalUriMatcherTest.java b/tests/src/com/android/providers/media/LocalUriMatcherTest.java
index 01ce3ec..32721ed 100644
--- a/tests/src/com/android/providers/media/LocalUriMatcherTest.java
+++ b/tests/src/com/android/providers/media/LocalUriMatcherTest.java
@@ -43,15 +43,6 @@
                 LocalUriMatcher.PICKER_ID,
                 assembleTestUri(new String[] {"picker", "0", "anything", "media", "anything"}));
 
-        assertMatchesPublic(
-                LocalUriMatcher.PICKER_GET_CONTENT_ID,
-                assembleTestUri(new String[]{"picker_get_content", Integer.toString(1),
-                        Integer.toString(1)}));
-        assertMatchesPublic(
-                LocalUriMatcher.PICKER_GET_CONTENT_ID,
-                assembleTestUri(
-                        new String[]{"picker_get_content", "0", "anything", "media", "anything"}));
-
         assertMatchesPublic(LocalUriMatcher.CLI, assembleTestUri(new String[] {"cli"}));
 
         assertMatchesPublic(
@@ -213,15 +204,6 @@
                 LocalUriMatcher.PICKER_ID,
                 assembleTestUri(new String[] {"picker", "0", "anything", "media", "anything"}));
 
-        assertMatchesHidden(
-                LocalUriMatcher.PICKER_GET_CONTENT_ID,
-                assembleTestUri(new String[]{"picker_get_content", Integer.toString(1),
-                        Integer.toString(1)}));
-        assertMatchesHidden(
-                LocalUriMatcher.PICKER_GET_CONTENT_ID,
-                assembleTestUri(
-                        new String[]{"picker_get_content", "0", "anything", "media", "anything"}));
-
         assertMatchesHidden(LocalUriMatcher.CLI, assembleTestUri(new String[] {"cli"}));
 
         assertMatchesHidden(
diff --git a/tests/src/com/android/providers/media/MediaProviderTest.java b/tests/src/com/android/providers/media/MediaProviderTest.java
index e8024ee..fe13649 100644
--- a/tests/src/com/android/providers/media/MediaProviderTest.java
+++ b/tests/src/com/android/providers/media/MediaProviderTest.java
@@ -16,7 +16,6 @@
 
 package com.android.providers.media;
 
-import static com.android.providers.media.photopicker.data.MediaGrantsProvider.fetchReadGrantedItemsUrisForPackage;
 import static com.android.providers.media.scan.MediaScannerTest.stage;
 import static com.android.providers.media.util.FileUtils.extractDisplayName;
 import static com.android.providers.media.util.FileUtils.extractRelativePath;
@@ -74,6 +73,7 @@
 import com.android.providers.media.MediaProvider.VolumeArgumentException;
 import com.android.providers.media.MediaProvider.VolumeNotFoundException;
 import com.android.providers.media.photopicker.PickerSyncController;
+import com.android.providers.media.photopicker.data.ItemsProvider;
 import com.android.providers.media.util.FileUtils;
 import com.android.providers.media.util.FileUtilsTest;
 import com.android.providers.media.util.SQLiteQueryBuilder;
@@ -107,6 +107,8 @@
     static final String PERMISSIONLESS_APP = "com.android.providers.media.testapp.withoutperms";
 
     private static Context sIsolatedContext;
+
+    private static ItemsProvider sItemsProvider;
     private static Context sContext;
     private static ContentResolver sIsolatedResolver;
 
@@ -116,6 +118,11 @@
                 .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
                         Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
                         Manifest.permission.READ_DEVICE_CONFIG,
+                        // Adding this to use getUserHandles() api of UserManagerService which
+                        // requires either MANAGE_USERS or CREATE_USERS. Since shell does not have
+                        // MANAGER_USERS permissions, using CREATE_USERS in test. This works with
+                        // MANAGE_USERS permission for MediaProvider module.
+                        Manifest.permission.CREATE_USERS,
                         Manifest.permission.INTERACT_ACROSS_USERS);
 
         resetIsolatedContext();
@@ -362,8 +369,8 @@
         try {
             String[] mimeTypes = {"image/*"};
             // Verify empty list with no grants.
-            List<Uri> grantedUris = fetchReadGrantedItemsUrisForPackage(
-                    sIsolatedContext, android.os.Process.myUid(), mimeTypes);
+            List<Uri> grantedUris = sItemsProvider.fetchReadGrantedItemsUrisForPackage(
+                    android.os.Process.myUid(), mimeTypes);
             assertTrue(grantedUris.isEmpty());
 
             // Grants the READ-GRANT for the testUris for the current package.
@@ -372,8 +379,8 @@
                     List.of(testUri));
 
             // Assert that the grant was returned.
-            List<Uri> grantedUris2 = fetchReadGrantedItemsUrisForPackage(
-                    sIsolatedContext, android.os.Process.myUid(), mimeTypes);
+            List<Uri> grantedUris2 = sItemsProvider.fetchReadGrantedItemsUrisForPackage(
+                    android.os.Process.myUid(), mimeTypes);
             assertEquals(ContentUris.parseId(uri), ContentUris.parseId(grantedUris2.get(0)));
         } finally {
             dir.delete();
@@ -406,16 +413,16 @@
             MediaStore.grantMediaReadForPackage(sIsolatedContext,
                     android.os.Process.myUid(),
                     List.of(testUri));
-            List<Uri> grantedUris = fetchReadGrantedItemsUrisForPackage(
-                    sIsolatedContext, android.os.Process.myUid(), mimeTypes);
+            List<Uri> grantedUris = sItemsProvider.fetchReadGrantedItemsUrisForPackage(
+                    android.os.Process.myUid(), mimeTypes);
             assertEquals(ContentUris.parseId(uri), ContentUris.parseId(grantedUris.get(0)));
 
             // Revoked the grant that was provided to testUri and verify that now the current
             // package has no grants.
             MediaStore.revokeMediaReadForPackages(sIsolatedContext, android.os.Process.myUid(),
                     grantedUris);
-            List<Uri> grantedUris2 = fetchReadGrantedItemsUrisForPackage(
-                    sIsolatedContext, android.os.Process.myUid(), mimeTypes);
+            List<Uri> grantedUris2 = sItemsProvider.fetchReadGrantedItemsUrisForPackage(
+                    android.os.Process.myUid(), mimeTypes);
             assertEquals(0, grantedUris2.size());
         } finally {
             dir.delete();
@@ -1831,5 +1838,6 @@
         sContext = InstrumentationRegistry.getTargetContext();
         sIsolatedContext = new IsolatedContext(sContext, "modern", /*asFuseThread*/ false);
         sIsolatedResolver = sIsolatedContext.getContentResolver();
+        sItemsProvider = new ItemsProvider(sIsolatedContext);
     }
 }
diff --git a/tests/src/com/android/providers/media/PickerUriResolverTest.java b/tests/src/com/android/providers/media/PickerUriResolverTest.java
index b3d9f70..3d8c260 100644
--- a/tests/src/com/android/providers/media/PickerUriResolverTest.java
+++ b/tests/src/com/android/providers/media/PickerUriResolverTest.java
@@ -16,10 +16,8 @@
 
 package com.android.providers.media;
 
-import static android.content.Intent.ACTION_GET_CONTENT;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.provider.MediaStore.ACTION_PICK_IMAGES;
 
 import static androidx.test.InstrumentationRegistry.getContext;
 import static androidx.test.InstrumentationRegistry.getTargetContext;
@@ -28,7 +26,6 @@
 
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -56,6 +53,7 @@
 import com.android.modules.utils.build.SdkLevel;
 import com.android.providers.media.photopicker.PickerSyncController;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -80,13 +78,10 @@
     private static Uri sTestPickerUri;
     private static String TEST_ID;
 
-    private static Uri sMediaStoreUriInOtherContext;
-
     private static class TestPickerUriResolver extends PickerUriResolver {
         TestPickerUriResolver(Context context) {
-            super(context, new PickerDbFacade(getTargetContext()),
-                    new ProjectionHelper(Column.class, ExportedSince.class),
-                    new LocalUriMatcher(MediaStore.AUTHORITY));
+            super(context, new PickerDbFacade(getTargetContext(), new PickerSyncLockManager()),
+                    new ProjectionHelper(Column.class, ExportedSince.class));
         }
 
         @Override
@@ -124,16 +119,14 @@
                         Manifest.permission.INTERACT_ACROSS_USERS);
         sCurrentContext = mock(Context.class);
         when(sCurrentContext.getUser()).thenReturn(UserHandle.of(UserHandle.myUserId()));
-        PackageManager packageManager = mock(PackageManager.class);
-        when(sCurrentContext.getPackageManager()).thenReturn(packageManager);
-        when(packageManager.getPackagesForUid(anyInt())).thenReturn(
-                new String[]{getContext().getPackageName()});
 
         final Context otherUserContext = createOtherUserContext(TEST_USER);
         sTestPickerUriResolver = new TestPickerUriResolver(sCurrentContext);
 
-        sMediaStoreUriInOtherContext = createTestFileInContext(otherUserContext);
-        TEST_ID = sMediaStoreUriInOtherContext.getLastPathSegment();
+        final Uri mediaStoreUriInOtherContext = createTestFileInContext(otherUserContext);
+        TEST_ID = mediaStoreUriInOtherContext.getLastPathSegment();
+        sTestPickerUri = getPickerUriForId(ContentUris.parseId(mediaStoreUriInOtherContext),
+                TEST_USER);
     }
 
     @AfterClass
@@ -143,37 +136,24 @@
 
     @Test
     public void wrapProviderUriValid() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String providerSuffix = "authority/media/media_id";
 
         final Uri providerUriUserImplicit = Uri.parse("content://" + providerSuffix);
 
         final Uri providerUriUser0 = Uri.parse("content://0@" + providerSuffix);
         final Uri mediaUriUser0 = Uri.parse("content://media/picker/0/" + providerSuffix);
-        final Uri mediaUriUser0PickerGetContent = Uri.parse(
-                "content://media/picker_get_content/0/" + providerSuffix);
 
         final Uri providerUriUser10 = Uri.parse("content://10@" + providerSuffix);
         final Uri mediaUriUser10 = Uri.parse("content://media/picker/10/" + providerSuffix);
 
-        assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit,
-                ACTION_PICK_IMAGES, 0))
+        assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit, 0))
                 .isEqualTo(mediaUriUser0);
-        assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit,
-                ACTION_GET_CONTENT, 0))
-                .isEqualTo(mediaUriUser0PickerGetContent);
-        assertThat(
-                PickerUriResolver.wrapProviderUri(providerUriUser0, ACTION_PICK_IMAGES,
-                        0)).isEqualTo(mediaUriUser0);
+        assertThat(PickerUriResolver.wrapProviderUri(providerUriUser0, 0)).isEqualTo(mediaUriUser0);
         assertThat(PickerUriResolver.unwrapProviderUri(mediaUriUser0)).isEqualTo(providerUriUser0);
 
-        assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit,
-                ACTION_PICK_IMAGES, 10))
+        assertThat(PickerUriResolver.wrapProviderUri(providerUriUserImplicit, 10))
                 .isEqualTo(mediaUriUser10);
-        assertThat(
-                PickerUriResolver.wrapProviderUri(providerUriUser10, ACTION_PICK_IMAGES,
-                        10))
+        assertThat(PickerUriResolver.wrapProviderUri(providerUriUser10, 10))
                 .isEqualTo(mediaUriUser10);
         assertThat(PickerUriResolver.unwrapProviderUri(mediaUriUser10))
                 .isEqualTo(providerUriUser10);
@@ -181,8 +161,6 @@
 
     @Test
     public void wrapProviderUriInvalid() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String providerSuffixLong = "authority/media/media_id/another_media_id";
         final String providerSuffixShort = "authority/media";
 
@@ -193,22 +171,18 @@
         final Uri mediaUriUserShort = Uri.parse("content://media/picker/0/" + providerSuffixShort);
 
         assertThrows(IllegalArgumentException.class,
-                () -> PickerUriResolver.wrapProviderUri(providerUriUserLong, ACTION_PICK_IMAGES,
-                        0));
+                () -> PickerUriResolver.wrapProviderUri(providerUriUserLong, 0));
         assertThrows(IllegalArgumentException.class,
                 () -> PickerUriResolver.unwrapProviderUri(mediaUriUserLong));
 
         assertThrows(IllegalArgumentException.class,
                 () -> PickerUriResolver.unwrapProviderUri(mediaUriUserShort));
         assertThrows(IllegalArgumentException.class,
-                () -> PickerUriResolver.wrapProviderUri(providerUriUserShort, ACTION_PICK_IMAGES,
-                        0));
+                () -> PickerUriResolver.wrapProviderUri(providerUriUserShort, 0));
     }
 
     @Test
     public void testGetAlbumUri() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String authority = "foo";
         final Uri uri = Uri.parse("content://foo/album");
         assertThat(PickerUriResolver.getAlbumUri(authority)).isEqualTo(uri);
@@ -216,8 +190,6 @@
 
     @Test
     public void testGetMediaUri() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String authority = "foo";
         final Uri uri = Uri.parse("content://foo/media");
         assertThat(PickerUriResolver.getMediaUri(authority)).isEqualTo(uri);
@@ -225,8 +197,6 @@
 
     @Test
     public void testGetDeletedMediaUri() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String authority = "foo";
         final Uri uri = Uri.parse("content://foo/deleted_media");
         assertThat(PickerUriResolver.getDeletedMediaUri(authority)).isEqualTo(uri);
@@ -234,8 +204,6 @@
 
     @Test
     public void testCreateSurfaceControllerUri() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final String authority = "foo";
         final Uri uri = Uri.parse("content://foo/surface_controller");
         assertThat(PickerUriResolver.createSurfaceControllerUri(authority)).isEqualTo(uri);
@@ -243,13 +211,10 @@
 
     @Test
     public void testOpenFile_mode_w() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         updateReadUriPermission(sTestPickerUri, /* grant */ true);
         try {
             sTestPickerUriResolver.openFile(sTestPickerUri, "w", /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+                    /* callingPid */ -1, /* callingUid */ -1);
             fail("Write is not supported for Picker Uris. uri: " + sTestPickerUri);
         } catch (SecurityException expected) {
             // expected
@@ -260,13 +225,10 @@
 
     @Test
     public void testOpenFile_mode_rw() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         updateReadUriPermission(sTestPickerUri, /* grant */ true);
         try {
             sTestPickerUriResolver.openFile(sTestPickerUri, "rw", /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+                    /* callingPid */ -1, /* callingUid */ -1);
             fail("Read-Write is not supported for Picker Uris. uri: " + sTestPickerUri);
         } catch (SecurityException expected) {
             // expected
@@ -277,13 +239,10 @@
 
     @Test
     public void testOpenFile_mode_invalid() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         updateReadUriPermission(sTestPickerUri, /* grant */ true);
         try {
             sTestPickerUriResolver.openFile(sTestPickerUri, "foo", /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+                    /* callingPid */ -1, /* callingUid */ -1);
             fail("Invalid mode should not be supported for openFile. uri: " + sTestPickerUri);
         } catch (IllegalArgumentException expected) {
             // expected
@@ -293,8 +252,6 @@
 
     @Test
     public void testPickerUriResolver_permissionDenied() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         updateReadUriPermission(sTestPickerUri, /* grant */ false);
 
         testOpenFile_permissionDenied(sTestPickerUri);
@@ -305,15 +262,13 @@
 
     @Test
     public void testPermissionGrantedOnOtherUserUri() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         // This test requires the uri to be valid in 2 different users, but the permission is
         // granted in one user only.
         final int otherUserId = 50;
         final Context otherUserContext = createOtherUserContext(otherUserId);
         final Uri mediaStoreUserInAnotherValidUser = createTestFileInContext(otherUserContext);
         final Uri grantedUri = getPickerUriForId(ContentUris.parseId(
-                mediaStoreUserInAnotherValidUser), otherUserId, ACTION_PICK_IMAGES);
+                mediaStoreUserInAnotherValidUser), otherUserId);
         updateReadUriPermission(grantedUri, /* grant */ true);
 
         final Uri deniedUri = sTestPickerUri;
@@ -327,12 +282,9 @@
 
     @Test
     public void testPickerUriResolver_userInvalid() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final int invalidUserId = 40;
 
-        final Uri inValidUserPickerUri = getPickerUriForId(/* id */ 1, invalidUserId,
-                ACTION_PICK_IMAGES);
+        final Uri inValidUserPickerUri = getPickerUriForId(/* id */ 1, invalidUserId);
         updateReadUriPermission(inValidUserPickerUri, /* grant */ true);
 
         // This method is called on current context when pickerUriResolver wants to get the content
@@ -350,8 +302,6 @@
 
     @Test
     public void testPickerUriResolver_userValid() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         updateReadUriPermission(sTestPickerUri, /* grant */ true);
 
         assertThat(PickerUriResolver.getUserId(sTestPickerUri)).isEqualTo(TEST_USER);
@@ -362,102 +312,7 @@
     }
 
     @Test
-    public void testPickerUriResolver_pickerUri_fileOpenWithRequireOriginal() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
-        // Grants given on original uri
-        updateReadUriPermission(sTestPickerUri, /* grant */ true);
-        sTestPickerUri = MediaStore.setRequireOriginal(sTestPickerUri);
-
-        assertThat(PickerUriResolver.getUserId(sTestPickerUri)).isEqualTo(TEST_USER);
-        try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(sTestPickerUri,
-                "r", /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
-            fail("Require original should not be supported for picker uri:" + sTestPickerUri);
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-        try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(sTestPickerUri,
-                "r", /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
-            fail("Require original should not be supported for picker uri:" + sTestPickerUri);
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-
-        try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(sTestPickerUri,
-                "image/*", /* opts */ null, /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
-            fail("Require original should not be supported for picker uri:" + sTestPickerUri);
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-        try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(sTestPickerUri,
-                "image/*", /* opts */ null, /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
-            fail("Require original should not be supported for picker uri:" + sTestPickerUri);
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-
-        testQuery(sTestPickerUri);
-        testGetType(sTestPickerUri, "image/jpeg");
-    }
-
-    @Test
-    public void testPickerUriResolver_pickerGetContentUri_fileOpenWithRequireOriginal()
-            throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_GET_CONTENT);
-        // Grants given on original uri
-        updateReadUriPermission(sTestPickerUri, /* grant */ true);
-        sTestPickerUri = MediaStore.setRequireOriginal(sTestPickerUri);
-
-        assertThat(PickerUriResolver.getUserId(sTestPickerUri)).isEqualTo(TEST_USER);
-        try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(sTestPickerUri,
-                "r", /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        ~LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED))) {
-            assertThat(pfd).isNotNull();
-        }
-        try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(sTestPickerUri,
-                "r", /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED))) {
-            fail("Require original should not be supported when calling package does not have "
-                    + "required permission");
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-
-        try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(sTestPickerUri,
-                "image/*", /* opts */ null, /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        ~LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED))) {
-            assertThat(afd).isNotNull();
-        }
-        try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(sTestPickerUri,
-                "image/*", /* opts */ null, /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        LocalCallingIdentity.PERMISSION_IS_REDACTION_NEEDED))) {
-            fail("Require original should not be supported when calling package does not have "
-                    + "required permission");
-        } catch (UnsupportedOperationException expected) {
-            // expected
-        }
-
-        testQuery(sTestPickerUri);
-        testGetType(sTestPickerUri, "image/jpeg");
-    }
-
-    @Test
     public void testQueryUnknownColumn() throws Exception {
-        sTestPickerUri = getPickerUriForId(ContentUris.parseId(sMediaStoreUriInOtherContext),
-                TEST_USER, ACTION_PICK_IMAGES);
         final int myUid = Process.myUid();
         final int myPid = Process.myPid();
         final String myPackageName = getContext().getPackageName();
@@ -512,28 +367,25 @@
                 Intent.FLAG_GRANT_READ_URI_PERMISSION)).thenReturn(permission);
     }
 
-    private static Uri getPickerUriForId(long id, int user, String action) {
+    private static Uri getPickerUriForId(long id, int user) {
         final Uri providerUri = PickerUriResolver
                 .getMediaUri(PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY)
                 .buildUpon()
                 .appendPath(String.valueOf(id))
                 .build();
-        return PickerUriResolver.wrapProviderUri(providerUri, action, user);
+        return PickerUriResolver.wrapProviderUri(providerUri, user);
     }
 
     private void testOpenFile(Uri uri) throws Exception {
         try (ParcelFileDescriptor pfd = sTestPickerUriResolver.openFile(uri, "r", /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
+                /* callingPid */ -1, /* callingUid */ -1)) {
             assertThat(pfd).isNotNull();
         }
     }
 
     private void testOpenTypedAssetFile(Uri uri) throws Exception {
         try (AssetFileDescriptor afd = sTestPickerUriResolver.openTypedAssetFile(uri, "image/*",
-                /* opts */ null, /* signal */ null,
-                LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                        0))) {
+                /* opts */ null, /* signal */ null, /* callingPid */ -1, /* callingUid */ -1)) {
             assertThat(afd).isNotNull();
         }
     }
@@ -557,9 +409,8 @@
 
     private void testOpenFileInvalidUser(Uri uri) {
         try {
-            sTestPickerUriResolver.openFile(uri, "r", /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+            sTestPickerUriResolver.openFile(uri, "r", /* signal */ null, /* callingPid */ -1,
+                    /* callingUid */ -1);
             fail("Invalid user specified in the picker uri: " + uri);
         } catch (FileNotFoundException expected) {
             // expected
@@ -570,9 +421,7 @@
     private void testOpenTypedAssetFileInvalidUser(Uri uri) throws Exception {
         try {
             sTestPickerUriResolver.openTypedAssetFile(uri, "image/*", /* opts */ null,
-                    /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+                    /* signal */ null, /* callingPid */ -1, /* callingUid */ -1);
             fail("Invalid user specified in the picker uri: " + uri);
         } catch (FileNotFoundException expected) {
             // expected
@@ -600,9 +449,8 @@
 
     private void testOpenFile_permissionDenied(Uri uri) throws Exception {
         try {
-            sTestPickerUriResolver.openFile(uri, "r", /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+            sTestPickerUriResolver.openFile(uri, "r", /* signal */ null, /* callingPid */ -1,
+                    /* callingUid */ -1);
             fail("openFile should fail if the caller does not have permission grant on the picker"
                     + " uri: " + uri);
         } catch (SecurityException expected) {
@@ -615,9 +463,7 @@
     private void testOpenTypedAssetFile_permissionDenied(Uri uri) throws Exception {
         try {
             sTestPickerUriResolver.openTypedAssetFile(uri, "image/*", /* opts */ null,
-                    /* signal */ null,
-                    LocalCallingIdentity.forTest(sCurrentContext, /* uid */ -1, /* permission */
-                            0));
+                    /* signal */ null, /* callingPid */ -1, /* callingUid */ -1);
             fail("openTypedAssetFile should fail if the caller does not have permission grant on"
                     + " the picker uri: " + uri);
         } catch (SecurityException expected) {
diff --git a/tests/src/com/android/providers/media/TestConfigStore.java b/tests/src/com/android/providers/media/TestConfigStore.java
index df7ef38..ca38ecc 100644
--- a/tests/src/com/android/providers/media/TestConfigStore.java
+++ b/tests/src/com/android/providers/media/TestConfigStore.java
@@ -35,6 +35,8 @@
  */
 public class TestConfigStore implements ConfigStore {
     private boolean mCloudMediaInPhotoPickerEnabled = false;
+
+    private boolean mPickerChoiceManagedSelectionEnabled = false;
     private List<String> mAllowedCloudProviderPackages = Collections.emptyList();
     private @Nullable String mDefaultCloudProviderPackage = null;
     private List<Pair<Executor, Runnable>> mObservers = new ArrayList<>();
@@ -61,6 +63,13 @@
         notifyObservers();
     }
 
+    /**
+     * Enables pickerChoiceManagedSelection flag in the test config.
+     */
+    public void enablePickerChoiceManagedSelectionEnabled() {
+        mPickerChoiceManagedSelectionEnabled = true;
+    }
+
     @Override
     public @NonNull List<String> getAllowedCloudProviderPackages() {
         return mAllowedCloudProviderPackages;
@@ -109,6 +118,11 @@
     }
 
     @Override
+    public boolean isPickerChoiceManagedSelectionEnabled() {
+        return mPickerChoiceManagedSelectionEnabled;
+    }
+
+    @Override
     public void addOnChangeListener(@NonNull Executor executor, @NonNull Runnable listener) {
         Pair p = Pair.create(executor, listener);
         mObservers.add(p);
diff --git a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
index 8395b1b..5f6f269 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
@@ -53,6 +53,7 @@
 import com.android.providers.media.photopicker.data.PickerDatabaseHelper;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
 import com.android.providers.media.photopicker.data.PickerSyncRequestExtras;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 import com.android.providers.media.photopicker.sync.PickerSyncManager;
 import com.android.providers.media.util.ForegroundThread;
 
@@ -137,14 +138,16 @@
         final File dbPath = mContext.getDatabasePath(DB_NAME);
         dbPath.delete();
 
+        final PickerSyncLockManager lockManager = new PickerSyncLockManager();
+
         mDbHelper = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
-        mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, mDbHelper);
+        mFacade = new PickerDbFacade(mContext, lockManager, LOCAL_PROVIDER_AUTHORITY, mDbHelper);
 
         mConfigStore = new TestConfigStore();
         mConfigStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(PACKAGE_NAME);
 
         mController = PickerSyncController.initialize(
-                mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, mFacade, mConfigStore, lockManager, LOCAL_PROVIDER_AUTHORITY);
 
         initializeTestWorkManager(mContext);
         final WorkManager workManager = WorkManager.getInstance(mContext);
diff --git a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
index 2462b32..e51678b 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
@@ -52,6 +52,8 @@
 import com.android.providers.media.photopicker.data.CloudProviderInfo;
 import com.android.providers.media.photopicker.data.PickerDatabaseHelper;
 import com.android.providers.media.photopicker.data.PickerDbFacade;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
 
 import org.junit.After;
 import org.junit.Before;
@@ -136,6 +138,7 @@
     private TestConfigStore mConfigStore;
     private PickerDbFacade mFacade;
     private PickerSyncController mController;
+    private PickerSyncLockManager mLockManager;
 
     @Before
     public void setUp() {
@@ -155,14 +158,16 @@
         final File dbPath = mContext.getDatabasePath(DB_NAME);
         dbPath.delete();
 
+        mLockManager = new PickerSyncLockManager();
+
         PickerDatabaseHelper dbHelper = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
-        mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, dbHelper);
+        mFacade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY, dbHelper);
 
         mConfigStore = new TestConfigStore();
         mConfigStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(PACKAGE_NAME);
 
         mController = PickerSyncController.initialize(
-                mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         // Set cloud provider to null to avoid trying to sync it during other tests
         // that might be using an IsolatedContext
@@ -192,7 +197,7 @@
         configStore.disableCloudMediaFeature();
 
         PickerSyncController controller =
-                PickerSyncController.initialize(mContext, mFacade, configStore);
+                PickerSyncController.initialize(mContext, mFacade, configStore, mLockManager);
         assertThat(controller.getCurrentCloudProviderInfo()).isEqualTo(CloudProviderInfo.EMPTY);
         configStore.setDefaultCloudProviderPackage(PACKAGE_NAME);
         configStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(PACKAGE_NAME);
@@ -208,7 +213,7 @@
     }
 
     @Test
-    public void testSyncIsCancelledIfCloudProviderIsChanged() {
+    public void testSyncIsCancelledIfCloudProviderIsChanged() throws UnableToAcquireLockException {
 
         PickerSyncController controller = spy(mController);
 
@@ -218,7 +223,7 @@
         doReturn(CLOUD_PRIMARY_PROVIDER_AUTHORITY,
                 CLOUD_SECONDARY_PROVIDER_AUTHORITY)
                 .when(controller)
-                .getCloudProvider();
+                .getCloudProviderWithTimeout();
 
         // Add local only media, we expect these to be successfully sync'd from the local provider.
         addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
@@ -245,10 +250,9 @@
         controller.syncAllMedia();
 
         // The cursor should only contain the items from the local provider. (Even though we've
-        // aded a total of 4 items to the linked providers.)
+        // added a total of 4 items to the linked providers.)
         try (Cursor cr = queryMedia()) {
             assertThat(cr.getCount()).isEqualTo(2);
-
             assertCursor(cr, LOCAL_ID_2, LOCAL_PROVIDER_AUTHORITY);
             assertCursor(cr, LOCAL_ID_1, LOCAL_PROVIDER_AUTHORITY);
         }
@@ -773,7 +777,7 @@
 
         mConfigStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(PACKAGE_NAME);
         final PickerSyncController controller = PickerSyncController.initialize(
-                mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
         final List<CloudProviderInfo> providers = controller.getAvailableCloudProviders();
         assertThat(providers).containsExactly(primaryInfo, secondaryInfo, flakyInfo);
     }
@@ -797,7 +801,7 @@
         mConfigStore.setDefaultCloudProviderPackage(PACKAGE_NAME);
 
         mController = PickerSyncController.initialize(mContext, mFacade, mConfigStore,
-                LOCAL_PROVIDER_AUTHORITY);
+                mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         assertThat(mController.getCurrentCloudProviderInfo().packageName).isEqualTo(PACKAGE_NAME);
     }
@@ -843,10 +847,10 @@
 
         final PickerDatabaseHelper dbHelper = new PickerDatabaseHelper(
                 mContext, DB_NAME, DB_VERSION_1);
-        PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        PickerDbFacade facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelper);
         PickerSyncController controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
         controller.syncAllMedia();
@@ -863,9 +867,9 @@
         final File dbPath = mContext.getDatabasePath(DB_NAME);
         dbPath.delete();
 
-        facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, dbHelper);
+        facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY, dbHelper);
         controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         // Initially empty db
         try (Cursor cr = queryMedia(facade)) {
@@ -887,10 +891,10 @@
         mConfigStore.clearAllowedCloudProviderPackagesAndDisableCloudMediaFeature();
 
         PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
-        PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        PickerDbFacade facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelperV1);
         PickerSyncController controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
         controller.syncAllMedia();
@@ -904,9 +908,9 @@
         // Upgrade db version
         dbHelperV1.close();
         PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
-        facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY, dbHelperV2);
+        facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY, dbHelperV2);
         controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         // Initially empty db
         try (Cursor cr = queryMedia(facade)) {
@@ -928,10 +932,10 @@
         mConfigStore.clearAllowedCloudProviderPackagesAndDisableCloudMediaFeature();
 
         PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
-        PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        PickerDbFacade facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelperV2);
         PickerSyncController controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         addMedia(mLocalMediaGenerator, LOCAL_ONLY_1);
         controller.syncAllMedia();
@@ -945,10 +949,10 @@
         // Downgrade db version
         dbHelperV2.close();
         PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
-        facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelperV1);
         controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         // Initially empty db
         try (Cursor cr = queryMedia(facade)) {
@@ -1102,10 +1106,10 @@
         mConfigStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(PACKAGE_NAME);
 
         PickerDatabaseHelper dbHelperV1 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_1);
-        PickerDbFacade facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        PickerDbFacade facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelperV1);
         PickerSyncController controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         controller.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
         assertThat(controller.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
@@ -1113,10 +1117,10 @@
         // Downgrade db version
         dbHelperV1.close();
         PickerDatabaseHelper dbHelperV2 = new PickerDatabaseHelper(mContext, DB_NAME, DB_VERSION_2);
-        facade = new PickerDbFacade(mContext, LOCAL_PROVIDER_AUTHORITY,
+        facade = new PickerDbFacade(mContext, mLockManager, LOCAL_PROVIDER_AUTHORITY,
                 dbHelperV2);
         controller = PickerSyncController.initialize(
-                mContext, facade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mContext, facade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         assertThat(controller.getCloudProvider()).isEqualTo(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
     }
@@ -1129,7 +1133,7 @@
         // Test the default NOT_SET state
         mController =
                 PickerSyncController.initialize(
-                        mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                        mContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         assertThat(mController.getCurrentCloudProviderInfo().packageName).isEqualTo(PACKAGE_NAME);
 
@@ -1138,7 +1142,7 @@
 
         mController =
                 PickerSyncController.initialize(
-                        mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                        mContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         assertThat(mController.getCloudProvider()).isNull();
 
@@ -1147,7 +1151,7 @@
 
         mController =
                 PickerSyncController.initialize(
-                        mContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                        mContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
 
         assertThat(mController.getCloudProvider()).isEqualTo(CLOUD_SECONDARY_PROVIDER_AUTHORITY);
     }
@@ -1376,7 +1380,8 @@
             mConfigStore.setDefaultCloudProviderPackage(PACKAGE_NAME);
 
             // The cloud provider is changed on PickerSyncController construction
-            mController = PickerSyncController.initialize(mContext, mFacade, mConfigStore);
+            mController = PickerSyncController
+                    .initialize(mContext, mFacade, mConfigStore, mLockManager);
             TimeUnit.MILLISECONDS.sleep(100);
             assertThat(refreshUiNotificationObserver.mNotificationReceived).isTrue();
 
@@ -1390,7 +1395,8 @@
             refreshUiNotificationObserver.mNotificationReceived = false;
 
             // The cloud provider remains unchanged on PickerSyncController construction
-            mController = PickerSyncController.initialize(mContext, mFacade, mConfigStore);
+            mController = PickerSyncController
+                    .initialize(mContext, mFacade, mConfigStore, mLockManager);
             TimeUnit.MILLISECONDS.sleep(100);
             assertThat(refreshUiNotificationObserver.mNotificationReceived).isFalse();
 
@@ -1473,7 +1479,7 @@
         }
 
         return PickerSyncController.initialize(
-                mockContext, mFacade, mConfigStore, LOCAL_PROVIDER_AUTHORITY);
+                mockContext, mFacade, mConfigStore, mLockManager, LOCAL_PROVIDER_AUTHORITY);
     }
 
     private static void assertCursor(Cursor cursor, String id, String expectedAuthority) {
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
index f6943a2..d7838af 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
@@ -21,6 +21,7 @@
 
 import static com.android.providers.media.util.MimeUtils.getExtensionFromMimeType;
 
+import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.junit.Assert.assertThrows;
@@ -40,8 +41,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.providers.media.PickerUriResolver;
 import com.android.providers.media.ProjectionHelper;
+import com.android.providers.media.photopicker.sync.PickerSyncLockManager;
 import com.android.providers.media.photopicker.sync.SyncTracker;
 import com.android.providers.media.photopicker.sync.SyncTrackerRegistry;
 
@@ -105,7 +106,7 @@
         mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
         File dbPath = mContext.getDatabasePath(PickerDatabaseHelper.PICKER_DATABASE_NAME);
         dbPath.delete();
-        mFacade = new PickerDbFacade(mContext, LOCAL_PROVIDER);
+        mFacade = new PickerDbFacade(mContext, new PickerSyncLockManager(), LOCAL_PROVIDER);
         mFacade.setCloudProvider(CLOUD_PROVIDER);
         mProjectionHelper = new ProjectionHelper(Column.class, ExportedSince.class);
 
@@ -1350,38 +1351,20 @@
         // Assert all projection columns
         final String[] allProjection = mProjectionHelper.getProjectionMap(
                 PickerMediaColumns.class).keySet().toArray(new String[0]);
-        try (Cursor cr = mFacade.queryMediaIdForApps(PickerUriResolver.PICKER_SEGMENT,
-                LOCAL_PROVIDER, LOCAL_ID, allProjection)) {
-            assertWithMessage(
-                    "Unexpected number of rows when asserting all projection columns with "
-                            + "PickerUriResolver as PICKER_SEGMENT on local provider.")
-                    .that(cr.getCount()).isEqualTo(1);
+        try (Cursor cr = mFacade.queryMediaIdForApps(LOCAL_PROVIDER, LOCAL_ID,
+                allProjection)) {
+            assertThat(cr.getCount()).isEqualTo(1);
 
             cr.moveToFirst();
-            assertMediaStoreCursor(cr, LOCAL_ID, DATE_TAKEN_MS, PickerUriResolver.PICKER_SEGMENT);
-        }
-
-        try (Cursor cr = mFacade.queryMediaIdForApps(PickerUriResolver.PICKER_GET_CONTENT_SEGMENT,
-                LOCAL_PROVIDER, LOCAL_ID, allProjection)) {
-            assertWithMessage(
-                    "Unexpected number of rows when asserting all projection columns with "
-                            + "PickerUriResolver as PICKER_GET_CONTENT_SEGMENT on local provider.")
-                    .that(cr.getCount()).isEqualTo(1);
-
-            cr.moveToFirst();
-            assertMediaStoreCursor(cr, LOCAL_ID, DATE_TAKEN_MS,
-                    PickerUriResolver.PICKER_GET_CONTENT_SEGMENT);
+            assertMediaStoreCursor(cr, LOCAL_ID, DATE_TAKEN_MS);
         }
 
         // Assert one projection column
         final String[] oneProjection = new String[]{PickerMediaColumns.DATE_TAKEN};
 
-        try (Cursor cr = mFacade.queryMediaIdForApps(PickerUriResolver.PICKER_SEGMENT,
-                CLOUD_PROVIDER, CLOUD_ID, oneProjection)) {
-            assertWithMessage(
-                    "Unexpected number of rows when asserting one projection column with cloud "
-                            + "provider.")
-                    .that(cr.getCount()).isEqualTo(1);
+        try (Cursor cr = mFacade.queryMediaIdForApps(CLOUD_PROVIDER, CLOUD_ID,
+                oneProjection)) {
+            assertThat(cr.getCount()).isEqualTo(1);
 
             cr.moveToFirst();
             assertWithMessage(
@@ -1397,12 +1380,9 @@
                 invalidColumn
         };
 
-        try (Cursor cr = mFacade.queryMediaIdForApps(PickerUriResolver.PICKER_SEGMENT,
-                CLOUD_PROVIDER, CLOUD_ID, invalidProjection)) {
-            assertWithMessage(
-                    "Unexpected number of rows when asserting invalid projection column with "
-                            + "cloud provider.")
-                    .that(cr.getCount()).isEqualTo(1);
+        try (Cursor cr = mFacade.queryMediaIdForApps(CLOUD_PROVIDER, CLOUD_ID,
+                invalidProjection)) {
+            assertThat(cr.getCount()).isEqualTo(1);
 
             cr.moveToFirst();
             assertWithMessage(
@@ -2263,8 +2243,8 @@
         return mediaId + getExtensionFromMimeType(mimeType);
     }
 
-    private static String getData(String authority, String displayName, String pickerSegmentType) {
-        return "/sdcard/.transforms/synthetic/" + pickerSegmentType + "/0/" + authority + "/media/"
+    private static String getData(String authority, String displayName) {
+        return "/sdcard/.transforms/synthetic/picker/0/" + authority + "/media/"
                 + displayName;
     }
 
@@ -2290,10 +2270,8 @@
 
     private static void assertCloudMediaCursor(Cursor cursor, String id, String mimeType) {
         final String displayName = getDisplayName(id, mimeType);
-        final String localData = getData(LOCAL_PROVIDER, displayName,
-                PickerUriResolver.PICKER_SEGMENT);
-        final String cloudData = getData(CLOUD_PROVIDER, displayName,
-                PickerUriResolver.PICKER_SEGMENT);
+        final String localData = getData(LOCAL_PROVIDER, displayName);
+        final String cloudData = getData(CLOUD_PROVIDER, displayName);
 
         assertWithMessage("Unexpected value of MediaColumns.ID for the cloud media cursor.")
                 .that(cursor.getString(cursor.getColumnIndex(MediaColumns.ID)))
@@ -2380,11 +2358,10 @@
         }
     }
 
-    private static void assertMediaStoreCursor(Cursor cursor, String id, long dateTakenMs,
-            String pickerSegmentType) {
+    private static void assertMediaStoreCursor(Cursor cursor, String id, long dateTakenMs) {
         final String displayName = getDisplayName(id, MP4_VIDEO_MIME_TYPE);
-        final String localData = getData(LOCAL_PROVIDER, displayName, pickerSegmentType);
-        final String cloudData = getData(CLOUD_PROVIDER, displayName, pickerSegmentType);
+        final String localData = getData(LOCAL_PROVIDER, displayName);
+        final String cloudData = getData(CLOUD_PROVIDER, displayName);
 
         assertWithMessage(
                 "Unexpected value for PickerMediaColumns.DISPLAY_NAME for the media store cursor.")
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
index 3fb365d..e4ded70 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerResultTest.java
@@ -16,12 +16,9 @@
 
 package com.android.providers.media.photopicker.data;
 
-import static android.content.Intent.ACTION_GET_CONTENT;
-import static android.provider.MediaStore.ACTION_PICK_IMAGES;
 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-import static com.android.providers.media.PickerUriResolver.PICKER_GET_CONTENT_SEGMENT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -61,7 +58,7 @@
     }
 
     /**
-     * Tests {@link PickerResult#getPickerResponseIntent(String, boolean, List)} with single item
+     * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} with single item
      * @throws Exception
      */
     @Test
@@ -69,10 +66,9 @@
         List<Item> items = null;
         try {
             items = createItemSelection(1);
-            final Uri expectedPickerUri = PickerResult.getPickerUri(ACTION_PICK_IMAGES,
-                    items.get(0).getContentUri());
+            final Uri expectedPickerUri = PickerResult.getPickerUri(items.get(0).getContentUri());
             final Intent intent = PickerResult.getPickerResponseIntent(
-                    ACTION_PICK_IMAGES, /* canSelectMultiple */ false, items);
+                    /* canSelectMultiple */ false, items);
 
             final Uri result = intent.getData();
             assertPickerUriFormat(result);
@@ -88,32 +84,8 @@
         }
     }
 
-    @Test
-    public void testGetResultSingleForActionGetContent() throws Exception {
-        List<Item> items = null;
-        try {
-            items = createItemSelection(1);
-            final Uri expectedPickerUri = PickerResult.getPickerUri(ACTION_GET_CONTENT,
-                    items.get(0).getContentUri());
-            final Intent intent = PickerResult.getPickerResponseIntent(
-                    ACTION_GET_CONTENT, /* canSelectMultiple */ false, items);
-
-            final Uri result = intent.getData();
-            assertGetContentPickerUriFormat(result);
-            assertThat(result).isEqualTo(expectedPickerUri);
-
-            final ClipData clipData = intent.getClipData();
-            assertThat(clipData).isNotNull();
-            final int count = clipData.getItemCount();
-            assertThat(count).isEqualTo(1);
-            assertThat(clipData.getItemAt(0).getUri()).isEqualTo(expectedPickerUri);
-        } finally {
-            deleteFiles(items);
-        }
-    }
-
     /**
-     * Tests {@link PickerResult#getPickerResponseIntent(String, boolean, List)} with multiple items
+     * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} with multiple items
      * @throws Exception
      */
     @Test
@@ -123,12 +95,11 @@
             final int itemCount = 3;
             items = createItemSelection(itemCount);
             List<Uri> expectedPickerUris = new ArrayList<>();
-            for (Item item : items) {
-                expectedPickerUris.add(PickerResult.getPickerUri(ACTION_PICK_IMAGES,
-                        item.getContentUri()));
+            for (Item item: items) {
+                expectedPickerUris.add(PickerResult.getPickerUri(item.getContentUri()));
             }
-            final Intent intent = PickerResult.getPickerResponseIntent(
-                    ACTION_PICK_IMAGES, /* canSelectMultiple */ true, items);
+            final Intent intent = PickerResult.getPickerResponseIntent(/* canSelectMultiple */ true,
+                    items);
 
             final ClipData clipData = intent.getClipData();
             final int count = clipData.getItemCount();
@@ -143,36 +114,10 @@
         }
     }
 
-    @Test
-    public void testGetResultMultipleForActionGetContent() throws Exception {
-        ArrayList<Item> items = null;
-        try {
-            final int itemCount = 3;
-            items = createItemSelection(itemCount);
-            List<Uri> expectedPickerUris = new ArrayList<>();
-            for (Item item : items) {
-                expectedPickerUris.add(PickerResult.getPickerUri(ACTION_GET_CONTENT,
-                        item.getContentUri()));
-            }
-            final Intent intent = PickerResult.getPickerResponseIntent(
-                    ACTION_GET_CONTENT, /* canSelectMultiple */ true, items);
-
-            final ClipData clipData = intent.getClipData();
-            final int count = clipData.getItemCount();
-            assertThat(count).isEqualTo(itemCount);
-            for (int i = 0; i < count; i++) {
-                Uri uri = clipData.getItemAt(i).getUri();
-                assertGetContentPickerUriFormat(uri);
-                assertThat(uri).isEqualTo(expectedPickerUris.get(i));
-            }
-        } finally {
-            deleteFiles(items);
-        }
-    }
-
     /**
-     * Tests {@link PickerResult#getPickerResponseIntent(String, boolean, List)} when the user
-     * selected only one item in multi-select mode
+     * Tests {@link PickerResult#getPickerResponseIntent(boolean, List)} when the user selected
+     * only one item in multi-select mode
+     * @throws Exception
      */
     @Test
     public void testGetResultMultiple_onlyOneItemSelected() throws Exception {
@@ -180,10 +125,9 @@
         try {
             final int itemCount = 1;
             items = createItemSelection(itemCount);
-            final Uri expectedPickerUri = PickerResult.getPickerUri(ACTION_PICK_IMAGES,
-                    items.get(0).getContentUri());
-            final Intent intent = PickerResult.getPickerResponseIntent(
-                    ACTION_PICK_IMAGES, /* canSelectMultiple */ true, items);
+            final Uri expectedPickerUri = PickerResult.getPickerUri(items.get(0).getContentUri());
+            final Intent intent = PickerResult.getPickerResponseIntent(/* canSelectMultiple */ true,
+                    items);
 
             final ClipData clipData = intent.getClipData();
             final int count = clipData.getItemCount();
@@ -200,12 +144,6 @@
         assertThat(uri.toString().startsWith(pickerUriPrefix)).isTrue();
     }
 
-    private void assertGetContentPickerUriFormat(Uri uri) {
-        final String pickerNonRedactedUriPrefix = MediaStore.AUTHORITY_URI.buildUpon().appendPath(
-                PICKER_GET_CONTENT_SEGMENT).build().toString();
-        assertThat(uri.toString().startsWith(pickerNonRedactedUriPrefix)).isTrue();
-    }
-
     /**
      * Returns a PhotoSelection on which the test app does not have access to.
      */
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
index 7be7b0c..e254445 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Process;
 import android.provider.MediaStore;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -55,10 +56,10 @@
 import java.util.concurrent.TimeoutException;
 
 public class PhotoPickerBaseTest {
-    public static final int PICKER_TAB_RECYCLERVIEW_ID = R.id.picker_tab_recyclerview;
-    public static final int TAB_LAYOUT_ID = R.id.tab_layout;
+    protected static final int PICKER_TAB_RECYCLERVIEW_ID = R.id.picker_tab_recyclerview;
+    protected static final int TAB_LAYOUT_ID = R.id.tab_layout;
     protected static final int PICKER_PHOTOS_STRING_ID = R.string.picker_photos;
-    public static final int PICKER_ALBUMS_STRING_ID = R.string.picker_albums;
+    protected static final int PICKER_ALBUMS_STRING_ID = R.string.picker_albums;
     protected static final int PREVIEW_VIEW_PAGER_ID = R.id.preview_viewPager;
     protected static final int ICON_CHECK_ID = R.id.icon_check;
     protected static final int ICON_THUMBNAIL_ID = R.id.icon_thumbnail;
@@ -118,7 +119,7 @@
         sUserSelectImagesForAppIntent = new Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP);
         sUserSelectImagesForAppIntent.addCategory(Intent.CATEGORY_FRAMEWORK_INSTRUMENTATION_TEST);
         Bundle extras = new Bundle();
-        extras.putInt(Intent.EXTRA_UID, 1234);
+        extras.putInt(Intent.EXTRA_UID, Process.myUid());
         sUserSelectImagesForAppIntent.putExtras(extras);
     }
 
@@ -134,7 +135,7 @@
     private static final long POLLING_SLEEP_MILLIS = 200;
 
     private static IsolatedContext sIsolatedContext;
-    static UserIdManager sUserIdManager;
+    private static UserIdManager sUserIdManager;
 
     public static Intent getSingleSelectMimeTypeFilterIntent(String mimeTypeFilter) {
         final Intent intent = new Intent(sSingleSelectIntent);
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java
index 242ad58..5bfedba 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerTestActivity.java
@@ -16,9 +16,13 @@
 
 package com.android.providers.media.photopicker.espresso;
 
+import static com.android.providers.media.photopicker.espresso.PhotoPickerUserSelectActivityTest.MANAGED_SELECTION_ENABLED_EXTRA;
+
 import static org.mockito.Mockito.RETURNS_SMART_NULLS;
 import static org.mockito.Mockito.mock;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.UiEventLogger;
 import com.android.providers.media.TestConfigStore;
@@ -32,18 +36,21 @@
     private final UiEventLogger mLogger = mock(UiEventLogger.class, RETURNS_SMART_NULLS);
     private InstanceId mInstanceId;
 
-    private PickerViewModel mPickerViewModel;
-
     @Override
+    @NonNull
     protected PickerViewModel getOrCreateViewModel() {
-        mPickerViewModel = super.getOrCreateViewModel();
-        mPickerViewModel.setConfigStore(mConfigStore);
-        mPickerViewModel.setItemsProvider(new ItemsProvider(
+        final PickerViewModel pickerViewModel = super.getOrCreateViewModel();
+        if (getIntent().getExtras() != null && getIntent().getExtras().getBoolean(
+                MANAGED_SELECTION_ENABLED_EXTRA)) {
+            mConfigStore.enablePickerChoiceManagedSelectionEnabled();
+        }
+        pickerViewModel.setConfigStore(mConfigStore);
+        pickerViewModel.setItemsProvider(new ItemsProvider(
                 PhotoPickerBaseTest.getIsolatedContext()));
-        mPickerViewModel.setUserIdManager(PhotoPickerBaseTest.getMockUserIdManager());
-        mPickerViewModel.setLogger(new PhotoPickerUiEventLogger(mLogger));
-        mInstanceId = mPickerViewModel.getInstanceId();
-        return mPickerViewModel;
+        pickerViewModel.setUserIdManager(PhotoPickerBaseTest.getMockUserIdManager());
+        pickerViewModel.setLogger(new PhotoPickerUiEventLogger(mLogger));
+        mInstanceId = pickerViewModel.getInstanceId();
+        return pickerViewModel;
     }
 
     TestConfigStore getConfigStore() {
@@ -57,12 +64,4 @@
     InstanceId getInstanceId() {
         return mInstanceId;
     }
-
-    void setItemsProvider(ItemsProvider itemsProvider) {
-        mPickerViewModel.setItemsProvider(itemsProvider);
-    }
-
-    void initSyncForPhotosGrid() {
-        mPickerViewModel.initPhotoPickerData();
-    }
 }
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerUserSelectActivityTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerUserSelectActivityTest.java
index a0df108..a37a466 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerUserSelectActivityTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerUserSelectActivityTest.java
@@ -56,6 +56,8 @@
 @RunWith(AndroidJUnit4ClassRunner.class)
 public class PhotoPickerUserSelectActivityTest extends PhotoPickerBaseTest {
 
+    public static final String MANAGED_SELECTION_ENABLED_EXTRA = "MANAGED_SELECTION_ENABLE";
+
     public ActivityScenario<PhotoPickerTestActivity> mScenario;
 
     @After
@@ -132,6 +134,34 @@
     }
 
     @Test
+    public void testAddButtonIsShowsAllowNone() {
+        launchValidActivityWithManagedSelectionEnabled();
+        final int bottomBarId = R.id.picker_bottom_bar;
+        final int viewSelectedId = R.id.button_view_selected;
+        final int addButtonId = R.id.button_add;
+
+        // Default view, no item selected.
+        onView(withId(bottomBarId)).check(matches(isDisplayed()));
+        onView(withId(viewSelectedId)).check(matches(not(isDisplayed())));
+        onView(withId(addButtonId)).check(matches(isDisplayed()));
+        // verify that 'Allow none' is displayed in this case.
+        onView(withId(addButtonId)).check(
+                matches(withText(R.string.picker_add_button_allow_none_option)));
+
+        clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
+
+        onView(withId(bottomBarId)).check(matches(isDisplayed()));
+        onView(withId(viewSelectedId)).check(matches(isDisplayed()));
+
+        onView(withId(addButtonId)).check(matches(withText("Allow (1)")));
+        onView(withId(addButtonId)).check(matches(isDisplayed()));
+
+
+        onView(withId(VIEW_SELECTED_BUTTON_ID)).perform(click());
+        onView(withId(addButtonId)).check(matches(withText("Allow (1)")));
+    }
+
+    @Test
     public void testNoCloudSettingsAndBanners() {
         launchValidActivity();
 
@@ -155,4 +185,12 @@
                 ActivityScenario.launchActivityForResult(
                         PhotoPickerBaseTest.getUserSelectImagesForAppIntent());
     }
+
+    /** Test helper to launch a valid test activity. */
+    private void launchValidActivityWithManagedSelectionEnabled() {
+        Intent intent = PhotoPickerBaseTest.getUserSelectImagesForAppIntent();
+        intent.putExtra(MANAGED_SELECTION_ENABLED_EXTRA, true);
+        mScenario =
+                ActivityScenario.launchActivityForResult(intent);
+    }
 }
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/ProgressBarTest.java b/tests/src/com/android/providers/media/photopicker/espresso/ProgressBarTest.java
index a9d3266..8db8149 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/ProgressBarTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/ProgressBarTest.java
@@ -27,19 +27,15 @@
 
 import static org.hamcrest.Matchers.allOf;
 
-import android.content.Context;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
 import android.text.format.DateUtils;
 
-import androidx.annotation.Nullable;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
 
 import com.android.providers.media.library.RunOnlyOnPostsubmit;
-import com.android.providers.media.photopicker.data.ItemsProvider;
-import com.android.providers.media.photopicker.data.model.UserId;
 
 import org.junit.After;
 import org.junit.Before;
@@ -84,29 +80,6 @@
                 PhotoPickerBaseTest.getSingleSelectionIntent());
     }
 
-    private void setItemsProviderWithDelayInActivity() {
-        FakeItemsProvider itemsProvider = new FakeItemsProvider(getIsolatedContext());
-        itemsProvider.delaySync();
-        mScenario.onActivity(
-                (activity -> {
-                    activity.setItemsProvider(itemsProvider);
-                }));
-    }
-
-    private void assertProgressBarAndLoadingTextAppears() {
-        final UiSelector progressBar = new UiSelector().resourceId(
-                getIsolatedContext().getPackageName()
-                        + ":id/progress_bar");
-        assertWithMessage("Waiting for progressBar to appear on photos grid").that(
-                new UiObject(progressBar).waitForExists(DateUtils.SECOND_IN_MILLIS)).isTrue();
-
-        final UiSelector loadingText = new UiSelector().resourceId(
-                getIsolatedContext().getPackageName()
-                        + ":id/loading_text_view");
-        assertWithMessage("Waiting for progressBar to appear on photos grid").that(
-                new UiObject(loadingText).waitForExists(DateUtils.SECOND_IN_MILLIS)).isTrue();
-    }
-
     private void assertProgressBarAndLoadingTextDoesNotAppears() {
         final UiSelector progressBar = new UiSelector().resourceId(
                 getIsolatedContext().getPackageName()
@@ -127,33 +100,4 @@
             mScenario.close();
         }
     }
-
-    public static class FakeItemsProvider extends ItemsProvider {
-
-        private boolean mShouldDelaySync = false;
-
-        public FakeItemsProvider(Context context) {
-            super(context);
-        }
-
-        public void delaySync() {
-            mShouldDelaySync = true;
-        }
-
-        @Override
-        public void initPhotoPickerData(@Nullable String albumId,
-                @Nullable String albumAuthority,
-                boolean initLocalOnlyData,
-                @Nullable UserId userId) {
-            if (mShouldDelaySync) {
-                try {
-                    Thread.sleep(DateUtils.SECOND_IN_MILLIS);
-                } catch (Exception e) {
-                    // no-op
-                }
-            } else {
-                super.initPhotoPickerData(albumId, albumAuthority, initLocalOnlyData, userId);
-            }
-        }
-    }
 }
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
index 2f1b5ab..5897164 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
@@ -34,7 +34,6 @@
 import com.android.providers.media.R;
 import com.android.providers.media.library.RunOnlyOnPostsubmit;
 
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -126,12 +125,10 @@
     }
 
     @Test
-    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_navigation() throws Exception {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
         // Select items
-        clickItem(PICKER_TAB_RECYCLERVIEW_ID, IMAGE_1_POSITION, ICON_THUMBNAIL_ID);
         clickItem(PICKER_TAB_RECYCLERVIEW_ID, GIF_POSITION, ICON_THUMBNAIL_ID);
         clickItem(PICKER_TAB_RECYCLERVIEW_ID, ANIMATED_WEBP_POSITION, ICON_THUMBNAIL_ID);
         clickItem(PICKER_TAB_RECYCLERVIEW_ID, MOTION_PHOTO_POSITION, ICON_THUMBNAIL_ID);
diff --git a/tests/src/com/android/providers/media/photopicker/sync/MediaResetWorkerTest.java b/tests/src/com/android/providers/media/photopicker/sync/MediaResetWorkerTest.java
index 62ee337..617028f 100644
--- a/tests/src/com/android/providers/media/photopicker/sync/MediaResetWorkerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/sync/MediaResetWorkerTest.java
@@ -92,7 +92,8 @@
         SyncTrackerRegistry.setLocalAlbumSyncTracker(mMockLocalAlbumSyncTracker);
         SyncTrackerRegistry.setCloudAlbumSyncTracker(mMockCloudAlbumSyncTracker);
 
-        doReturn(new Object()).when(mMockPickerSyncController).getCloudAlbumSyncLock();
+        doReturn(new PickerSyncLockManager())
+                .when(mMockPickerSyncController).getPickerSyncLockManager();
         doReturn(TEST_CLOUD_AUTHORITY).when(mMockPickerSyncController).getCloudProvider();
         doReturn(TEST_LOCAL_AUTHORITY).when(mMockPickerSyncController).getLocalProvider();
 
@@ -102,7 +103,7 @@
         File dbPath = mContext.getDatabasePath(PickerDatabaseHelper.PICKER_DATABASE_NAME);
         dbPath.delete();
 
-        mDbFacade = new PickerDbFacade(mContext, TEST_LOCAL_AUTHORITY);
+        mDbFacade = new PickerDbFacade(mContext, new PickerSyncLockManager(), TEST_LOCAL_AUTHORITY);
         mDbFacade.setCloudProvider(TEST_CLOUD_AUTHORITY);
 
         initializeTestWorkManager(mContext);
diff --git a/tests/src/com/android/providers/media/photopicker/sync/PickerSyncLockManagerTest.java b/tests/src/com/android/providers/media/photopicker/sync/PickerSyncLockManagerTest.java
new file mode 100644
index 0000000..de01adb
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/sync/PickerSyncLockManagerTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.sync;
+
+import static com.android.providers.media.photopicker.sync.PickerSyncLockManager.CLOUD_ALBUM_SYNC_LOCK;
+import static com.android.providers.media.photopicker.sync.PickerSyncLockManager.CLOUD_SYNC_LOCK;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+
+import com.android.providers.media.photopicker.util.exceptions.UnableToAcquireLockException;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class PickerSyncLockManagerTest {
+    private PickerSyncLockManager mSyncLockManager;
+
+    @Before
+    public void setup() {
+        mSyncLockManager = new PickerSyncLockManager();
+    }
+
+    @Test
+    public void testLockIsCloseable() {
+        try (CloseableReentrantLock lock = mSyncLockManager.lock(CLOUD_SYNC_LOCK)) {
+            // Assert that the lock is help by the current thread.
+            assertThat(lock.isHeldByCurrentThread()).isTrue();
+            assertThat(lock.getHoldCount()).isEqualTo(1);
+
+            try (CloseableReentrantLock lockInLock = mSyncLockManager.lock(CLOUD_SYNC_LOCK)) {
+                // Assert that this is a reentrant lock and the thread was able to increment hold
+                // count.
+                assertThat(lock.isHeldByCurrentThread()).isTrue();
+                assertThat(lock).isEqualTo(lockInLock);
+                assertThat(lock.getHoldCount()).isEqualTo(2);
+            }
+
+            // Assert that the hold count has been decremented.
+            assertThat(lock.isHeldByCurrentThread()).isTrue();
+            assertThat(lock.getHoldCount()).isEqualTo(1);
+            assertThat(lock).isEqualTo(mSyncLockManager.getLock(CLOUD_SYNC_LOCK));
+        }
+
+        assertThat(mSyncLockManager.getLock(CLOUD_SYNC_LOCK).isHeldByCurrentThread()).isFalse();
+    }
+
+    @Test
+    public void testLockWithTimeoutIsCloseable() throws UnableToAcquireLockException {
+        try (CloseableReentrantLock lock = mSyncLockManager.tryLock(CLOUD_ALBUM_SYNC_LOCK)) {
+            // Assert that the lock is help by the current thread.
+            assertThat(lock.isHeldByCurrentThread()).isTrue();
+            assertThat(lock.getHoldCount()).isEqualTo(1);
+
+            try (CloseableReentrantLock lockInLock =
+                         mSyncLockManager.tryLock(CLOUD_ALBUM_SYNC_LOCK)) {
+                // Assert that this is a reentrant lock and the thread was able to increment hold
+                // count.
+                assertThat(lock.isHeldByCurrentThread()).isTrue();
+                assertThat(lock).isEqualTo(lockInLock);
+                assertThat(lock.getHoldCount()).isEqualTo(2);
+            }
+
+            // Assert that the hold count has been decremented.
+            assertThat(lock.isHeldByCurrentThread()).isTrue();
+            assertThat(lock.getHoldCount()).isEqualTo(1);
+            assertThat(lock).isEqualTo(mSyncLockManager.getLock(CLOUD_ALBUM_SYNC_LOCK));
+        }
+
+        assertThat(mSyncLockManager.getLock(CLOUD_ALBUM_SYNC_LOCK).isHeldByCurrentThread())
+                .isFalse();
+    }
+
+    @Test
+    public void testLockTimeout() throws InterruptedException, TimeoutException {
+        CloseableReentrantLock lock = new CloseableReentrantLock("testLock");
+        try (CloseableReentrantLock ignored =
+                     mSyncLockManager.tryLock(lock, 5, TimeUnit.MILLISECONDS)) {
+            // it is expected that the lock is held by the current thread within timeout.
+        } catch (UnableToAcquireLockException e) {
+            throw new AssertionError(
+                    "Should be able to acquire the lock since no other thread holds it.", e);
+        }
+
+        HandlerThread thread = new HandlerThread("PickerSyncLockTestThread",
+                android.os.Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        acquireLock(handler, lock);
+
+        try (CloseableReentrantLock ignored =
+                     mSyncLockManager.tryLock(lock, 5, TimeUnit.MILLISECONDS)) {
+            throw new AssertionError("The lock should not be acquired by this thread because "
+                    + "it is already held by a different thread");
+        } catch (UnableToAcquireLockException e) {
+            // The expectation is that lock is not acquired within the timeout and
+            // UnableToAcquireLockException is thrown.
+        }
+
+        releaseLock(handler, lock);
+        thread.quitSafely();
+    }
+
+    private void acquireLock(Handler handler, CloseableReentrantLock lock)
+            throws InterruptedException, TimeoutException {
+        handler.post(() -> lock.lock());
+        waitForHandler(handler);
+    }
+
+    private void releaseLock(Handler handler, CloseableReentrantLock lock)
+            throws InterruptedException, TimeoutException {
+        handler.post(() -> lock.unlock());
+        waitForHandler(handler);
+    }
+
+    private void waitForHandler(Handler handler) throws InterruptedException, TimeoutException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        handler.post(() -> latch.countDown());
+        final boolean success = latch.await(30, TimeUnit.SECONDS);
+        if (!success) {
+            throw new TimeoutException("Could not wait for handler task to finish");
+        }
+    }
+}
diff --git a/tests/src/com/android/providers/media/photopicker/ui/PhotosTabAdapterTest.java b/tests/src/com/android/providers/media/photopicker/ui/PhotosTabAdapterTest.java
index 6b2b399..169e86a 100644
--- a/tests/src/com/android/providers/media/photopicker/ui/PhotosTabAdapterTest.java
+++ b/tests/src/com/android/providers/media/photopicker/ui/PhotosTabAdapterTest.java
@@ -33,6 +33,8 @@
 import com.android.providers.media.photopicker.data.model.Item;
 import com.android.providers.media.photopicker.ui.PhotosTabAdapter.DateHeader;
 
+import com.bumptech.glide.util.ViewPreloadSizeProvider;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -191,7 +193,8 @@
                 mock(TabAdapter.OnBannerEventListener.class),
                 /* onChooseAccountBannerEventListener */
                 mock(TabAdapter.OnBannerEventListener.class),
-                /* onHoverListener */ mock(View.OnHoverListener.class));
+                /* onHoverListener */ mock(View.OnHoverListener.class),
+                /* mPreloadSizeProvider */ mock(ViewPreloadSizeProvider.class));
     }
 
     private static Item generateFakeImageItem(String id) {
diff --git a/tests/src/com/android/providers/media/scan/DrmTest.java b/tests/src/com/android/providers/media/scan/DrmTest.java
index ea3562f..ccc9090 100644
--- a/tests/src/com/android/providers/media/scan/DrmTest.java
+++ b/tests/src/com/android/providers/media/scan/DrmTest.java
@@ -19,6 +19,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
@@ -48,7 +50,6 @@
 import com.android.providers.media.util.FileUtils;
 
 import org.junit.After;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -99,7 +100,7 @@
 
     @Test
     public void testForwardLock_Audio() throws Exception {
-        Assume.assumeTrue(isForwardLockSupported());
+        assumeTrue(isForwardLockSupported());
         doForwardLock("audio/mpeg", R.raw.test_audio, (values) -> {
             assertEquals(1_045L, (long) values.getAsLong(FileColumns.DURATION));
             assertEquals(FileColumns.MEDIA_TYPE_AUDIO,
@@ -109,7 +110,7 @@
 
     @Test
     public void testForwardLock_Video() throws Exception {
-        Assume.assumeTrue(isForwardLockSupported());
+        assumeTrue(isForwardLockSupported());
         doForwardLock("video/mp4", R.raw.test_video, (values) -> {
             assertEquals(40_000L, (long) values.getAsLong(FileColumns.DURATION));
             assertEquals(FileColumns.MEDIA_TYPE_VIDEO,
@@ -119,7 +120,7 @@
 
     @Test
     public void testForwardLock_Image() throws Exception {
-        Assume.assumeTrue(isForwardLockSupported());
+        assumeTrue(isForwardLockSupported());
         doForwardLock("image/jpeg", R.raw.test_image, (values) -> {
             // ExifInterface currently doesn't know how to scan DRM images, so
             // the best we can do is verify the base test metadata
@@ -130,7 +131,7 @@
 
     @Test
     public void testForwardLock_Binary() throws Exception {
-        Assume.assumeTrue(isForwardLockSupported());
+        assumeTrue(isForwardLockSupported());
         doForwardLock("application/octet-stream", R.raw.test_image, null);
     }
 
@@ -140,7 +141,7 @@
      */
     @Test
     public void testForwardLock_130680734() throws Exception {
-        Assume.assumeTrue(isForwardLockSupported());
+        assumeTrue(isForwardLockSupported());
 
         final ContentValues values = new ContentValues();
         values.put(MediaColumns.DISPLAY_NAME, "temp" + System.nanoTime() + ".fl");
@@ -165,10 +166,16 @@
                 ContentUris.parseId(uri));
         try (Cursor c = mResolver.query(filesUri, null, null, null)) {
             assertTrue(c.moveToFirst());
+
+            final String mimeType = c.getString(c.getColumnIndex(FileColumns.MIME_TYPE));
+            // To be consistent with the logic in doForwardLock() below: if the devices does not
+            // handle .fl files we won't consider the test failing, but we also can't carry on here,
+            // thus let's do an AssumptionViolatedException.
+            assumeFalse(MIME_UNSUPPORTED.equals(mimeType));
+            assertEquals("video/mp4", mimeType);
+
             assertEquals(FileColumns.MEDIA_TYPE_VIDEO,
                     c.getInt(c.getColumnIndex(FileColumns.MEDIA_TYPE)));
-            assertEquals("video/mp4",
-                    c.getString(c.getColumnIndex(FileColumns.MIME_TYPE)));
         }
     }
 
@@ -232,7 +239,7 @@
                 }
             } else if (Objects.equals(MIME_UNSUPPORTED, actualMimeType)) {
                 // We don't scan unsupported items, so we can't check our custom
-                // verifier, but we're still willing to consider this as passing
+                // verifier, but we're still willing to consider this as passing.
             } else {
                 fail("Unexpected MIME type " + actualMimeType);
             }
diff --git a/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java b/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java
index 0c8c5df..b913114 100644
--- a/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java
+++ b/tests/src/com/android/providers/media/util/SyntheticPathUtilsTest.java
@@ -23,13 +23,9 @@
 import static com.android.providers.media.util.SyntheticPathUtils.isPickerPath;
 import static com.android.providers.media.util.SyntheticPathUtils.isRedactedPath;
 import static com.android.providers.media.util.SyntheticPathUtils.isSyntheticPath;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import androidx.test.runner.AndroidJUnit4;
-
-import com.android.providers.media.PickerUriResolver;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -50,10 +46,7 @@
 
     @Test
     public void testGetPickerRelativePath() throws Exception {
-        assertThat(getPickerRelativePath(PickerUriResolver.PICKER_SEGMENT)).isEqualTo(
-                ".transforms/synthetic/picker");
-        assertThat(getPickerRelativePath(PickerUriResolver.PICKER_GET_CONTENT_SEGMENT)).isEqualTo(
-                ".transforms/synthetic/picker_get_content");
+        assertThat(getPickerRelativePath()).isEqualTo(".transforms/synthetic/picker");
     }
 
     @Test