Create a new permission that allows apps to see downloads from other
Leon Scroggins [Thu, 28 Jan 2010 22:53:26 +0000 (17:53 -0500)]
applications to the SD card.

Necessary for http://b/issue?id=2384554

Also create names for files by default, so they do not display as
<Untitled>.  Remove calls to createTitleFromFilename, which are
no longer necessary.

Requires a change to frameworks/base.

AndroidManifest.xml
res/values/strings.xml
src/com/android/providers/downloads/DownloadNotification.java
src/com/android/providers/downloads/DownloadProvider.java

index 374b985..1b111f0 100644 (file)
         android:description="@string/permdesc_downloadCompletedIntent"
         android:protectionLevel="signature" />
 
+    <permission android:name="android.permission.SEE_ALL_EXTERNAL"
+        android:label="@string/permlab_seeAllExternal"
+        android:description="@string/permdesc_seeAllExternal"
+        android:protectionLevel="normal"/>
+
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
     <uses-permission android:name="android.permission.ACCESS_DRM" />
index e7fda55..7e40b60 100644 (file)
         can use this to confuse other applications that download
         files.</string>
 
+    <!-- Title for permission to see all downloads to EXTERNAL -->
+    <string name="permlab_seeAllExternal">See all downloads to SD card</string>
+    <!-- Description for the permission to see all downloads to EXTERNAL -->
+    <string name="permdesc_seeAllExternal">Allows the application to see all
+        downloads to the SD card, regardless of which application downloaded
+        them.</string>
     <!-- This is the title that is used when displaying the notification
     for a download that doesn't have a title associated with it. -->
     <string name="download_unknown_title">&lt;Untitled&gt;</string>
index abd975b..e9c0d4e 100644 (file)
@@ -119,7 +119,7 @@ class DownloadNotification {
                         Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
                         Downloads.Impl.COLUMN_CURRENT_BYTES,
                         Downloads.Impl.COLUMN_TOTAL_BYTES,
-                        Downloads.Impl.COLUMN_STATUS, Downloads.Impl._DATA
+                        Downloads.Impl.COLUMN_STATUS
                 },
                 WHERE_RUNNING, null, Downloads.Impl._ID);
         
@@ -136,7 +136,6 @@ class DownloadNotification {
         final int currentBytesColumn = 5;
         final int totalBytesColumn = 6;
         final int statusColumn = 7;
-        final int filenameColumnId = 8;
 
         // Collate the notifications
         mNotifications.clear();
@@ -147,14 +146,8 @@ class DownloadNotification {
             long id = c.getLong(idColumn);
             String title = c.getString(titleColumn);
             if (title == null || title.length() == 0) {
-                String filename = c.getString(filenameColumnId);
-                if (filename == null) {
-                    title = mContext.getResources().getString(
-                            R.string.download_unknown_title);
-                } else {
-                    title = Downloads.Impl.createTitleFromFilename(mContext,
-                            filename, id);
-                }
+                title = mContext.getResources().getString(
+                        R.string.download_unknown_title);
             }
             if (mNotifications.containsKey(packageName)) {
                 mNotifications.get(packageName).addItem(title, progress, max);
@@ -232,7 +225,6 @@ class DownloadNotification {
                         Downloads.Impl.COLUMN_CURRENT_BYTES,
                         Downloads.Impl.COLUMN_TOTAL_BYTES,
                         Downloads.Impl.COLUMN_STATUS,
-                        Downloads.Impl._DATA,
                         Downloads.Impl.COLUMN_LAST_MODIFICATION,
                         Downloads.Impl.COLUMN_DESTINATION
                 },
@@ -251,9 +243,8 @@ class DownloadNotification {
         final int currentBytesColumn = 5;
         final int totalBytesColumn = 6;
         final int statusColumn = 7;
-        final int filenameColumnId = 8;
-        final int lastModColumnId = 9;
-        final int destinationColumnId = 10;
+        final int lastModColumnId = 8;
+        final int destinationColumnId = 9;
 
         for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
             // Add the notifications
@@ -263,14 +254,8 @@ class DownloadNotification {
             long id = c.getLong(idColumn);
             String title = c.getString(titleColumn);
             if (title == null || title.length() == 0) {
-                String filename = c.getString(filenameColumnId);
-                if (filename == null) {
-                    title = mContext.getResources().getString(
-                            R.string.download_unknown_title);
-                } else {
-                    title = Downloads.Impl.createTitleFromFilename(mContext,
-                            filename, id);
-                }
+                title = mContext.getResources().getString(
+                        R.string.download_unknown_title);
             }
             Uri contentUri = Uri.parse(Downloads.Impl.CONTENT_URI + "/" + id);
             String caption;
index db00554..d7c24f9 100644 (file)
@@ -413,23 +413,40 @@ public final class DownloadProvider extends ContentProvider {
                 callingUid != mSystemUid &&
                 callingUid != mDefContainerUid &&
                 Process.supportsProcesses()) {
-            if (!emptyWhere) {
-                qb.appendWhere(" AND ");
-            }
-            qb.appendWhere("( " + Constants.UID + "=" +  Binder.getCallingUid() + " OR "
-                    + Downloads.Impl.COLUMN_OTHER_UID + "=" +  Binder.getCallingUid() + " )");
-            emptyWhere = false;
-
+            boolean canSeeAllExternal;
             if (projection == null) {
                 projection = sAppReadableColumnsArray;
+                // sAppReadableColumnsArray includes _DATA, which is not allowed
+                // to be seen except by the initiating application
+                canSeeAllExternal = false;
             } else {
+                canSeeAllExternal = getContext().checkCallingPermission(
+                        Downloads.Impl.PERMISSION_SEE_ALL_EXTERNAL)
+                        == PackageManager.PERMISSION_GRANTED;
                 for (int i = 0; i < projection.length; ++i) {
                     if (!sAppReadableColumnsSet.contains(projection[i])) {
                         throw new IllegalArgumentException(
                                 "column " + projection[i] + " is not allowed in queries");
                     }
+                    canSeeAllExternal = canSeeAllExternal
+                            && !projection[i].equals(Downloads.Impl._DATA);
                 }
             }
+            if (!emptyWhere) {
+                qb.appendWhere(" AND ");
+                emptyWhere = false;
+            }
+            String validUid = "( " + Constants.UID + "="
+                    + Binder.getCallingUid() + " OR "
+                    + Downloads.Impl.COLUMN_OTHER_UID + "="
+                    + Binder.getCallingUid() + " )";
+            if (canSeeAllExternal) {
+                qb.appendWhere("( " + validUid + " OR "
+                        + Downloads.Impl.DESTINATION_EXTERNAL + " = "
+                        + Downloads.Impl.COLUMN_DESTINATION + " )");
+            } else {
+                qb.appendWhere(validUid);
+            }
         }
 
         if (Constants.LOGVV) {
@@ -526,6 +543,16 @@ public final class DownloadProvider extends ContentProvider {
             copyString(Downloads.Impl.COLUMN_DESCRIPTION, values, filteredValues);
         } else {
             filteredValues = values;
+            String filename = values.getAsString(Downloads.Impl._DATA);
+            if (filename != null) {
+                Cursor c = query(uri, new String[]
+                        { Downloads.Impl.COLUMN_TITLE }, null, null, null);
+                if (!c.moveToFirst() || c.getString(0) == null) {
+                    values.put(Downloads.Impl.COLUMN_TITLE,
+                            new File(filename).getName());
+                }
+                c.close();
+            }
         }
         int match = sURIMatcher.match(uri);
         switch (match) {