diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2019-05-23 03:09:02 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2019-05-23 03:09:02 +0000 |
commit | 9fbc33175656193ff282c3e92439bbd29edaecec (patch) | |
tree | fef2ebc72a6a0632f43b4f48cf0b21e78203ba10 | |
parent | 794fc5d7032563f8f716dd0d02406635e33f0dcd (diff) | |
parent | f2ac1e336e3f34b5ae6a528f0931e6aa237f3979 (diff) | |
download | android_packages_providers_DownloadProvider-9fbc33175656193ff282c3e92439bbd29edaecec.tar.gz android_packages_providers_DownloadProvider-9fbc33175656193ff282c3e92439bbd29edaecec.tar.bz2 android_packages_providers_DownloadProvider-9fbc33175656193ff282c3e92439bbd29edaecec.zip |
Snap for 5595555 from f2ac1e336e3f34b5ae6a528f0931e6aa237f3979 to qt-qpr1-release
Change-Id: I3bdd7fe6ce49bac4626a5d69669d69bcc6f27133
-rw-r--r-- | src/com/android/providers/downloads/DownloadProvider.java | 167 | ||||
-rw-r--r-- | src/com/android/providers/downloads/DownloadStorageProvider.java | 99 |
2 files changed, 78 insertions, 188 deletions
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java index a2f8532e..550c8fba 100644 --- a/src/com/android/providers/downloads/DownloadProvider.java +++ b/src/com/android/providers/downloads/DownloadProvider.java @@ -79,6 +79,7 @@ import android.util.SparseArray; import com.android.internal.util.ArrayUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; import libcore.io.IoUtils; @@ -636,8 +637,7 @@ public final class DownloadProvider extends ContentProvider { public Bundle call(String method, String arg, Bundle extras) { switch (method) { case Downloads.CALL_MEDIASTORE_DOWNLOADS_DELETED: { - getContext().enforceCallingOrSelfPermission( - android.Manifest.permission.WRITE_MEDIA_STORAGE, + Preconditions.checkArgument(Binder.getCallingUid() == Process.myUid(), "Not allowed to call " + Downloads.CALL_MEDIASTORE_DOWNLOADS_DELETED); final long[] deletedDownloadIds = extras.getLongArray(Downloads.EXTRA_IDS); final String[] mimeTypes = extras.getStringArray(Downloads.EXTRA_MIME_TYPES); @@ -662,6 +662,12 @@ public final class DownloadProvider extends ContentProvider { } return null; } + case Downloads.CALL_REVOKE_MEDIASTORE_URI_PERMS : { + Preconditions.checkArgument(Binder.getCallingUid() == Process.myUid(), + "Not allowed to call " + Downloads.CALL_REVOKE_MEDIASTORE_URI_PERMS); + DownloadStorageProvider.revokeAllMediaStoreUriPermissions(getContext()); + return null; + } default: throw new UnsupportedOperationException("Unsupported call: " + method); } @@ -1324,82 +1330,7 @@ public final class DownloadProvider extends ContentProvider { final SQLiteQueryBuilder qb = getQueryBuilder(uri, match); - // map of volumeName -> { mediastore ids that need to be queried } - final ArrayMap<String, LongArray> mediaStoreIdsForVolumes = new ArrayMap<>(); - try (Cursor cursor = qb.query(db, new String[] { Downloads.Impl.COLUMN_MEDIASTORE_URI }, - selection, selectionArgs, null, null, sort)) { - while (cursor.moveToNext()) { - final String uriString = cursor.getString(0); - if (uriString == null) { - continue; - } - final Uri mediaStoreUri = Uri.parse(uriString); - final String volumeName = MediaStore.getVolumeName(mediaStoreUri); - LongArray ids = mediaStoreIdsForVolumes.get(volumeName); - if (ids == null) { - ids = new LongArray(); - mediaStoreIdsForVolumes.put(volumeName, ids); - } - ids.add(ContentUris.parseId(mediaStoreUri)); - } - } - // map of volumeName -> { map of {mediastore id -> mediastore data} } - final ArrayMap<String, LongSparseArray<String>> mediaStoreDataForVolumes - = new ArrayMap<>(); - final CallingIdentity token = clearCallingIdentity(); - try (ContentProviderClient client = getContext().getContentResolver() - .acquireContentProviderClient(MediaStore.AUTHORITY)) { - final String[] projectionIn = new String[] { - MediaStore.Downloads._ID, - MediaStore.Downloads.DATA, - }; - for (int i = 0; i < mediaStoreIdsForVolumes.size(); ++i) { - final String volumeName = mediaStoreIdsForVolumes.keyAt(i); - final LongArray ids = mediaStoreIdsForVolumes.valueAt(i); - final LongSparseArray<String> mediaStoreDataForIds - = new LongSparseArray<>(); - mediaStoreDataForVolumes.put(volumeName, mediaStoreDataForIds); - try (Cursor mediaCursor = getMediaProviderRowsForIds( - client, projectionIn, volumeName, ids)) { - while (mediaCursor.moveToNext()) { - final long id = mediaCursor.getLong(0); - final String filePath = mediaCursor.getString(1); - mediaStoreDataForIds.put(id, filePath); - } - } - } - } catch (RemoteException e) { - // Should not happen - } finally { - restoreCallingIdentity(token); - } - - final TranslatingCursor.Config config = getTranslatingCursorConfig(match); - final TranslatingCursor.Translator translator - = (data, auxiliaryColIndex, matchingColumn, cursor) -> { - final String uriString = cursor.getString(auxiliaryColIndex); - if (uriString != null) { - final Uri mediaStoreUri = Uri.parse(uriString); - final String volumeName = MediaStore.getVolumeName(mediaStoreUri); - final LongSparseArray<String> mediaStoreDataForIds - = mediaStoreDataForVolumes.get(volumeName); - if (mediaStoreDataForIds != null) { - switch (matchingColumn) { - case Downloads.Impl._DATA: - case Downloads.Impl.COLUMN_FILE_NAME_HINT: - case DownloadManager.COLUMN_LOCAL_FILENAME: - final long id = ContentUris.parseId(mediaStoreUri); - return mediaStoreDataForIds.get(id, data); - default: - return data; - } - } - } - - return data; - }; - final Cursor ret = TranslatingCursor.query(config, translator, - qb, db, projection, selection, selectionArgs, null, null, sort, null, null); + final Cursor ret = qb.query(db, projection, selection, selectionArgs, null, null, sort); if (ret != null) { ret.setNotificationUri(getContext().getContentResolver(), uri); @@ -1416,39 +1347,6 @@ public final class DownloadProvider extends ContentProvider { return ret; } - private Cursor getMediaProviderRowsForIds(ContentProviderClient mediaProvider, - String[] projection, String volumeName, LongArray ids) throws RemoteException { - final StringBuilder queryString = new StringBuilder(); - queryString.append(MediaStore.Downloads._ID + " in ("); - final int size = ids.size(); - for (int i = 0; i < size; ++i) { - queryString.append(ids.get(i)); - queryString.append((i == size - 1) ? ")" : ","); - } - return mediaProvider.query(MediaStore.Downloads.getContentUri(volumeName), - projection, queryString.toString(), null, null); - } - - private TranslatingCursor.Config getTranslatingCursorConfig(int match) { - final Uri baseUri; - switch (match) { - case MY_DOWNLOADS: - case MY_DOWNLOADS_ID: - baseUri = Downloads.Impl.CONTENT_URI; - break; - case ALL_DOWNLOADS: - case ALL_DOWNLOADS_ID: - baseUri = Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI; - break; - default: - baseUri = null; - } - return new TranslatingCursor.Config(baseUri, Downloads.Impl.COLUMN_MEDIASTORE_URI, - Downloads.Impl._DATA, - Downloads.Impl.COLUMN_FILE_NAME_HINT, - DownloadManager.COLUMN_LOCAL_FILENAME); - } - private void logVerboseQueryInfo(String[] projection, final String selection, final String[] selectionArgs, final String sort, SQLiteDatabase db) { java.lang.StringBuilder sb = new java.lang.StringBuilder(); @@ -1622,7 +1520,8 @@ public final class DownloadProvider extends ContentProvider { || (info.mMediaScanned != MEDIA_NOT_SCANNABLE); if (info.mFileName == null) { if (info.mMediaStoreUri != null) { - client.delete(Uri.parse(info.mMediaStoreUri), null, null); + // If there was a mediastore entry, it would be deleted in it's + // next idle pass. updateValues.clear(); updateValues.putNull(Downloads.Impl.COLUMN_MEDIASTORE_URI); qb.update(db, updateValues, Downloads.Impl._ID + "=?", @@ -1660,8 +1559,6 @@ public final class DownloadProvider extends ContentProvider { info.sendIntentIfRequested(); } } - } catch (RemoteException e) { - // Should not happen } finally { restoreCallingIdentity(token); } @@ -1788,32 +1685,18 @@ public final class DownloadProvider extends ContentProvider { final String path = info.mFileName; if (!TextUtils.isEmpty(path)) { - boolean fileDeleted = false; try { final File file = new File(path).getCanonicalFile(); if (Helpers.isFilenameValid(getContext(), file)) { Log.v(Constants.TAG, "Deleting " + file + " via provider delete"); file.delete(); - fileDeleted = true; + deleteMediaStoreEntry(file); + } else { + Log.d(Constants.TAG, "Ignoring invalid file: " + file); } - } catch (IOException ignore) { - } - if (!fileDeleted) { - Log.d(Constants.TAG, "Ignoring invalid path: " + path); - } - } - - final String mediaUri = info.mMediaStoreUri; - if (!TextUtils.isEmpty(mediaUri)) { - final long token = Binder.clearCallingIdentity(); - try { - getContext().getContentResolver().delete(Uri.parse(mediaUri), null, - null); - } catch (Exception e) { - Log.w(Constants.TAG, "Failed to delete media entry: " + e); - } finally { - Binder.restoreCallingIdentity(token); + } catch (IOException e) { + Log.e(Constants.TAG, "Couldn't delete file: " + path, e); } } @@ -1848,6 +1731,24 @@ public final class DownloadProvider extends ContentProvider { return count; } + private void deleteMediaStoreEntry(File file) { + final long token = Binder.clearCallingIdentity(); + try { + final String path = file.getAbsolutePath(); + final Uri.Builder builder = MediaStore.setIncludePending( + MediaStore.Files.getContentUriForPath(path).buildUpon()); + builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); + + final Uri filesUri = builder.build(); + getContext().getContentResolver().delete(filesUri, + MediaStore.Files.FileColumns.DATA + "=?", new String[] { path }); + } catch (Exception e) { + Log.d(Constants.TAG, "Failed to delete mediastore entry for file:" + file, e); + } finally { + Binder.restoreCallingIdentity(token); + } + } + /** * Remotely opens a file */ diff --git a/src/com/android/providers/downloads/DownloadStorageProvider.java b/src/com/android/providers/downloads/DownloadStorageProvider.java index 56911729..2ffe6ce8 100644 --- a/src/com/android/providers/downloads/DownloadStorageProvider.java +++ b/src/com/android/providers/downloads/DownloadStorageProvider.java @@ -30,12 +30,11 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; +import android.content.UriPermission; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; -import android.database.sqlite.SQLiteQueryBuilder; import android.media.MediaFile; -import android.mtp.MtpConstants; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -53,7 +52,6 @@ import android.provider.MediaStore; import android.provider.MediaStore.DownloadColumns; import android.text.TextUtils; import android.util.Log; -import android.util.LongArray; import android.util.Pair; import com.android.internal.annotations.GuardedBy; @@ -66,6 +64,7 @@ import java.io.FileNotFoundException; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -97,15 +96,6 @@ public class DownloadStorageProvider extends FileSystemProvider { private DownloadManager mDm; - private static final String[] DOWNLOADS_PROJECTION - = new String[DownloadManager.UNDERLYING_COLUMNS.length + 1]; - static { - System.arraycopy(DownloadManager.UNDERLYING_COLUMNS, 0, - DOWNLOADS_PROJECTION, 0, DownloadManager.UNDERLYING_COLUMNS.length); - DOWNLOADS_PROJECTION[DOWNLOADS_PROJECTION.length - 1] - = Downloads.Impl.COLUMN_MEDIASTORE_URI; - } - private static final int NO_LIMIT = -1; @Override @@ -148,6 +138,22 @@ public class DownloadStorageProvider extends FileSystemProvider { } } + static void revokeAllMediaStoreUriPermissions(Context context) { + final List<UriPermission> uriPermissions = + context.getContentResolver().getOutgoingUriPermissions(); + final int size = uriPermissions.size(); + final StringBuilder sb = new StringBuilder("Revoking permissions for uris: "); + for (int i = 0; i < size; ++i) { + final Uri uri = uriPermissions.get(i).getUri(); + if (AUTHORITY.equals(uri.getAuthority()) + && isMediaStoreDownload(DocumentsContract.getDocumentId(uri))) { + context.revokeUriPermission(uri, ~0); + sb.append(uri + ","); + } + } + Log.d(TAG, sb.toString()); + } + @Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { // It's possible that the folder does not exist on disk, so we will create the folder if @@ -288,14 +294,13 @@ public class DownloadStorageProvider extends FileSystemProvider { includeDownloadFromMediaStore(result, cursor, null /* filePaths */); } } else { - cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId)), - DOWNLOADS_PROJECTION); + cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId))); copyNotificationUri(result, cursor); if (cursor.moveToFirst()) { // We don't know if this queryDocument() call is from Downloads (manage) // or Files. Safely assume it's Files. includeDownloadFromCursor(result, cursor, null /* filePaths */, - null /* mediaStoreIds */, null /* queryArgs */); + null /* queryArgs */); } } result.start(); @@ -335,29 +340,25 @@ public class DownloadStorageProvider extends FileSystemProvider { final ArrayList<Uri> notificationUris = new ArrayList<>(); if (isMediaStoreDownloadDir(parentDocId)) { includeDownloadsFromMediaStore(result, null /* queryArgs */, - null /* idsToExclude */, null /* filePaths */, notificationUris, + null /* filePaths */, notificationUris, getMediaStoreIdString(parentDocId), NO_LIMIT, manage); } else { assert (DOC_ID_ROOT.equals(parentDocId)); if (manage) { cursor = mDm.query( - new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true), - DOWNLOADS_PROJECTION); + new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true)); } else { cursor = mDm.query( new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true) - .setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL), - DOWNLOADS_PROJECTION); + .setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL)); } final Set<String> filePaths = new HashSet<>(); - final LongArray mediaStoreIds = new LongArray(); while (cursor.moveToNext()) { - includeDownloadFromCursor(result, cursor, filePaths, mediaStoreIds, - null /* queryArgs */); + includeDownloadFromCursor(result, cursor, filePaths, null /* queryArgs */); } notificationUris.add(cursor.getNotificationUri()); includeDownloadsFromMediaStore(result, null /* queryArgs */, - mediaStoreIds, filePaths, notificationUris, + filePaths, notificationUris, null /* parentId */, NO_LIMIT, manage); includeFilesFromSharedStorage(result, filePaths, null); } @@ -401,9 +402,8 @@ public class DownloadStorageProvider extends FileSystemProvider { final ArrayList<Uri> notificationUris = new ArrayList<>(); try { cursor = mDm.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true) - .setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL), - DOWNLOADS_PROJECTION); - final LongArray mediaStoreIds = new LongArray(); + .setFilterByStatus(DownloadManager.STATUS_SUCCESSFUL)); + final Set<String> filePaths = new HashSet<>(); while (cursor.moveToNext() && result.getCount() < limit) { final String mimeType = cursor.getString( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_MEDIA_TYPE)); @@ -417,8 +417,8 @@ public class DownloadStorageProvider extends FileSystemProvider { || MediaFile.isVideoMimeType(mimeType)) && !TextUtils.isEmpty(uri)) { continue; } - includeDownloadFromCursor(result, cursor, null /* filePaths */, - mediaStoreIds, null /* queryArgs */); + includeDownloadFromCursor(result, cursor, filePaths, + null /* queryArgs */); } notificationUris.add(cursor.getNotificationUri()); @@ -427,7 +427,7 @@ public class DownloadStorageProvider extends FileSystemProvider { final Bundle args = new Bundle(); args.putBoolean(DocumentsContract.QUERY_ARG_EXCLUDE_MEDIA, true); - includeDownloadsFromMediaStore(result, args, mediaStoreIds, null /* filePaths */, + includeDownloadsFromMediaStore(result, args, filePaths, notificationUris, null /* parentId */, (limit - result.getCount()), false /* includePending */); } finally { @@ -453,15 +453,13 @@ public class DownloadStorageProvider extends FileSystemProvider { Cursor cursor = null; try { cursor = mDm.query(new DownloadManager.Query().setOnlyIncludeVisibleInDownloadsUi(true) - .setFilterByString(DocumentsContract.getSearchDocumentsQuery(queryArgs)), - DOWNLOADS_PROJECTION); - final LongArray mediaStoreIds = new LongArray(); + .setFilterByString(DocumentsContract.getSearchDocumentsQuery(queryArgs))); final Set<String> filePaths = new HashSet<>(); while (cursor.moveToNext()) { - includeDownloadFromCursor(result, cursor, filePaths, mediaStoreIds, queryArgs); + includeDownloadFromCursor(result, cursor, filePaths, queryArgs); } notificationUris.add(cursor.getNotificationUri()); - includeDownloadsFromMediaStore(result, queryArgs, mediaStoreIds, filePaths, + includeDownloadsFromMediaStore(result, queryArgs, filePaths, notificationUris, null /* parentId */, NO_LIMIT, true /* includePending */); includeSearchFilesFromSharedStorage(result, projection, filePaths, queryArgs); @@ -572,8 +570,7 @@ public class DownloadStorageProvider extends FileSystemProvider { Cursor cursor = null; String localFilePath = null; try { - cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId)), - DOWNLOADS_PROJECTION); + cursor = mDm.query(new Query().setFilterById(Long.parseLong(docId))); if (cursor.moveToFirst()) { localFilePath = cursor.getString( cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_FILENAME)); @@ -620,7 +617,7 @@ public class DownloadStorageProvider extends FileSystemProvider { * if the file exists in the file system. */ private void includeDownloadFromCursor(MatrixCursor result, Cursor cursor, - Set<String> filePaths, LongArray mediaStoreIds, Bundle queryArgs) { + Set<String> filePaths, Bundle queryArgs) { final long id = cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID)); final String docId = String.valueOf(id); @@ -703,14 +700,7 @@ public class DownloadStorageProvider extends FileSystemProvider { includeDownload(result, docId, displayName, summary, size, mimeType, lastModified, extraFlags, status == DownloadManager.STATUS_RUNNING); - if (mediaStoreIds != null) { - final String mediaStoreUri = cursor.getString( - cursor.getColumnIndex(Downloads.Impl.COLUMN_MEDIASTORE_URI)); - if (mediaStoreUri != null) { - mediaStoreIds.add(ContentUris.parseId(Uri.parse(mediaStoreUri))); - } - } - if (filePaths != null) { + if (filePaths != null && localFilePath != null) { filePaths.add(localFilePath); } } @@ -881,7 +871,7 @@ public class DownloadStorageProvider extends FileSystemProvider { } private void includeDownloadsFromMediaStore(@NonNull MatrixCursor result, - @Nullable Bundle queryArgs, @Nullable LongArray idsToExclude, + @Nullable Bundle queryArgs, @Nullable Set<String> filePaths, @NonNull ArrayList<Uri> notificationUris, @Nullable String parentId, int limit, boolean includePending) { if (limit == 0) { @@ -890,7 +880,7 @@ public class DownloadStorageProvider extends FileSystemProvider { final long token = Binder.clearCallingIdentity(); final Pair<String, String[]> selectionPair - = buildSearchSelection(queryArgs, idsToExclude, parentId); + = buildSearchSelection(queryArgs, filePaths, parentId); final Uri.Builder queryUriBuilder = MediaStore.Downloads.EXTERNAL_CONTENT_URI.buildUpon(); if (limit != NO_LIMIT) { queryUriBuilder.appendQueryParameter(MediaStore.PARAM_LIMIT, String.valueOf(limit)); @@ -953,19 +943,18 @@ public class DownloadStorageProvider extends FileSystemProvider { // Copied from MediaDocumentsProvider with some tweaks private Pair<String, String[]> buildSearchSelection(@Nullable Bundle queryArgs, - @Nullable LongArray idsToExclude, @Nullable String parentId) { + @Nullable Set<String> filePaths, @Nullable String parentId) { final StringBuilder selection = new StringBuilder(); final ArrayList<String> selectionArgs = new ArrayList<>(); - if (parentId == null && idsToExclude != null && idsToExclude.size() > 0) { + if (parentId == null && filePaths != null && filePaths.size() > 0) { if (selection.length() > 0) { selection.append(" AND "); } - selection.append(DownloadColumns._ID + " NOT IN ("); - final int size = idsToExclude.size(); - for (int i = 0; i < size; ++i) { - selection.append(idsToExclude.get(i) + ((i == size - 1) ? ")" : ",")); - } + selection.append(DownloadColumns.DATA + " NOT IN ("); + selection.append(TextUtils.join(",", Collections.nCopies(filePaths.size(), "?"))); + selection.append(")"); + selectionArgs.addAll(filePaths); } if (parentId != null) { |