merge in nougat-cts-release history after reset to nougat-cts-dev
diff --git a/src/com/android/providers/downloads/DownloadProvider.java b/src/com/android/providers/downloads/DownloadProvider.java
index d30018f..d50eedb 100644
--- a/src/com/android/providers/downloads/DownloadProvider.java
+++ b/src/com/android/providers/downloads/DownloadProvider.java
@@ -468,6 +468,19 @@
         if (appInfo != null) {
             mDefContainerUid = appInfo.uid;
         }
+
+        // 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);
+        try {
+            while (cursor.moveToNext()) {
+                grantAllDownloadsPermission(cursor.getLong(0), cursor.getInt(1));
+            }
+        } finally {
+            cursor.close();
+        }
+
         return true;
     }
 
@@ -690,6 +703,7 @@
         }
 
         insertRequestHeaders(db, rowID, values);
+        grantAllDownloadsPermission(rowID, Binder.getCallingUid());
         notifyContentChanged(uri, match);
 
         final long token = Binder.clearCallingIdentity();
@@ -1211,7 +1225,7 @@
                     while (cursor.moveToNext()) {
                         final long id = cursor.getLong(0);
                         scheduler.cancel((int) id);
-
+                        revokeAllDownloadsPermission(id);
                         DownloadStorageProvider.onDownloadProviderDelete(getContext(), id);
 
                         final String path = cursor.getString(1);
@@ -1260,6 +1274,19 @@
             logVerboseOpenFileInfo(uri, mode);
         }
 
+        // Perform normal query to enforce caller identity access before
+        // clearing it to reach internal-only columns
+        final Cursor probeCursor = query(uri, new String[] {
+                Downloads.Impl._DATA }, null, null, null);
+        try {
+            if ((probeCursor == null) || (probeCursor.getCount() == 0)) {
+                throw new FileNotFoundException(
+                        "No file found for " + uri + " as UID " + Binder.getCallingUid());
+            }
+        } finally {
+            IoUtils.closeQuietly(probeCursor);
+        }
+
         final Cursor cursor = queryCleared(uri, new String[] {
                 Downloads.Impl._DATA, Downloads.Impl.COLUMN_STATUS,
                 Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.COLUMN_MEDIA_SCANNED }, null,
@@ -1442,4 +1469,20 @@
             to.put(key, defaultValue);
         }
     }
+
+    private void grantAllDownloadsPermission(long id, int uid) {
+        final String[] packageNames = getContext().getPackageManager().getPackagesForUid(uid);
+        if (packageNames == null || packageNames.length == 0) return;
+
+        // We only need to grant to the first package, since the
+        // platform internally tracks based on UIDs
+        final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
+        getContext().grantUriPermission(packageNames[0], uri,
+                Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+    }
+
+    private void revokeAllDownloadsPermission(long id) {
+        final Uri uri = ContentUris.withAppendedId(Downloads.Impl.ALL_DOWNLOADS_CONTENT_URI, id);
+        getContext().revokeUriPermission(uri, ~0);
+    }
 }