[coastguard skipped] Merge sparse cherrypicks from sparse-13775243-L92100030014435199 into 24Q3-platform-release.

COASTGUARD_SKIP: I91c44428dc730be4cf42b3cb9c02e8eca4440f81
COASTGUARD_SKIP: I6cb652cecf3a3023532bb17eee275bf66fee250d
COASTGUARD_SKIP: Ibe8a80d65c4dff6368352944e004c5c934430953

Change-Id: Ie412064bca0eb653cbc36f45b2ed966488856b38
diff --git a/src/com/android/providers/media/AccessChecker.java b/src/com/android/providers/media/AccessChecker.java
index 23704d5..4dea3c4 100644
--- a/src/com/android/providers/media/AccessChecker.java
+++ b/src/com/android/providers/media/AccessChecker.java
@@ -55,8 +55,10 @@
 import static com.android.providers.media.LocalUriMatcher.VIDEO_THUMBNAILS;
 import static com.android.providers.media.LocalUriMatcher.VIDEO_THUMBNAILS_ID;
 import static com.android.providers.media.MediaGrants.PACKAGE_USER_ID_COLUMN;
+import static com.android.providers.media.MediaProvider.INCLUDED_DEFAULT_DIRECTORIES;
 import static com.android.providers.media.util.DatabaseUtils.bindSelection;
 
+import android.os.Bundle;
 import android.provider.MediaStore;
 import android.provider.MediaStore.Files.FileColumns;
 import android.provider.MediaStore.MediaColumns;
@@ -67,8 +69,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
 
 /**
  * Class responsible for performing all access checks (read/write access states for calling package)
@@ -267,7 +267,7 @@
     @NonNull
     public static String getWhereForConstrainedAccess(
             @NonNull LocalCallingIdentity callingIdentity, int uriType,
-            boolean forWrite, Optional<List<String>> includedDefaultDirectoriesOptional) {
+            boolean forWrite, @NonNull Bundle extras) {
         switch (uriType) {
             case AUDIO_MEDIA_ID:
             case AUDIO_MEDIA: {
@@ -361,12 +361,9 @@
 
                 // Allow access to file in directories. This si particularly used only for
                 // SystemGallery use-case
-                if (includedDefaultDirectoriesOptional.isPresent()) {
-                    final String defaultDirectorySql = getWhereForDefaultDirectoryMatch(
-                            includedDefaultDirectoriesOptional.get());
-                    if (defaultDirectorySql != null) {
-                        options.add(defaultDirectorySql);
-                    }
+                final String defaultDirectorySql = getWhereForDefaultDirectoryMatch(extras);
+                if (defaultDirectorySql != null) {
+                    options.add(defaultDirectorySql);
                 }
 
                 return TextUtils.join(" OR ", options);
@@ -444,11 +441,12 @@
      * @see MediaProvider#INCLUDED_DEFAULT_DIRECTORIES
      */
     @Nullable
-    private static String getWhereForDefaultDirectoryMatch(
-            List<String> includedDefaultDirectories) {
+    private static String getWhereForDefaultDirectoryMatch(@NonNull Bundle extras) {
+        final ArrayList<String> includedDefaultDirs = extras.getStringArrayList(
+                INCLUDED_DEFAULT_DIRECTORIES);
         final ArrayList<String> options = new ArrayList<>();
-        if (includedDefaultDirectories != null) {
-            for (String defaultDir : includedDefaultDirectories) {
+        if (includedDefaultDirs != null) {
+            for (String defaultDir : includedDefaultDirs) {
                 options.add(FileColumns.RELATIVE_PATH + " LIKE '" + defaultDir + "/%'");
             }
         }
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index fc2b34d..34e726a 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -421,6 +421,15 @@
     private static final String FILE_DATABASE_UUID = ".database_uuid";
 
     /**
+     * Specify what default directories the caller gets full access to. By default, the caller
+     * shouldn't get full access to any default dirs.
+     * But for example, we do an exception for System Gallery apps and allow them full access to:
+     * DCIM, Pictures, Movies.
+     */
+    static final String INCLUDED_DEFAULT_DIRECTORIES =
+            "android:included-default-directories";
+
+    /**
      * Value indicating that operations should include database rows matching the criteria defined
      * by this key only when calling package has write permission to the database row or column is
      * {@column MediaColumns#IS_PENDING} and is set by FUSE.
@@ -2841,8 +2850,7 @@
         }
 
         final String writeAccessCheckSql = getWhereForConstrainedAccess(mCallingIdentity.get(),
-                uriType, /* forWrite */ true, /* includedDefaultDirectoriesOptional */
-                Optional.empty());
+                uriType, /* forWrite */ true, Bundle.EMPTY);
 
         final String matchWritableRowsClause = String.format("%s=0 OR (%s=1 AND (%s OR %s))",
                 column, column, MATCH_PENDING_FROM_FUSE, writeAccessCheckSql);
@@ -3001,8 +3009,7 @@
         final String[] selectionArgs = new String[] {path};
 
         final SQLiteQueryBuilder qbForQuery =
-                getQueryBuilder(TYPE_QUERY, match, uri, Bundle.EMPTY,
-                        null, /* includedDefaultDirectoriesOptional */ Optional.empty());
+                getQueryBuilder(TYPE_QUERY, match, uri, Bundle.EMPTY, null);
         try (Cursor c = qbForQuery.query(helper, new String[] {FileColumns.OWNER_PACKAGE_NAME},
                 selection, selectionArgs, null, null, null, null, null)) {
             if (!c.moveToFirst()) {
@@ -3019,8 +3026,7 @@
         }
 
         final SQLiteQueryBuilder qbForUpdate =
-                getQueryBuilder(TYPE_UPDATE, match, uri, Bundle.EMPTY, null,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty());
+                getQueryBuilder(TYPE_UPDATE, match, uri, Bundle.EMPTY, null);
         ContentValues values = new ContentValues();
         values.put(FileColumns.OWNER_PACKAGE_NAME, "null");
         return qbForUpdate.update(helper, values, selection, selectionArgs) == 1;
@@ -3028,15 +3034,14 @@
 
     private boolean updateDatabaseForFuseRename(@NonNull DatabaseHelper helper,
             @NonNull String oldPath, @NonNull String newPath, @NonNull ContentValues values) {
-        return updateDatabaseForFuseRename(helper, oldPath, newPath, values, Bundle.EMPTY,
-                /* includedDefaultDirectoriesOptional */ Optional.empty());
+        return updateDatabaseForFuseRename(helper, oldPath, newPath, values, Bundle.EMPTY);
     }
 
     private boolean updateDatabaseForFuseRename(@NonNull DatabaseHelper helper,
             @NonNull String oldPath, @NonNull String newPath, @NonNull ContentValues values,
-            @NonNull Bundle qbExtras, Optional<List<String>> includedDefaultDirectoriesOptional) {
+            @NonNull Bundle qbExtras) {
         return updateDatabaseForFuseRename(helper, oldPath, newPath, values, qbExtras,
-                FileUtils.getContentUriForPath(oldPath), includedDefaultDirectoriesOptional);
+                FileUtils.getContentUriForPath(oldPath));
     }
 
     /**
@@ -3044,12 +3049,10 @@
      */
     private boolean updateDatabaseForFuseRename(@NonNull DatabaseHelper helper,
             @NonNull String oldPath, @NonNull String newPath, @NonNull ContentValues values,
-            @NonNull Bundle qbExtras, Uri uriOldPath,
-            Optional<List<String>> includedDefaultDirectoriesOptional) {
+            @NonNull Bundle qbExtras, Uri uriOldPath) {
         boolean allowHidden = isCallingPackageAllowedHidden();
         final SQLiteQueryBuilder qbForUpdate = getQueryBuilder(TYPE_UPDATE,
-                matchUri(uriOldPath, allowHidden), uriOldPath, qbExtras, null,
-                includedDefaultDirectoriesOptional);
+                matchUri(uriOldPath, allowHidden), uriOldPath, qbExtras, null);
 
         // uriOldPath may use Files uri which doesn't allow modifying AudioColumns. Include
         // AudioColumns projection map if we are modifying any audio columns while renaming
@@ -3086,8 +3089,7 @@
         }
 
         if (retryUpdateWithReplace) {
-            if (deleteForFuseRename(helper, oldPath, newPath, qbExtras, selection, allowHidden,
-                    includedDefaultDirectoriesOptional)) {
+            if (deleteForFuseRename(helper, oldPath, newPath, qbExtras, selection, allowHidden)) {
                 Log.i(TAG, "Retrying database update after deleting conflicting entry");
                 count = qbForUpdate.update(helper, values, selection, new String[]{oldPath});
             } else {
@@ -3098,15 +3100,13 @@
     }
 
     private boolean deleteForFuseRename(DatabaseHelper helper, String oldPath,
-            String newPath, Bundle qbExtras, String selection, boolean allowHidden,
-            Optional<List<String>> includedDefaultDirectoriesOptional) {
+            String newPath, Bundle qbExtras, String selection, boolean allowHidden) {
         // We are replacing file in newPath with file in oldPath. If calling package has
         // write permission for newPath, delete existing database entry and retry update.
         final Uri uriNewPath = FileUtils.getContentUriForPath(oldPath);
         final SQLiteQueryBuilder qbForDelete = getQueryBuilder(TYPE_DELETE,
-                matchUri(uriNewPath, allowHidden), uriNewPath, qbExtras, null,
-                includedDefaultDirectoriesOptional);
-        if (qbForDelete.delete(helper, selection, new String[]{newPath}) == 1) {
+                matchUri(uriNewPath, allowHidden), uriNewPath, qbExtras, null);
+        if (qbForDelete.delete(helper, selection, new String[] {newPath}) == 1) {
             return true;
         }
         // Check if delete can be done using other URI grants
@@ -3234,7 +3234,7 @@
 
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE,
                 matchUri(uriOldPath, isCallingPackageAllowedHidden()), uriOldPath, Bundle.EMPTY,
-                null, /* includedDefaultDirectoriesOptional */ Optional.empty());
+                null);
         final DatabaseHelper helper;
         try {
             helper = getDatabaseForUri(uriOldPath);
@@ -3326,6 +3326,9 @@
 
         helper.beginTransaction();
         try {
+            final Bundle qbExtras = new Bundle();
+            qbExtras.putStringArrayList(INCLUDED_DEFAULT_DIRECTORIES,
+                    getIncludedDefaultDirectories());
             final boolean wasHidden = FileUtils.shouldDirBeHidden(new File(oldPath));
             final boolean isHidden = FileUtils.shouldDirBeHidden(new File(newPath));
             for (String filePath : fileList) {
@@ -3334,7 +3337,7 @@
                 if(!updateDatabaseForFuseRename(helper, oldPath + "/" + filePath, newFilePath,
                         getContentValuesForFuseRename(newFilePath, mimeType, wasHidden, isHidden,
                                 /* isSameMimeType */ true),
-                        new Bundle(), Optional.of(getIncludedDefaultDirectories()))) {
+                        qbExtras)) {
                     Log.e(TAG, "Calling package doesn't have write permission to rename file.");
                     return OsConstants.EPERM;
                 }
@@ -3458,7 +3461,7 @@
             return false;
         }
         return updateDatabaseForFuseRename(helper, oldPath, newPath, contentValues, Bundle.EMPTY,
-                oldPathGrantedUri, /* includedDefaultDirectoriesOptional */ Optional.empty());
+                oldPathGrantedUri);
     }
 
     /**
@@ -3639,8 +3642,7 @@
                 type = TYPE_QUERY;
             }
 
-            final SQLiteQueryBuilder qb = getQueryBuilder(type, table, uri, Bundle.EMPTY,
-                    null, /* includedDefaultDirectoriesOptional */ Optional.empty());
+            final SQLiteQueryBuilder qb = getQueryBuilder(type, table, uri, Bundle.EMPTY, null);
             try (Cursor c = qb.query(helper,
                     new String[] { BaseColumns._ID }, null, null, null, null, null, null, null)) {
                 if (c.getCount() == 1) {
@@ -3730,6 +3732,9 @@
         PulledMetrics.logVolumeAccessViaMediaProvider(getCallingUidOrSelf(), volumeName);
         queryArgs = (queryArgs != null) ? queryArgs : new Bundle();
 
+        // INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
+        queryArgs.remove(INCLUDED_DEFAULT_DIRECTORIES);
+
         final ArraySet<String> honoredArgs = new ArraySet<>();
         DatabaseUtils.resolveQueryArgs(queryArgs, honoredArgs::add, this::ensureCustomCollator);
 
@@ -3787,7 +3792,7 @@
 
         final DatabaseHelper helper = getDatabaseForUri(uri);
         final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, queryArgs,
-                honoredArgs::add, /* includedDefaultDirectoriesOptional */ Optional.empty());
+                honoredArgs::add);
         // Allowing hidden column _user_id for this query to support Cloned Profile use case.
         if (table == FILES) {
             qb.allowColumn(FileColumns._USER_ID);
@@ -5269,7 +5274,7 @@
         // row irrespective of is_download=1.
         final Uri uri = FileUtils.getContentUriForPath(path);
         SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, matchUri(uri, allowHidden), uri,
-                extras, null, /* includedDefaultDirectoriesOptional */ Optional.empty());
+                extras, null);
 
         // We won't be able to update columns that are not part of projection map of Files table. We
         // have already checked strict columns in previous insert operation which failed with
@@ -5347,6 +5352,9 @@
         // REDACTED_URI_BUNDLE_KEY extra should only be set inside MediaProvider.
         extras.remove(QUERY_ARG_REDACTED_URI);
 
+        // INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
+        extras.remove(INCLUDED_DEFAULT_DIRECTORIES);
+
         final boolean allowHidden = isCallingPackageAllowedHidden();
         final int match = matchUri(uri, allowHidden);
 
@@ -5495,8 +5503,7 @@
         long rowId = -1;
         Uri newUri = null;
 
-        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_INSERT, match, uri, extras, null,
-                /* includedDefaultDirectoriesOptional */ Optional.empty());
+        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_INSERT, match, uri, extras, null);
 
         switch (match) {
             case IMAGES_MEDIA: {
@@ -5768,8 +5775,7 @@
         // We already handle the required permission checks for the app before we get here
         final LocalCallingIdentity token = clearLocalCallingIdentity();
         try {
-            return getQueryBuilder(type, match, uri, extras, honored,
-                    /* includedDefaultDirectoriesOptional */ Optional.empty());
+            return getQueryBuilder(type, match, uri, extras, honored);
         } finally {
             restoreLocalCallingIdentity(token);
         }
@@ -5791,20 +5797,17 @@
      * </ul>
      */
     private @NonNull SQLiteQueryBuilder getQueryBuilder(int type, int match,
-            @NonNull Uri uri, @NonNull Bundle extras, @Nullable Consumer<String> honored,
-            Optional<List<String>> includedDefaultDirectoriesOptional) {
+            @NonNull Uri uri, @NonNull Bundle extras, @Nullable Consumer<String> honored) {
         Trace.beginSection("MP.getQueryBuilder");
         try {
-            return getQueryBuilderInternal(type, match, uri, extras, honored,
-                    includedDefaultDirectoriesOptional);
+            return getQueryBuilderInternal(type, match, uri, extras, honored);
         } finally {
             Trace.endSection();
         }
     }
 
     private @NonNull SQLiteQueryBuilder getQueryBuilderInternal(int type, int match,
-            @NonNull Uri uri, @NonNull Bundle extras, @Nullable Consumer<String> honored,
-            Optional<List<String>> includedDefaultDirectoriesOptional) {
+            @NonNull Uri uri, @NonNull Bundle extras, @Nullable Consumer<String> honored) {
         final boolean forWrite;
         switch (type) {
             case TYPE_QUERY: forWrite = false; break;
@@ -5874,8 +5877,7 @@
         // to commit to this as an API.
         final boolean includeAllVolumes = shouldIncludeRecentlyUnmountedVolumes(uri, extras);
 
-        appendAccessCheckQuery(qb, forWrite, uri, match, extras, volumeName,
-                includedDefaultDirectoriesOptional);
+        appendAccessCheckQuery(qb, forWrite, uri, match, extras, volumeName);
 
         switch (match) {
             case IMAGES_MEDIA_ID:
@@ -6273,8 +6275,7 @@
     }
 
     private void appendAccessCheckQuery(@NonNull SQLiteQueryBuilder qb, boolean forWrite,
-            @NonNull Uri uri, int uriType, @NonNull Bundle extras, @NonNull String volumeName,
-            Optional<List<String>> includedDefaultDirectoriesOptional) {
+            @NonNull Uri uri, int uriType, @NonNull Bundle extras, @NonNull String volumeName) {
         Objects.requireNonNull(extras);
         final Uri redactedUri = extras.getParcelable(QUERY_ARG_REDACTED_URI);
 
@@ -6315,7 +6316,7 @@
                 // Allow access to files which are owned by the caller. Or allow access to files
                 // based on legacy or any other special access permissions.
                 options.add(getWhereForConstrainedAccess(mCallingIdentity.get(), uriType, forWrite,
-                        includedDefaultDirectoriesOptional));
+                        extras));
             }
         } else {
             if (isLatestSelectionOnlyRequired) {
@@ -6325,7 +6326,7 @@
             // Allow access to files which are owned by the caller. Or allow access to files
             // based on legacy or any other special access permissions.
             options.add(getWhereForConstrainedAccess(mCallingIdentity.get(), uriType, forWrite,
-                    includedDefaultDirectoriesOptional));
+                    extras));
         }
 
         appendWhereStandalone(qb, TextUtils.join(" OR ", options));
@@ -6419,6 +6420,9 @@
             return 0;
         }
 
+        // INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
+        extras.remove(INCLUDED_DEFAULT_DIRECTORIES);
+
         uri = safeUncanonicalize(uri);
         final boolean allowHidden = isCallingPackageAllowedHidden();
         final int match = matchUri(uri, allowHidden);
@@ -6494,8 +6498,7 @@
             }
         }
 
-        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_DELETE, match, uri, extras, null,
-                /* includedDefaultDirectoriesOptional */ Optional.empty());
+        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_DELETE, match, uri, extras, null);
 
         {
             // Give callers interacting with a specific media item a chance to
@@ -6633,8 +6636,7 @@
                 // 2. delete file row from the db
                 final boolean allowHidden = isCallingPackageAllowedHidden();
                 final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_DELETE,
-                        matchUri(uriGranted, allowHidden), uriGranted, extras, null,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty());
+                        matchUri(uriGranted, allowHidden), uriGranted, extras, null);
                 int count = qb.delete(helper, BaseColumns._ID + "=" + id, null);
 
                 if (isDownload == 1) {
@@ -7957,6 +7959,8 @@
         // Related items are only considered for new media creation, and they
         // can't be leveraged to move existing content into blocked locations
         extras.remove(QUERY_ARG_RELATED_URI);
+        // INCLUDED_DEFAULT_DIRECTORIES extra should only be set inside MediaProvider.
+        extras.remove(INCLUDED_DEFAULT_DIRECTORIES);
 
         final String userWhere = extras.getString(QUERY_ARG_SQL_SELECTION);
         final String[] userWhereArgs = extras.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS);
@@ -8029,8 +8033,7 @@
             }
         }
 
-        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, match, uri, extras, null,
-                /* includedDefaultDirectoriesOptional */ Optional.empty());
+        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, match, uri, extras, null);
 
         // Give callers interacting with a specific media item a chance to
         // escalate access if they don't already have it
@@ -8549,8 +8552,7 @@
                 extras.putInt(QUERY_ARG_MATCH_PENDING, MATCH_INCLUDE);
                 extras.putInt(QUERY_ARG_MATCH_TRASHED, MATCH_INCLUDE);
                 final SQLiteQueryBuilder qbForReplace = getQueryBuilder(TYPE_DELETE,
-                        matchUri(uri, allowHidden), uri, extras, null,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty());
+                        matchUri(uri, allowHidden), uri, extras, null);
                 final long rowId = getIdIfPathOwnedByPackages(qbForReplace, helper, path,
                         mCallingIdentity.get().getSharedPackagesAsString());
 
@@ -8846,8 +8848,7 @@
         try {
             helper = getDatabaseForUri(membersUri);
             qb = getQueryBuilder(TYPE_DELETE, AUDIO_PLAYLISTS_ID_MEMBERS,
-                    membersUri, queryArgs, null,
-                    /* includedDefaultDirectoriesOptional */ Optional.empty());
+                    membersUri, queryArgs, null);
         } catch (VolumeNotFoundException ignored) {
             return new int[0];
         }
@@ -10856,8 +10857,7 @@
 
         // First, check to see if caller has direct write access
         if (forWrite) {
-            final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_UPDATE, table, uri, extras,
-                    null, /* includedDefaultDirectoriesOptional */ Optional.empty());
+            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 },
                     selection, selectionArgs, null, null, null, null, null)) {
@@ -10881,8 +10881,7 @@
         }
 
         // Second, check to see if caller has direct read access
-        final SQLiteQueryBuilder qb = getQueryBuilder(TYPE_QUERY, table, uri, extras, null,
-                /* includedDefaultDirectoriesOptional */ Optional.empty());
+        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 },
                 selection, selectionArgs, null, null, null, null, null)) {
diff --git a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
index 7714109..4e248c8 100644
--- a/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
+++ b/src/com/android/providers/media/photopicker/data/PickerDbFacade.java
@@ -32,7 +32,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.database.MatrixCursor;
 import android.database.MergeCursor;
 import android.database.sqlite.SQLiteConstraintException;
@@ -1045,31 +1044,11 @@
     }
 
     private Cursor queryMediaIdForAppsLocked(@NonNull SQLiteQueryBuilder qb,
-            @NonNull String[] columns, @NonNull String[] selectionArgs,
+            @NonNull String[] projection, @NonNull String[] selectionArgs,
             String pickerSegmentType) {
-        final Cursor cursor =
-                qb.query(mDatabase, getMediaStoreProjectionLocked(columns, pickerSegmentType),
-                    /* selection */ null, selectionArgs, /* groupBy */ null, /* having */ null,
-                    /* orderBy */ null, /* limitStr */ null);
-
-        if (columns == null || columns.length == 0 || cursor.getColumnCount() == columns.length) {
-            return cursor;
-        } else {
-            // An unknown column was encountered. Populate it will null for backwards compatibility.
-            final MatrixCursor result = new MatrixCursor(columns);
-            if (cursor.moveToFirst()) {
-                do {
-                    final ContentValues contentValues = new ContentValues();
-                    DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
-                    final MatrixCursor.RowBuilder rowBuilder = result.newRow();
-                    for (String column : columns) {
-                        rowBuilder.add(column, contentValues.get(column));
-                    }
-                } while (cursor.moveToNext());
-            }
-            cursor.close();
-            return result;
-        }
+        return qb.query(mDatabase, getMediaStoreProjectionLocked(projection, pickerSegmentType),
+                /* selection */ null, selectionArgs, /* groupBy */ null, /* having */ null,
+                /* orderBy */ null, /* limitStr */ null);
     }
 
     /**
@@ -1221,51 +1200,54 @@
     }
 
     private String[] getMediaStoreProjectionLocked(String[] columns, String pickerSegmentType) {
-        final List<String> projection = new ArrayList<>();
+        final String[] projection = new String[columns.length];
 
-        for (int i = 0; i < columns.length; i++) {
+        for (int i = 0; i < projection.length; i++) {
             switch (columns[i]) {
                 case PickerMediaColumns.DATA:
-                    projection.add(getProjectionDataLocked(PickerMediaColumns.DATA,
-                            pickerSegmentType));
+                    projection[i] = getProjectionDataLocked(PickerMediaColumns.DATA,
+                            pickerSegmentType);
                     break;
                 case PickerMediaColumns.DISPLAY_NAME:
-                    projection.add(getProjectionSimple(
-                            getDisplayNameSql(), PickerMediaColumns.DISPLAY_NAME));
+                    projection[i] =
+                            getProjectionSimple(
+                                    getDisplayNameSql(), PickerMediaColumns.DISPLAY_NAME);
                     break;
                 case PickerMediaColumns.MIME_TYPE:
-                    projection.add(getProjectionSimple(
-                            KEY_MIME_TYPE, PickerMediaColumns.MIME_TYPE));
+                    projection[i] =
+                            getProjectionSimple(KEY_MIME_TYPE, PickerMediaColumns.MIME_TYPE);
                     break;
                 case PickerMediaColumns.DATE_TAKEN:
-                    projection.add(getProjectionSimple(
-                            KEY_DATE_TAKEN_MS, PickerMediaColumns.DATE_TAKEN));
+                    projection[i] =
+                            getProjectionSimple(KEY_DATE_TAKEN_MS, PickerMediaColumns.DATE_TAKEN);
                     break;
                 case PickerMediaColumns.SIZE:
-                    projection.add(getProjectionSimple(KEY_SIZE_BYTES, PickerMediaColumns.SIZE));
+                    projection[i] = getProjectionSimple(KEY_SIZE_BYTES, PickerMediaColumns.SIZE);
                     break;
                 case PickerMediaColumns.DURATION_MILLIS:
-                    projection.add(getProjectionSimple(
-                            KEY_DURATION_MS, PickerMediaColumns.DURATION_MILLIS));
+                    projection[i] =
+                            getProjectionSimple(
+                                    KEY_DURATION_MS, PickerMediaColumns.DURATION_MILLIS);
                     break;
                 case PickerMediaColumns.HEIGHT:
-                    projection.add(getProjectionSimple(KEY_HEIGHT, PickerMediaColumns.HEIGHT));
+                    projection[i] = getProjectionSimple(KEY_HEIGHT, PickerMediaColumns.HEIGHT);
                     break;
                 case PickerMediaColumns.WIDTH:
-                    projection.add(getProjectionSimple(KEY_WIDTH, PickerMediaColumns.WIDTH));
+                    projection[i] = getProjectionSimple(KEY_WIDTH, PickerMediaColumns.WIDTH);
                     break;
                 case PickerMediaColumns.ORIENTATION:
-                    projection.add(getProjectionSimple(
-                            KEY_ORIENTATION, PickerMediaColumns.ORIENTATION));
+                    projection[i] =
+                            getProjectionSimple(KEY_ORIENTATION, PickerMediaColumns.ORIENTATION);
                     break;
                 default:
+                    projection[i] = getProjectionSimple("NULL", columns[i]);
                     // Ignore unsupported columns; we do not throw error here to support
-                    // backward compatibility for ACTION_GET_CONTENT takeover.
+                    // backward compatibility
                     Log.w(TAG, "Unexpected Picker column: " + columns[i]);
             }
         }
 
-        return projection.toArray(new String[0]);
+        return projection;
     }
 
     private String getProjectionAuthorityLocked() {
diff --git a/tests/src/com/android/providers/media/AccessCheckerTest.java b/tests/src/com/android/providers/media/AccessCheckerTest.java
index 17286c9..677feb7 100644
--- a/tests/src/com/android/providers/media/AccessCheckerTest.java
+++ b/tests/src/com/android/providers/media/AccessCheckerTest.java
@@ -51,8 +51,7 @@
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 
-import android.os.Environment;
-import android.provider.MediaStore;
+import android.os.Bundle;
 import android.system.Os;
 import android.text.TextUtils;
 
@@ -64,8 +63,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
 public class AccessCheckerTest {
@@ -367,27 +364,22 @@
 
         // App with no permissions only has access to owned files
         assertWithMessage("Expected owned access SQL for Audio collection")
-                .that(getWhereForConstrainedAccess(hasNoPerms, AUDIO_MEDIA, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasNoPerms, AUDIO_MEDIA, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasNoPerms)
                         + " OR is_ringtone=1 OR is_alarm=1 OR is_notification=1");
         assertWithMessage("Expected owned access SQL for Video collection")
-                .that(getWhereForConstrainedAccess(hasNoPerms, VIDEO_MEDIA, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasNoPerms, VIDEO_MEDIA, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasNoPerms));
         assertWithMessage("Expected owned access SQL for Images collection")
-                .that(getWhereForConstrainedAccess(hasNoPerms, IMAGES_MEDIA, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasNoPerms, IMAGES_MEDIA, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasNoPerms));
 
         // App with no permissions only has access to owned files
         assertWithMessage("Expected owned access SQL for Downloads collection")
-                .that(getWhereForConstrainedAccess(hasNoPerms, DOWNLOADS, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasNoPerms, DOWNLOADS, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasNoPerms));
         assertWithMessage("Expected owned access SQL for FILES collection")
-                .that(getWhereForConstrainedAccess(hasNoPerms, FILES, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasNoPerms, FILES, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasNoPerms));
     }
 
@@ -402,27 +394,12 @@
         // App with READ_EXTERNAL_STORAGE or READ_MEDIA_* permission has access to only owned
         // non-media files or media files.
         assertWithMessage("Expected owned access SQL for Downloads collection")
-                .that(getWhereForConstrainedAccess(hasReadMedia, DOWNLOADS, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasReadMedia, DOWNLOADS, false, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasReadMedia));
         assertWithMessage("Expected owned access SQL for FILES collection")
-                .that(getWhereForConstrainedAccess(hasReadMedia, FILES, false,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasReadMedia, FILES, false, Bundle.EMPTY))
                 .isEqualTo(
                         getWhereForOwnerPackageMatch(hasReadMedia) + " OR " + getFilesAccessSql());
-        assertWithMessage("Expected owned access SQL for FILES collection")
-                .that(getWhereForConstrainedAccess(hasReadMedia, FILES, false,
-                        /* includedDefaultDirectoriesOptional */Optional.of(
-                                List.of(Environment.DIRECTORY_DCIM, Environment.DIRECTORY_PICTURES,
-                                        Environment.DIRECTORY_MOVIES))))
-                .isEqualTo(
-                        getWhereForOwnerPackageMatch(hasReadMedia) + " OR "
-                                + getFilesAccessSql() + " OR "
-                                + MediaStore.Files.FileColumns.RELATIVE_PATH + " LIKE 'DCIM/%'"
-                                + " OR " + MediaStore.Files.FileColumns.RELATIVE_PATH
-                                + " LIKE 'Pictures/%'"
-                                + " OR " + MediaStore.Files.FileColumns.RELATIVE_PATH
-                                + " LIKE 'Movies/%'");
     }
 
     @Test
@@ -432,27 +409,22 @@
 
         // App with no permissions only has access to owned files.
         assertWithMessage("Expected owned access SQL for Audio collection")
-                .that(getWhereForConstrainedAccess(noPerms, AUDIO_MEDIA, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(noPerms, AUDIO_MEDIA, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(noPerms)
                         + " OR is_ringtone=1 OR is_alarm=1 OR is_notification=1");
         assertWithMessage("Expected owned access SQL for Video collection")
-                .that(getWhereForConstrainedAccess(noPerms, VIDEO_MEDIA, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(noPerms, VIDEO_MEDIA, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(noPerms));
         assertWithMessage("Expected owned access SQL for Images collection")
-                .that(getWhereForConstrainedAccess(noPerms, IMAGES_MEDIA, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(noPerms, IMAGES_MEDIA, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(noPerms));
 
         // App with no permissions only has access to owned files
         assertWithMessage("Expected owned access SQL for Downloads collection")
-                .that(getWhereForConstrainedAccess(noPerms, DOWNLOADS, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(noPerms, DOWNLOADS, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(noPerms));
         assertWithMessage("Expected owned access SQL for FILES collection")
-                .that(getWhereForConstrainedAccess(noPerms, FILES, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(noPerms, FILES, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(noPerms));
     }
 
@@ -467,12 +439,10 @@
         // App with write permission to media files has access write access to media files and owned
         // files.
         assertWithMessage("Expected owned access SQL for Downloads collection")
-                .that(getWhereForConstrainedAccess(hasReadPerms, DOWNLOADS, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasReadPerms, DOWNLOADS, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasReadPerms));
         assertWithMessage("Expected owned access SQL for FILES collection")
-                .that(getWhereForConstrainedAccess(hasReadPerms, FILES, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasReadPerms, FILES, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasReadPerms) + " OR "
                         + getFilesAccessSql());
     }
@@ -511,13 +481,11 @@
         // Legacy app with WRITE_EXTERNAL_STORAGE permission has access to non-media files as well.
         // However, they don't have global write access to secondary volume.
         assertWithMessage("Expected where clause SQL for Downloads collection to be")
-                .that(getWhereForConstrainedAccess(hasLegacyWrite, DOWNLOADS, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasLegacyWrite, DOWNLOADS, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasLegacyWrite) + " OR "
                         + AccessChecker.getWhereForExternalPrimaryMatch());
         assertWithMessage("Expected where clause SQL for FILES collection to be")
-                .that(getWhereForConstrainedAccess(hasLegacyWrite, FILES, true,
-                        /* includedDefaultDirectoriesOptional */ Optional.empty()))
+                .that(getWhereForConstrainedAccess(hasLegacyWrite, FILES, true, Bundle.EMPTY))
                 .isEqualTo(getWhereForOwnerPackageMatch(hasLegacyWrite) + " OR "
                         + AccessChecker.getWhereForExternalPrimaryMatch() + " OR "
                         + getFilesAccessSql());
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 1feb763..1283dd1 100644
--- a/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
+++ b/tests/src/com/android/providers/media/photopicker/data/PickerDbFacadeTest.java
@@ -1401,7 +1401,7 @@
         }
 
         // Assert invalid projection column
-        final String invalidColumn = "test invalid column";
+        final String invalidColumn = "testInvalidColumn";
         final String[] invalidProjection = new String[]{
                 PickerMediaColumns.DATE_TAKEN,
                 invalidColumn
@@ -1413,17 +1413,12 @@
                     "Unexpected number of rows when asserting invalid projection column with "
                             + "cloud provider.")
                     .that(cr.getCount()).isEqualTo(1);
-            assertWithMessage("Unexpected number of columns in cursor")
-                    .that(cr.getColumnCount())
-                    .isEqualTo(2);
 
             cr.moveToFirst();
-            assertWithMessage("Unexpected value of the invalidColumn with cloud provider.")
+            assertWithMessage(
+                    "Unexpected value of the invalidColumn with cloud provider.")
                     .that(cr.getLong(cr.getColumnIndexOrThrow(invalidColumn)))
                     .isEqualTo(0);
-            assertWithMessage("Unexpected value of the invalidColumn with cloud provider.")
-                    .that(cr.getString(cr.getColumnIndexOrThrow(invalidColumn)))
-                    .isEqualTo(null);
             assertWithMessage(
                     "Unexpected value of PickerMediaColumns.DATE_TAKEN with cloud provider.")
                     .that(cr.getLong(cr.getColumnIndexOrThrow(PickerMediaColumns.DATE_TAKEN)))