summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-05-23 03:09:02 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-05-23 03:09:02 +0000
commit9fbc33175656193ff282c3e92439bbd29edaecec (patch)
treefef2ebc72a6a0632f43b4f48cf0b21e78203ba10
parent794fc5d7032563f8f716dd0d02406635e33f0dcd (diff)
parentf2ac1e336e3f34b5ae6a528f0931e6aa237f3979 (diff)
downloadandroid_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.java167
-rw-r--r--src/com/android/providers/downloads/DownloadStorageProvider.java99
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) {