使用Android内部的DownloadProvider下载文件,并获取cache权限 .
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,发现竟然也是可以调用的。
不过这种方法在模拟器上成功了,但是在真机上没成功,可能还有些问题没解决。第一种方法是完全成功的。
使用Android内部的DownloadProvider下载文件,并获取cache权限 .相关推荐
- android下载通知栏,Android开发中实现下载文件通知栏显示进度条
android开发中实现下载文件通知栏显示进度条. 1.使用asynctask异步任务实现,调用publishprogress()方法刷新进度来实现(已优化) public class myasync ...
- 简单易懂SpringBoot和Android上传和下载文件方案——采用URL
上传思路 SpringBoot把文件保存在静态资源里,并且开启静态资源访问 数据库保存文件的URL地址(URL地址是一个字符串) Android采用OkHttp上传文件 下载思路 SpringBoot ...
- android xml获取指定,android:如何从xml文件中获取信息?
我得到一个程序,从一个链接的服务器获取天气.我已经做了一些将字符串结合到URL的字符串.我现在需要从XML文件中获取信息.android:如何从xml文件中获取信息? 这是我的代码:(我更换了,为了安 ...
- Android应用.三星i9000系列(4).SuperOneClick获取Root权限的原理
Android应用.三星i9000系列(4).SuperOneClick获取Root权限的原理 草木瓜 20110408 一.前言 经过笔者自己测试与分析,得出结论:所以Android手机获取Root ...
- Android --- Retrofit 上传/下载文件扩展实现进度的监听
本文使用okhttp作为client来做,其实说白了跟用okhttp做下载上传进度监听几乎一样,参考了这篇文章:Android OkHttp文件上传与下载的进度监听扩展 1. 首先我们写两个接口用来下 ...
- android 从本地服务器下载文件,Retrofit2-如何从服务器下载文件
在这篇博客中,将会讲述使用Retrofit十分需要的一个功能:怎么去下载文件,下面会展示一些下载文件需要写的代码片段,从小的 png 图片到大的 zip文件.
- 小米手机下载文件失败,提示权限不足
其他机型可以,小米手机下载失败,提示读写权限不足.然后发现小米需要读写权限都有,和权限组的理论相悖,所以要做如下处理 ActivityCompat.requestPermissions(this, n ...
- Android WIFI密码查看器实例(在获取Root权限下查看系统文件)
Android WIFI密码查看器实例 实现原理:使用shell命令查看保存WIFI密码的系统文件 涉及的知识 界面展示 基本的Shell命令 shell查看WIFI密码 ShellUtil的使用 正 ...
- android 常用adb 和 adb shell 命令 获取root权限
android 常用adb 和 adb shell 命令 ADB常用命令: 1. 查看设备 adb devices 这个命令是查看当前连接的设备, 连接到计算机的android设备或者模拟器将会 ...
最新文章
- Samba服务器的安装与配置
- 服务器暴力破解的程序(python开发)
- bzoj1503 郁闷的出纳员(平衡树,思维)
- 查找DetailsView1数据控件中的数据
- dynamic programming for knapsack with repeated items algorithm demonstration
- SQL:我为什么慢你心里没数吗?
- MySQL学习笔记——第1章 数据库和MySQL
- 开发你自己的XMPP 续 - Openfire 插件开发
- 关于IOC反射错误(无法加载一个或多个请求的类型。有关更多信息,请检索 LoaderExceptions 属性...)的诊断办法...
- delphi madExcept使用指南
- 计算机丢失dll文件怎么弄,电脑缺少dll文件怎么办
- 2022深圳限行一年几次免罚 有几次机会
- zcmu-1410: Polynomial Showdown
- word文档封面免费下载_为Word 2007文档添加封面
- landesk桌面管理服务器安装篇
- 【IDEA】 弹出Server's certificate is not trusted 解决方法
- 删除linux系统后无法开机启动,Win10/Linux双系统为什么把Linux删掉之后就不能正常启动开机了该如何处理恢复...
- Application was not properly initialized at startup, could not find Factory:
- 男人20不勤,30不立,40则不富……
- JAVA --数字与字符串(四)格式化输出