Merge "Make DownloadProvider honor the cleartext traffic policy."
diff --git a/src/com/android/providers/downloads/DownloadThread.java b/src/com/android/providers/downloads/DownloadThread.java
index c75e419..325b4ee 100644
--- a/src/com/android/providers/downloads/DownloadThread.java
+++ b/src/com/android/providers/downloads/DownloadThread.java
@@ -48,6 +48,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkPolicyManager;
 import android.net.TrafficStats;
+import android.net.Uri;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.Process;
@@ -350,8 +351,17 @@
             throw new StopRequestException(STATUS_BAD_REQUEST, e);
         }
 
+        boolean cleartextTrafficPermitted = mSystemFacade.isCleartextTrafficPermitted(mInfo.mUid);
         int redirectionCount = 0;
         while (redirectionCount++ < Constants.MAX_REDIRECTS) {
+            // Enforce the cleartext traffic opt-out for the UID. This cannot be enforced earlier
+            // because of HTTP redirects which can change the protocol between HTTP and HTTPS.
+            if ((!cleartextTrafficPermitted) && ("http".equalsIgnoreCase(url.getProtocol()))) {
+                throw new StopRequestException(STATUS_BAD_REQUEST,
+                        "Cleartext traffic not permitted for UID " + mInfo.mUid + ": "
+                        + Uri.parse(url.toString()).toSafeString());
+            }
+
             // Open connection and follow any redirects until we have a useful
             // response with body.
             HttpURLConnection conn = null;
diff --git a/src/com/android/providers/downloads/RealSystemFacade.java b/src/com/android/providers/downloads/RealSystemFacade.java
index fa4f348..b3f170f 100644
--- a/src/com/android/providers/downloads/RealSystemFacade.java
+++ b/src/com/android/providers/downloads/RealSystemFacade.java
@@ -16,9 +16,14 @@
 
 package com.android.providers.downloads;
 
+import com.android.internal.util.ArrayUtils;
+
 import android.app.DownloadManager;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
@@ -96,4 +101,43 @@
     public boolean userOwnsPackage(int uid, String packageName) throws NameNotFoundException {
         return mContext.getPackageManager().getApplicationInfo(packageName, 0).uid == uid;
     }
+
+    @Override
+    public boolean isCleartextTrafficPermitted(int uid) {
+        PackageManager packageManager = mContext.getPackageManager();
+        String[] packageNames = packageManager.getPackagesForUid(uid);
+        if (ArrayUtils.isEmpty(packageNames)) {
+            // Unknown UID -- fail safe: cleartext traffic not permitted
+            return false;
+        }
+
+        // Cleartext traffic is permitted from the UID if it's permitted for any of the packages
+        // belonging to that UID.
+        for (String packageName : packageNames) {
+            if (isCleartextTrafficPermitted(packageName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether cleartext network traffic (HTTP) is permitted for the provided package.
+     */
+    private boolean isCleartextTrafficPermitted(String packageName) {
+        PackageManager packageManager = mContext.getPackageManager();
+        PackageInfo packageInfo;
+        try {
+            packageInfo = packageManager.getPackageInfo(packageName, 0);
+        } catch (NameNotFoundException e) {
+            // Unknown package -- fail safe: cleartext traffic not permitted
+            return false;
+        }
+        ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+        if (applicationInfo == null) {
+            // No app info -- fail safe: cleartext traffic not permitted
+            return false;
+        }
+        return (applicationInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC) != 0;
+    }
 }
diff --git a/src/com/android/providers/downloads/SystemFacade.java b/src/com/android/providers/downloads/SystemFacade.java
index 15fc31f..83fc7a6 100644
--- a/src/com/android/providers/downloads/SystemFacade.java
+++ b/src/com/android/providers/downloads/SystemFacade.java
@@ -61,4 +61,9 @@
      * Returns true if the specified UID owns the specified package name.
      */
     public boolean userOwnsPackage(int uid, String pckg) throws NameNotFoundException;
+
+    /**
+     * Returns true if cleartext network traffic is permitted for the specified UID.
+     */
+    public boolean isCleartextTrafficPermitted(int uid);
 }
diff --git a/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
index dbab203..41dff67 100644
--- a/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
+++ b/tests/src/com/android/providers/downloads/DownloadProviderFunctionalTest.java
@@ -101,6 +101,23 @@
         runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS);
     }
 
+    public void testCleartextTrafficPermittedFlagHonored() throws Exception {
+        enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
+        enqueueResponse(buildResponse(HTTP_OK, FILE_CONTENT));
+
+        // Assert that HTTP request succeeds when cleartext traffic is permitted
+        mSystemFacade.mCleartextTrafficPermitted = true;
+        Uri downloadUri = requestDownload("/path");
+        assertEquals("http", downloadUri.getScheme());
+        runUntilStatus(downloadUri, Downloads.Impl.STATUS_SUCCESS);
+
+        // Assert that HTTP request fails when cleartext traffic is not permitted
+        mSystemFacade.mCleartextTrafficPermitted = false;
+        downloadUri = requestDownload("/path");
+        assertEquals("http", downloadUri.getScheme());
+        runUntilStatus(downloadUri, Downloads.Impl.STATUS_BAD_REQUEST);
+    }
+
     /**
      * Read a downloaded file from disk.
      */
diff --git a/tests/src/com/android/providers/downloads/FakeSystemFacade.java b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
index 5a15d39..7581e6f 100644
--- a/tests/src/com/android/providers/downloads/FakeSystemFacade.java
+++ b/tests/src/com/android/providers/downloads/FakeSystemFacade.java
@@ -16,6 +16,7 @@
     Long mMaxBytesOverMobile = null;
     Long mRecommendedMaxBytesOverMobile = null;
     List<Intent> mBroadcastsSent = new ArrayList<Intent>();
+    boolean mCleartextTrafficPermitted = true;
     private boolean mReturnActualTime = false;
 
     public void setUp() {
@@ -82,6 +83,11 @@
         return true;
     }
 
+    @Override
+    public boolean isCleartextTrafficPermitted(int uid) {
+        return mCleartextTrafficPermitted;
+    }
+
     public void setReturnActualTime(boolean flag) {
         mReturnActualTime = flag;
     }