9

I'm using the DownloadManager service in Android to download files over https URLs, such as https://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf. The files are not (currently) password protected, in case that makes any difference.

Once I enqueue a download request, the DownloadManager starts the download, but it seems to hang. When I check the status, I get

COLUMN_BYTES_DOWNLOADED_SO_FAR: 0
COLUMN_STATUS: 4
COLUMN_REASON: 1

COLUMN_STATUS 4 is STATUS_PAUSED, "when the download is waiting to retry or resume."

COLUMN_REASON 1 is PAUSED_WAITING_TO_RETRY, "when the download is paused because some network error occurred and the download manager is waiting before retrying the request." But there doesn't seem to be a way to determine what network error occurred. The download never successfully completes.

I've checked the logcat monitor for related warnings and errors, but found nothing.

I've tried this with multiple different servers, both in-house and public, with the same result.

There is no obvious network problem: the Wi-Fi connection is up, and downloads using http:// work just fine: the file is downloaded promptly and appears in the filesystem at the specified destination.

In the case of https downloads, our server logs show that the files are being successfully served from the server's point of view. Testing the same https URLs in a browser on a laptop result in successful download of the files, without any obvious problems or extra negotiations showing up in the developer tools network panel.

My code (summarized):

sManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request req = new DownloadManager.Request(sourceURI);
final Uri destinationUri = Uri.fromFile(destinationFile);
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
            | DownloadManager.Request.NETWORK_MOBILE)
        .setDestinationUri(destinationUri)
        .setMimeType(...);

long id = sManager.enqueue(req);

Summary: An https download started via DownloadManager hangs indefinitely, while the same https download works fine in a browser, and plain http downloads work fine with the same app using DownloadManager. The best clue I have is that PAUSED_WAITING_TO_RETRY indicates "some network error occurred." How can I determine what the network error is?

LarsH
  • 27,481
  • 8
  • 94
  • 152
  • 2
    Are you sure the SSL certificate of the URL is issued by a CA which is trusted by Android? – Prerak Sola Jan 02 '17 at 16:52
  • 1
    On stock Android, the component that actually does the downloading [appears to use `HttpURLConnection`](https://android.googlesource.com/platform/packages/providers/DownloadProvider/+/master/src/com/android/providers/downloads/DownloadThread.java). You might toss together a quick-and-dirty app using `HttpURLConnection` (or modify [one of mine](https://github.com/commonsguy/cw-omnibus/tree/v8.1/Service/Downloader)) and see what you get. I agree with Prerak, that this feels like an SSL handshake issue. – CommonsWare Jan 02 '17 at 16:55
  • @PrerakSola: Good question. I'll look into it. – LarsH Jan 02 '17 at 16:55
  • @PrerakSola: In both the example URL given above and on our in-house server, the root CA is AddTrust External CA Root, which is listed in my device's trusted credentials. The intermediate CAs are different... not sure if that matters... – LarsH Jan 02 '17 at 17:40
  • @CommonsWare: Good idea. I did that, and got a `CertificateException: CertPathValidatorException: Trust anchor for certification path not found.` So ... I'm not sure what that means, given that the root CA for each site is a CA that's on the device's trusted list. – LarsH Jan 02 '17 at 21:09
  • 1
    "I'm not sure what that means, given that the root CA for each site is a CA that's on the device's trusted list" -- how are you determining this? Browsers may have their own CA list, rather than go with some platform set (which, for all I know, might only be available to Java-based apps anyway). – CommonsWare Jan 02 '17 at 21:13
  • @CommonsWare: In both the antennahouse.com URL given above and on our in-house server, the root CA is AddTrust External CA Root, which is listed in my device's trusted credentials under Settings > Security > Trusted credentials. Now I'm looking at https://developer.android.com/training/articles/security-ssl.html#MissingCa to try and figure out if there's a missing intermediate CA causing the error... – LarsH Jan 02 '17 at 21:28

1 Answers1

1

The proximate cause seems to be a CertificateException: CertPathValidatorException: Trust anchor for certification path not found. I found this out by using an HttpURLConnection directly (thanks to @CommonsWare for the suggestion) instead of the DownloadManager, which gave more direct access to exceptions. (This was true in the case of our in-house server. It does not seem to be the case with the sample URL I mentioned, which seemed to be having the same problem with DownloadManager ... but maybe that same problem had a different cause.)

The root CA (in all cases) is "AddTrust External CA Root", which is present in the list of trusted CAs on the device, under Settings > Security > Trusted credentials. So if this is a trusted CA root, why is there a "Trust anchor for certification path not found" exception?

It looks like there is an intermediate CA not being included in the chain served by the server, as described here. In fact, using openssl s_client -connect to check the CA chain confirms that the intermediate CA's are not included. The Android doc article suggests two possible solutions: include the intermediate CA's in the server chain, or explicitly trust them in the app by creating a special TrustManager. I hope to do the former.

LarsH
  • 27,481
  • 8
  • 94
  • 152
  • P.S. The diagnosis and the fix (include intermediate CA's in the served certificate chain) are confirmed. After making that change on the server, the problem has disappeared. – LarsH Jan 03 '17 at 19:38