Read Android (1): Use downloadprovider in Android to download the file and obtain the cache permission.

Source: Internet
Author: User

Android provides a downloadprovider, which is a complete download tool. It provides good external interfaces that can be called by other applications to complete the download. It also provides download, notification, and storage mechanisms.
This downloadprovider is used in Android browser and other tools.

Unfortunately, this downloadprovider is not open to app developers and is only used internally.

We will explore how to use downloadprovider for ourselves.

Let's first find out why downloadprovider is not available:
First find its source code, in this location:/packages/providers/downloadprovider
Open the androidmanifest. xml file with several custom permissions.

<! -- 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"/>

These permissions are Android: protectionlevel = "signatureorsystem" or Android: protectionlevel = "signature". This means that only your app has the system permission or the same signature as the system, to call it.

Here is the key to the problem. We have two ideas:
One idea is to change the protectionlevel to normal and re-compile the downloadprovider project so that other apps can directly call it.
Another idea is to create your own app with the system permission or the same signature as the system.

The previous idea has been completely successful, and the second one has proved part.

First look at the first approach:
1) First, change the preceding permissions to Android: protectionlevel = "normal"
2) recompile downloadprovider
Mmm packages/providers/downloadprovider
3) Replace the compiled APK with the existing APK.
Because downloadprovider.apk is a system app, you can give/system root permission first, and then replace this app. (It can also be installed as a user app, but it will not be available after restart)
Run the command # Mount-T ubifs-O remount ubi0: System/system or # Mount-O remount ubi0: System/system to grant the/system RW permission.
Then, use ADB push to push downloadprovider.apk to/system/APP. The system automatically replaces this app.

4) Write a project to use downloadprovider.
Directly paste the source code:
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
* Use 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, "cocould 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 ("icationicationpackage", getpackagename ());
Values. Put ("icationicationclass", "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 );



// For the parameters, see helpers. Java in the downloadprovider project.
// Public static downloadfileinfo generatesavefile (
// Context,
// String URL,
// String hint,
// String contentdisposition,
// String contentlocation,
// String mimetype,
// Int destination,
// Int contentlength) throws filenotfoundexception {
// And: downloads. Java in the framework;


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" type = "codeph" text = "/codeph">
<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>

The code references the parseexception and webaddress classes. You can find the copy from the android source code. Here, frameworks/base/CORE/Java/Android/net is used.

There are several important points in the Code:
A) The downloadprovider execution can be triggered by inserting data into the contentprovider "content: // downloads/download" provided by downloadprovider.
B) values. put ("destination", 1); indicates where the downloaded file is stored. If this parameter is not set, it is stored under the download of sdcard by default (constants. default_dl_subdir = "/download" in Java ")
If this parameter is set to 1, something is stored in the/cache directory of the memory (in/frameworks/base/CORE/Java/Android/provider/downloads. defined in Java, public static final int destination_cache_partition = 1 ;)
B) Pay attention to a bunch of permissions in manifest: access_download_manager is the most basic permission, which can be downloaded using downloadprovider.
If Destination = 1 is required, access_download_manager is required. (Comment in 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 .)

If you do not have this permission, an error is returned when you have the permission to insert content: // downloads/download:
17:16:38 09-16. 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)
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.exe ctransact (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

Because downloadprovider. Java contains this Code:
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 ");
}
Therefore, you must remember this permission to store things in the/cache directory.

In actual operation, it is not enough to add this permission to the/cache to save things. I have added a bunch of other permissions, but I have not looked at the specific functions.

5) install the app directly as a normal app and run it. You can see that the download is successful in/cache.

The second approach is to obtain the system permission or signature:
In this way, the downloadprovider code is not modified.
Instead, compile the compiled app in the/packages/APP directory together with the entire system and compile it to the system app in the IMG, use the compiled IMG to run the simulator. Start your own downloadprovider app in the simulator and find that it can also be called.
However, this method is successful on the simulator, but it fails on the real machine, and some problems may not be solved. The first method is completely successful.

My blog: blog.csdn.net/lixinso
You are welcome to discuss various technical issues. My email lixinso [at] Gmail [Dot] com

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.