Make all public API downloads visible.
Steve Howard [Thu, 29 Jul 2010 00:51:02 +0000 (17:51 -0700)]
This change makes all downloads through the public API visible by
default.  It removes the API that had allowed applications to control
notifications while the download runs.  This has been replaced with a
hidden API, since such behavior is needed by SystemUpdater and Market
(for self-updates).  Additionally, the behavior is now protected by a
new permission.

I'm making this permission signatureOrSystem, and changing the
non-purgeable permission to the same (it should've been that, I just
didn't know).  I'm also adding string descriptions to appease the
translation folks.

Change-Id: I192e8b19ff9b0e425257cef0db081c3d75996ea5

AndroidManifest.xml
res/values/strings.xml
src/com/android/providers/downloads/DownloadProvider.java
tests/public_api_access/src/com/android/providers/downloads/public_api_access_tests/PublicApiAccessTest.java
tests/src/com/android/providers/downloads/PublicApiFunctionalTest.java

index d6c85d3..8431d1e 100644 (file)
     <permission android:name="android.permission.DOWNLOAD_CACHE_NON_PURGEABLE"
         android:label="@string/permlab_downloadCacheNonPurgeable"
         android:description="@string/permdesc_downloadCacheNonPurgeable"
-        android:protectionLevel="dangerous"/>
+        android:protectionLevel="signatureOrSystem"/>
+
+    <!-- Allows to queue downloads without a notification shown while the download runs. -->
+    <permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"
+        android:label="@string/permlab_downloadWithoutNotification"
+        android:description="@string/permdesc_downloadWithoutNotification"
+        android:protectionLevel="signatureOrSystem"/>
 
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
index 0bc5c09..62ba38f 100644 (file)
         downloads to the SD card, regardless of which application downloaded
         them.</string>
 
-    <string name="permlab_downloadCacheNonPurgeable">Download non-purgeable
-      files to the cache</string>
+    <!-- The label for the permission to download files to the download cache
+    that can't be automatically deleted by the download manager to free up
+    space [CHAR LIMIT=50] -->
+    <string name="permlab_downloadCacheNonPurgeable">Reserve space in the
+    download cache</string>
+    <!-- The full sentence description for the permission to download files to
+    the download cache that can't be automatically deleted by the download
+    manager to free up space [CHAR LIMIT=160] -->
     <string name="permdesc_downloadCacheNonPurgeable">Allows the application to
-    download files to the download cache through the public API which will not
-    be automatically deleted when the download manager needs more space.
-    Malicious applications can use this to block other applications from using
-    the download cache.</string>
+    download files to the download cache which cannot be automatically deleted
+    when the download manager needs more space.</string>
+
+    <!-- The label for the permission to download files through the download
+    manager without any notification being shown to the user [CHAR LIMIT=50] -->
+    <string name="permlab_downloadWithoutNotification">Use download manager
+    without notification</string>
+
+    <!-- The full sentence description for the permission to download files
+    through the download manager without any notification being shown to the
+    user [CHAR LIMIT=160] -->
+    <string name="permdesc_downloadWithoutNotification">Allows the application
+    to download files through the download manager without any notification
+    being shown to the user.</string>
+
 
     <!-- This is the title that is used when displaying the notification
     for a download that doesn't have a title associated with it. -->
index a116f87..41b732f 100644 (file)
@@ -499,8 +499,15 @@ public final class DownloadProvider extends ContentProvider {
         enforceAllowedValues(values, Downloads.Impl.COLUMN_DESTINATION,
                 Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE,
                 Downloads.Impl.DESTINATION_FILE_URI);
-        enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
-                null, Downloads.Impl.VISIBILITY_VISIBLE);
+
+        if (getContext().checkCallingOrSelfPermission(Downloads.Impl.PERMISSION_NO_NOTIFICATION)
+                == PackageManager.PERMISSION_GRANTED) {
+            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
+                    Downloads.Impl.VISIBILITY_HIDDEN, Downloads.Impl.VISIBILITY_VISIBLE);
+        } else {
+            enforceAllowedValues(values, Downloads.Impl.COLUMN_VISIBILITY,
+                    Downloads.Impl.VISIBILITY_VISIBLE);
+        }
 
         // remove the rest of the columns that are allowed (with any value)
         values.remove(Downloads.Impl.COLUMN_URI);
index aca5791..4b2ae09 100644 (file)
@@ -53,7 +53,7 @@ public class PublicApiAccessTest extends AndroidTestCase {
     @Override
     protected void tearDown() throws Exception {
         if (mContentResolver != null) {
-            mContentResolver.delete(Downloads.CONTENT_URI, null, null); 
+            mContentResolver.delete(Downloads.CONTENT_URI, null, null);
         }
         super.tearDown();
     }
@@ -61,7 +61,7 @@ public class PublicApiAccessTest extends AndroidTestCase {
     public void testMinimalValidWrite() {
         mContentResolver.insert(Downloads.Impl.CONTENT_URI, buildValidValues());
     }
-    
+
     public void testMaximalValidWrite() {
         ContentValues values = buildValidValues();
         values.put(Downloads.Impl.COLUMN_TITLE, "foo");
@@ -76,18 +76,19 @@ public class PublicApiAccessTest extends AndroidTestCase {
     private ContentValues buildValidValues() {
         ContentValues values = new ContentValues();
         values.put(Downloads.Impl.COLUMN_URI, "foo");
-        values.put(Downloads.Impl.COLUMN_DESTINATION, 
+        values.put(Downloads.Impl.COLUMN_DESTINATION,
                 Downloads.Impl.DESTINATION_CACHE_PARTITION_PURGEABLE);
+        values.put(Downloads.Impl.COLUMN_VISIBILITY, Downloads.Impl.VISIBILITY_VISIBLE);
         values.put(Downloads.Impl.COLUMN_IS_PUBLIC_API, true);
         return values;
     }
-    
+
     public void testNoPublicApi() {
         ContentValues values = buildValidValues();
         values.remove(Downloads.Impl.COLUMN_IS_PUBLIC_API);
         testInvalidValues(values);
     }
-    
+
     public void testInvalidDestination() {
         ContentValues values = buildValidValues();
         values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_EXTERNAL);
@@ -95,14 +96,20 @@ public class PublicApiAccessTest extends AndroidTestCase {
         values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_CACHE_PARTITION);
         testInvalidValues(values);
     }
-    
+
     public void testInvalidVisibility() {
         ContentValues values = buildValidValues();
-        values.put(Downloads.Impl.COLUMN_VISIBILITY, 
+        values.put(Downloads.Impl.COLUMN_VISIBILITY,
                 Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
         testInvalidValues(values);
+
+        values.put(Downloads.Impl.COLUMN_VISIBILITY, Downloads.Impl.VISIBILITY_HIDDEN);
+        testInvalidValues(values);
+
+        values.remove(Downloads.Impl.COLUMN_VISIBILITY);
+        testInvalidValues(values);
     }
-    
+
     public void testDisallowedColumns() {
         for (String column : DISALLOWED_COLUMNS) {
             ContentValues values = buildValidValues();
@@ -110,7 +117,7 @@ public class PublicApiAccessTest extends AndroidTestCase {
             testInvalidValues(values);
         }
     }
-    
+
     public void testFileUriWithoutExternalPermission() {
         ContentValues values = buildValidValues();
         values.put(Downloads.Impl.COLUMN_DESTINATION, Downloads.Impl.DESTINATION_FILE_URI);
index b601846..840b20a 100644 (file)
@@ -358,8 +358,7 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
     }
 
     public void testNotificationClickedBroadcast() throws Exception {
-        Download download = enqueueRequest(getRequest().setShowNotification(
-                DownloadManager.Request.NOTIFICATION_WHEN_RUNNING));
+        Download download = enqueueRequest(getRequest());
 
         DownloadReceiver receiver = new DownloadReceiver();
         receiver.mSystemFacade = mSystemFacade;
@@ -431,15 +430,13 @@ public class PublicApiFunctionalTest extends AbstractPublicApiTest {
 
     public void testNotifications() throws Exception {
         enqueueEmptyResponse(HTTP_OK);
-        Download download = enqueueRequest(getRequest()); // no visibility requested
+        Download download = enqueueRequest(getRequest().setShowRunningNotification(false));
         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
         assertEquals(0, mSystemFacade.mActiveNotifications.size());
         assertEquals(0, mSystemFacade.mCanceledNotifications.size());
 
         enqueueEmptyResponse(HTTP_OK);
-        download = enqueueRequest(
-                getRequest()
-                .setShowNotification(DownloadManager.Request.NOTIFICATION_WHEN_RUNNING));
+        download = enqueueRequest(getRequest()); // notifications by default
         download.runUntilStatus(DownloadManager.STATUS_SUCCESSFUL);
         assertEquals(1, mSystemFacade.mActiveNotifications.size());