FileDocCategorySizeDatePackage
FileProvider.javaAPI DocAndroid 5.1 API34324Thu Mar 12 22:22:56 GMT 2015android.support.v4.content

FileProvider

public class FileProvider extends android.content.ContentProvider
FileProvider is a special subclass of {@link ContentProvider} that facilitates secure sharing of files associated with an app by creating a content:// {@link Uri} for a file instead of a file:/// {@link Uri}.

A content URI allows you to grant read and write access using temporary access permissions. When you create an {@link Intent} containing a content URI, in order to send the content URI to a client app, you can also call {@link Intent#setFlags(int) Intent.setFlags()} to add permissions. These permissions are available to the client app for as long as the stack for a receiving {@link android.app.Activity} is active. For an {@link Intent} going to a {@link android.app.Service}, the permissions are available as long as the {@link android.app.Service} is running.

In comparison, to control access to a file:/// {@link Uri} you have to modify the file system permissions of the underlying file. The permissions you provide become available to any app, and remain in effect until you change them. This level of access is fundamentally insecure.

The increased level of file access security offered by a content URI makes FileProvider a key part of Android's security infrastructure.

This overview of FileProvider includes the following topics:

  1. Defining a FileProvider
  2. Specifying Available Files
  3. Retrieving the Content URI for a File
  4. Granting Temporary Permissions to a URI
  5. Serving a Content URI to Another App

Defining a FileProvider

Since the default functionality of FileProvider includes content URI generation for files, you don't need to define a subclass in code. Instead, you can include a FileProvider in your app by specifying it entirely in XML. To specify the FileProvider component itself, add a <provider> element to your app manifest. Set the android:name attribute to android.support.v4.content.FileProvider. Set the android:authorities attribute to a URI authority based on a domain you control; for example, if you control the domain mydomain.com you should use the authority com.mydomain.fileprovider. Set the android:exported attribute to false; the FileProvider does not need to be public. Set the android:grantUriPermissions attribute to true, to allow you to grant temporary access to files. For example:

<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
...
</provider>
...
</application>
</manifest>

If you want to override any of the default behavior of FileProvider methods, extend the FileProvider class and use the fully-qualified class name in the android:name attribute of the <provider> element.

Specifying Available Files

A FileProvider can only generate a content URI for files in directories that you specify beforehand. To specify a directory, specify the its storage area and path in XML, using child elements of the <paths> element. For example, the following paths element tells FileProvider that you intend to request content URIs for the images/ subdirectory of your private file area.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>

The <paths> element must contain one or more of the following child elements:

<files-path name="name" path="path" />
Represents files in the files/ subdirectory of your app's internal storage area. This subdirectory is the same as the value returned by {@link Context#getFilesDir() Context.getFilesDir()}.
<external-path name="name" path="path" />
Represents files in the root of your app's external storage area. The path {@link Context#getExternalFilesDir(String) Context.getExternalFilesDir()} returns the files/ subdirectory of this this root.
<cache-path name="name" path="path" />
Represents files in the cache subdirectory of your app's internal storage area. The root path of this subdirectory is the same as the value returned by {@link Context#getCacheDir() getCacheDir()}.

These child elements all use the same attributes:

name="name"
A URI path segment. To enforce security, this value hides the name of the subdirectory you're sharing. The subdirectory name for this value is contained in the path attribute.
path="path"
The subdirectory you're sharing. While the name attribute is a URI path segment, the path value is an actual subdirectory name. Notice that the value refers to a subdirectory, not an individual file or files. You can't share a single file by its file name, nor can you specify a subset of files using wildcards.

You must specify a child element of <paths> for each directory that contains files for which you want content URIs. For example, these XML elements specify two directories:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
<files-path name="my_docs" path="docs/"/>
</paths>

Put the <paths> element and its children in an XML file in your project. For example, you can add them to a new file called res/xml/file_paths.xml. To link this file to the FileProvider, add a <meta-data> element as a child of the <provider> element that defines the FileProvider. Set the <meta-data> element's "android:name" attribute to android.support.FILE_PROVIDER_PATHS. Set the element's "android:resource" attribute to @xml/file_paths (notice that you don't specify the .xml extension). For example:

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

Generating the Content URI for a File

To share a file with another app using a content URI, your app has to generate the content URI. To generate the content URI, create a new {@link File} for the file, then pass the {@link File} to {@link #getUriForFile(Context, String, File) getUriForFile()}. You can send the content URI returned by {@link #getUriForFile(Context, String, File) getUriForFile()} to another app in an {@link android.content.Intent}. The client app that receives the content URI can open the file and access its contents by calling {@link android.content.ContentResolver#openFileDescriptor(Uri, String) ContentResolver.openFileDescriptor} to get a {@link ParcelFileDescriptor}.

For example, suppose your app is offering files to other apps with a FileProvider that has the authority com.mydomain.fileprovider. To get a content URI for the file default_image.jpg in the images/ subdirectory of your internal storage add the following code:

File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
As a result of the previous snippet, {@link #getUriForFile(Context, String, File) getUriForFile()} returns the content URI content://com.mydomain.fileprovider/my_images/default_image.jpg.

Granting Temporary Permissions to a URI

To grant an access permission to a content URI returned from {@link #getUriForFile(Context, String, File) getUriForFile()}, do one of the following:
  • Call the method {@link Context#grantUriPermission(String, Uri, int) Context.grantUriPermission(package, Uri, mode_flags)} for the content:// {@link Uri}, using the desired mode flags. This grants temporary access permission for the content URI to the specified package, according to the value of the the mode_flags parameter, which you can set to {@link Intent#FLAG_GRANT_READ_URI_PERMISSION}, {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION} or both. The permission remains in effect until you revoke it by calling {@link Context#revokeUriPermission(Uri, int) revokeUriPermission()} or until the device reboots.
  • Put the content URI in an {@link Intent} by calling {@link Intent#setData(Uri) setData()}.
  • Next, call the method {@link Intent#setFlags(int) Intent.setFlags()} with either {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} or {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION} or both.
  • Finally, send the {@link Intent} to another app. Most often, you do this by calling {@link android.app.Activity#setResult(int, android.content.Intent) setResult()}.

    Permissions granted in an {@link Intent} remain in effect while the stack of the receiving {@link android.app.Activity} is active. When the stack finishes, the permissions are automatically removed. Permissions granted to one {@link android.app.Activity} in a client app are automatically extended to other components of that app.

Serving a Content URI to Another App

There are a variety of ways to serve the content URI for a file to a client app. One common way is for the client app to start your app by calling {@link android.app.Activity#startActivityForResult(Intent, int, Bundle) startActivityResult()}, which sends an {@link Intent} to your app to start an {@link android.app.Activity} in your app. In response, your app can immediately return a content URI to the client app or present a user interface that allows the user to pick a file. In the latter case, once the user picks the file your app can return its content URI. In both cases, your app returns the content URI in an {@link Intent} sent via {@link android.app.Activity#setResult(int, Intent) setResult()}.

You can also put the content URI in a {@link android.content.ClipData} object and then add the object to an {@link Intent} you send to a client app. To do this, call {@link Intent#setClipData(ClipData) Intent.setClipData()}. When you use this approach, you can add multiple {@link android.content.ClipData} objects to the {@link Intent}, each with its own content URI. When you call {@link Intent#setFlags(int) Intent.setFlags()} on the {@link Intent} to set temporary access permissions, the same permissions are applied to all of the content URIs.

Note: The {@link Intent#setClipData(ClipData) Intent.setClipData()} method is only available in platform version 16 (Android 4.1) and later. If you want to maintain compatibility with previous versions, you should send one content URI at a time in the {@link Intent}. Set the action to {@link Intent#ACTION_SEND} and put the URI in data by calling {@link Intent#setData setData()}.

More Information

To learn more about FileProvider, see the Android training class Sharing Files Securely with URIs.

Fields Summary
private static final String[]
COLUMNS
private static final String
META_DATA_FILE_PROVIDER_PATHS
private static final String
TAG_ROOT_PATH
private static final String
TAG_FILES_PATH
private static final String
TAG_CACHE_PATH
private static final String
TAG_EXTERNAL
private static final String
ATTR_NAME
private static final String
ATTR_PATH
private static final File
DEVICE_ROOT
private static HashMap
sCache
private PathStrategy
mStrategy
Constructors Summary
Methods Summary
public voidattachInfo(android.content.Context context, android.content.pm.ProviderInfo info)
After the FileProvider is instantiated, this method is called to provide the system with information about the provider.

param
context A {@link Context} for the current component.
param
info A {@link ProviderInfo} for the new provider.

        super.attachInfo(context, info);

        // Sanity check our security
        if (info.exported) {
            throw new SecurityException("Provider must not be exported");
        }
        if (!info.grantUriPermissions) {
            throw new SecurityException("Provider must grant uri permissions");
        }

        mStrategy = getPathStrategy(context, info.authority);
    
private static java.io.FilebuildPath(java.io.File base, java.lang.String segments)

        File cur = base;
        for (String segment : segments) {
            if (segment != null) {
                cur = new File(cur, segment);
            }
        }
        return cur;
    
private static java.lang.String[]copyOf(java.lang.String[] original, int newLength)

        final String[] result = new String[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    
private static java.lang.Object[]copyOf(java.lang.Object[] original, int newLength)

        final Object[] result = new Object[newLength];
        System.arraycopy(original, 0, result, 0, newLength);
        return result;
    
public intdelete(android.net.Uri uri, java.lang.String selection, java.lang.String[] selectionArgs)
Deletes the file associated with the specified content URI, as returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this method does not throw an {@link java.io.IOException}; you must check its return value.

param
uri A content URI for a file, as returned by {@link #getUriForFile(Context, String, File) getUriForFile()}.
param
selection Ignored. Set to {@code null}.
param
selectionArgs Ignored. Set to {@code null}.
return
1 if the delete succeeds; otherwise, 0.

        // ContentProvider has already checked granted permissions
        final File file = mStrategy.getFileForUri(uri);
        return file.delete() ? 1 : 0;
    
private static android.support.v4.content.FileProvider$PathStrategygetPathStrategy(android.content.Context context, java.lang.String authority)
Return {@link PathStrategy} for given authority, either by parsing or returning from cache.

        PathStrategy strat;
        synchronized (sCache) {
            strat = sCache.get(authority);
            if (strat == null) {
                try {
                    strat = parsePathStrategy(context, authority);
                } catch (IOException e) {
                    throw new IllegalArgumentException(
                            "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
                } catch (XmlPullParserException e) {
                    throw new IllegalArgumentException(
                            "Failed to parse " + META_DATA_FILE_PROVIDER_PATHS + " meta-data", e);
                }
                sCache.put(authority, strat);
            }
        }
        return strat;
    
public java.lang.StringgetType(android.net.Uri uri)
Returns the MIME type of a content URI returned by {@link #getUriForFile(Context, String, File) getUriForFile()}.

param
uri A content URI returned by {@link #getUriForFile(Context, String, File) getUriForFile()}.
return
If the associated file has an extension, the MIME type associated with that extension; otherwise application/octet-stream.

        // ContentProvider has already checked granted permissions
        final File file = mStrategy.getFileForUri(uri);

        final int lastDot = file.getName().lastIndexOf('.");
        if (lastDot >= 0) {
            final String extension = file.getName().substring(lastDot + 1);
            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
            if (mime != null) {
                return mime;
            }
        }

        return "application/octet-stream";
    
public static android.net.UrigetUriForFile(android.content.Context context, java.lang.String authority, java.io.File file)
Return a content URI for a given {@link File}. Specific temporary permissions for the content URI can be set with {@link Context#grantUriPermission(String, Uri, int)}, or added to an {@link Intent} by calling {@link Intent#setData(Uri) setData()} and then {@link Intent#setFlags(int) setFlags()}; in both cases, the applicable flags are {@link Intent#FLAG_GRANT_READ_URI_PERMISSION} and {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION}. A FileProvider can only return a content {@link Uri} for file paths defined in their <paths> meta-data element. See the Class Overview for more information.

param
context A {@link Context} for the current component.
param
authority The authority of a {@link FileProvider} defined in a {@code <provider>} element in your app's manifest.
param
file A {@link File} pointing to the filename for which you want a content {@link Uri}.
return
A content URI for the file.
throws
IllegalArgumentException When the given {@link File} is outside the paths supported by the provider.

        final PathStrategy strategy = getPathStrategy(context, authority);
        return strategy.getUriForFile(file);
    
public android.net.Uriinsert(android.net.Uri uri, android.content.ContentValues values)
By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must subclass FileProvider if you want to provide different functionality.

        throw new UnsupportedOperationException("No external inserts");
    
private static intmodeToMode(java.lang.String mode)
Copied from ContentResolver.java

        int modeBits;
        if ("r".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
        } else if ("w".equals(mode) || "wt".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
                    | ParcelFileDescriptor.MODE_CREATE
                    | ParcelFileDescriptor.MODE_TRUNCATE;
        } else if ("wa".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
                    | ParcelFileDescriptor.MODE_CREATE
                    | ParcelFileDescriptor.MODE_APPEND;
        } else if ("rw".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
                    | ParcelFileDescriptor.MODE_CREATE;
        } else if ("rwt".equals(mode)) {
            modeBits = ParcelFileDescriptor.MODE_READ_WRITE
                    | ParcelFileDescriptor.MODE_CREATE
                    | ParcelFileDescriptor.MODE_TRUNCATE;
        } else {
            throw new IllegalArgumentException("Invalid mode: " + mode);
        }
        return modeBits;
    
public booleanonCreate()
The default FileProvider implementation does not need to be initialized. If you want to override this method, you must provide your own subclass of FileProvider.


                                  
    
       
        return true;
    
public android.os.ParcelFileDescriptoropenFile(android.net.Uri uri, java.lang.String mode)
By default, FileProvider automatically returns the {@link ParcelFileDescriptor} for a file associated with a content:// {@link Uri}. To get the {@link ParcelFileDescriptor}, call {@link android.content.ContentResolver#openFileDescriptor(Uri, String) ContentResolver.openFileDescriptor}. To override this method, you must provide your own subclass of FileProvider.

param
uri A content URI associated with a file, as returned by {@link #getUriForFile(Context, String, File) getUriForFile()}.
param
mode Access mode for the file. May be "r" for read-only access, "rw" for read and write access, or "rwt" for read and write access that truncates any existing file.
return
A new {@link ParcelFileDescriptor} with which you can access the file.

        // ContentProvider has already checked granted permissions
        final File file = mStrategy.getFileForUri(uri);
        final int fileMode = modeToMode(mode);
        return ParcelFileDescriptor.open(file, fileMode);
    
private static android.support.v4.content.FileProvider$PathStrategyparsePathStrategy(android.content.Context context, java.lang.String authority)
Parse and return {@link PathStrategy} for given authority as defined in {@link #META_DATA_FILE_PROVIDER_PATHS} {@code <meta-data>}.

see
#getPathStrategy(Context, String)

        final SimplePathStrategy strat = new SimplePathStrategy(authority);

        final ProviderInfo info = context.getPackageManager()
                .resolveContentProvider(authority, PackageManager.GET_META_DATA);
        final XmlResourceParser in = info.loadXmlMetaData(
                context.getPackageManager(), META_DATA_FILE_PROVIDER_PATHS);
        if (in == null) {
            throw new IllegalArgumentException(
                    "Missing " + META_DATA_FILE_PROVIDER_PATHS + " meta-data");
        }

        int type;
        while ((type = in.next()) != END_DOCUMENT) {
            if (type == START_TAG) {
                final String tag = in.getName();

                final String name = in.getAttributeValue(null, ATTR_NAME);
                String path = in.getAttributeValue(null, ATTR_PATH);

                File target = null;
                if (TAG_ROOT_PATH.equals(tag)) {
                    target = buildPath(DEVICE_ROOT, path);
                } else if (TAG_FILES_PATH.equals(tag)) {
                    target = buildPath(context.getFilesDir(), path);
                } else if (TAG_CACHE_PATH.equals(tag)) {
                    target = buildPath(context.getCacheDir(), path);
                } else if (TAG_EXTERNAL.equals(tag)) {
                    target = buildPath(Environment.getExternalStorageDirectory(), path);
                }

                if (target != null) {
                    strat.addRoot(name, target);
                }
            }
        }

        return strat;
    
public android.database.Cursorquery(android.net.Uri uri, java.lang.String[] projection, java.lang.String selection, java.lang.String[] selectionArgs, java.lang.String sortOrder)
Use a content URI returned by {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file managed by the FileProvider. FileProvider reports the column names defined in {@link android.provider.OpenableColumns}:
  • {@link android.provider.OpenableColumns#DISPLAY_NAME}
  • {@link android.provider.OpenableColumns#SIZE}
For more information, see {@link ContentProvider#query(Uri, String[], String, String[], String) ContentProvider.query()}.

param
uri A content URI returned by {@link #getUriForFile}.
param
projection The list of columns to put into the {@link Cursor}. If null all columns are included.
param
selection Selection criteria to apply. If null then all data that matches the content URI is returned.
param
selectionArgs An array of {@link java.lang.String}, containing arguments to bind to the selection parameter. The query method scans selection from left to right and iterates through selectionArgs, replacing the current "?" character in selection with the value at the current position in selectionArgs. The values are bound to selection as {@link java.lang.String} values.
param
sortOrder A {@link java.lang.String} containing the column name(s) on which to sort the resulting {@link Cursor}.
return
A {@link Cursor} containing the results of the query.

        // ContentProvider has already checked granted permissions
        final File file = mStrategy.getFileForUri(uri);

        if (projection == null) {
            projection = COLUMNS;
        }

        String[] cols = new String[projection.length];
        Object[] values = new Object[projection.length];
        int i = 0;
        for (String col : projection) {
            if (OpenableColumns.DISPLAY_NAME.equals(col)) {
                cols[i] = OpenableColumns.DISPLAY_NAME;
                values[i++] = file.getName();
            } else if (OpenableColumns.SIZE.equals(col)) {
                cols[i] = OpenableColumns.SIZE;
                values[i++] = file.length();
            }
        }

        cols = copyOf(cols, i);
        values = copyOf(values, i);

        final MatrixCursor cursor = new MatrixCursor(cols, 1);
        cursor.addRow(values);
        return cursor;
    
public intupdate(android.net.Uri uri, android.content.ContentValues values, java.lang.String selection, java.lang.String[] selectionArgs)
By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must subclass FileProvider if you want to provide different functionality.

        throw new UnsupportedOperationException("No external updates");