Loading core/java/android/content/ContentResolver.java +12 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading core/java/android/database/DatabaseUtils.java +21 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.database; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentValues; Loading Loading @@ -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(); } } core/java/com/android/internal/content/FileSystemProvider.java +20 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); } Loading Loading
core/java/android/content/ContentResolver.java +12 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading
core/java/android/database/DatabaseUtils.java +21 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.database; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentValues; Loading Loading @@ -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(); } }
core/java/com/android/internal/content/FileSystemProvider.java +20 −18 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) { Loading @@ -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); } Loading