summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-05-01 03:02:14 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-05-01 03:02:14 +0000
commit1a2ddf0a75e755b3e9d98148a54014ca8c4d412b (patch)
tree74d93cb18ce5383527a76bec896cdae6788e2220
parent8f2879758e870b8e2e65ac5b152930a0293968ed (diff)
parent178b6d89e9799473679a0d50417bda8bae2ec963 (diff)
downloadandroid_packages_providers_DownloadProvider-1a2ddf0a75e755b3e9d98148a54014ca8c4d412b.tar.gz
android_packages_providers_DownloadProvider-1a2ddf0a75e755b3e9d98148a54014ca8c4d412b.tar.bz2
android_packages_providers_DownloadProvider-1a2ddf0a75e755b3e9d98148a54014ca8c4d412b.zip
Snap for 5519018 from 178b6d89e9799473679a0d50417bda8bae2ec963 to qt-release
Change-Id: Id473234167b852783e1afeba615cd27774c5ee42
-rw-r--r--src/com/android/providers/downloads/DownloadProvider.java100
-rw-r--r--src/com/android/providers/downloads/DownloadReceiver.java17
-rw-r--r--tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java5
3 files changed, 98 insertions, 24 deletions
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index a6159ca7..a7f2093c 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -22,11 +22,14 @@ import static android.provider.Downloads.Impl.COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI;
import static android.provider.Downloads.Impl.COLUMN_MEDIASTORE_URI;
import static android.provider.Downloads.Impl.COLUMN_MEDIA_SCANNED;
import static android.provider.Downloads.Impl.COLUMN_OTHER_UID;
+import static android.provider.Downloads.Impl.DESTINATION_EXTERNAL;
+import static android.provider.Downloads.Impl.DESTINATION_FILE_URI;
import static android.provider.Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD;
import static android.provider.Downloads.Impl.MEDIA_NOT_SCANNABLE;
import static android.provider.Downloads.Impl.MEDIA_NOT_SCANNED;
import static android.provider.Downloads.Impl.MEDIA_SCANNED;
import static android.provider.Downloads.Impl.PERMISSION_ACCESS_ALL;
+import static android.provider.Downloads.Impl._DATA;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -56,6 +59,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
import android.os.Process;
@@ -100,7 +104,7 @@ public final class DownloadProvider extends ContentProvider {
/** Database filename */
private static final String DB_NAME = "downloads.db";
/** Current database version */
- private static final int DB_VERSION = 112;
+ private static final int DB_VERSION = 113;
/** Name of table in the database */
private static final String DB_TABLE = "downloads";
/** Memory optimization - close idle connections after 30s of inactivity */
@@ -327,7 +331,11 @@ public final class DownloadProvider extends ContentProvider {
break;
case 112:
- UpdateMediaStoreUrisFromFilesToDownloads(db);
+ updateMediaStoreUrisFromFilesToDownloads(db);
+ break;
+
+ case 113:
+ canonicalizeDataPaths(db);
break;
default:
@@ -423,7 +431,7 @@ public final class DownloadProvider extends ContentProvider {
* MediaStore.Downloads specific columns. To avoid this, update the existing entries to
* use MediaStore.Downloads based uris only.
*/
- private void UpdateMediaStoreUrisFromFilesToDownloads(SQLiteDatabase db) {
+ private void updateMediaStoreUrisFromFilesToDownloads(SQLiteDatabase db) {
try (Cursor cursor = db.query(DB_TABLE,
new String[] { Downloads.Impl._ID, COLUMN_MEDIASTORE_URI },
COLUMN_MEDIASTORE_URI + " IS NOT NULL", null, null, null, null)) {
@@ -445,6 +453,30 @@ public final class DownloadProvider extends ContentProvider {
}
}
+ private void canonicalizeDataPaths(SQLiteDatabase db) {
+ try (Cursor cursor = db.query(DB_TABLE,
+ new String[] { Downloads.Impl._ID, Downloads.Impl._DATA},
+ Downloads.Impl._DATA + " IS NOT NULL", null, null, null, null)) {
+ final ContentValues updateValues = new ContentValues();
+ while (cursor.moveToNext()) {
+ final long id = cursor.getLong(0);
+ final String filePath = cursor.getString(1);
+ final String canonicalPath;
+ try {
+ canonicalPath = new File(filePath).getCanonicalPath();
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Found invalid path='" + filePath + "' for id=" + id);
+ continue;
+ }
+
+ updateValues.clear();
+ updateValues.put(Downloads.Impl._DATA, canonicalPath);
+ db.update(DB_TABLE, updateValues, Downloads.Impl._ID + "=?",
+ new String[] { Long.toString(id) });
+ }
+ }
+ }
+
/**
* Add a column to a table using ALTER TABLE.
* @param dbTable name of the table
@@ -526,18 +558,38 @@ public final class DownloadProvider extends ContentProvider {
mStorageManager = getContext().getSystemService(StorageManager.class);
+ reconcileRemovedUidEntries();
+ return true;
+ }
+
+ private void reconcileRemovedUidEntries() {
// Grant access permissions for all known downloads to the owning apps
final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
- final Cursor cursor = db.query(DB_TABLE, new String[] {
- Downloads.Impl._ID, Constants.UID }, null, null, null, null, null);
+ final Cursor cursor = db.query(DB_TABLE,
+ new String[] { Downloads.Impl._ID, Constants.UID, COLUMN_DESTINATION, _DATA },
+ null, null, null, null, null);
final ArrayList<Long> idsToDelete = new ArrayList<>();
+ final ArrayList<Long> idsToOrphan = new ArrayList<>();
+ final String downloadsDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
try {
while (cursor.moveToNext()) {
final long downloadId = cursor.getLong(0);
final int uid = cursor.getInt(1);
+
final String ownerPackage = getPackageForUid(uid);
if (ownerPackage == null) {
- idsToDelete.add(downloadId);
+ final int destination = cursor.getInt(2);
+ final String filePath = cursor.getString(3);
+
+ if ((destination == DESTINATION_EXTERNAL
+ || destination == DESTINATION_FILE_URI
+ || destination == DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD)
+ && FileUtils.contains(downloadsDir, filePath)) {
+ idsToOrphan.add(downloadId);
+ } else {
+ idsToDelete.add(downloadId);
+ }
} else {
grantAllDownloadsPermission(ownerPackage, downloadId);
}
@@ -545,25 +597,29 @@ public final class DownloadProvider extends ContentProvider {
} finally {
cursor.close();
}
+ if (idsToOrphan.size() > 0) {
+ Log.i(Constants.TAG, "Orphaning downloads with ids "
+ + Arrays.toString(idsToOrphan.toArray()) + " as owner package is missing");
+ final ContentValues values = new ContentValues();
+ values.putNull(Constants.UID);
+ update(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, values,
+ buildQueryWithIds(idsToOrphan), null);
+ }
if (idsToDelete.size() > 0) {
- Log.i(Constants.TAG,
- "Deleting downloads with ids " + idsToDelete + " as owner package is missing");
- deleteDownloadsWithIds(idsToDelete);
+ Log.i(Constants.TAG, "Deleting downloads with ids "
+ + Arrays.toString(idsToDelete.toArray()) + " as owner package is missing");
+ delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, buildQueryWithIds(idsToDelete), null);
}
- return true;
}
- private void deleteDownloadsWithIds(ArrayList<Long> downloadIds) {
- final int N = downloadIds.size();
- if (N == 0) {
- return;
- }
+ private String buildQueryWithIds(ArrayList<Long> downloadIds) {
final StringBuilder queryBuilder = new StringBuilder(Downloads.Impl._ID + " in (");
- for (int i = 0; i < N; i++) {
+ final int size = downloadIds.size();
+ for (int i = 0; i < size; i++) {
queryBuilder.append(downloadIds.get(i));
- queryBuilder.append((i == N - 1) ? ")" : ",");
+ queryBuilder.append((i == size - 1) ? ")" : ",");
}
- delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, queryBuilder.toString(), null);
+ return queryBuilder.toString();
}
/**
@@ -1002,6 +1058,7 @@ public final class DownloadProvider extends ContentProvider {
final File file;
try {
file = new File(path).getCanonicalFile();
+ values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, Uri.fromFile(file).toString());
} catch (IOException e) {
throw new SecurityException(e);
}
@@ -1040,6 +1097,7 @@ public final class DownloadProvider extends ContentProvider {
final File file;
try {
file = new File(path).getCanonicalFile();
+ values.put(Downloads.Impl._DATA, file.getPath());
} catch (IOException e) {
throw new SecurityException(e);
}
@@ -1516,6 +1574,12 @@ public final class DownloadProvider extends ContentProvider {
filteredValues = values;
String filename = values.getAsString(Downloads.Impl._DATA);
if (filename != null) {
+ try {
+ filteredValues.put(Downloads.Impl._DATA, new File(filename).getCanonicalPath());
+ } catch (IOException e) {
+ throw new IllegalStateException("Invalid path: " + filename);
+ }
+
Cursor c = null;
try {
c = query(uri, new String[]
diff --git a/src/com/android/providers/downloads/DownloadReceiver.java b/src/com/android/providers/downloads/DownloadReceiver.java
index 92d0bad4..6a0707bb 100644
--- a/src/com/android/providers/downloads/DownloadReceiver.java
+++ b/src/com/android/providers/downloads/DownloadReceiver.java
@@ -37,11 +37,14 @@ import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Downloads;
+import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.widget.Toast;
+import java.util.regex.Pattern;
+
/**
* Receives system broadcasts (boot, network connectivity)
*/
@@ -144,11 +147,17 @@ public class DownloadReceiver extends BroadcastReceiver {
// First, disown any downloads that live in shared storage
final ContentValues values = new ContentValues();
values.putNull(Constants.UID);
+
+ final StringBuilder queryString = new StringBuilder(Constants.UID + "=" + uid);
+ queryString.append(" AND ").append(Downloads.Impl.COLUMN_DESTINATION + " IN ("
+ + Downloads.Impl.DESTINATION_EXTERNAL + ","
+ + Downloads.Impl.DESTINATION_FILE_URI + ","
+ + Downloads.Impl.DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD + ")");
+ queryString.append(" AND ").append(Downloads.Impl._DATA
+ + " REGEXP '" + MediaStore.Downloads.PATTERN_DOWNLOADS_FILE.pattern() + "'");
+
final int disowned = resolver.update(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, values,
- Constants.UID + "=" + uid + " AND " + Downloads.Impl.COLUMN_DESTINATION + " IN ("
- + Downloads.Impl.DESTINATION_EXTERNAL + ","
- + Downloads.Impl.DESTINATION_FILE_URI + ")",
- null);
+ queryString.toString(), null);
// Finally, delete any remaining downloads owned by UID
final int deleted = resolver.delete(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI,
diff --git a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
index 97bc4a22..cabcc313 100644
--- a/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java
@@ -85,8 +85,9 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
mNotifManager = getContext().getSystemService(NotificationManager.class);
mDownloadManager = getContext().getSystemService(DownloadManager.class);
- mTestDirectory = new File(Environment.getExternalStorageDirectory() + File.separator
- + "download_manager_functional_test");
+ mTestDirectory = new File(Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_DOWNLOADS)
+ + File.separator + "download_manager_functional_test");
if (mTestDirectory.exists()) {
IoUtils.deleteContents(mTestDirectory);
} else {