使用Android内部的DownloadProvider下载文件,并获取cache权限

网友投稿 1075 2022-12-01

使用Android内部的DownloadProvider-文件,并获取cache权限

使用Android内部的DownloadProvider-文件,并获取cache权限

Android内部提供了一个DownloadProvider,是一个非常完整的-工具,提供了很好的外部接口可以被其他应用程序调用,来完成-工作。同时也提供和很好的-、通知、存储等机制。 在Android的Browser等工具里面都用到了这个DownloadProvider。 但是很遗憾的是,这个DownloadProvider不对app开发人员开放,只作为内部使用。 我们现在去探究如何将DownloadProvider拿来给自己用。 让我们先找到DownloadProvider不能用的原因: 先找到它的源代码,在这个位置:/packages/providers/DownloadProvider 打开AndroidManifest.xml文件,里面有几个自定义的权限

这几个权限里面都是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.javapackage com.xxxx.usedownload; import java.io.FileNotFoundException;import java-.URI;import android.app.Activity;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Context;import android-.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 = " 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

代码里面引用了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 parcel09-16 17:16:38.062: ERROR/DatabaseUtils(763): java.lang.SecurityException: unauthorized destination code09-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,发现竟然也是可以调用的。 不过这种方法在模拟器上成功了,但是在真机上没成功,可能还有些问题没解决。第一种方法是完全成功的。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Security系列教程之会话管理处理会话过期问题
下一篇:Android进阶:实现多线程下载文件
相关文章

 发表评论

暂时没有评论,来抢沙发吧~