Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d28b5bf authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Include pending media as well when deleting mediastore entries.

Bug: 146914674
Bug: 140565622
Bug: 137381621
Test: manual
Change-Id: I903a13deb467b45839a7d03b60754c8fc464456c
parent 227ae85d
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -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.
     *
+21 −0
Original line number Diff line number Diff line
@@ -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();
    }
}
+20 −18
Original line number Diff line number Diff line
@@ -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);
            }