admin管理员组

文章数量:1532323

2023年12月13日发(作者:)

使用Download Provider 进行文件下载全攻略

Android内部提供了一个DownloadProvider,是一个非常完整的下载工具,提供了很好的外部接口可以被其他应用程序调用,来完成下载工作。同时也提供和很好的下载、通知、存储等机制。

在Android的Browser等工具里面都用到了这个DownloadProvider。

但是很遗憾的是,这个DownloadProvider不对app开发人员开放,只作为内部使用。

我们现在去探究如何将DownloadProvider拿来给自己用。

让我们先找到DownloadProvider不能用的原因:

先找到它的源代码,在这个位置:/packages/providers/DownloadProvider

打开文件,里面有几个自定义的权限

1.

2.

android:name="_DOWNLOAD_MANAGER"

3. android:label="@string/permlab_downloadManager"

4. android:description="@string/permdesc_downloadManager"

5. android:protectionLevel="signatureOrSystem" />

6.

7.

android:name="_DOWNLOAD_MANAGER_ADVANCED"

8. android:label="@string/permlab_downloadManagerAdvanced"

9. android:description="@string/permdesc_downloadManagerAdvanced"

10. android:protectionLevel="signatureOrSystem" />

11.

12.

android:name="_CACHE_FILESYSTEM"

13. android:label="@string/permlab_cacheFilesystem"

14. android:description="@string/permdesc_cacheFilesystem"

15. android:protectionLevel="signature" />

16. 17.

android:name="_DOWNLOAD_COMPLETED_INTENTS"

18. android:label="@string/permlab_downloadCompletedIntent"

19. android:description="@string/permdesc_downloadCompletedIntent"

20. android:protectionLevel="signature" />

复制代码

这几个权限里面都是android:protectionLevel="signatureOrSystem" 或者 android:protectionLevel="signature", 这个意思是只有你的app拥有system权限,或者和系统一样的签名,才能调用它。

这里是问题的关键。那我们有两种思路:

一种思路是:将这个protectionLevel改成normal,重新编译DownloadProvider工程,让其他app可以直接调用。

另一种思路是:将你自己的app弄成system权限或者和系统一样的签名。

前一种思路已经完全成功了,第二种思路验证了一部分。

先看第一种思路的办法:

1)先将上面几个权限都改成:android:protectionLevel="normal"

2)重新编译DownloadProvider

mmm packages/providers/DownloadProvider

3) 将编译后的apk替换现有的apk

因为是系统app,你可以先给/system以root权限,然后将这个app替换掉。 (作为一个用户app安装也可以,不过重启以后就没有了)

使用类似 # mount -t ubifs -o remount ubi0:system /system 或者 # mount -o

remount ubi0:system /system 给/system rw权限。

然后通过adb push 将 push到 /system/app/下。系统会自动替换这个app。

4)写一个工程来使用DownloadProvider.

直接贴源码了:

1. package nload; 2.

3. import tFoundException;

4. import ;

5. import ty;

6. import tResolver;

7. import tValues;

8. import t;

9. import ;

10. import ;

11. import l;

12. /**

13. * @author lixinso

14. * 使用DownloadProvider

15. */

16. public class DownloadActivity extends Activity {

17. @Override

18. public void onCreate(Bundle savedInstanceState) {

19. te(savedInstanceState);

20. setContentView();

21.

22. //String url =

"192.168.200.76:8080/webserver/dancing-skeleton.3gp";

23. String contentDisposition = "attachment;

filename="dancing-skeleton.3gp"";

24. String mimetype = "video/3GPP";

25.

26. String filename = ileName(url,contentDisposition,

mimetype);

27.

28. URI uri = null;

29.

30. try { 31. // Undo the percent-encoding that KURL may have done.

32. String newUrl = new String((es()));

33. // Parse the url into pieces

34. WebAddress w = new WebAddress(newUrl);

35. String frag = null;

36. String query = null;

37. String path = ;

38. // Break the path into path, query, and fragment

39. if (() > 0) {

40. // Strip the fragment

41. int idx = dexOf('#');

42. if (idx != -1) {

43. frag = ing(idx + 1);

44. path = ing(0, idx);

45. }

46. idx = dexOf('?');

47. if (idx != -1) {

48. query = ing(idx + 1);

49. path = ing(0, idx);

50. }

51. }

52. uri = new URI(e, nfo, , , path,

53. query, frag);

54. } catch (Exception e) {

55. //Log.e(LOGTAG, "Could not parse url for download: " + url, e);

56. return;

57. }

58.

59. ContentValues values = new ContentValues();

60. ("uri", ng()); 61. ("useragent", "Mozilla/5.0 (linux; U; Android 1.5; en-us;

SDK Build/CUPCAKE) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2

Mobile Safari/525.20.1");

62. ("notificationpackage", getPackageName());

63. ("notificationclass", "HelloWorld");

64. ("visibility", 1);

65. ("mimetype", mimetype);

66. ("hint", filename);

67. ("description", t());

68. ("total_bytes", 1349528);

69. ("destination", 1);

70.

71.

72.

73. //这些参数参考:DownloadProvider工程中的:

74. //public static DownloadFileInfo generateSaveFile(

75. // Context context,

76. // String url,

77. // String hint,

78. // String contentDisposition,

79. // String contentLocation,

80. // String mimeType,

81. // int destination,

82. // int contentLength) throws FileNotFoundException {

83. //以及: framework里的;

84.

85.

86. ContentResolver mResolver = getContentResolver();

87. (("content://downloads/download"),

values);

88.

89. } 90. }

91.

92.

93.

94. package="nload"

95. android:versionCode="1"

96. android:versionName="1.0">

97.

android:label="@string/app_name">

98.

99. android:label="@string/app_name">

100.

101.

102.

android:name="ER" />

103.

104.

105.

106.

107.

108.

109.

android:name="_CACHE_FILESYSTEM" />

110.

android:name="E_BOOT_COMPLETED" />

111.

android:name="_DOWNLOAD_MANAGER" />

112.

android:name="_DOWNLOAD_MANAGER_ADVANCED" />

113.

114.

android:name="_DOWNLOAD_COMPLETED_INTENTS" /> 115.

android:name="_NETWORK_STATE" />

116.

117.

android:name="_EXTERNAL_STORAGE" />

118.

119.

120.

复制代码

代码里面引用了ParseException和WebAddress两个类,可以从Android源代码里找到copy进来,在这里frameworks/base/core/java/android/net。代码里面有几个地方比较重要的:

a) 通过往DownloadProvider提供的ContentProvider “content://downloads/download” 中插入数据就能触发DownloadProvider的执行。

b) ("destination", 1); 是下载文件存储在什么地方, 如果没有这个参数,默认保存在sdcard的download 下面 ( 中的 DEFAULT_DL_SUBDIR =

"/download" )

如果指定为1,是往内存的 /cache目录下存东西 (在/frameworks/base/core/java/android/provider/中定义, public static final

int DESTINATION_CACHE_PARTITION = 1; )

b) 注意Manifest中的一堆权限: ACCESS_DOWNLOAD_MANAGER是最基本的权限,这样可以使用DownloadProvider下载。

如果需要destination=1,则需要 ACCESS_DOWNLOAD_MANAGER权限。(中的注释 : All file types are allowed, and only the initiating

application can access the file (indirectly through a content provider). This requires the

_DOWNLOAD_MANAGER_ADVANCED permission.)如果没有这个权限,在往 content://downloads/download插入的时候有权限问题报错:

09-16 17:16:38.062: ERROR/DatabaseUtils(763): Writing exception to parcel 09-16 17:16:38.062: ERROR/DatabaseUtils(763): tyException:

unauthorized destination code

09-16 17:16:38.062: ERROR/DatabaseUtils(763): at

(:277)

09-16 17:16:38.062: ERROR/DatabaseUtils(763): at

tProvider$(:150)

09-16 17:16:38.062: ERROR/DatabaseUtils(763): at

sact(:140)

09-16 17:16:38.062: ERROR/DatabaseUtils(763): at

ansact(:287)

09-16 17:16:38.062: ERROR/DatabaseUtils(763): at

(Native Method)

09-16 17:16:38.102: DEBUG/AndroidRuntime(4086): Shutting down VM因为中有这段代码:

if (dest != null) {

if

(getContext().checkCallingPermission(SION_ACCESS_ADVANCED)

!= SION_GRANTED

&& dest != ATION_EXTERNAL

&& dest != ATION_CACHE_PARTITION_PURGEABLE)

{

throw new SecurityException("unauthorized destination code");

}

所以:要往/cache目录下存东西,一定要记得这个权限哦。实际运行起来,只加这个权限往/cache下存东西还不够,就又把其他一堆权限都加上了,具体哪些有用还没细看。5) 将这个app直接以普通app安装上去,运行,可以看到下载成功到/cache里了。

第二种思路就是想办法获得system权限或者签名:

这样不修改DownloadProvider的代码,不动它。

而是将自己编写的app做完以后放到/packages/app目录下和整个系统一起编译,将其编译到img中的系统app下 这样编译完成以后运行,使用编译的img运行模拟器。在模拟器中启动自己写的调用DownloadProvider的app,发现竟然也是可以调用的。 不过这种方法在模拟器上成功了,但是在真机上没成功,可能还有些问题没解决。第一种方法是完全成功的。

本文标签: 权限下载运行编译