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;
}