diff options
| author | Sudheer Shanka <sudheersai@google.com> | 2020-02-27 17:49:53 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-02-27 17:49:53 +0000 |
| commit | 442399b1c19bd6d02a6be3aa45ed8a0e3887295d (patch) | |
| tree | 361c71bec89b2f0e0cbd09cd8a688f9f747d9cf7 | |
| parent | 14862018b9bc71a72163719027e1ca0118632080 (diff) | |
| parent | 7d28b5bf8185250c41c50f34c430e4de25889354 (diff) | |
Merge "Include pending media as well when deleting mediastore entries." into rvc-dev
| -rw-r--r-- | core/java/android/content/ContentResolver.java | 12 | ||||
| -rw-r--r-- | core/java/android/database/DatabaseUtils.java | 21 | ||||
| -rw-r--r-- | core/java/com/android/internal/content/FileSystemProvider.java | 38 |
3 files changed, 53 insertions, 18 deletions
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index b748cfa775ed..c7f42cb85943 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -3827,6 +3827,18 @@ public abstract class ContentResolver implements ContentInterface { return queryArgs; } + /** @hide */ + public static @NonNull Bundle includeSqlSelectionArgs(@NonNull Bundle queryArgs, + @Nullable String selection, @Nullable String[] selectionArgs) { + if (selection != null) { + queryArgs.putString(QUERY_ARG_SQL_SELECTION, selection); + } + if (selectionArgs != null) { + queryArgs.putStringArray(QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs); + } + return queryArgs; + } + /** * Returns structured sort args formatted as an SQL sort clause. * diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java index 4246b84dc52f..34cc856e000f 100644 --- a/core/java/android/database/DatabaseUtils.java +++ b/core/java/android/database/DatabaseUtils.java @@ -16,6 +16,7 @@ package android.database; +import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentValues; @@ -1548,4 +1549,24 @@ public class DatabaseUtils { } return -1; } + + /** + * Escape the given argument for use in a {@code LIKE} statement. + * @hide + */ + public static String escapeForLike(@NonNull String arg) { + // Shamelessly borrowed from com.android.providers.media.util.DatabaseUtils + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < arg.length(); i++) { + final char c = arg.charAt(i); + switch (c) { + case '%': sb.append('\\'); + break; + case '_': sb.append('\\'); + break; + } + sb.append(c); + } + return sb.toString(); + } } diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index ef9b3d1021ef..73ef8c6f6fca 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -23,6 +23,7 @@ import android.content.ContentResolver; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.database.Cursor; +import android.database.DatabaseUtils; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; @@ -38,6 +39,7 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsProvider; import android.provider.MediaStore; +import android.provider.MediaStore.Files.FileColumns; import android.provider.MetadataReader; import android.system.Int64Ref; import android.text.TextUtils; @@ -333,15 +335,17 @@ public abstract class FileSystemProvider extends DocumentsProvider { if (isDirectory) { FileUtils.deleteContents(file); } - if (!file.delete()) { + // We could be deleting pending media which doesn't have any content yet, so only throw + // if the file exists and we fail to delete it. + if (file.exists() && !file.delete()) { throw new IllegalStateException("Failed to delete " + file); } onDocIdChanged(docId); - removeFromMediaStore(visibleFile, isDirectory); + removeFromMediaStore(visibleFile); } - private void removeFromMediaStore(@Nullable File visibleFile, boolean isFolder) + private void removeFromMediaStore(@Nullable File visibleFile) throws FileNotFoundException { // visibleFolder is null if we're removing a document from external thumb drive or SD card. if (visibleFile != null) { @@ -350,21 +354,19 @@ public abstract class FileSystemProvider extends DocumentsProvider { try { final ContentResolver resolver = getContext().getContentResolver(); final Uri externalUri = MediaStore.Files.getContentUri("external"); - - // Remove media store entries for any files inside this directory, using - // path prefix match. Logic borrowed from MtpDatabase. - if (isFolder) { - final String path = visibleFile.getAbsolutePath() + "/"; - resolver.delete(externalUri, - "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)", - new String[]{path + "%", Integer.toString(path.length()), path}); - } - - // Remove media store entry for this exact file. - final String path = visibleFile.getAbsolutePath(); - resolver.delete(externalUri, - "_data LIKE ?1 AND lower(_data)=lower(?2)", - new String[]{path, path}); + final Bundle queryArgs = new Bundle(); + queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE); + + // Remove the media store entry corresponding to visibleFile and if it is a + // directory, also remove media store entries for any files inside this directory. + // Logic borrowed from com.android.providers.media.scan.ModernMediaScanner. + final String pathEscapedForLike = DatabaseUtils.escapeForLike( + visibleFile.getAbsolutePath()); + ContentResolver.includeSqlSelectionArgs(queryArgs, + FileColumns.DATA + " LIKE ? ESCAPE '\\' OR " + + FileColumns.DATA + " LIKE ? ESCAPE '\\'", + new String[] {pathEscapedForLike + "/%", pathEscapedForLike}); + resolver.delete(externalUri, queryArgs); } finally { Binder.restoreCallingIdentity(token); } |
