Merge "Support MATCH_WRITABLE for IS_PENDING AND IS_TRASHED match." into rvc-dev
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 5e2e144..68f82d5 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -266,6 +266,15 @@
"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.
+ * <p>
+ * Note that items <em>not</em> matching the criteria will also be included, and as part of this
+ * match no additional write permission checks are carried out for those items.
+ */
+ private static final int MATCH_WRITABLE = 32;
+
+ /**
* Set of {@link Cursor} columns that refer to raw filesystem paths.
*/
private static final ArrayMap<String, Object> sDataColumns = new ArrayMap<>();
@@ -1246,6 +1255,94 @@
}
/**
+ * @return where clause to include database rows where
+ * <ul>
+ * <li> {@code column} is not set or
+ * <li> {@code column} is set and calling package has write permission to corresponding db row.
+ * </ul>
+ * The method is used to match db rows corresponding to writable pending and trashed files.
+ */
+ @Nullable
+ private String getWhereClauseForWritableMatch(@NonNull Uri uri, @NonNull String column) {
+ if (isCallingPackageLegacyWrite() || checkCallingPermissionGlobal(uri, /*forWrite*/ true)) {
+ // No special filtering needed
+ return null;
+ }
+
+ final String callingPackage = getCallingPackageOrSelf();
+
+ final ArrayList<String> options = new ArrayList<>();
+ switch(matchUri(uri, isCallingPackageAllowedHidden())) {
+ case IMAGES_MEDIA_ID:
+ case IMAGES_MEDIA:
+ case IMAGES_THUMBNAILS_ID:
+ case IMAGES_THUMBNAILS:
+ if (checkCallingPermissionImages(/*forWrite*/ true, callingPackage)) {
+ // No special filtering needed
+ return null;
+ }
+ break;
+ case AUDIO_MEDIA_ID:
+ case AUDIO_MEDIA:
+ case AUDIO_PLAYLISTS_ID:
+ case AUDIO_PLAYLISTS:
+ if (checkCallingPermissionAudio(/*forWrite*/ true, callingPackage)) {
+ // No special filtering needed
+ return null;
+ }
+ break;
+ case VIDEO_MEDIA_ID:
+ case VIDEO_MEDIA:
+ case VIDEO_THUMBNAILS_ID:
+ case VIDEO_THUMBNAILS:
+ if (checkCallingPermissionVideo(/*firWrite*/ true, callingPackage)) {
+ // No special filtering needed
+ return null;
+ }
+ break;
+ case DOWNLOADS_ID:
+ case DOWNLOADS:
+ // No app has special permissions for downloads.
+ break;
+ case FILES_ID:
+ case FILES:
+ if (checkCallingPermissionAudio(/*forWrite*/ true, callingPackage)) {
+ // Allow apps with audio permission to include audio* media types.
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_AUDIO));
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_PLAYLIST));
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_SUBTITLE));
+ }
+ if (checkCallingPermissionVideo(/*forWrite*/ true, callingPackage)) {
+ // Allow apps with video permission to include video* media types.
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_VIDEO));
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_SUBTITLE));
+ }
+ if (checkCallingPermissionImages(/*forWrite*/ true, callingPackage)) {
+ // Allow apps with images permission to include images* media types.
+ options.add(DatabaseUtils.bindSelection("media_type=?",
+ FileColumns.MEDIA_TYPE_IMAGE));
+ }
+ break;
+ default:
+ // is_pending, is_trashed are not applicable for rest of the media tables.
+ return null;
+ }
+
+ final String matchSharedPackagesClause = FileColumns.OWNER_PACKAGE_NAME + " IN "
+ + getSharedPackages(callingPackage);
+ options.add(DatabaseUtils.bindSelection(matchSharedPackagesClause));
+
+ final String matchWritableRowsClause = String.format("%s=0 OR (%s=1 AND %s)", column,
+ column, TextUtils.join(" OR ", options));
+ return matchWritableRowsClause;
+ }
+
+ /**
* Gets list of files in {@code path} from media provider database.
*
* @param path path of the directory.
@@ -3256,8 +3353,8 @@
}
}
- private static void appendWhereStandaloneMatch(@NonNull SQLiteQueryBuilder qb,
- @NonNull String column, /* @Match */ int match) {
+ private void appendWhereStandaloneMatch(@NonNull SQLiteQueryBuilder qb,
+ @NonNull String column, /* @Match */ int match, Uri uri) {
switch (match) {
case MATCH_INCLUDE:
// No special filtering needed
@@ -3268,6 +3365,12 @@
case MATCH_ONLY:
appendWhereStandalone(qb, column + "=?", 1);
break;
+ case MATCH_WRITABLE:
+ final String whereClause = getWhereClauseForWritableMatch(uri, column);
+ if (whereClause != null) {
+ appendWhereStandalone(qb, whereClause);
+ }
+ break;
default:
throw new IllegalArgumentException();
}
@@ -3378,8 +3481,16 @@
if (MediaStore.getIncludePending(uri)) matchPending = MATCH_INCLUDE;
// Resolve any remaining default options
- if (matchPending == MATCH_DEFAULT) matchPending = MATCH_EXCLUDE;
- if (matchTrashed == MATCH_DEFAULT) matchTrashed = MATCH_EXCLUDE;
+ final int defaultMatchForPendingAndTrashed;
+ if (isFuseThread()) {
+ // Write operations always check for file ownership, we don't need additional write
+ // permission check for is_pending and is_trashed.
+ defaultMatchForPendingAndTrashed = forWrite ? MATCH_INCLUDE : MATCH_WRITABLE;
+ } else {
+ defaultMatchForPendingAndTrashed = MATCH_EXCLUDE;
+ }
+ if (matchPending == MATCH_DEFAULT) matchPending = defaultMatchForPendingAndTrashed;
+ if (matchTrashed == MATCH_DEFAULT) matchTrashed = defaultMatchForPendingAndTrashed;
if (matchFavorite == MATCH_DEFAULT) matchFavorite = MATCH_INCLUDE;
// Handle callers using legacy filtering
@@ -3408,9 +3519,9 @@
if (!allowGlobal && !checkCallingPermissionImages(forWrite, callingPackage)) {
appendWhereStandalone(qb, matchSharedPackagesClause);
}
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);
@@ -3468,9 +3579,9 @@
appendWhereStandaloneFilter(qb, new String[] {
AudioColumns.ARTIST_KEY, AudioColumns.ALBUM_KEY, AudioColumns.TITLE_KEY
}, filter);
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);
@@ -3558,9 +3669,9 @@
if (!allowGlobal && !checkCallingPermissionAudio(forWrite, callingPackage)) {
appendWhereStandalone(qb, matchSharedPackagesClause);
}
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);
@@ -3700,9 +3811,9 @@
if (!allowGlobal && !checkCallingPermissionVideo(forWrite, callingPackage)) {
appendWhereStandalone(qb, matchSharedPackagesClause);
}
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);
@@ -3779,9 +3890,9 @@
appendWhereStandaloneFilter(qb, new String[] {
AudioColumns.ARTIST_KEY, AudioColumns.ALBUM_KEY, AudioColumns.TITLE_KEY
}, filter);
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);
@@ -3821,9 +3932,9 @@
appendWhereStandalone(qb, TextUtils.join(" OR ", options));
}
- appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending);
- appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed);
- appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_PENDING, matchPending, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_TRASHED, matchTrashed, uri);
+ appendWhereStandaloneMatch(qb, FileColumns.IS_FAVORITE, matchFavorite, uri);
if (honored != null) {
honored.accept(QUERY_ARG_MATCH_PENDING);
honored.accept(QUERY_ARG_MATCH_TRASHED);