本文主要介绍如何修改Android系统下载管理,以支持更多的功能及部分bug修改和如何编译生效。目前内容包括暂停下载、继续下载、通知设置NotiExtra和NotiClass、wifi切换到3g自动暂停、Bug修改。
PS: 很多童鞋不是自己做rom,所以就算修改了系统源码编译出来的包在其他系统上也不通用
这里推荐DownloadProvider@Github,系统下载管理的独立版,可整合进自己的应用,感谢@DONG童鞋提供地址。
下面需要修改的DownloadManager.java所在目录为frameworks/base/core/java/android/app
DownloadInfo.java, DownloadProvider.java,DownloadThread.java文件所在目录为packages/providers/DownloadProvider/src/com/android/providers/downloads
1、暂停、继续下载功能
(1) DownloadProvider类修改
1
|
public
int
update
(
final
Uri
uri
,
final
ContentValues
values
,
final
String
where
,
final
String
[
]
whereArgs
)
|
函数,修改后代码如下(只增加了一行有效代码):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
if
(
Binder
.
getCallingPid
(
)
!=
Process
.
myPid
(
)
)
{
filteredValues
=
new
ContentValues
(
)
;
copyString
(
Downloads
.
Impl
.
COLUMN_APP_DATA
,
values
,
filteredValues
)
;
copyInteger
(
Downloads
.
Impl
.
COLUMN_VISIBILITY
,
values
,
filteredValues
)
;
Integer
i
=
values
.
getAsInteger
(
Downloads
.
Impl
.
COLUMN_CONTROL
)
;
if
(
i
!=
null
)
{
filteredValues
.
put
(
Downloads
.
Impl
.
COLUMN_CONTROL
,
i
)
;
startService
=
true
;
}
// trinea BEGIN, added by trinea@trinea.cn 2013/03/01
copyInteger
(
Downloads
.
Impl
.
COLUMN_STATUS
,
values
,
filteredValues
)
;
// trinea END
copyInteger
(
Downloads
.
Impl
.
COLUMN_CONTROL
,
values
,
filteredValues
)
;
copyString
(
Downloads
.
Impl
.
COLUMN_TITLE
,
values
,
filteredValues
)
;
copyString
(
Downloads
.
Impl
.
COLUMN_MEDIAPROVIDER_URI
,
values
,
filteredValues
)
;
copyString
(
Downloads
.
Impl
.
COLUMN_DESCRIPTION
,
values
,
filteredValues
)
;
copyInteger
(
Downloads
.
Impl
.
COLUMN_DELETED
,
values
,
filteredValues
)
;
}
else
{
|
其中以// trinea BEGIN开头,// trinea END结尾为修改部分,下面代码示例同样如此。因为DownloadProvider安全策略对非该进程id的修改会过滤掉COLUMN_STATUS状态,所以我们需要添加该行。
(2) DownloadThread类修改
1
|
private
void
setupDestinationFile
(
State
state
,
InnerState
innerState
)
|
函数中这个注释掉一个else if,如下:
1
2
3
4
5
6
7
8
9
10
11
|
// trinea BEGIN, noted by trinea@trinea.cn 2013/03/01
//} else if (mInfo.mETag == null && !mInfo.mNoIntegrity) {
// // This should've been caught upon failure
// if (Constants.LOGVV) {
// Log.d(TAG, "setupDestinationFile() unable to resume download, deleting "
// + state.mFilename);
// }
// f.delete();
// throw new StopRequestException(Downloads.Impl.STATUS_CANNOT_RESUME,
// "Trying to resume a download that can't be resumed");
// trinea END
|
上面一段代码表示一个验证过程,可以去掉。
mETag为数据库中的etag字段值,代码中没有解释,感觉是一个验证值,类似hashcode。
mNoIntegrity为数据中no_integrity字段值,表示启动下载的应用程序能否验证下载的文件的完整性。不过坑爹的是对于etag和no_integrity都没有提供设置的接口
(3) DownloadManager类中添加对外接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/**
* pause download, added by trinea@trinea.cn 2013/03/01
*
* @param ids the IDs of the downloads to be paused
* @return the number of downloads actually paused
*/
public
int
pauseDownload
(
long
.
.
.
ids
)
{
if
(
ids
==
null
||
ids
.
length
==
0
)
{
// called with nothing to remove!
throw
new
IllegalArgumentException
(
"input param 'ids' can't be null"
)
;
}
ContentValues
values
=
new
ContentValues
(
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_CONTROL
,
Downloads
.
Impl
.
CONTROL_PAUSED
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_STATUS
,
Downloads
.
Impl
.
STATUS_PAUSED_BY_APP
)
;
if
(
ids
.
length
==
1
)
{
return
mResolver
.
update
(
ContentUris
.
withAppendedId
(
mBaseUri
,
ids
[
0
]
)
,
values
,
null
,
null
)
;
}
return
mResolver
.
update
(
mBaseUri
,
values
,
getWhereClauseForIds
(
ids
)
,
getWhereArgsForIds
(
ids
)
)
;
}
/**
* resume download, added by trinea@trinea.cn 2013/03/01
*
* @param ids the IDs of the downloads to be resumed
* @return the number of downloads actually resumed
*/
public
int
resumeDownload
(
long
.
.
.
ids
)
{
if
(
ids
==
null
||
ids
.
length
==
0
)
{
// called with nothing to remove!
throw
new
IllegalArgumentException
(
"input param 'ids' can't be null"
)
;
}
ContentValues
values
=
new
ContentValues
(
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_CONTROL
,
Downloads
.
Impl
.
CONTROL_RUN
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_STATUS
,
Downloads
.
Impl
.
STATUS_RUNNING
)
;
if
(
ids
.
length
==
1
)
{
return
mResolver
.
update
(
ContentUris
.
withAppendedId
(
mBaseUri
,
ids
[
0
]
)
,
values
,
null
,
null
)
;
}
return
mResolver
.
update
(
mBaseUri
,
values
,
getWhereClauseForIds
(
ids
)
,
getWhereArgsForIds
(
ids
)
)
;
}
|
无论是暂停还是继续我们都是同时把Downloads.Impl.COLUMN_CONTROL和Downloads.Impl.COLUMN_STATUS字段进行修改,因为在DownloadInfo的private boolean isReadyToStart(long now)函数中,会对COLUMN_CONTROL字段进行判断,如果是用户手动暂停的话,是不会自动继续的,部分代码如下:
之后我们直接调用DownloadManager的pauseDownload和resumeDownload接口即可
PS:也可以试试不做第二步的修改,而将第一步DownloadProvider的update函数修改变为
1
2
3
4
|
// trinea BEGIN, added by trinea@trinea.cn 2013/03/01
copyInteger
(
Downloads
.
Impl
.
COLUMN_STATUS
,
values
,
filteredValues
)
;
copyInteger
(
Downloads
.
Impl
.
COLUMN_NO_INTEGRITY
,
values
,
filteredValues
)
;
// trinea END
|
第二步修改变为在public int resumeDownload(long… ids)加入
1
2
3
|
values
.
put
(
Downloads
.
Impl
.
COLUMN_CONTROL
,
Downloads
.
Impl
.
CONTROL_RUN
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_STATUS
,
Downloads
.
Impl
.
STATUS_RUNNING
)
;
values
.
put
(
Downloads
.
Impl
.
COLUMN_NO_INTEGRITY
,
true
)
;
|
没有亲自试,不过按照逻辑应该也可以。
2、通知栏可以设置NotiExtra和NotiClass
(1) DownloadProvider类中修改private void checkInsertPermissions(ContentValues values)函数
1
2
3
4
5
6
|
values
.
remove
(
Downloads
.
Impl
.
COLUMN_IS_VISIBLE_IN_DOWNLOADS_UI
)
;
values
.
remove
(
Downloads
.
Impl
.
COLUMN_MEDIA_SCANNED
)
;
// BEGIN, added by trinea@trinea.cn 2013/03/01
values
.
remove
(
Downloads
.
Impl
.
COLUMN_NOTIFICATION_CLASS
)
;
values
.
remove
(
Downloads
.
Impl
.
COLUMN_NOTIFICATION_EXTRAS
)
;
// trinea END
|
在DownloadProvider insert之前会调用checkInsertPermissions检查不能被插入的字段插入,这里我们需要允许这两个字段存在。
(2) DownloadManager.Request添加对外接口
在Request中添加接口以及允许字段修改。通过允许设置NotiExtra和NotiClass,我们可以给系统传递更丰富的参数,在通知栏点击相应或是DownloadUi中通过broadcast将这些参数传递出来方便应用调用。
3、wifi切换到3g自动暂停
(1) 修改DownloadInfo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
private
int
checkIsNetworkTypeAllowed
(
int
networkType
)
{
if
(
mIsPublicApi
)
{
final
int
flag
=
translateNetworkTypeToApiFlag
(
networkType
)
;
final
boolean
allowAllNetworkTypes
=
mAllowedNetworkTypes
==
~
0
;
if
(
!
allowAllNetworkTypes
&&
(
flag
&
mAllowedNetworkTypes
)
==
0
)
{
return
NETWORK_TYPE_DISALLOWED_BY_REQUESTOR
;
}
// trinea BEGIN
if
(
mStatus
==
Downloads
.
Impl
.
STATUS_WAITING_FOR_NETWORK
&&
flag
!=
DownloadManager
.
Request
.
NETWORK_WIFI
)
{
return
NETWORK_TYPE_DISALLOWED_BY_REQUESTOR
;
}
// trinea END
}
return
checkSizeAllowedForNetwork
(
networkType
)
;
}
|
表示等待网络时始终只等待wifi
(2) 修改DownloadReceiver.java
1
2
3
4
5
6
7
|
}
else
if
(
action
.
equals
(
ConnectivityManager
.
CONNECTIVITY_ACTION
)
)
{
NetworkInfo
info
=
(
NetworkInfo
)
intent
.
getParcelableExtra
(
ConnectivityManager
.
EXTRA_NETWORK_INFO
)
;
if
(
info
!=
null
&&
info
.
isConnected
(
)
)
{
startService
(
context
)
;
}
}
else
if
(
action
.
equals
(
Constants
.
ACTION_RETRY
)
)
{
|
修改为:
1
2
3
4
5
6
7
8
9
10
11
12
|
}
else
if
(
action
.
equals
(
ConnectivityManager
.
CONNECTIVITY_ACTION
)
)
{
NetworkInfo
info
=
(
NetworkInfo
)
intent
.
getParcelableExtra
(
ConnectivityManager
.
EXTRA_NETWORK_INFO
)
;
// trinea BEGIN
/**
* modified by trinea@trinea.cn @2013/04/01, resume download only when network type is wifi
*/
if
(
info
!=
null
&&
info
.
isConnected
(
)
&&
info
.
getType
(
)
==
ConnectivityManager
.
TYPE_WIFI
)
{
// trinea END
startService
(
context
)
;
}
}
else
if
(
action
.
equals
(
Constants
.
ACTION_RETRY
)
)
{
|
表示只有连接wifi时才唤醒service去检查是否下载
(3) 修改DownloadThread.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
/**
* Check if the download has been paused or canceled, stopping the request appropriately if it
* has been.
*/
private
void
checkPausedOrCanceled
(
State
state
)
throws
StopRequestException
{
synchronized
(
mInfo
)
{
if
(
mInfo
.
mControl
==
Downloads
.
Impl
.
CONTROL_PAUSED
)
{
Xlog
.
i
(
Constants
.
DL_ENHANCE
,
"DownloadThread: checkPausedOrCanceled: user pause download"
)
;
throw
new
StopRequestException
(
Downloads
.
Impl
.
STATUS_PAUSED_BY_APP
,
"download paused by owner"
)
;
}
if
(
mInfo
.
mStatus
==
Downloads
.
Impl
.
STATUS_CANCELED
)
{
throw
new
StopRequestException
(
Downloads
.
Impl
.
STATUS_CANCELED
,
"download canceled"
)
;
}
}
// if policy has been changed, trigger connectivity check
if
(
mPolicyDirty
)
{
// trinea BEGIN
/**
* add by trinea@trinea.cn @2013/04/01, when switched from wifi to 3g, pause all download
*/
NetworkInfo
info
=
mSystemFacade
.
getActiveNetworkInfo
(
mInfo
.
mUid
)
;
if
(
info
!=
null
&&
!
info
.
isConnected
(
)
)
{
mInfo
.
mStatus
=
Downloads
.
Impl
.
STATUS_WAITING_FOR_NETWORK
;
}
// trinea END
checkConnectivity
(
)
;
}
}
|
表示如果网络变化并且表示网络断开时,下载状态变为等待网络。
4、Bug修改
(1) 当存储空间不足时,利用DownloadManager下载,只显示通知栏提示,在下载管理UI中不显示
DownloadManager的Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri)函数修改如下:
1
2
3
4
5
6
|
if
(
(
mStatusFlags
&
STATUS_RUNNING
)
!=
0
)
{
parts
.
add
(
statusClause
(
"="
,
Downloads
.
Impl
.
STATUS_RUNNING
)
)
;
// trinea BEGIN
parts
.
add
(
statusClause
(
"="
,
Downloads
.
Impl
.
STATUS_INSUFFICIENT_SPACE_ERROR
)
)
;
// trinea END
}
|
DownloadManager的CursorTranslator类的private int translateStatus(int status) 函数修改如下:
1
2
3
4
5
|
// trinea BEGIN
case
Downloads
.
Impl
.
STATUS_INSUFFICIENT_SPACE_ERROR
:
// trinea END
case
Downloads
.
Impl
.
STATUS_RUNNING
:
return
STATUS_RUNNING
;
|
5、编译安装
修改后是需要重新编译的,需同时编译framweork和DownloadProvider。
framework编译命令为:./makeMtk model mm frameworks/base/core/
编译后apk所在路径为out\target\product\model\system\framework\secondary_framework.jar,之后push到system/framework重启即可。编译命令中model为机型,非mtk平台命令有所不同
DownloadProvider编译命令为./makeMtk model mm packages/providers/DownloadProvider/
编译后apk所在路径为out\target\product\model\system\app\DownloadProvider.apk,之后push到system/app即可(可能需要先删除/system/app/目录下的DownloadProvider.odex)
转自: