为什么 https 下载暂停 PAUSED_WAITING_TO_RETRY?

Why are https downloads pausing with PAUSED_WAITING_TO_RETRY?

我正在使用 DownloadManager service in Android to download files over https URLs, such as https://www.antennahouse.com/XSLsample/pdf/sample-link_1.pdf。这些文件(当前)不受密码保护,以防有任何不同。

一旦我将下载请求加入队列,DownloadManager 就会开始下载,但似乎挂起。当我检查状态时,我得到

COLUMN_BYTES_DOWNLOADED_SO_FAR: 0
COLUMN_STATUS: 4
COLUMN_REASON: 1

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

COLUMN_REASON 1 是 PAUSED_WAITING_TO_RETRY, "when the download is paused because some network error occurred and the download manager is waiting before retrying the request." 但是似乎没有办法确定 发生了什么网络错误 。下载从未成功完成。

我已经检查了 logcat 监视器是否有相关的警告和错误,但一无所获。

我已经在内部和 public 的多个不同服务器上进行了尝试,结果相同。

没有明显的网络问题:Wi-Fi连接上了,用http://下载没问题:文件下载很快,出现在指定目的地的文件系统。

对于 https 下载,我们的服务器日志显示从服务器的角度来看,文件已成功提供。在笔记本电脑上的 浏览器 中测试相同的 https URL 可以成功下载文件,开发人员工具网络面板中不会出现任何明显问题或额外协商。

我的代码(总结):

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

总结:通过 DownloadManager 启动的 https 下载会无限期挂起,而相同的 https 下载在浏览器中运行良好,而普通的 http 下载在使用 DownloadManager 的同一应用程序中运行良好。我的最佳线索是 PAUSED_WAITING_TO_RETRY 表示 "some network error occurred." 我如何确定网络错误是什么?

最直接的原因似乎是 CertificateException: CertPathValidatorException: Trust anchor for certification path not found. 我是通过 using an HttpURLConnection directly(感谢@CommonsWare 的建议)而不是 DownloadManager 发现的,它提供了更直接的访问例外。 (在我们内部服务器的情况下确实如此。我提到的样本 URL 似乎并非如此,它似乎与 DownloadManager 有同样的问题......但也许是一样的问题有不同的原因。)

根 CA(在所有情况下)是 "AddTrust External CA Root",它存在于设备上受信任的 CA 列表中,位于“设置”>“安全”>“受信任的凭据”下。因此,如果这是受信任的 CA 根,为什么会出现 "Trust anchor for certification path not found" 异常?

服务器服务的链中似乎没有包含中间 CA,因为 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 提出了两种可能的解决方案:将中间 CA 包含在服务器链中,或者在应用程序通过创建一个特殊的 TrustManager。我希望做前者。