收藏
Android内部提供了一个DownloadProvider,是一个非常完整的下载工具,提供了很好的外部接口可以被其他应用程序调用,来完成下载工作。同时也提供和很好的下载、通知、存储等机制。
在Android的Browser等工具里面都用到了这个DownloadProvider。
但是很遗憾的是,这个DownloadProvider不对app开发人员开放,只作为内部使用。
我们现在去探究如何将DownloadProvider拿来给自己用。
让我们先找到DownloadProvider不能用的原因:
先找到它的源代码,在这个位置:/packages/providers/DownloadProvider
打开AndroidManifest.xml文件,里面有几个自定义的权限
<!-- Allows access to the Download Manager -->
<permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER"
android:label="@string/permlab_downloadManager"
android:description="@string/permdesc_downloadManager"
android:protectionLevel="signatureOrSystem" />
<!-- Allows advanced access to the Download Manager -->
<permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED"
android:label="@string/permlab_downloadManagerAdvanced"
android:description="@string/permdesc_downloadManagerAdvanced"
android:protectionLevel="signatureOrSystem" />
<!-- Allows filesystem access to /cache -->
<permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
android:label="@string/permlab_cacheFilesystem"
android:description="@string/permdesc_cacheFilesystem"
android:protectionLevel="signature" />
<!-- Allows to send download completed intents -->
<permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS"
android:label="@string/permlab_downloadCompletedIntent"
android:description="@string/permdesc_downloadCompletedIntent"
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
因为DownloadProvider.apk是系统app,你可以先给/system以root权限,然后将这个app替换掉。 (作为一个用户app安装也可以,不过重启以后就没有了)
使用类似 # mount -t ubifs -o remount ubi0:system /system 或者 # mount -o remount ubi0:system /system 给/system rw权限。
然后通过adb push 将DownloadProvider.apk push到 /system/app/下。系统会自动替换这个app。
4)写一个工程来使用DownloadProvider.
直接贴源码了:
DownloadActivity.Java
package com.xxxx.usedownload;
import java.io.FileNotFoundException;
import java.NET.URI;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.webkit.URLUtil;
/**
* @author lixinso
* 使用DownloadProvider
*/
public class DownloadActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//String url = "http://192.168.200.76:8080/webserver/dancing-skeleton.3gp";
String contentDisposition = "attachment; filename=/"dancing-skeleton.3gp/"";
String mimetype = "video/3GPP";
String filename = URLUtil.guessFileName(url,contentDisposition, mimetype);
URI uri = null;
try {
// Undo the percent-encoding that KURL may have done.
String newUrl = new String(URLUtil.decode(url.getBytes()));
// Parse the url into pieces
WebAddress w = new WebAddress(newUrl);
String frag = null;
String query = null;
String path = w.mPath;
// Break the path into path, query, and fragment
if (path.length() > 0) {
// Strip the fragment
int idx = path.lastIndexOf('#');
if (idx != -1) {
frag = path.substring(idx + 1);
path = path.substring(0, idx);
}
idx = path.lastIndexOf('?');
if (idx != -1) {
query = path.substring(idx + 1);
path = path.substring(0, idx);
}
}
uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
query, frag);
} catch (Exception e) {
//Log.e(LOGTAG, "Could not parse url for download: " + url, e);
return;
}
ContentValues values = new ContentValues();
values.put("uri", uri.toString());
values.put("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");
values.put("notificationpackage", getPackageName());
values.put("notificationclass", "HelloWorld");
values.put("visibility", 1);
values.put("mimetype", mimetype);
values.put("hint", filename);
values.put("description", uri.getHost());
values.put("total_bytes", 1349528);
values.put("destination", 1);
//这些参数参考:DownloadProvider工程中的:Helpers.java
//public static DownloadFileInfo generateSaveFile(
// Context context,
// String url,
// String hint,
// String contentDisposition,
// String contentLocation,
// String mimeType,
// int destination,
// int contentLength) throws FileNotFoundException {
//以及: framework里的Downloads.java;
ContentResolver mResolver = getContentResolver();
mResolver.insert(Uri.parse("content://downloads/download"), values);
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxxx.usedownload"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".DownloadActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="7" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER_ADVANCED" />
<uses-permission android:name="android.permission.ACCESS_DRM" />
<uses-permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INSTALL_DRM" />
</manifest>
代码里面引用了ParseException和WebAddress两个类,可以从Android源代码里找到copy进来,在这里frameworks/base/core/java/android/net。
代码里面有几个地方比较重要的:
a) 通过往DownloadProvider提供的ContentProvider “content://downloads/download” 中插入数据就能触发DownloadProvider的执行。
b) values.put("destination", 1); 是下载文件存储在什么地方, 如果没有这个参数,默认保存在sdcard的download 下面 (Constants.java 中的 DEFAULT_DL_SUBDIR = "/download" )
如果指定为1,是往内存的 /cache目录下存东西 (在/frameworks/base/core/java/android/provider/Downloads.java中定义, public static final int DESTINATION_CACHE_PARTITION = 1; )
b) 注意Manifest中的一堆权限: ACCESS_DOWNLOAD_MANAGER是最基本的权限,这样可以使用DownloadProvider下载。
如果需要destination=1,则需要 ACCESS_DOWNLOAD_MANAGER权限。(Downloads.java中的注释 : All file types are allowed, and only the initiating
application can access the file (indirectly through a content provider). This requires the android.permission.ACCESS_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): java.lang.SecurityException: unauthorized destination code
09-16 17:16:38.062: ERROR/DatabaseUtils(763): at com.android.providers.downloads.DownloadProvider.insert(DownloadProvider.java:277)
09-16 17:16:38.062: ERROR/DatabaseUtils(763): at android.content.ContentProvider$Transport.insert(ContentProvider.java:150)
09-16 17:16:38.062: ERROR/DatabaseUtils(763): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:140)
09-16 17:16:38.062: ERROR/DatabaseUtils(763): at android.os.Binder.execTransact(Binder.java:287)
09-16 17:16:38.062: ERROR/DatabaseUtils(763): at dalvik.system.NativeStart.run(Native Method)
09-16 17:16:38.102: DEBUG/AndroidRuntime(4086): Shutting down VM
因为DownloadProvider.java中有这段代码:
if (dest != null) {
if (getContext().checkCallingPermission(Downloads.PERMISSION_ACCESS_ADVANCED)
!= PackageManager.PERMISSION_GRANTED
&& dest != Downloads.DESTINATION_EXTERNAL
&& dest != Downloads.DESTINATION_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,发现竟然也是可以调用的。
不过这种方法在模拟器上成功了,但是在真机上没成功,可能还有些问题没解决。第一种方法是完全成功的。
分享到:
相关推荐
使用DownloadProvider进行文件下载全攻略.pdf
android利用系统DownloadProvider实现下载,多文件多线程后台下载,有需要下载功能的可以直接导入到程序中
apk文件 DownloadProvider(电视直播视频)apk文件 DownloadProvider(电视直播视频)apk文件 DownloadProvider(电视直播视频)apk文件 DownloadProvider(电视直播视频)apk文件 DownloadProvider(电视直播视频)...
主要介绍了Android DownloadProvider 源码详解的相关资料,需要的朋友可以参考下
主要是因为手机一般不会下载多么大的文件,而多线程本身的线程开销加上使用数据库或额外的记录文件产生的IO开销也不小,使用多线程的意义并不是很大。 已发现的问题:用fileobserver观察文件大小来更新界面UI会出现...
修改过的DownloadProvider-master,方便大家使用
DownloadProvider.apk
Android JPush SDK 集成文档,快速集成极光推送服务功能!
This project ports the DownloadProvider of Android 2.3.7. It supports Android 2.2 and above. It is standard Eclipse project, which could be open by Eclipse with the Andoird Development Tools. To ...
com.xunlei.downloadprovider.apk
Android browser源代码 /android/packages/apps/browser /android/packages/providers/DownloadProvider /android/frameworks/base/core/java/android/webkit
安卓系统文件夹及其主要文件详解 \system\app文件夹下是系统默认安装的软件 ...\system\app\Contacts.apk 联系人 \system\app\DownloadProvider.apk下载提供者 \system\app\DrmProvider. apk DRM数字版权提供
contentprovider存储,主要基于本地存储为基础,是移动开发的重要存储方式,源码已经打包,内部有详细注释。
网上所流传的是安装 GoogleCalendarSyncAdapter.apk GoogleContactsSyncAdapter.apk GoogleQuickSearchBox.apk GoogleServicesFramework.apk 这四个应用程序。...可以查找并下载应用程序就可以了。
Download Provider Documentation From: http://www.netmite.com/android/mydroid/1.6/packages/providers/DownloadProvider/docs/
DownloadProvider.apk=下载管理器(绝不能删除) DrmProvider.apk=DRM受保护数据存储服务(绝不能删除) FileManager.apk=简易文件管理(可删,可用ES或RE文件管理器替代) Gallery3D.apk=3D图片浏览器 (可删) ...