Snap for 8188251 from 922690e61aabab8cf09384d9f2385343a2fc150c to mainline-wifi-release

Change-Id: Id1d45ce247e2b3a25e8411423fdc32b18b2c2bd5
diff --git a/apex/framework/java/android/provider/AsyncContentProvider.java b/apex/framework/java/android/provider/AsyncContentProvider.java
new file mode 100644
index 0000000..25d5609
--- /dev/null
+++ b/apex/framework/java/android/provider/AsyncContentProvider.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.provider;
+
+import static android.provider.CloudMediaProviderContract.EXTRA_ERROR_MESSAGE;
+import static android.provider.CloudMediaProviderContract.EXTRA_FILE_DESCRIPTOR;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import java.io.FileNotFoundException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * @hide
+ */
+public final class AsyncContentProvider {
+
+    private static final long TIMEOUT_IN_SECONDS = 5L;
+
+    private final IAsyncContentProvider mAsyncContentProvider;
+
+    public AsyncContentProvider(@NonNull IAsyncContentProvider asyncContentProvider) {
+        this.mAsyncContentProvider = asyncContentProvider;
+    }
+
+    @NonNull
+    public ParcelFileDescriptor openMedia(@NonNull Uri uri, @NonNull String mode)
+            throws FileNotFoundException, ExecutionException, InterruptedException,
+            TimeoutException, RemoteException {
+        String mediaId = uri.getLastPathSegment();
+        CompletableFuture<ParcelFileDescriptor> future = new CompletableFuture<>();
+        RemoteCallback callback = new RemoteCallback(result -> setResult(result, future));
+        mAsyncContentProvider.openMedia(mediaId, callback);
+        return future.get(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+    }
+
+    private void setResult(Bundle result, CompletableFuture<ParcelFileDescriptor> future) {
+        if (result.containsKey(EXTRA_FILE_DESCRIPTOR)) {
+            ParcelFileDescriptor pfd = result.getParcelable(EXTRA_FILE_DESCRIPTOR);
+            future.complete(pfd);
+        } else if (result.containsKey(EXTRA_ERROR_MESSAGE)) {
+            future.completeExceptionally(
+                    new RemoteException(
+                            result.getString(EXTRA_ERROR_MESSAGE)));
+        } else {
+            future.completeExceptionally(
+                    new RemoteException(
+                            "File descriptor and error message missing in response from "
+                                    + "CloudMediaProvider"));
+        }
+    }
+}
\ No newline at end of file
diff --git a/apex/framework/java/android/provider/CloudMediaProvider.java b/apex/framework/java/android/provider/CloudMediaProvider.java
index bf2e55c..00fa772 100644
--- a/apex/framework/java/android/provider/CloudMediaProvider.java
+++ b/apex/framework/java/android/provider/CloudMediaProvider.java
@@ -16,18 +16,21 @@
 
 package android.provider;
 
+import static android.provider.CloudMediaProviderContract.EXTRA_ERROR_MESSAGE;
+import static android.provider.CloudMediaProviderContract.EXTRA_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.EXTRA_FILE_DESCRIPTOR;
 import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
 import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER;
 import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
 import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_EVENT_CALLBACK;
 import static android.provider.CloudMediaProviderContract.METHOD_CREATE_SURFACE_CONTROLLER;
-import static android.provider.CloudMediaProviderContract.METHOD_GET_ACCOUNT_INFO;
-import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_INFO;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO;
 import static android.provider.CloudMediaProviderContract.URI_PATH_ALBUM;
 import static android.provider.CloudMediaProviderContract.URI_PATH_DELETED_MEDIA;
 import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA;
 import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA_EXACT;
-import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA_INFO;
+import static android.provider.CloudMediaProviderContract.URI_PATH_MEDIA_COLLECTION_INFO;
 import static android.provider.CloudMediaProviderContract.URI_PATH_SURFACE_CONTROLLER;
 
 import android.annotation.DurationMillisLong;
@@ -50,6 +53,7 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -97,7 +101,8 @@
  * zero or more albums. Albums cannot contain other albums.
  * <p>
  * Each item under a provider is uniquely referenced by its media or album id, which must not
- * change without changing the provider version as returned by {@link #onGetMediaInfo}.
+ * change which must be unique across all collection IDs as returned by
+ * {@link #onGetMediaCollectionInfo}.
  *
  * @see MediaStore#ACTION_PICK_IMAGES
  *
@@ -110,7 +115,7 @@
     private static final int MATCH_MEDIA_ID = 2;
     private static final int MATCH_DELETED_MEDIAS = 3;
     private static final int MATCH_ALBUMS = 4;
-    private static final int MATCH_MEDIA_INFO = 5;
+    private static final int MATCH_MEDIA_COLLECTION_INFO = 5;
     private static final int MATCH_SURFACE_CONTROLLER = 6;
 
     private static final boolean DEFAULT_LOOPING_PLAYBACK_ENABLED = true;
@@ -119,6 +124,9 @@
     private final UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     private volatile int mMediaStoreAuthorityAppId;
 
+    private final AsyncContentProviderWrapper mAsyncContentProviderWrapper =
+            new AsyncContentProviderWrapper();
+
     /**
      * Implementation is provided by the parent class. Cannot be overridden.
      */
@@ -134,27 +142,20 @@
         mMatcher.addURI(authority, URI_PATH_MEDIA_EXACT, MATCH_MEDIA_ID);
         mMatcher.addURI(authority, URI_PATH_DELETED_MEDIA, MATCH_DELETED_MEDIAS);
         mMatcher.addURI(authority, URI_PATH_ALBUM, MATCH_ALBUMS);
-        mMatcher.addURI(authority, URI_PATH_MEDIA_INFO, MATCH_MEDIA_INFO);
+        mMatcher.addURI(authority, URI_PATH_MEDIA_COLLECTION_INFO, MATCH_MEDIA_COLLECTION_INFO);
         mMatcher.addURI(authority, URI_PATH_SURFACE_CONTROLLER, MATCH_SURFACE_CONTROLLER);
     }
 
     /**
-     * Returns account related information for the media collection.
-     * <p>
-     * This is useful for the OS to populate a settings page with account information and allow
-     * users configure their media collection account.
+     * Returns {@link Bundle} containing binder to {@link IAsyncContentProvider}.
      *
-     * @param extras containing keys to filter result:
-     * <ul>
-     * <li> {@link CloudMediaProviderContract.AccountInfo#ACTIVE_ACCOUNT_NAME}
-     * <li> {@link CloudMediaProviderContract.AccountInfo#ACCOUNT_CONFIGURATION_INTENT}
-     * </ul>
-     *
-     * @return {@link Bundle} containing {@link CloudMediaProviderContract.AccountInfo}
+     * @hide
      */
     @NonNull
-    public Bundle onGetAccountInfo(@Nullable Bundle extras) {
-        throw new UnsupportedOperationException("getAccountInfo not supported");
+    public final Bundle onGetAsyncContentProvider() {
+        Bundle bundle = new Bundle();
+        bundle.putBinder(EXTRA_ASYNC_CONTENT_PROVIDER, mAsyncContentProviderWrapper.asBinder());
+        return bundle;
     }
 
     /**
@@ -174,11 +175,17 @@
      * <li> {@link CloudMediaProviderContract#EXTRA_FILTER_ALBUM}
      * </ul>
      *
-     * @return {@link Bundle} containing {@link CloudMediaProviderContract.MediaInfo}
+     * @return {@link Bundle} containing {@link CloudMediaProviderContract.MediaCollectionInfo}
+     * <ul>
+     * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#MEDIA_COLLECTION_ID}
+     * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION}
+     * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#ACCOUNT_NAME}
+     * <li> {@link CloudMediaProviderContract.MediaCollectionInfo#ACCOUNT_CONFIGURATION_INTENT}
+     * </ul>
      */
     @SuppressWarnings("unused")
     @NonNull
-    public abstract Bundle onGetMediaInfo(@Nullable Bundle extras);
+    public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);
 
     /**
      * Returns a {@link Cursor} to a single media item containing the columns representing the media
@@ -205,7 +212,7 @@
      *
      * @param extras containing keys to filter media items:
      * <ul>
-     * <li> {@link CloudMediaProviderContract#EXTRA_GENERATION}
+     * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
      * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
      * <li> {@link CloudMediaProviderContract#EXTRA_FILTER_ALBUM}
      * </ul>
@@ -214,12 +221,12 @@
      */
     @SuppressWarnings("unused")
     @NonNull
-    public abstract Cursor onQueryMedia(@Nullable Bundle extras);
+    public abstract Cursor onQueryMedia(@NonNull Bundle extras);
 
     /**
      * Returns a {@link Cursor} representing all deleted media items in the entire media collection
-     * within the current provider version as returned by {@link #onGetMediaInfo}. These items can
-     * be optionally filtered by {@code extras}.
+     * within the current provider version as returned by {@link #onGetMediaCollectionInfo}. These
+     * items can be optionally filtered by {@code extras}.
      * <p>
      * If the provider handled any filters in {@code extras}, it must add the key to
      * the {@link ContentResolver#EXTRA_HONORED_ARGS} as part of the returned
@@ -227,7 +234,7 @@
      *
      * @param extras containing keys to filter deleted media items:
      * <ul>
-     * <li> {@link CloudMediaProviderContract#EXTRA_GENERATION}
+     * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
      * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
      * </ul>
      * @return cursor representing deleted media items containing just the
@@ -235,7 +242,7 @@
      */
     @SuppressWarnings("unused")
     @NonNull
-    public abstract Cursor onQueryDeletedMedia(@Nullable Bundle extras);
+    public abstract Cursor onQueryDeletedMedia(@NonNull Bundle extras);
 
     /**
      * Returns a cursor representing all album items in the media collection optionally filtered
@@ -249,7 +256,7 @@
      *
      * @param extras containing keys to filter album items:
      * <ul>
-     * <li> {@link CloudMediaProviderContract#EXTRA_GENERATION}
+     * <li> {@link CloudMediaProviderContract#EXTRA_SYNC_GENERATION}
      * <li> {@link CloudMediaProviderContract#EXTRA_PAGE_TOKEN}
      * </ul>
      * @return cursor representing album items containing all
@@ -257,12 +264,12 @@
      */
     @SuppressWarnings("unused")
     @NonNull
-    public Cursor onQueryAlbums(@Nullable Bundle extras) {
+    public Cursor onQueryAlbums(@NonNull Bundle extras) {
         throw new UnsupportedOperationException("queryAlbums not supported");
     }
 
     /**
-     * Returns a preview of {@code size} for a media item identified by {@code mediaId}.
+     * Returns a thumbnail of {@code size} for a media item identified by {@code mediaId}.
      * <p>
      * This is expected to be a much lower resolution version than the item returned by
      * {@link #onOpenMedia}.
@@ -352,13 +359,13 @@
 
     private Bundle callUnchecked(String method, String arg, Bundle extras)
             throws FileNotFoundException {
-        if (METHOD_GET_MEDIA_INFO.equals(method)) {
-            return onGetMediaInfo(extras);
-        } else if (METHOD_GET_ACCOUNT_INFO.equals(method)) {
-            return onGetAccountInfo(extras);
+        if (METHOD_GET_MEDIA_COLLECTION_INFO.equals(method)) {
+            return onGetMediaCollectionInfo(extras);
         } else if (METHOD_CREATE_SURFACE_CONTROLLER.equals(method)) {
             return onCreateSurfaceController(extras);
-        }  else {
+        } else if (METHOD_GET_ASYNC_CONTENT_PROVIDER.equals(method)) {
+            return onGetAsyncContentProvider();
+        } else {
             throw new UnsupportedOperationException("Method not supported " + method);
         }
     }
@@ -823,4 +830,36 @@
             mSurfaceController.onDestroy();
         }
     }
+
+    /**
+     * @hide
+     */
+    private class AsyncContentProviderWrapper extends IAsyncContentProvider.Stub {
+
+        @Override
+        public void openMedia(String mediaId, RemoteCallback remoteCallback) {
+            try {
+                ParcelFileDescriptor pfd = onOpenMedia(mediaId,/* extras */
+                        null,/* cancellationSignal */ null);
+                sendResult(pfd, null, remoteCallback);
+            } catch (Exception e) {
+                sendResult(null, e, remoteCallback);
+            }
+        }
+
+        private void sendResult(ParcelFileDescriptor pfd, Throwable throwable,
+                RemoteCallback remoteCallback) {
+            Bundle bundle = new Bundle();
+            if (pfd == null && throwable == null) {
+                throw new IllegalStateException("Expected ParcelFileDescriptor or an exception.");
+            }
+            if (pfd != null) {
+                bundle.putParcelable(EXTRA_FILE_DESCRIPTOR, pfd);
+            }
+            if (throwable != null) {
+                bundle.putString(EXTRA_ERROR_MESSAGE, throwable.getMessage());
+            }
+            remoteCallback.sendResult(bundle);
+        }
+    }
 }
diff --git a/apex/framework/java/android/provider/CloudMediaProviderContract.java b/apex/framework/java/android/provider/CloudMediaProviderContract.java
index 3e0c3c9..d0d5a69 100644
--- a/apex/framework/java/android/provider/CloudMediaProviderContract.java
+++ b/apex/framework/java/android/provider/CloudMediaProviderContract.java
@@ -90,20 +90,22 @@
         public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
 
         /**
-         * Generation number associated with a media item.
+         * Number associated with a media item indicating what generation or batch the media item
+         * was synced into the media collection.
          * <p>
-         * Providers should associate a monotonically increasing generation number to each media
-         * item which is expected to increase for each atomic modification on the media item. This
-         * is useful for the OS to quickly identify that a media item has changed since a previous
-         * point in time. Note that this does not need to be unique across all media items, i.e.,
-         * multiple media items can have the same GENERATION_MODIFIED value. However, the
-         * modification of a media item should increase the {@link MediaInfo#MEDIA_GENERATION}.
+         * Providers should associate a monotonically increasing sync generation number to each
+         * media item which is expected to increase for each atomic modification on the media item.
+         * This is useful for the OS to quickly identify that a media item has changed since a
+         * previous point in time. Note that this does not need to be unique across all media items,
+         * i.e. multiple media items can have the same SYNC_GENERATION value. However, the
+         * modification of a media item should increase the
+         * {@link MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION}.
          * <p>
          * Type: LONG
          *
-         * @see MediaInfo#MEDIA_GENERATION
+         * @see MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION
          */
-        public static final String GENERATION_MODIFIED = "generation_modified";
+        public static final String SYNC_GENERATION = "sync_generation";
 
         /**
          * Concrete MIME type of a media file. For example, "image/png" or
@@ -216,7 +218,7 @@
          * Unique ID of an album. This ID is both provided by and interpreted
          * by a {@link CloudMediaProvider}.
          * <p>
-         * Each album item must have a unique ID within a provider and a given version.
+         * Each album item must have a unique ID within a media collection.
          * <p>
          * A provider should return durable IDs, since they will be used to cache
          * album information in the OS.
@@ -308,81 +310,64 @@
         public static final String TYPE_UNRELIABLE_VOLUME = "UNRELIABLE_VOLUME";
     }
 
-    /** Constants related to the entire media collection */
-    public static final class MediaInfo {
-        private MediaInfo() {}
+    /** Constants related to a media collection */
+    public static final class MediaCollectionInfo {
+        private MediaCollectionInfo() {}
 
         /**
-         * Media collection version identifier
+         * Media collection identifier
          * <p>
-         * The only requirement on the value of a version is uniqueness on a device, i.e. a
-         * a version should never be reused on a device.
+         * The only requirement on the collection ID is uniqueness on a device.
          * <p>
          * This value will not be interpreted by the OS, however it will be used to check the
-         * validity of cached data and URI grants to client apps. Anytime the media or album ids get
-         * re-indexed, the version should change so that the OS can clear its cache and more
-         * importantly, revoke any URI grants to apps.
+         * validity of cached data and URI grants to client apps. Anytime the media or album ids
+         * get re-indexed, a new collection with a new and unique id should be created so that the
+         * OS can clear its cache and more importantly, revoke any URI grants to apps.
          * <p>
-         * Apps are recommended to generate unique versions with, {@link UUID#randomUUID}. This is
-         * preferred to using a simple monotonic sequence because the provider data could get
-         * cleared and it might have to re-index media items on the device without any history of
-         * its last version. With random UUIDs, if data gets cleared, a new one can easily be
+         * Apps are recommended to generate unique collection ids with, {@link UUID#randomUUID}.
+         * This is preferred to using a simple monotonic sequence because the provider data could
+         * get cleared and it might have to re-index media items on the device without any history
+         * of its last ID. With random UUIDs, if data gets cleared, a new one can easily be
          * generated safely.
          * <p>
          * Type: STRING
          *
-         * @see CloudMediaProvider#onGetMediaInfo
+         * @see CloudMediaProvider#onGetMediaCollectionInfo
          */
-        public static final String MEDIA_VERSION = "media_version";
+        public static final String MEDIA_COLLECTION_ID = "media_collection_id";
 
         /**
-         * Maximum generation number of media items in the entire media collection.
+         * Last {@link CloudMediaProviderContract.MediaColumns#SYNC_GENERATION} in the media
+         * collection including deleted media items.
          * <p>
-         * Providers should associate a monotonically increasing generation number to each media
-         * item change (insertion/deletion/update). This is useful for the OS to quickly identify
-         * exactly which media items have changed since a previous point in time.
+         * Providers should associate a monotonically increasing sync generation to each
+         * media item change (insertion/deletion/update). This is useful for the OS to quickly
+         * identify exactly which media items have changed since a previous point in time.
          * <p>
          * Type: LONG
          *
-         * @see CloudMediaProviderContract#EXTRA_GENERATION
-         * @see CloudMediaProvider#onGetMediaInfo
-         * @see CloudMediaProviderContract.MediaColumns#GENERATION_MODIFIED
+         * @see CloudMediaProviderContract#EXTRA_SYNC_GENERATION
+         * @see CloudMediaProvider#onGetMediaCollectionInfo
+         * @see CloudMediaProviderContract.MediaColumns#SYNC_GENERATION
          */
-        public static final String MEDIA_GENERATION = "media_generation";
+        public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
 
         /**
-         * Total count of the media items in the entire media collection.
-         * <p>
-         * Along with the {@link #MEDIA_GENERATION} this helps the OS identify if there have been
-         * changes to media items in the media collection.
-         * <p>
-         * Type: LONG
-         *
-         * @see CloudMediaProvider#onGetMediaInfo
-         */
-        public static final String MEDIA_COUNT = "media_count";
-    }
-
-    /** Constants related to the account information */
-    public static final class AccountInfo {
-        private AccountInfo() {}
-
-        /**
-         * Name of the account owning the media collection synced from the cloud provider.
+         * Name of the account that owns the media collection.
          * <p>
          * Type: STRING
          *
-         * @see CloudMediaProvider#onGetAccountInfo
+         * @see CloudMediaProvider#onGetMediaCollectionInfo
          */
-        public static final String ACTIVE_ACCOUNT_NAME = "active_account_name";
+        public static final String ACCOUNT_NAME = "account_name";
 
         /**
          * {@link Intent} Intent to launch an {@link Activity} to allow users configure their media
-         * collection account information like the active account.
+         * collection account information like the account name.
          * <p>
          * Type: PARCELABLE
          *
-         * @see CloudMediaProvider#onGetAccountInfo
+         * @see CloudMediaProvider#onGetMediaCollectionInfo
          */
         public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
     }
@@ -411,43 +396,43 @@
      * Generation number to fetch the latest media or album metadata changes from the media
      * collection.
      * <p>
-     * The provider should associate a monotonically increasing generation number to each media item
-     * change (insertion/deletion/update). This is useful to quickly identify exactly which media
-     * items have changed since a previous point in time.
+     * The provider should associate a monotonically increasing sync generation to each media
+     * item change (insertion/deletion/update). This is useful to quickly identify exactly which
+     * media items have changed since a previous point in time.
      * <p>
-     * Providers should associate a separate monotonically increasing generation number for album
-     * item changes (insertion/deletion/update). Unlike the media generation number, the album
-     * generation number should also record insertions and deletions to media items within the
-     * album. E.g., a direct change to an albums
+     * Providers should also associate a separate monotonically increasing sync generation
+     * for album changes (insertion/deletion/update). This album sync generation, should record
+     * both changes to the album metadata itself and changes to the media items contained in the
+     * album. E.g. a direct change to an album's
      * {@link CloudMediaProviderContract.AlbumColumns#DISPLAY_NAME} will increase the
-     * album generation number, likewise adding a photo to that album.
+     * album sync generation, likewise adding a photo to that album should also increase the
+     * sync generation.
      * <p>
-     * Note that multiple media (or album) items can share a generation number as long as the entire
+     * Note that multiple media (or album) items can share a sync generation as long as the entire
      * change appears atomic from the perspective of the query APIs. E.g. each item in a batch photo
-     * sync from the cloud can have the same generation number if they all occurred within the same
-     * database transaction and hence guarantee that a db query result either has all they synced
-     * items or none.
+     * sync from the cloud can have the same sync generation if they were all synced atomically into
+     * the collection from the perspective of an external observer.
      * <p>
      * This extra can be passed as a {@link Bundle} parameter to the media or album query methods
-     * and the provider should only return items with a generation number that are strictly greater
-     * than the filter.
+     * and the provider should only return items with a sync generation that is strictly greater
+     * than the one provided in the filter.
      * <p>
      * If the provider supports this filter, it must support the respective
-     * {@link CloudMediaProvider#onGetMediaInfo} methods to return the {@code count} and
+     * {@link CloudMediaProvider#onGetMediaCollectionInfo} methods to return the {@code count} and
      * {@code max generation} for media or albums.
      * <p>
      * If the provider handled the generation, they must add the
-     * {@link #EXTRA_GENERATION} key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS}
+     * {@link #EXTRA_SYNC_GENERATION} key to the array of {@link ContentResolver#EXTRA_HONORED_ARGS}
      * as part of the returned {@link Cursor#setExtras} {@link Bundle}.
      *
-     * @see MediaInfo#MEDIA_GENERATION
+     * @see MediaCollectionInfo#LAST_MEDIA_SYNC_GENERATION
      * @see CloudMediaProvider#onQueryMedia
      * @see CloudMediaProvider#onQueryAlbums
      * @see MediaStore.MediaColumns#GENERATION_MODIFIED
      * <p>
      * Type: LONG
      */
-    public static final String EXTRA_GENERATION = "android.provider.extra.GENERATION";
+    public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
 
     /**
      * Limits the query results to only media items matching the given album id.
@@ -500,20 +485,12 @@
             "android.provider.extra.PREVIEW_THUMBNAIL";
 
     /**
-     * Constant used to execute {@link CloudMediaProvider#onGetMediaInfo} via
+     * Constant used to execute {@link CloudMediaProvider#onGetMediaCollectionInfo} via
      * {@link ContentProvider#call}.
      *
      * {@hide}
      */
-    public static final String METHOD_GET_MEDIA_INFO = "android:getMediaInfo";
-
-    /**
-     * Constant used to execute {@link CloudMediaProvider#onGetAccountInfo} via
-     * {@link ContentProvider#call}.
-     *
-     * {@hide}
-     */
-    public static final String METHOD_GET_ACCOUNT_INFO = "android:getAccountInfo";
+    public static final String METHOD_GET_MEDIA_COLLECTION_INFO = "android:getMediaCollectionInfo";
 
     /**
      * Constant used to execute {@link CloudMediaProvider#onCreateSurfaceController} via
@@ -568,6 +545,37 @@
             "android.provider.extra.SURFACE_EVENT_CALLBACK";
 
     /**
+     * Constant used to execute {@link CloudMediaProvider#onGetAsyncContentProvider()} via
+     * {@link android.content.ContentProvider#call}.
+     *
+     * {@hide}
+     */
+    public static final String METHOD_GET_ASYNC_CONTENT_PROVIDER =
+            "android:getAsyncContentProvider";
+
+    /**
+     * Constant used to get/set {@link IAsyncContentProvider} in {@link Bundle}.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_ASYNC_CONTENT_PROVIDER =
+            "android.provider.extra.ASYNC_CONTENT_PROVIDER";
+
+    /**
+     * Constant used to get/set {@link android.os.ParcelFileDescriptor} in {@link Bundle}.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_FILE_DESCRIPTOR = "android.provider.extra.file_descriptor";
+
+    /**
+     * Constant used to get/set CMP exception message in {@link Bundle}.
+     *
+     * {@hide}
+     */
+    public static final String EXTRA_ERROR_MESSAGE = "android.provider.extra.error_message";
+
+    /**
      * URI path for {@link CloudMediaProvider#onQueryMedia}
      *
      * {@hide}
@@ -596,18 +604,11 @@
     public static final String URI_PATH_ALBUM = "album";
 
     /**
-     * URI path for {@link CloudMediaProvider#onGetMediaInfo}
+     * URI path for {@link CloudMediaProvider#onGetMediaCollectionInfo}
      *
      * {@hide}
      */
-    public static final String URI_PATH_MEDIA_INFO = "media_info";
-
-    /**
-     * URI path for {@link CloudMediaProvider#onGetAccountInfo}
-     *
-     * {@hide}
-     */
-    public static final String URI_PATH_ACCOUNT_INFO = "account_info";
+    public static final String URI_PATH_MEDIA_COLLECTION_INFO = "media_collection_info";
 
     /**
      * URI path for {@link CloudMediaProvider#onCreateSurfaceController}
diff --git a/apex/framework/java/android/provider/IAsyncContentProvider.aidl b/apex/framework/java/android/provider/IAsyncContentProvider.aidl
new file mode 100644
index 0000000..3e8340f
--- /dev/null
+++ b/apex/framework/java/android/provider/IAsyncContentProvider.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 android.provider;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IAsyncContentProvider {
+
+    void openMedia(String mediaId, in RemoteCallback callback);
+}
\ No newline at end of file
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 856684a..f9357d0 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -702,6 +702,11 @@
      * Unlike other MediaStore URIs, these are referred to as 'picker' URIs and
      * expose a limited set of read-only operations. Specifically, picker URIs
      * can only be opened for read and queried for columns in {@link PickerMediaColumns}.
+     * <p>
+     * Before this API, apps could use {@link Intent#ACTION_GET_CONTENT}. However, this
+     * new action is recommended for images and videos use-cases, since it ofers a
+     * better user experience.
+     *
      * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index b7aab10..9739f71 100755
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -1380,7 +1380,8 @@
     fuse_reply_open(req, fi);
 }
 
-static void do_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info* fi) {
+static void do_read(fuse_req_t req, size_t size, off_t off, struct fuse_file_info* fi,
+                    bool direct_io) {
     handle* h = reinterpret_cast<handle*>(fi->fh);
     struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size);
 
@@ -1388,8 +1389,13 @@
     buf.buf[0].pos = off;
     buf.buf[0].flags =
             (enum fuse_buf_flags) (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK);
-
-    fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags) 0);
+    if (direct_io) {
+        // sdcardfs does not register splice_read_file_operations and some requests fail with EFAULT
+        // Specifically, FUSE splice is only enabled for 8KB+ buffers, hence such reads fail
+        fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags)FUSE_BUF_NO_SPLICE);
+    } else {
+        fuse_reply_data(req, &buf, (enum fuse_buf_copy_flags)0);
+    }
 }
 
 /**
@@ -1416,7 +1422,8 @@
     buf->mem = nullptr;
 }
 
-static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi) {
+static void do_read_with_redaction(fuse_req_t req, size_t size, off_t off, fuse_file_info* fi,
+                                   bool direct_io) {
     handle* h = reinterpret_cast<handle*>(fi->fh);
 
     std::vector<ReadRange> ranges;
@@ -1424,7 +1431,7 @@
 
     // As an optimization, return early if there are no ranges to redact.
     if (ranges.size() == 0) {
-        do_read(req, size, off, fi);
+        do_read(req, size, off, fi, direct_io);
         return;
     }
 
@@ -1456,6 +1463,7 @@
                     struct fuse_file_info* fi) {
     ATRACE_CALL();
     handle* h = reinterpret_cast<handle*>(fi->fh);
+    const bool direct_io = !h->cached;
     struct fuse* fuse = get_fuse(req);
 
     node* node = fuse->FromInode(ino);
@@ -1473,9 +1481,9 @@
     fuse->fadviser.Record(h->fd, size);
 
     if (h->ri->isRedactionNeeded()) {
-        do_read_with_redaction(req, size, off, fi);
+        do_read_with_redaction(req, size, off, fi, direct_io);
     } else {
-        do_read(req, size, off, fi);
+        do_read(req, size, off, fi, direct_io);
     }
 }
 
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 3d26564..4e73f4a 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -68,7 +68,7 @@
     <string name="permission_progress_write_audio" msgid="6029375427984180097">"{count,plural, =1{Audio fayl oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻzgartirilmoqda…}}"</string>
     <string name="permission_write_video" msgid="103902551603700525">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_write_video" msgid="7014908418349819148">"{count,plural, =1{Video oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video oʻzgartirilmoqda…}}"</string>
-    <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
+    <string name="permission_write_image" msgid="3518991791620523786">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻzgartirishga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻzgartirishga ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_write_image" msgid="3623580315590025262">"{count,plural, =1{Rasm oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm oʻzgartirilmoqda…}}"</string>
     <string name="permission_write_generic" msgid="7431128739233656991">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻzgartirishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻzgartirishi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_write_generic" msgid="2806560971318391443">"{count,plural, =1{Element oʻzgartirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element oʻzgartirilmoqda…}}"</string>
@@ -76,7 +76,7 @@
     <string name="permission_progress_trash_audio" msgid="3116279868733641329">"{count,plural, =1{Audio fayl chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdonga tashlanmoqda…}}"</string>
     <string name="permission_trash_video" msgid="7555850843259959642">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_trash_video" msgid="4637821778329459681">"{count,plural, =1{Video chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video chiqitdonga tashlanmoqda…}}"</string>
-    <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
+    <string name="permission_trash_image" msgid="3333128084684156675">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdonga tashlashga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdonga tashlashga ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_trash_image" msgid="3063857679090024764">"{count,plural, =1{Rasm chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdonga tashlanmoqda…}}"</string>
     <string name="permission_trash_generic" msgid="5545420534785075362">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdonga tashlashi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_trash_generic" msgid="7815124979717814057">"{count,plural, =1{Element chiqitdonga tashlanmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element chiqitdonga tashlanmoqda…}}"</string>
@@ -84,7 +84,7 @@
     <string name="permission_progress_untrash_audio" msgid="2775372344946464508">"{count,plural, =1{Audio fayl chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl chiqitdondan chiqarilmoqda…}}"</string>
     <string name="permission_untrash_video" msgid="3178914827607608162">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_untrash_video" msgid="5500929409733841567">"{count,plural, =1{Video chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video chiqitdondan chiqarilmoqda…}}"</string>
-    <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
+    <string name="permission_untrash_image" msgid="3397523279351032265">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni chiqitdondan qayta tiklashga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni chiqitdondan qayta tiklashga ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_untrash_image" msgid="5295061520504846264">"{count,plural, =1{Rasm chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm chiqitdondan chiqarilmoqda…}}"</string>
     <string name="permission_untrash_generic" msgid="2118366929431671046">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni chiqitdondan chiqarib olishi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_untrash_generic" msgid="1489511601966842579">"{count,plural, =1{Element chiqitdondan chiqarilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element chiqitdondan chiqarilmoqda…}}"</string>
@@ -92,7 +92,7 @@
     <string name="permission_progress_delete_audio" msgid="1734871539021696401">"{count,plural, =1{Audio fayl oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta audio fayl oʻchirilmoqda…}}"</string>
     <string name="permission_delete_video" msgid="604024971828349279">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu videoni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta videoni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_delete_video" msgid="1846702435073793157">"{count,plural, =1{Video oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta video oʻchirilmoqda…}}"</string>
-    <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu suratni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
+    <string name="permission_delete_image" msgid="3109056012794330510">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> uchun bu suratni oʻchirishga ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> uchun <xliff:g id="COUNT">^2</xliff:g> ta suratni oʻchirishga ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_delete_image" msgid="8580517204901148906">"{count,plural, =1{Rasm oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta rasm oʻchirilmoqda…}}"</string>
     <string name="permission_delete_generic" msgid="7891939881065520271">"{count,plural, =1{<xliff:g id="APP_NAME_0">^1</xliff:g> ilovasiga bu elementni oʻchirib tashlashi uchun ruxsat berilsinmi?}other{<xliff:g id="APP_NAME_1">^1</xliff:g> ilovasiga <xliff:g id="COUNT">^2</xliff:g> ta elementni oʻchirib tashlashi uchun ruxsat berilsinmi?}}"</string>
     <string name="permission_progress_delete_generic" msgid="6709118146245087898">"{count,plural, =1{Element oʻchirilmoqda…}other{<xliff:g id="COUNT">^1</xliff:g> ta element oʻchirilmoqda…}}"</string>
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 7c97208..777b6f2 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -25,6 +25,8 @@
 import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.database.Cursor.FIELD_TYPE_BLOB;
+import static android.provider.CloudMediaProviderContract.EXTRA_ASYNC_CONTENT_PROVIDER;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ASYNC_CONTENT_PROVIDER;
 import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE;
 import static android.provider.MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE;
 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT;
@@ -171,12 +173,14 @@
 import android.os.storage.StorageManager.StorageVolumeCallback;
 import android.os.storage.StorageVolume;
 import android.preference.PreferenceManager;
+import android.provider.AsyncContentProvider;
 import android.provider.BaseColumns;
 import android.provider.Column;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.OnPropertiesChangedListener;
 import android.provider.DocumentsContract;
 import android.provider.ExportedSince;
+import android.provider.IAsyncContentProvider;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.Audio.AudioColumns;
@@ -268,7 +272,9 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 import java.util.function.UnaryOperator;
@@ -1749,7 +1755,7 @@
                 extractSyntheticRelativePathSegements(path, userId);
         final int segmentCount = syntheticRelativePathSegments.size();
 
-        if (segmentCount < 1 || segmentCount > 4) {
+        if (segmentCount < 1 || segmentCount > 5) {
             throw new IllegalStateException("Unexpected synthetic picker path: " + file);
         }
 
@@ -1764,19 +1770,30 @@
                 }
                 break;
             case 2:
-                // .../picker/<authority>
-                result = preparePickerAuthorityPathSegment(file, lastSegment, uid);
+                // .../picker/<user-id>
+                try {
+                    Integer.parseInt(lastSegment);
+                    result = file.exists() || file.mkdir();
+                } catch (NumberFormatException e) {
+                    Log.w(TAG, "Invalid user id for picker file lookup: " + lastSegment
+                            + ". File: " + file);
+                }
                 break;
             case 3:
-                // .../picker/<authority>/media
+                // .../picker/<user-id>/<authority>
+                result = preparePickerAuthorityPathSegment(file, lastSegment, uid);
+                break;
+            case 4:
+                // .../picker/<user-id>/<authority>/media
                 if (lastSegment.equals("media")) {
                     result = file.exists() || file.mkdir();
                 }
                 break;
-            case 4:
-                // .../picker/<authority>/media/<media-id.extension>
-                final String authority = syntheticRelativePathSegments.get(1);
-                result = preparePickerMediaIdPathSegment(file, authority, lastSegment);
+            case 5:
+                // .../picker/<user-id>/<authority>/media/<media-id.extension>
+                final String fileUserId = syntheticRelativePathSegments.get(1);
+                final String authority = syntheticRelativePathSegments.get(2);
+                result = preparePickerMediaIdPathSegment(file, authority, lastSegment, fileUserId);
                 break;
         }
 
@@ -1788,17 +1805,17 @@
 
     private FileOpenResult handlePickerFileOpen(String path, int uid) {
         final String[] segments = path.split("/");
-        if (segments.length != 10) {
+        if (segments.length != 11) {
             Log.e(TAG, "Picker file open failed. Unexpected segments: " + path);
             return new FileOpenResult(OsConstants.ENOENT /* status */, uid, /* transformsUid */ 0,
                     new long[0]);
         }
 
-        // ['', 'storage', 'emulated', '0', 'transforms', 'synthetic', 'picker', '<host>',
-        // 'media', '<fileName>']
-        final String userId = segments[3];
-        final String fileName = segments[9];
-        final String host = segments[7];
+        // ['', '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];
         final String authority = userId + "@" + host;
         final int lastDotIndex = fileName.lastIndexOf('.');
 
@@ -1810,11 +1827,21 @@
         final String mediaId = fileName.substring(0, lastDotIndex);
         final Uri uri = getMediaUri(authority).buildUpon().appendPath(mediaId).build();
 
+        IBinder binder = getContext().getContentResolver()
+                .call(uri, METHOD_GET_ASYNC_CONTENT_PROVIDER, null, null)
+                .getBinder(EXTRA_ASYNC_CONTENT_PROVIDER);
+        if (binder == null) {
+            Log.e(TAG, "Picker file open failed. No cloud media provider found.");
+            return FileOpenResult.createError(OsConstants.ENOENT, uid);
+        }
+        IAsyncContentProvider iAsyncContentProvider = IAsyncContentProvider.Stub.asInterface(
+                binder);
+        AsyncContentProvider asyncContentProvider = new AsyncContentProvider(iAsyncContentProvider);
         final ParcelFileDescriptor pfd;
         try {
-            pfd = getContext().getContentResolver().openFile(uri, "r",
-                    /* cancellationSignal */ null);
-        } catch (IOException e) {
+            pfd = asyncContentProvider.openMedia(uri, "r");
+        } catch (FileNotFoundException | ExecutionException | InterruptedException
+                | TimeoutException | RemoteException e) {
             Log.e(TAG, "Picker file open failed. Failed to open URI: " + uri, e);
             return FileOpenResult.createError(OsConstants.ENOENT, uid);
         }
@@ -1832,17 +1859,21 @@
 
     private boolean preparePickerAuthorityPathSegment(File file, String authority, int uid) {
         if (mPickerSyncController.isProviderEnabled(authority)) {
-            return file.mkdir();
+            return file.exists() || file.mkdir();
         }
 
         return false;
     }
 
-    private boolean preparePickerMediaIdPathSegment(File file, String authority, String fileName) {
+    private boolean preparePickerMediaIdPathSegment(File file, String authority, String fileName,
+            String userId) {
         final String mediaId = extractFileName(fileName);
+        final String[] projection = new String[] { MediaStore.PickerMediaColumns.SIZE };
 
-        try (Cursor cursor = mPickerDbFacade.queryMediaIdForApps(authority, mediaId,
-                        new String[] { MediaStore.PickerMediaColumns.SIZE })) {
+        final Uri uri = Uri.parse("content://media/picker/" + userId + "/" + authority + "/media/"
+                + mediaId);
+        try (Cursor cursor =  mPickerUriResolver.query(uri, projection, /* queryArgs */ null,
+                        /* signal */ null, 0, android.os.Process.myUid())) {
             if (cursor != null && cursor.moveToFirst()) {
                 final int sizeBytesIdx = cursor.getColumnIndex(MediaStore.PickerMediaColumns.SIZE);
 
@@ -9598,12 +9629,15 @@
         final boolean allowHidden = isCallingPackageAllowedHidden();
         final int table = matchUri(uri, allowHidden);
 
+        final String selection = extras.getString(QUERY_ARG_SQL_SELECTION);
+        final String[] selectionArgs = extras.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS);
+
         // First, check to see if caller has direct write access
         if (forWrite) {
             final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, table, uri, extras, null);
             qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
             try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
-                    null, null, null, null, null, null, null)) {
+                    selection, selectionArgs, null, null, null, null, null)) {
                 if (c.moveToFirst()) {
                     // Direct write access granted, yay!
                     return;
@@ -9627,7 +9661,7 @@
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, extras, null);
         qb.allowColumn(SQLiteQueryBuilder.ROWID_COLUMN);
         try (Cursor c = qb.query(helper, new String[] { SQLiteQueryBuilder.ROWID_COLUMN },
-                null, null, null, null, null, null, null)) {
+                selection, selectionArgs, null, null, null, null, null)) {
             if (c.moveToFirst()) {
                 if (!forWrite) {
                     // Direct read access granted, yay!
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
index bd251c8..14e1e21 100644
--- a/src/com/android/providers/media/PickerUriResolver.java
+++ b/src/com/android/providers/media/PickerUriResolver.java
@@ -221,14 +221,9 @@
                 + CloudMediaProviderContract.URI_PATH_DELETED_MEDIA);
     }
 
-    public static Uri getMediaInfoUri(String authority) {
+    public static Uri getMediaCollectionInfoUri(String authority) {
         return Uri.parse("content://" + authority + "/"
-                + CloudMediaProviderContract.URI_PATH_MEDIA_INFO);
-    }
-
-    public static Uri getAccountInfoUri(String authority) {
-        return Uri.parse("content://" + authority + "/"
-                + CloudMediaProviderContract.URI_PATH_ACCOUNT_INFO);
+                + CloudMediaProviderContract.URI_PATH_MEDIA_COLLECTION_INFO);
     }
 
     public static Uri getAlbumUri(String authority) {
@@ -255,6 +250,9 @@
         try (Cursor cursor = queryPickerUri(uri, projection)) {
             if (cursor != null && cursor.getCount() == 1 && cursor.moveToFirst()) {
                 String path = getCursorString(cursor, MediaStore.PickerMediaColumns.DATA);
+                // First replace /sdcard with /storage/emulated path
+                path = path.replaceFirst("/sdcard", "/storage/emulated/" + MediaStore.MY_USER_ID);
+                // Then convert /storage/emulated patht to /mnt/user/ path
                 return toFuseFile(new File(path));
             }
         }
diff --git a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
index 820161b..653f732 100644
--- a/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
+++ b/src/com/android/providers/media/photopicker/PhotoPickerProvider.java
@@ -18,7 +18,7 @@
 
 import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
 import static android.provider.CloudMediaProvider.SurfaceEventCallback.PLAYBACK_EVENT_READY;
-import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 
 import android.annotation.DurationMillisLong;
 import android.content.ContentProviderClient;
@@ -141,20 +141,21 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(@Nullable Bundle extras) {
+    public Bundle onGetMediaCollectionInfo(@Nullable Bundle extras) {
         final CloudProviderQueryExtras queryExtras =
                 CloudProviderQueryExtras.fromCloudMediaBundle(extras);
 
         // TODO(b/190713331): Handle extra_filter_albums
         Bundle bundle = new Bundle();
-        try (Cursor cursor = mDbFacade.getMediaInfo(queryExtras.getGeneration())) {
+        try (Cursor cursor = mDbFacade.getMediaCollectionInfo(queryExtras.getGeneration())) {
             if (cursor.moveToFirst()) {
-                int generationIndex = cursor.getColumnIndexOrThrow(MediaInfo.MEDIA_GENERATION);
-                int countIndex = cursor.getColumnIndexOrThrow(MediaInfo.MEDIA_COUNT);
+                int generationIndex = cursor.getColumnIndexOrThrow(
+                        MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-                bundle.putString(MediaInfo.MEDIA_VERSION, MediaStore.getVersion(getContext()));
-                bundle.putLong(MediaInfo.MEDIA_GENERATION, cursor.getLong(generationIndex));
-                bundle.putLong(MediaInfo.MEDIA_COUNT, cursor.getLong(countIndex));
+                bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID,
+                        MediaStore.getVersion(getContext()));
+                bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION,
+                        cursor.getLong(generationIndex));
             }
         }
         return bundle;
diff --git a/src/com/android/providers/media/photopicker/PickerDataLayer.java b/src/com/android/providers/media/photopicker/PickerDataLayer.java
index b51bf88..98f6d08 100644
--- a/src/com/android/providers/media/photopicker/PickerDataLayer.java
+++ b/src/com/android/providers/media/photopicker/PickerDataLayer.java
@@ -16,15 +16,14 @@
 
 package com.android.providers.media.photopicker;
 
-import static android.provider.CloudMediaProviderContract.EXTRA_GENERATION;
-import static android.provider.CloudMediaProviderContract.METHOD_GET_ACCOUNT_INFO;
+import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO;
 import static android.provider.CloudMediaProviderContract.MediaColumns;
-import static android.provider.CloudMediaProviderContract.MediaInfo;
-import static com.android.providers.media.PickerUriResolver.getAccountInfoUri;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.PickerUriResolver.getAlbumUri;
 import static com.android.providers.media.PickerUriResolver.getMediaUri;
 import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
-import static com.android.providers.media.PickerUriResolver.getMediaInfoUri;
+import static com.android.providers.media.PickerUriResolver.getMediaCollectionInfoUri;
 import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LIMIT_DEFAULT;
 import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
 import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
@@ -130,12 +129,12 @@
 
         try {
             final Bundle accountBundle = mContext.getContentResolver().call(
-                    getAccountInfoUri(cloudProvider), METHOD_GET_ACCOUNT_INFO, /* arg */ null,
-                    /* extras */ null);
+                    getMediaCollectionInfoUri(cloudProvider), METHOD_GET_MEDIA_COLLECTION_INFO,
+                    /* arg */ null, /* extras */ null);
             final String accountName = accountBundle.getString(
-                    CloudMediaProviderContract.AccountInfo.ACTIVE_ACCOUNT_NAME);
+                    CloudMediaProviderContract.MediaCollectionInfo.ACCOUNT_NAME);
             final Intent configIntent = (Intent) accountBundle.getParcelable(
-                    CloudMediaProviderContract.AccountInfo.ACCOUNT_CONFIGURATION_INTENT);
+                    CloudMediaProviderContract.MediaCollectionInfo.ACCOUNT_CONFIGURATION_INTENT);
 
             if (accountName == null) {
                 return null;
diff --git a/src/com/android/providers/media/photopicker/PickerSyncController.java b/src/com/android/providers/media/photopicker/PickerSyncController.java
index f6a6f28..74b9a03 100644
--- a/src/com/android/providers/media/photopicker/PickerSyncController.java
+++ b/src/com/android/providers/media/photopicker/PickerSyncController.java
@@ -16,12 +16,12 @@
 
 package com.android.providers.media.photopicker;
 
-import static android.provider.CloudMediaProviderContract.EXTRA_GENERATION;
+import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
 import static android.provider.CloudMediaProviderContract.EXTRA_PAGE_TOKEN;
-import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.PickerUriResolver.getMediaUri;
 import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
-import static com.android.providers.media.PickerUriResolver.getMediaInfoUri;
+import static com.android.providers.media.PickerUriResolver.getMediaCollectionInfoUri;
 
 import android.annotation.IntDef;
 import android.content.Context;
@@ -69,7 +69,8 @@
 
     private static final String DEFAULT_CLOUD_PROVIDER_PKG = null;
     private static final int DEFAULT_CLOUD_PROVIDER_UID = -1;
-    private static final long DEFAULT_SYNC_DELAY_MS = 1000;
+    private static final long DEFAULT_SYNC_DELAY_MS =
+            PickerDbFacade.getDefaultPickerDbSyncDelayMs();
 
     private static final int SYNC_TYPE_NONE = 0;
     private static final int SYNC_TYPE_INCREMENTAL = 1;
@@ -202,7 +203,7 @@
         if (authority == null || !newProviderInfo.isEmpty()) {
             synchronized (mLock) {
                 setCloudProviderInfo(newProviderInfo);
-                resetCachedMediaInfo(newProviderInfo.authority);
+                resetCachedMediaCollectionInfo(newProviderInfo.authority);
 
                 // Disable cloud provider queries on the db until next sync
                 // This will temporarily *clear* the cloud provider on the db facade and prevent
@@ -276,30 +277,30 @@
 
         switch (params.syncType) {
             case SYNC_TYPE_RESET:
-                // Odd! Can only happen if provider gave us unexpected MediaInfo
+                // Odd! Can only happen if provider gave us unexpected MediaCollectionInfo
                 // We reset the cloud media in the picker db
                 executeSyncReset(authority);
 
-                // And clear our cached MediaInfo, so that whenever the provider recovers,
+                // And clear our cached MediaCollectionInfo, so that whenever the provider recovers,
                 // we force a full sync
-                resetCachedMediaInfo(authority);
+                resetCachedMediaCollectionInfo(authority);
                 return;
             case SYNC_TYPE_FULL:
                 executeSyncReset(authority);
                 executeSyncAdd(authority, new Bundle() /* queryArgs */);
 
                 // Commit sync position
-                cacheMediaInfo(authority, params.latestMediaInfo);
+                cacheMediaCollectionInfo(authority, params.latestMediaCollectionInfo);
                 return;
             case SYNC_TYPE_INCREMENTAL:
                 final Bundle queryArgs = new Bundle();
-                queryArgs.putLong(EXTRA_GENERATION, params.syncGeneration);
+                queryArgs.putLong(EXTRA_SYNC_GENERATION, params.syncGeneration);
 
                 executeSyncAdd(authority, queryArgs);
                 executeSyncRemove(authority, queryArgs);
 
                 // Commit sync position
-                cacheMediaInfo(authority, params.latestMediaInfo);
+                cacheMediaCollectionInfo(authority, params.latestMediaCollectionInfo);
                 return;
             case SYNC_TYPE_NONE:
                 return;
@@ -358,10 +359,10 @@
             editor.putInt(PREFS_KEY_CLOUD_PROVIDER_UID, info.uid);
         }
 
-        editor.commit();
+        editor.apply();
     }
 
-    private void cacheMediaInfo(String authority, Bundle bundle) {
+    private void cacheMediaCollectionInfo(String authority, Bundle bundle) {
         if (authority == null) {
             Log.d(TAG, "Ignoring cache media info for null authority with bundle: " + bundle);
             return;
@@ -370,47 +371,46 @@
         final SharedPreferences.Editor editor = mSyncPrefs.edit();
 
         if (bundle == null) {
-            editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_VERSION));
-            editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_GENERATION));
-            editor.remove(getPrefsKey(authority, MediaInfo.MEDIA_COUNT));
+            editor.remove(getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID));
+            editor.remove(getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION));
         } else {
-            final String version = bundle.getString(MediaInfo.MEDIA_VERSION);
-            final long generation = bundle.getLong(MediaInfo.MEDIA_GENERATION);
-            final long count = bundle.getLong(MediaInfo.MEDIA_COUNT);
+            final String collectionId = bundle.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+            final long generation = bundle.getLong(
+                    MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-            editor.putString(getPrefsKey(authority, MediaInfo.MEDIA_VERSION), version);
-            editor.putLong(getPrefsKey(authority, MediaInfo.MEDIA_GENERATION), generation);
-            editor.putLong(getPrefsKey(authority, MediaInfo.MEDIA_COUNT), count);
+            editor.putString(getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID),
+                    collectionId);
+            editor.putLong(getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION),
+                    generation);
         }
 
-        editor.commit();
+        editor.apply();
     }
 
-    private void resetCachedMediaInfo(String authority) {
-        cacheMediaInfo(authority, /* bundle */ null);
+    private void resetCachedMediaCollectionInfo(String authority) {
+        cacheMediaCollectionInfo(authority, /* bundle */ null);
     }
 
-    private Bundle getCachedMediaInfo(String authority) {
+    private Bundle getCachedMediaCollectionInfo(String authority) {
         final Bundle bundle = new Bundle();
 
-        final String version = mSyncPrefs.getString(getPrefsKey(authority, MediaInfo.MEDIA_VERSION),
+        final String collectionId = mSyncPrefs.getString(
+                getPrefsKey(authority, MediaCollectionInfo.MEDIA_COLLECTION_ID),
                 /* default */ null);
         final long generation = mSyncPrefs.getLong(
-                getPrefsKey(authority, MediaInfo.MEDIA_GENERATION), /* default */ -1);
-        final long count = mSyncPrefs.getLong(getPrefsKey(authority, MediaInfo.MEDIA_COUNT),
+                getPrefsKey(authority, MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION),
                 /* default */ -1);
 
-        bundle.putString(MediaInfo.MEDIA_VERSION, version);
-        bundle.putLong(MediaInfo.MEDIA_GENERATION, generation);
-        bundle.putLong(MediaInfo.MEDIA_COUNT, count);
+        bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID, collectionId);
+        bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION, generation);
 
         return bundle;
     }
 
-    private Bundle getLatestMediaInfo(String authority) {
+    private Bundle getLatestMediaCollectionInfo(String authority) {
         try {
-            return mContext.getContentResolver().call(getMediaInfoUri(authority),
-                    CloudMediaProviderContract.METHOD_GET_MEDIA_INFO, /* arg */ null,
+            return mContext.getContentResolver().call(getMediaCollectionInfoUri(authority),
+                    CloudMediaProviderContract.METHOD_GET_MEDIA_COLLECTION_INFO, /* arg */ null,
                     /* extras */ null);
         } catch (Exception e) {
             Log.w(TAG, "Failed to fetch latest media info from authority: " + authority, e);
@@ -426,40 +426,44 @@
             return SyncRequestParams.forReset();
         }
 
-        final Bundle cachedMediaInfo = getCachedMediaInfo(authority);
-        final Bundle latestMediaInfo = getLatestMediaInfo(authority);
+        final Bundle cachedMediaCollectionInfo = getCachedMediaCollectionInfo(authority);
+        final Bundle latestMediaCollectionInfo = getLatestMediaCollectionInfo(authority);
 
-        final String latestVersion = latestMediaInfo.getString(MediaInfo.MEDIA_VERSION);
-        final long latestGeneration = latestMediaInfo.getLong(MediaInfo.MEDIA_GENERATION);
-        final long latestCount = latestMediaInfo.getLong(MediaInfo.MEDIA_COUNT);
+        final String latestCollectionId =
+                latestMediaCollectionInfo.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+        final long latestGeneration =
+                latestMediaCollectionInfo.getLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-        final String cachedVersion = cachedMediaInfo.getString(MediaInfo.MEDIA_VERSION);
-        final long cachedGeneration = cachedMediaInfo.getLong(MediaInfo.MEDIA_GENERATION);
-        final long cachedCount = cachedMediaInfo.getLong(MediaInfo.MEDIA_COUNT);
+        final String cachedCollectionId =
+                cachedMediaCollectionInfo.getString(MediaCollectionInfo.MEDIA_COLLECTION_ID);
+        final long cachedGeneration = cachedMediaCollectionInfo.getLong(
+                MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-        Log.d(TAG, "Fetching SyncRequestParams. Authority: " + authority + ". LatestMediaInfo: "
-                + latestMediaInfo + ". CachedMediaInfo: " + cachedMediaInfo);
+        Log.d(TAG, "Fetching SyncRequestParams. Authority: " + authority
+                + ". LatestMediaCollectionInfo: " + latestMediaCollectionInfo
+                + ". CachedMediaCollectionInfo: " + cachedMediaCollectionInfo);
 
-        if (TextUtils.isEmpty(latestVersion) || latestGeneration < 0 || latestCount < 0) {
-            // If results from |latestMediaInfo| are unexpected, we reset the cloud provider
+        if (TextUtils.isEmpty(latestCollectionId) || latestGeneration < 0) {
+            // If results from |latestMediaCollectionInfo| are unexpected, we reset the
+            // cloud provider
             Log.w(TAG, "SyncRequestParams. Authority: " + authority
-                    + ". Result: SYNC_TYPE_RESET. Unexpected results: " + latestMediaInfo);
+                    + ". Result: SYNC_TYPE_RESET. Unexpected result: " + latestMediaCollectionInfo);
             return SyncRequestParams.forReset();
         }
 
-        if (!Objects.equals(latestVersion, cachedVersion)) {
+        if (!Objects.equals(latestCollectionId, cachedCollectionId)) {
             Log.d(TAG, "SyncRequestParams. Authority: " + authority + ". Result: SYNC_TYPE_FULL");
-            return SyncRequestParams.forFull(latestMediaInfo);
+            return SyncRequestParams.forFull(latestMediaCollectionInfo);
         }
 
-        if (cachedGeneration == latestGeneration && cachedCount == latestCount) {
+        if (cachedGeneration == latestGeneration) {
             Log.d(TAG, "SyncRequestParams. Authority: " + authority + ". Result: SYNC_TYPE_NONE");
             return SyncRequestParams.forNone();
         }
 
         Log.d(TAG, "SyncRequestParams. Authority: " + authority
                 + ". Result: SYNC_TYPE_INCREMENTAL");
-        return SyncRequestParams.forIncremental(cachedGeneration, latestMediaInfo);
+        return SyncRequestParams.forIncremental(cachedGeneration, latestMediaCollectionInfo);
     }
 
     private String getPrefsKey(String authority, String key) {
@@ -572,17 +576,17 @@
         // Only valid for SYNC_TYPE_INCREMENTAL
         private final long syncGeneration;
         // Only valid for SYNC_TYPE_[INCREMENTAL|FULL]
-        private final Bundle latestMediaInfo;
+        private final Bundle latestMediaCollectionInfo;
 
         private SyncRequestParams(@SyncType int syncType) {
-            this(syncType, /* syncGeneration */ 0, /* latestMediaInfo */ null);
+            this(syncType, /* syncGeneration */ 0, /* latestMediaCollectionInfo */ null);
         }
 
         private SyncRequestParams(@SyncType int syncType, long syncGeneration,
-                Bundle latestMediaInfo) {
+                Bundle latestMediaCollectionInfo) {
             this.syncType = syncType;
             this.syncGeneration = syncGeneration;
-            this.latestMediaInfo = latestMediaInfo;
+            this.latestMediaCollectionInfo = latestMediaCollectionInfo;
         }
 
         static SyncRequestParams forNone() {
@@ -593,12 +597,14 @@
             return SYNC_REQUEST_RESET;
         }
 
-        static SyncRequestParams forFull(Bundle latestMediaInfo) {
-            return new SyncRequestParams(SYNC_TYPE_FULL, /* generation */ 0, latestMediaInfo);
+        static SyncRequestParams forFull(Bundle latestMediaCollectionInfo) {
+            return new SyncRequestParams(SYNC_TYPE_FULL, /* generation */ 0,
+                    latestMediaCollectionInfo);
         }
 
-        static SyncRequestParams forIncremental(long generation, Bundle latestMediaInfo) {
-            return new SyncRequestParams(SYNC_TYPE_INCREMENTAL, generation, latestMediaInfo);
+        static SyncRequestParams forIncremental(long generation, Bundle latestMediaCollectionInfo) {
+            return new SyncRequestParams(SYNC_TYPE_INCREMENTAL, generation,
+                    latestMediaCollectionInfo);
         }
     }
 }
diff --git a/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
index c2ce8a2..6ad121e 100644
--- a/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
+++ b/src/com/android/providers/media/photopicker/data/CloudProviderQueryExtras.java
@@ -98,7 +98,7 @@
 
         final long sizeBytes = bundle.getLong(CloudMediaProviderContract.EXTRA_FILTER_SIZE_BYTES,
                 LONG_DEFAULT);
-        final long generation = bundle.getLong(CloudMediaProviderContract.EXTRA_GENERATION,
+        final long generation = bundle.getLong(CloudMediaProviderContract.EXTRA_SYNC_GENERATION,
                 LONG_DEFAULT);
         final int limit = LIMIT_DEFAULT;
 
diff --git a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
index 42dbbea..d985f01 100644
--- a/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/ExternalDbFacade.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.media.photopicker.data;
 
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
 import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
 import static com.android.providers.media.util.DatabaseUtils.replaceMatchAnyChar;
@@ -66,7 +67,7 @@
         "COALESCE(" + MediaColumns.DATE_TAKEN + "," + MediaColumns.DATE_MODIFIED +
                     "* 1000) AS " + CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS,
         MediaColumns.GENERATION_MODIFIED + " AS " +
-                CloudMediaProviderContract.MediaColumns.GENERATION_MODIFIED,
+                CloudMediaProviderContract.MediaColumns.SYNC_GENERATION,
         MediaColumns.SIZE + " AS " + CloudMediaProviderContract.MediaColumns.SIZE_BYTES,
         MediaColumns.MIME_TYPE + " AS " + CloudMediaProviderContract.MediaColumns.MIME_TYPE,
         FileColumns._SPECIAL_FORMAT + " AS " +
@@ -75,14 +76,8 @@
         MediaColumns.IS_FAVORITE + " AS " + CloudMediaProviderContract.MediaColumns.IS_FAVORITE
     };
     private static final String[] PROJECTION_MEDIA_INFO = new String[] {
-        "COUNT(" + MediaColumns.GENERATION_MODIFIED + ") AS "
-        + CloudMediaProviderContract.MediaInfo.MEDIA_COUNT,
         "MAX(" + MediaColumns.GENERATION_MODIFIED + ") AS "
-        + CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION
-    };
-    private static final String[] PROJECTION_DELETED_MEDIA_INFO = new String[] {
-        "MAX(" + MediaColumns.GENERATION_MODIFIED + ") AS "
-        + CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION
+        + MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION
     };
     private static final String[] PROJECTION_ALBUM_DB = new String[] {
         "COUNT(" + MediaColumns._ID + ") AS " + CloudMediaProviderContract.AlbumColumns.MEDIA_COUNT,
@@ -292,11 +287,10 @@
      * Returns the total count and max {@link MediaColumns#GENERATION_MODIFIED} value
      * of the media items in the files table greater than {@code generation}.
      */
-    public Cursor getMediaInfo(long generation) {
+    public Cursor getMediaCollectionInfo(long generation) {
         final String[] selectionArgs = new String[] {String.valueOf(generation)};
         final String[] projection = new String[] {
-            CloudMediaProviderContract.MediaInfo.MEDIA_COUNT,
-            CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION
+            MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION
         };
 
         return mDatabaseHelper.runWithTransaction(db -> {
@@ -307,19 +301,15 @@
 
                 try (Cursor mediaCursor = query(qbMedia, db, PROJECTION_MEDIA_INFO, selectionArgs);
                         Cursor deletedMediaCursor = query(qbDeletedMedia, db,
-                                PROJECTION_DELETED_MEDIA_INFO, selectionArgs)) {
-                    final int mediaCountIndex = mediaCursor.getColumnIndexOrThrow(
-                            CloudMediaProviderContract.MediaInfo.MEDIA_COUNT);
+                                PROJECTION_MEDIA_INFO, selectionArgs)) {
                     final int mediaGenerationIndex = mediaCursor.getColumnIndexOrThrow(
-                            CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION);
+                            MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
                     final int deletedMediaGenerationIndex =
                             deletedMediaCursor.getColumnIndexOrThrow(
-                                    CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION);
+                                    MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-                    long mediaCount = 0;
                     long mediaGeneration = 0;
                     if (mediaCursor.moveToFirst()) {
-                        mediaCount = mediaCursor.getLong(mediaCountIndex);
                         mediaGeneration = mediaCursor.getLong(mediaGenerationIndex);
                     }
 
@@ -331,7 +321,7 @@
 
                     long maxGeneration = Math.max(mediaGeneration, deletedMediaGeneration);
                     MatrixCursor result = new MatrixCursor(projection);
-                    result.addRow(new Long[] { mediaCount, maxGeneration });
+                    result.addRow(new Long[] { maxGeneration });
 
                     return result;
                 }
diff --git a/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java b/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
index 946df63..b7f72dc 100644
--- a/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
+++ b/src/com/android/providers/media/photopicker/data/PickerDatabaseHelper.java
@@ -37,7 +37,7 @@
     @VisibleForTesting
     static final String PICKER_DATABASE_NAME = "picker.db";
 
-    private static final int VERSION_T = 4;
+    private static final int VERSION_T = 5;
     private static final int VERSION_LATEST = VERSION_T;
 
     final Context mContext;
@@ -116,7 +116,7 @@
                 + "cloud_id TEXT UNIQUE,"
                 + "is_visible INTEGER CHECK(is_visible == 1),"
                 + "date_taken_ms INTEGER NOT NULL CHECK(date_taken_ms >= 0),"
-                + "generation_modified INTEGER NOT NULL CHECK(generation_modified >= 0),"
+                + "sync_generation INTEGER NOT NULL CHECK(sync_generation >= 0),"
                 + "size_bytes INTEGER NOT NULL CHECK(size_bytes > 0),"
                 + "duration_ms INTEGER CHECK(duration_ms >= 0),"
                 + "mime_type TEXT NOT NULL,"
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
index 3f9b243..205af85 100644
--- a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
@@ -54,6 +54,10 @@
  * MediaProvider for the Photo Picker.
  */
 public class PickerDbFacade {
+    public static final String PROP_ENABLED = "sys.photopicker.pickerdb.enabled";
+    public static final String PROP_DEFAULT_SYNC_DELAY_MS =
+            "persist.sys.photopicker.pickerdb.default_sync_delay_ms";
+
     private final Object mLock = new Object();
     private final Context mContext;
     private final SQLiteDatabase mDatabase;
@@ -83,8 +87,10 @@
     private static final int FAIL = -1;
 
     private static final String TABLE_MEDIA = "media";
-    private static final String PICKER_PATH = buildPrimaryVolumeFile(MediaStore.MY_USER_ID,
-            getPickerRelativePath()).getAbsolutePath();
+    // 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();
 
     @VisibleForTesting
     public static final String KEY_ID = "_id";
@@ -97,7 +103,7 @@
     @VisibleForTesting
     public static final String KEY_DATE_TAKEN_MS = "date_taken_ms";
     @VisibleForTesting
-    public static final String KEY_GENERATION_MODIFIED = "generation_modified";
+    public static final String KEY_SYNC_GENERATION = "sync_generation";
     @VisibleForTesting
     public static final String KEY_SIZE_BYTES = "size_bytes";
     @VisibleForTesting
@@ -679,7 +685,11 @@
     }
 
     public static boolean isPickerDbEnabled() {
-        return SystemProperties.getBoolean("sys.photopicker.pickerdb.enabled", true);
+        return SystemProperties.getBoolean(PROP_ENABLED, true);
+    }
+
+    public static int getDefaultPickerDbSyncDelayMs() {
+        return SystemProperties.getInt(PROP_DEFAULT_SYNC_DELAY_MS, 1000);
     }
 
     private boolean isLocal(String authority) {
@@ -712,7 +722,7 @@
             getProjectionDataLocked(MediaColumns.DATA),
             getProjectionId(MediaColumns.ID),
             getProjectionSimple(KEY_DATE_TAKEN_MS, MediaColumns.DATE_TAKEN_MILLIS),
-            getProjectionSimple(KEY_GENERATION_MODIFIED, MediaColumns.GENERATION_MODIFIED),
+            getProjectionSimple(KEY_SYNC_GENERATION, MediaColumns.SYNC_GENERATION),
             getProjectionSimple(KEY_SIZE_BYTES, MediaColumns.SIZE_BYTES),
             getProjectionSimple(KEY_DURATION_MS, MediaColumns.DURATION_MILLIS),
             getProjectionSimple(KEY_MIME_TYPE, MediaColumns.MIME_TYPE),
@@ -770,15 +780,15 @@
 
     private String getProjectionDataLocked(String asColumn) {
         // _data format:
-        // /storage/emulated/<user-id>/.transforms/synthetic/<authority>/media/<display-name>
+        // /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 = "'" + PICKER_PATH + "/'"
+                + "||" + "'" + MediaStore.MY_USER_ID + "/'"
                 + "||" + authority
                 + "||" + "'/" + CloudMediaProviderContract.URI_PATH_MEDIA + "/'"
                 + "||" + getDisplayNameSql();
-
         return String.format("%s AS %s", fullPath, asColumn);
     }
 
@@ -833,8 +843,8 @@
                 case CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS:
                     values.put(KEY_DATE_TAKEN_MS, cursor.getLong(index));
                     break;
-                case CloudMediaProviderContract.MediaColumns.GENERATION_MODIFIED:
-                    values.put(KEY_GENERATION_MODIFIED, cursor.getLong(index));
+                case CloudMediaProviderContract.MediaColumns.SYNC_GENERATION:
+                    values.put(KEY_SYNC_GENERATION, cursor.getLong(index));
                     break;
                 case CloudMediaProviderContract.MediaColumns.SIZE_BYTES:
                     values.put(KEY_SIZE_BYTES, cursor.getLong(index));
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 8d03c38..def58e2 100644
--- a/src/com/android/providers/media/photopicker/data/model/Item.java
+++ b/src/com/android/providers/media/photopicker/data/model/Item.java
@@ -48,7 +48,7 @@
         // TODO(b/195009139): Remove after fully switching to picker db
         public static String DATE_MODIFIED = MediaStore.MediaColumns.DATE_MODIFIED;
         public static String GENERATION_MODIFIED =
-                CloudMediaProviderContract.MediaColumns.GENERATION_MODIFIED;
+                CloudMediaProviderContract.MediaColumns.SYNC_GENERATION;
         public static String DURATION = CloudMediaProviderContract.MediaColumns.DURATION_MILLIS;
         public static String SIZE = CloudMediaProviderContract.MediaColumns.SIZE_BYTES;
         public static String AUTHORITY = CloudMediaProviderContract.MediaColumns.AUTHORITY;
diff --git a/src/com/android/providers/media/util/SyntheticPathUtils.java b/src/com/android/providers/media/util/SyntheticPathUtils.java
index 0b7cb94..aa0db93 100644
--- a/src/com/android/providers/media/util/SyntheticPathUtils.java
+++ b/src/com/android/providers/media/util/SyntheticPathUtils.java
@@ -81,8 +81,8 @@
 
     public static List<String> extractSyntheticRelativePathSegements(String path, int userId) {
         final List<String> segments = new ArrayList<>();
-        final String syntheticDir = buildPrimaryVolumeFile(userId, getSyntheticRelativePath())
-                .getAbsolutePath();
+        final String syntheticDir = buildPrimaryVolumeFile(userId,
+                getSyntheticRelativePath()).getAbsolutePath();
 
         if (path.toLowerCase(Locale.ROOT).indexOf(syntheticDir.toLowerCase(Locale.ROOT)) < 0) {
             return segments;
diff --git a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
index 67cc992..3591c45 100644
--- a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
+++ b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
@@ -16,8 +16,8 @@
 
 package com.android.providers.media;
 
-import static android.provider.CloudMediaProviderContract.AccountInfo;
 import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static android.provider.CloudMediaProviderContract.MediaColumns;
 import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
 import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
@@ -47,7 +47,7 @@
         MediaColumns.MIME_TYPE,
         MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
         MediaColumns.DATE_TAKEN_MILLIS,
-        MediaColumns.GENERATION_MODIFIED,
+        MediaColumns.SYNC_GENERATION,
         MediaColumns.SIZE_BYTES,
         MediaColumns.DURATION_MILLIS,
         MediaColumns.IS_FAVORITE,
@@ -75,8 +75,8 @@
         private final List<TestMedia> mMedia = new ArrayList<>();
         private final List<TestMedia> mDeletedMedia = new ArrayList<>();
         private final List<TestAlbum> mAlbums = new ArrayList<>();
-        private String mVersion;
-        private long mGeneration;
+        private String mCollectionId;
+        private long mLastSyncGeneration;
         private String mAccountName;
         private Intent mAccountConfigurationIntent;
 
@@ -97,10 +97,12 @@
                     /* isDeleted */ true);
         }
 
-        public Bundle getAccountInfo() {
+        public Bundle getMediaCollectionInfo() {
             Bundle bundle = new Bundle();
-            bundle.putString(AccountInfo.ACTIVE_ACCOUNT_NAME, mAccountName);
-            bundle.putParcelable(AccountInfo.ACCOUNT_CONFIGURATION_INTENT,
+            bundle.putString(MediaCollectionInfo.MEDIA_COLLECTION_ID, mCollectionId);
+            bundle.putLong(MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION, mLastSyncGeneration);
+            bundle.putString(MediaCollectionInfo.ACCOUNT_NAME, mAccountName);
+            bundle.putParcelable(MediaCollectionInfo.ACCOUNT_CONFIGURATION_INTENT,
                     mAccountConfigurationIntent);
 
             return bundle;
@@ -140,16 +142,8 @@
             mAlbums.clear();
         }
 
-        public void setVersion(String version) {
-            mVersion = version;
-        }
-
-        public String getVersion() {
-            return mVersion;
-        }
-
-        public long getGeneration() {
-            return mGeneration;
+        public void setMediaCollectionId(String id) {
+            mCollectionId = id;
         }
 
         public long getCount() {
@@ -162,7 +156,7 @@
 
         private TestMedia createTestMedia(String localId, String cloudId) {
             // Increase generation
-            return new TestMedia(localId, cloudId, ++mGeneration);
+            return new TestMedia(localId, cloudId, ++mLastSyncGeneration);
         }
 
         private TestMedia createTestMedia(String localId, String cloudId, String albumId,
@@ -170,7 +164,7 @@
                 boolean isFavorite) {
             // Increase generation
             return new TestMedia(localId, cloudId, albumId, mimeType, standardMimeTypeExtension,
-                    sizeBytes, /* durationMs */ 0, ++mGeneration, isFavorite);
+                    sizeBytes, /* durationMs */ 0, ++mLastSyncGeneration, isFavorite);
         }
 
         private static TestMedia createPlaceholderMedia(String localId, String cloudId) {
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
index 8c0989c..f440b1e 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoIntentFilter.java
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(Bundle extras) {
-        throw new UnsupportedOperationException("onGetMediaInfo not supported");
+    public Bundle onGetMediaCollectionInfo(Bundle extras) {
+        throw new UnsupportedOperationException("onGetMediaCollectionInfo not supported");
     }
 }
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
index 4bc2c40..97b4aec 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderNoPermission.java
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(Bundle extras) {
-        throw new UnsupportedOperationException("onGetMediaInfo not supported");
+    public Bundle onGetMediaCollectionInfo(Bundle extras) {
+        throw new UnsupportedOperationException("onGetMediaCollectionInfo not supported");
     }
 }
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
index 9306c56..eca46ab 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
@@ -16,7 +16,7 @@
 
 package com.android.providers.media.cloudproviders;
 
-import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
 
 import android.content.res.AssetFileDescriptor;
@@ -92,17 +92,7 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(Bundle extras) {
-        Bundle bundle = new Bundle();
-        bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
-        bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
-        bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
-        return bundle;
-    }
-
-    @Override
-    public Bundle onGetAccountInfo(Bundle extras) {
-        return mMediaGenerator.getAccountInfo();
+    public Bundle onGetMediaCollectionInfo(Bundle extras) {
+        return mMediaGenerator.getMediaCollectionInfo();
     }
 }
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
index b975f96..2aafb0a 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderSecondary.java
@@ -16,7 +16,7 @@
 
 package com.android.providers.media.cloudproviders;
 
-import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
 
 import android.content.res.AssetFileDescriptor;
@@ -92,12 +92,7 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(Bundle extras) {
-        Bundle bundle = new Bundle();
-        bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
-        bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
-        bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
-        return bundle;
+    public Bundle onGetMediaCollectionInfo(Bundle extras) {
+        return mMediaGenerator.getMediaCollectionInfo();
     }
 }
diff --git a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
index 49dfaf6..a9eb6bf 100644
--- a/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
+++ b/tests/src/com/android/providers/media/photopicker/ItemsProviderTest.java
@@ -18,6 +18,7 @@
 
 import static android.provider.MediaStore.VOLUME_EXTERNAL;
 
+import static com.android.providers.media.photopicker.data.PickerDbFacade.PROP_DEFAULT_SYNC_DELAY_MS;
 import static com.android.providers.media.util.MimeUtils.isImageMimeType;
 import static com.android.providers.media.util.MimeUtils.isVideoMimeType;
 
@@ -25,6 +26,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.Manifest;
+import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -70,12 +72,18 @@
 
     @Before
     public void setUp() {
-        InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+        final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
+                .getUiAutomation();
+
+        uiAutomation.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
                         Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
                         Manifest.permission.READ_DEVICE_CONFIG,
                         Manifest.permission.INTERACT_ACROSS_USERS);
 
+        // Remove sync delay to avoid flaky tests
+        final String setSyncDelayCommand = "setprop " + PROP_DEFAULT_SYNC_DELAY_MS + " 0";
+        uiAutomation.executeShellCommand(setSyncDelayCommand);
+
         final Context context = InstrumentationRegistry.getTargetContext();
         final Context isolatedContext
                 = new IsolatedContext(context, "databases", /*asFuseThread*/ false);
diff --git a/tests/src/com/android/providers/media/photopicker/LocalProvider.java b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
index e6fb3a1..40eb092 100644
--- a/tests/src/com/android/providers/media/photopicker/LocalProvider.java
+++ b/tests/src/com/android/providers/media/photopicker/LocalProvider.java
@@ -16,7 +16,7 @@
 
 package com.android.providers.media.photopicker;
 
-import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
 import static com.android.providers.media.PickerProviderMediaGenerator.MediaGenerator;
 
 import android.content.res.AssetFileDescriptor;
@@ -91,12 +91,7 @@
     }
 
     @Override
-    public Bundle onGetMediaInfo(Bundle extras) {
-        Bundle bundle = new Bundle();
-        bundle.putString(MediaInfo.MEDIA_VERSION, mMediaGenerator.getVersion());
-        bundle.putLong(MediaInfo.MEDIA_GENERATION, mMediaGenerator.getGeneration());
-        bundle.putLong(MediaInfo.MEDIA_COUNT, mMediaGenerator.getCount());
-
-        return bundle;
+    public Bundle onGetMediaCollectionInfo(Bundle extras) {
+        return mMediaGenerator.getMediaCollectionInfo();
     }
 }
diff --git a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
index 9b9fff1..72a94db 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
@@ -93,8 +93,8 @@
     private static final Pair<String, String> CLOUD_AND_LOCAL_1
             = Pair.create(LOCAL_ID_1, CLOUD_ID_1);
 
-    private static final String VERSION_1 = "1";
-    private static final String VERSION_2 = "2";
+    private static final String COLLECTION_1 = "1";
+    private static final String COLLECTION_2 = "2";
 
     private static final String IMAGE_MIME_TYPE = "image/jpeg";
     private static final String VIDEO_MIME_TYPE = "video/mp4";
@@ -112,9 +112,9 @@
         mCloudPrimaryMediaGenerator.resetAll();
         mCloudSecondaryMediaGenerator.resetAll();
 
-        mLocalMediaGenerator.setVersion(VERSION_1);
-        mCloudPrimaryMediaGenerator.setVersion(VERSION_1);
-        mCloudSecondaryMediaGenerator.setVersion(VERSION_1);
+        mLocalMediaGenerator.setMediaCollectionId(COLLECTION_1);
+        mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+        mCloudSecondaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
 
         mContext = InstrumentationRegistry.getTargetContext();
 
diff --git a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
index 603f568..f30d8de 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerSyncControllerTest.java
@@ -88,8 +88,8 @@
     private static final Pair<String, String> CLOUD_AND_LOCAL_1
             = Pair.create(LOCAL_ID_1, CLOUD_ID_1);
 
-    private static final String VERSION_1 = "1";
-    private static final String VERSION_2 = "2";
+    private static final String COLLECTION_1 = "1";
+    private static final String COLLECTION_2 = "2";
 
     private static final String IMAGE_MIME_TYPE = "image/jpeg";
     private static final String VIDEO_MIME_TYPE = "video/mp4";
@@ -112,9 +112,9 @@
         mCloudPrimaryMediaGenerator.resetAll();
         mCloudSecondaryMediaGenerator.resetAll();
 
-        mLocalMediaGenerator.setVersion(VERSION_1);
-        mCloudPrimaryMediaGenerator.setVersion(VERSION_1);
-        mCloudSecondaryMediaGenerator.setVersion(VERSION_1);
+        mLocalMediaGenerator.setMediaCollectionId(COLLECTION_1);
+        mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
+        mCloudSecondaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
 
         mContext = InstrumentationRegistry.getTargetContext();
 
@@ -173,7 +173,7 @@
         }
 
         // 5. Bump version
-        mLocalMediaGenerator.setVersion(VERSION_2);
+        mLocalMediaGenerator.setMediaCollectionId(COLLECTION_2);
         mController.syncAllMedia();
 
         assertEmptyCursor();
@@ -244,12 +244,12 @@
         }
 
         // 3. Set invalid cloud version
-        mCloudPrimaryMediaGenerator.setVersion(/* version */ null);
+        mCloudPrimaryMediaGenerator.setMediaCollectionId(/* version */ null);
         mController.syncAllMedia();
         assertEmptyCursor();
 
         // 4. Set valid cloud version
-        mCloudPrimaryMediaGenerator.setVersion(VERSION_1);
+        mCloudPrimaryMediaGenerator.setMediaCollectionId(COLLECTION_1);
         mController.syncAllMedia();
 
         try (Cursor cr = queryMedia()) {
diff --git a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
index e0c4e8a..d24dcab 100644
--- a/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/ExternalDbFacadeTest.java
@@ -690,7 +690,7 @@
     }
 
     @Test
-    public void testGetMediaInfoFiltering() throws Exception {
+    public void testGetMediaCollectionInfoFiltering() throws Exception {
         try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
             ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
 
@@ -701,31 +701,31 @@
             cv.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
             helper.runWithTransaction(db -> db.insert(TABLE_FILES, null, cv));
 
-            try (Cursor cursor = facade.getMediaInfo(/* generation */ 0)) {
+            try (Cursor cursor = facade.getMediaCollectionInfo(/* generation */ 0)) {
                 assertThat(cursor.getCount()).isEqualTo(1);
 
                 cursor.moveToFirst();
-                assertMediaInfo(facade, cursor, /* count */ 2, /* generation */ 2);
+                assertMediaCollectionInfo(facade, cursor, /* count */ 2, /* generation */ 2);
             }
 
-            try (Cursor cursor = facade.getMediaInfo(GENERATION_MODIFIED1)) {
+            try (Cursor cursor = facade.getMediaCollectionInfo(GENERATION_MODIFIED1)) {
                 assertThat(cursor.getCount()).isEqualTo(1);
 
                 cursor.moveToFirst();
-                assertMediaInfo(facade, cursor, /* count */ 1, GENERATION_MODIFIED2);
+                assertMediaCollectionInfo(facade, cursor, /* count */ 1, GENERATION_MODIFIED2);
             }
 
-            try (Cursor cursor = facade.getMediaInfo(GENERATION_MODIFIED2)) {
+            try (Cursor cursor = facade.getMediaCollectionInfo(GENERATION_MODIFIED2)) {
                 assertThat(cursor.getCount()).isEqualTo(1);
 
                 cursor.moveToFirst();
-                assertMediaInfo(facade, cursor, /* count */ 0, /* generation */ 0);
+                assertMediaCollectionInfo(facade, cursor, /* count */ 0, /* generation */ 0);
             }
         }
     }
 
     @Test
-    public void testGetMediaInfoWithDeleted() throws Exception {
+    public void testGetMediaCollectionInfoWithDeleted() throws Exception {
         try (DatabaseHelper helper = new TestDatabaseHelper(sIsolatedContext)) {
             ExternalDbFacade facade = new ExternalDbFacade(sIsolatedContext, helper);
 
@@ -737,11 +737,11 @@
             cvDeleted.put(MediaColumns.GENERATION_MODIFIED, GENERATION_MODIFIED2);
             helper.runWithTransaction(db -> db.insert(TABLE_DELETED_MEDIA, null, cvDeleted));
 
-            try (Cursor cursor = facade.getMediaInfo(/* generation */ 0)) {
+            try (Cursor cursor = facade.getMediaCollectionInfo(/* generation */ 0)) {
                 assertThat(cursor.getCount()).isEqualTo(1);
 
                 cursor.moveToFirst();
-                assertMediaInfo(facade, cursor, /* count */ 1, /* generation */ 2);
+                assertMediaCollectionInfo(facade, cursor, /* count */ 1, /* generation */ 2);
             }
         }
     }
@@ -948,13 +948,11 @@
         assertThat(cursor.getLong(countIndex)).isEqualTo(count);
     }
 
-    private static void assertMediaInfo(ExternalDbFacade facade, Cursor cursor,
+    private static void assertMediaCollectionInfo(ExternalDbFacade facade, Cursor cursor,
             long count, long generation) {
-        int countIndex = cursor.getColumnIndex(CloudMediaProviderContract.MediaInfo.MEDIA_COUNT);
         int generationIndex = cursor.getColumnIndex(
-                CloudMediaProviderContract.MediaInfo.MEDIA_GENERATION);
+                CloudMediaProviderContract.MediaCollectionInfo.LAST_MEDIA_SYNC_GENERATION);
 
-        assertThat(cursor.getLong(countIndex)).isEqualTo(count);
         assertThat(cursor.getLong(generationIndex)).isEqualTo(generation);
     }
 
diff --git a/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java b/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
index 554de5b..28d1bab 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDatabaseHelperTest.java
@@ -45,7 +45,7 @@
     private static final String KEY_CLOUD_ID = "cloud_id";
     private static final String KEY_IS_VISIBLE = "is_visible";
     private static final String KEY_DATE_TAKEN_MS = "date_taken_ms";
-    private static final String KEY_GENERATION_MODIFIED = "generation_modified";
+    private static final String KEY_SYNC_GENERATION = "sync_generation";
     private static final String KEY_SIZE_BYTES = "size_bytes";
     private static final String KEY_DURATION_MS = "duration_ms";
     private static final String KEY_MIME_TYPE = "mime_type";
@@ -76,7 +76,7 @@
             KEY_CLOUD_ID,
             KEY_IS_VISIBLE,
             KEY_DATE_TAKEN_MS,
-            KEY_GENERATION_MODIFIED,
+            KEY_SYNC_GENERATION,
             KEY_SIZE_BYTES,
             KEY_DURATION_MS,
             KEY_MIME_TYPE,
@@ -282,13 +282,13 @@
 
             // generation_modified=NULL
             ContentValues values = getBasicContentValues();
-            values.remove(KEY_GENERATION_MODIFIED);
+            values.remove(KEY_SYNC_GENERATION);
             values.put(KEY_CLOUD_ID, CLOUD_ID);
             assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
 
             // generation_modified=-1
             values = getBasicContentValues();
-            values.put(KEY_GENERATION_MODIFIED, -1);
+            values.put(KEY_SYNC_GENERATION, -1);
             values.put(KEY_CLOUD_ID, CLOUD_ID);
             assertThat(db.insert(MEDIA_TABLE, null, values)).isEqualTo(-1);
         }
@@ -316,7 +316,7 @@
     private static ContentValues getBasicContentValues() {
         ContentValues values = new ContentValues();
         values.put(KEY_DATE_TAKEN_MS, DATE_TAKEN_MS);
-        values.put(KEY_GENERATION_MODIFIED, GENERATION_MODIFIED);
+        values.put(KEY_SYNC_GENERATION, GENERATION_MODIFIED);
         values.put(KEY_DURATION_MS, DURATION_MS);
         values.put(KEY_MIME_TYPE, MIME_TYPE);
         values.put(KEY_STANDARD_MIME_TYPE_EXTENSION, STANDARD_MIME_TYPE_EXTENSION);
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 9b378a9..5173e74 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
@@ -991,7 +991,7 @@
             MediaColumns.ID,
             MediaColumns.MEDIA_STORE_URI,
             MediaColumns.DATE_TAKEN_MILLIS,
-            MediaColumns.GENERATION_MODIFIED,
+            MediaColumns.SYNC_GENERATION,
             MediaColumns.SIZE_BYTES,
             MediaColumns.MIME_TYPE,
             MediaColumns.STANDARD_MIME_TYPE_EXTENSION,
@@ -1043,7 +1043,7 @@
     }
 
     private static String getData(String authority, String displayName) {
-        return "/storage/emulated/0/.transforms/synthetic/picker/" + authority + "/media/"
+        return "/sdcard/.transforms/synthetic/picker/0/" + authority + "/media/"
                 + displayName;
     }
 
@@ -1083,7 +1083,7 @@
                 .isEqualTo(STANDARD_MIME_TYPE_EXTENSION);
         assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.DATE_TAKEN_MILLIS)))
                 .isEqualTo(dateTakenMs);
-        assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.GENERATION_MODIFIED)))
+        assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.SYNC_GENERATION)))
                 .isEqualTo(GENERATION_MODIFIED);
         assertThat(cursor.getLong(cursor.getColumnIndex(MediaColumns.SIZE_BYTES)))
                 .isEqualTo(SIZE_BYTES);
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java
index bc66a18..95ea816 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerActivityTest.java
@@ -50,6 +50,7 @@
 
 import com.android.providers.media.R;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -76,6 +77,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testDoesNotShowProfileButton() {
         // Register bottom sheet idling resource so that we don't read bottom sheet state when
         // in between changing states
@@ -126,6 +128,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testBottomSheetState() {
         // Register bottom sheet idling resource so that we don't read bottom sheet state when
         // in between changing states
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java
index 1bf726d..61bd82d 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotosTabTest.java
@@ -42,6 +42,7 @@
 import com.android.providers.media.R;
 import com.android.providers.media.photopicker.util.DateTimeUtils;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,6 +59,7 @@
             = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPhotoGridLayout_photoGrid() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -74,6 +76,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPhotoGridLayout_image() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -89,6 +92,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPhotoGridLayout_video() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
index ee5d381..5280c34 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectLongPressTest.java
@@ -50,6 +50,7 @@
 import com.android.providers.media.photopicker.data.Selection;
 import com.android.providers.media.photopicker.viewmodel.PickerViewModel;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -63,6 +64,7 @@
             = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_longPress_image() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -94,6 +96,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_longPress_video() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -111,6 +114,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_longPress_select() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -152,6 +156,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_longPress_showsOnlyOne() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -188,6 +193,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_selectButtonWidth() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
         // Navigate to preview
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
index e06740d..e4ccb76 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewMultiSelectTest.java
@@ -63,6 +63,7 @@
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -76,6 +77,7 @@
             = new ActivityScenarioRule<>(PhotoPickerBaseTest.getMultiSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_common() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
         final BottomSheetIdlingResource bottomSheetIdlingResource =
@@ -189,6 +191,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_navigation() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -260,6 +263,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_fromAlbumsTab() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -297,6 +301,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_viewSelectedAfterLongPress() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
index 9892778..26570b5 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PreviewSingleSelectTest.java
@@ -54,6 +54,7 @@
 
 import com.android.providers.media.R;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,6 +67,7 @@
             = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_singleSelect_image() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -168,6 +170,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_noScrimLayerAndHasSolidColorInPortrait() {
         setPortraitOrientation(mRule);
 
@@ -186,6 +189,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_showScrimLayerInLandscape() {
         setLandscapeOrientation(mRule);
 
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 128af56..0119856 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatMultiSelectTest.java
@@ -35,6 +35,7 @@
 
 import com.android.providers.media.R;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -98,6 +99,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_longPress_motionPhoto() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -113,6 +115,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPreview_multiSelect_navigation() {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java
index 8219194..a2fc2c1 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/SpecialFormatSingleSelectTest.java
@@ -40,6 +40,7 @@
 
 import com.android.providers.media.R;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -50,6 +51,7 @@
             = new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPhotoGridLayout_motionPhoto() throws Exception {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
@@ -66,6 +68,7 @@
     }
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testPhotoGridLayout_gif() throws Exception {
         onView(withId(PICKER_TAB_RECYCLERVIEW_ID)).check(matches(isDisplayed()));
 
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java b/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java
index 032715d..b804286 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/WorkAppsOffProfileButtonTest.java
@@ -38,6 +38,7 @@
 import static org.hamcrest.Matchers.not;
 
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,6 +56,7 @@
             new ActivityScenarioRule<>(PhotoPickerBaseTest.getSingleSelectionIntent());
 
     @Test
+    @Ignore("Enable after b/218806007 is fixed")
     public void testProfileButton_dialog() throws Exception {
         // Register bottom sheet idling resource so that we don't read bottom sheet state when
         // in between changing states