此问题是一个恢复网络后,下载管理没有自动恢复下载的问题。 手机在来电过程中是会断掉数据网络的,
但是在挂断电话之后会恢复,刚开始以为这个问题是应用没有收到 数据网络连接 恢复的广播去重新启动下载服务造成,
因为对比机器 三星 是正常的, 所以很天真地以为增加一个 接收广播 android.intent.action.ANY_DATA_STATE ,然后启动下载服务就可以了。
但是在调试的过程中发现并不行, 因为下载管理的 receiver 配置的export 属性是false:
当export 属性为 false 时, 当前broadcast Receiver 只能收到同一个应用或者拥有同一 user ID 应用发出广播。这个应该是原生处于安全考虑,不能随意改动。
调试时发现 android.intent.action.PHONE_STATE 广播虽然同样报警告错误:
Permission Denial: broadcasting Intent { act=android.intent.action.PHONE_STATE flg=0x10 (has extras) } from android (pid=1786, uid=1001) is not exported from uid 10009 due to receiver com.android.providers.downloads/com.oppo.providers.downloads.OppoDownloadReceiver
Line 14997: 0711 20:10:05.599 1197 1498 V ActivityManager: Broadcast: Intent { act=android.intent.action.PHONE_STATE flg=0x10 (has extras) } ordered=false userid=-1
Line 14999: 07-11 20:10:05.599 1197 1498 V ActivityManager: broadcastIntentLocked callingPid: 2226 callingUid=1001
$ adb shell ps | grep 1197
system 1197 432 2383876 144428 ffffffff 94ed9010 S system_server
80080800@P80080800 /cygdrive/z/git_project/server_150/app/OppoDownLoadProvider/bin
$ adb shell ps | grep 2226
radio 2226 432 2154872 65992 ffffffff 94ed9010 S com.android.phone
最终此路不通~~~~
后来想了想原生既然没有增加监听这个 数据网络状态变化的 广播, 那应该是有其他的方式来恢复下载,并且三星应该也不会去修改原生的,所以继续分析源码,后面发现果不其然, 下载管理中有一个闹钟唤醒机制, 当在某些条件下不能正常下载时, 会设置一个闹钟,在一段时间后重试,重新启动服务。
ACTION_RETRY android.intent.action.DOWNLOAD_WAKEUP
和系统一起分析log :
ActivityManager: Broadcast intent Intent { act=android.intent.action.DOWNLOAD_WAKEUP flg=0x14 cmp=com.android.providers.downloads/.DownloadReceiver (has extras) } on background queue
Line 91967: V/BroadcastQueue( 1183): Delivering to component ComponentInfo{com.android.providers.downloads/com.oppo.providers.downloads.OppoDownloadReceiver}: BroadcastRecord{1a52090b u0 android.intent.action.DOWNLOAD_WAKEUP}
从系统查看到没有发送广播出去的原因为,该广播没有注册receiver进行接收,对比查看receiver 在manifest中名字和代码中指定名字不对应。发送的OPPO重写的类名,但是manifest中注册的还是原生的DownloadReceiver类名。
至此真相浮出水面,水落石出~~~
原生的闹钟定时精妙算法要分享一下:
/**
* Returns the time when a download should be restarted.
*/
public long restartTime(long now) {
if (mNumFailed == 0) {
return now;
}
if (mRetryAfter > 0) {
return mLastMod + mRetryAfter;
}
return mLastMod +
Constants.RETRY_FIRST_DELAY *
(1000 + mFuzz) * (1 << (mNumFailed - 1));
}
推迟基数定位 30 秒,加一个随机时间 也在30秒之类, 失败次数越多,相应时间翻倍处理。
------------------------------------------------------------------
原因:
原生有一个闹钟机制,当无网络时会定一个时间值,然后等时间到了之后发送广播,由广播接收器接受重新启动下载服务,时间值是由基准加随机数计算出来的,如果之前没有下载失败的话,时间值在30秒到一分钟之内,如果之前有失败,则会增长时间值。
但是由于OPPO ROM重写了下载管理广播接收器,在manifest中声明的receiver其名字和代码中延时发送广播指定的receiver名称没有保持一致,导致接受不到广播,无法重新启动下载。
修改方法:
将广播接收器receiver的名称改为和manifest中一致