summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt9
-rw-r--r--core/java/android/content/ContentProvider.java227
-rw-r--r--core/java/android/content/ContentProviderClient.java63
-rw-r--r--core/java/android/content/ContentProviderNative.java30
-rw-r--r--core/java/android/content/ContentResolver.java199
-rw-r--r--core/java/android/content/IContentProvider.java8
-rw-r--r--core/java/android/content/Intent.java19
-rw-r--r--core/java/android/provider/DocumentsContract.java141
-rw-r--r--packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java1
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--test-runner/src/android/test/mock/MockContentProvider.java8
-rw-r--r--test-runner/src/android/test/mock/MockIContentProvider.java8
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java8
13 files changed, 625 insertions, 98 deletions
diff --git a/api/current.txt b/api/current.txt
index 0d4dccf..2fc08f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5501,10 +5501,13 @@ package android.content {
method public void onLowMemory();
method public void onTrimMemory(int);
method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method protected final android.os.ParcelFileDescriptor openFileHelper(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public android.os.ParcelFileDescriptor openPipeHelper(android.net.Uri, java.lang.String, android.os.Bundle, T, android.content.ContentProvider.PipeDataWriter<T>) throws java.io.FileNotFoundException;
method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public android.content.res.AssetFileDescriptor openTypedAssetFile(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public abstract android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method protected final void setPathPermissions(android.content.pm.PathPermission[]);
@@ -5528,8 +5531,11 @@ package android.content {
method public java.lang.String getType(android.net.Uri) throws android.os.RemoteException;
method public android.net.Uri insert(android.net.Uri, android.content.ContentValues) throws android.os.RemoteException;
method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public android.content.res.AssetFileDescriptor openAssetFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public android.os.ParcelFileDescriptor openFile(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException, android.os.RemoteException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException, android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) throws android.os.RemoteException;
method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal) throws android.os.RemoteException;
method public boolean release();
@@ -5617,11 +5623,14 @@ package android.content {
method public void notifyChange(android.net.Uri, android.database.ContentObserver);
method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+ method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final java.io.InputStream openInputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri) throws java.io.FileNotFoundException;
method public final java.io.OutputStream openOutputStream(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException;
+ method public final android.content.res.AssetFileDescriptor openTypedAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.Bundle, android.os.CancellationSignal) throws java.io.FileNotFoundException;
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
method public final void registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index cf627d7..b9121c7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -16,11 +16,9 @@
package android.content;
-import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.app.AppOpsManager;
-import android.content.pm.PackageManager;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
@@ -261,17 +259,21 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
@Override
- public ParcelFileDescriptor openFile(String callingPkg, Uri uri, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
- return ContentProvider.this.openFile(uri, mode);
+ return ContentProvider.this.openFile(
+ uri, mode, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri uri, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal)
throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, mode);
- return ContentProvider.this.openAssetFile(uri, mode);
+ return ContentProvider.this.openAssetFile(
+ uri, mode, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
@@ -286,9 +288,10 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType,
- Bundle opts) throws FileNotFoundException {
+ Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException {
enforceFilePermission(callingPkg, uri, "r");
- return ContentProvider.this.openTypedAssetFile(uri, mimeType, opts);
+ return ContentProvider.this.openTypedAssetFile(
+ uri, mimeType, opts, CancellationSignal.fromTransport(cancellationSignal));
}
@Override
@@ -874,6 +877,18 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
* <p>The returned ParcelFileDescriptor is owned by the caller, so it is
* their responsibility to close it when done. That is, the implementation
* of this method should create a new ParcelFileDescriptor for each call.
+ * <p>
+ * If opened with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+ * supports seeking.
+ * <p>
+ * If you need to detect when the returned ParcelFileDescriptor has been
+ * closed, or if the remote process has crashed or encountered some other
+ * error, you can use {@link ParcelFileDescriptor#open(File, int,
+ * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+ * {@link ParcelFileDescriptor#createReliablePipe()}, or
+ * {@link ParcelFileDescriptor#createReliableSocketPair()}.
*
* <p class="note">For use in Intents, you will want to implement {@link #getType}
* to return the appropriate MIME type for the data returned here with
@@ -911,6 +926,74 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/**
+ * Override this to handle requests to open a file blob.
+ * The default implementation always throws {@link FileNotFoundException}.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * <p>This method returns a ParcelFileDescriptor, which is returned directly
+ * to the caller. This way large data (such as images and documents) can be
+ * returned without copying the content.
+ *
+ * <p>The returned ParcelFileDescriptor is owned by the caller, so it is
+ * their responsibility to close it when done. That is, the implementation
+ * of this method should create a new ParcelFileDescriptor for each call.
+ * <p>
+ * If opened with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor can be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" or "rwt" modes implies a file on disk that
+ * supports seeking.
+ * <p>
+ * If you need to detect when the returned ParcelFileDescriptor has been
+ * closed, or if the remote process has crashed or encountered some other
+ * error, you can use {@link ParcelFileDescriptor#open(File, int,
+ * android.os.Handler, android.os.ParcelFileDescriptor.OnCloseListener)},
+ * {@link ParcelFileDescriptor#createReliablePipe()}, or
+ * {@link ParcelFileDescriptor#createReliableSocketPair()}.
+ *
+ * <p class="note">For use in Intents, you will want to implement {@link #getType}
+ * to return the appropriate MIME type for the data returned here with
+ * the same URI. This will allow intent resolution to automatically determine the data MIME
+ * type and select the appropriate matching targets as part of its operation.</p>
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+ * You may also want to support other common columns if you have additional meta-data
+ * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+ * in {@link android.provider.MediaStore.MediaColumns}.</p>
+ *
+ * @param uri The URI whose file is to be opened.
+ * @param mode Access mode for the file. May be "r" for read-only access,
+ * "w" for write-only access, "rw" for read and write access, or
+ * "rwt" for read and write access that truncates any existing
+ * file.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new ParcelFileDescriptor which you can use to access
+ * the file.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the file.
+ *
+ * @see #openAssetFile(Uri, String)
+ * @see #openFileHelper(Uri, String)
+ * @see #getType(android.net.Uri)
+ */
+ public ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openFile(uri, mode);
+ }
+
+ /**
* This is like {@link #openFile}, but can be implemented by providers
* that need to be able to return sub-sections of files, often assets
* inside of their .apk.
@@ -924,11 +1007,14 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
* {@link ContentResolver#openInputStream ContentResolver.openInputStream}
* or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
* methods.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
*
* <p class="note">If you are implementing this to return a full file, you
* should create the AssetFileDescriptor with
* {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
- * applications that can not handle sub-sections of files.</p>
+ * applications that cannot handle sub-sections of files.</p>
*
* <p class="note">For use in Intents, you will want to implement {@link #getType}
* to return the appropriate MIME type for the data returned here with
@@ -965,6 +1051,68 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
}
/**
+ * This is like {@link #openFile}, but can be implemented by providers
+ * that need to be able to return sub-sections of files, often assets
+ * inside of their .apk.
+ * This method can be called from multiple threads, as described in
+ * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
+ * and Threads</a>.
+ *
+ * <p>If you implement this, your clients must be able to deal with such
+ * file slices, either directly with
+ * {@link ContentResolver#openAssetFileDescriptor}, or by using the higher-level
+ * {@link ContentResolver#openInputStream ContentResolver.openInputStream}
+ * or {@link ContentResolver#openOutputStream ContentResolver.openOutputStream}
+ * methods.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
+ *
+ * <p class="note">If you are implementing this to return a full file, you
+ * should create the AssetFileDescriptor with
+ * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
+ * applications that cannot handle sub-sections of files.</p>
+ *
+ * <p class="note">For use in Intents, you will want to implement {@link #getType}
+ * to return the appropriate MIME type for the data returned here with
+ * the same URI. This will allow intent resolution to automatically determine the data MIME
+ * type and select the appropriate matching targets as part of its operation.</p>
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
+ *
+ * @param uri The URI whose file is to be opened.
+ * @param mode Access mode for the file. May be "r" for read-only access,
+ * "w" for write-only access (erasing whatever data is currently in
+ * the file), "wa" for write-only access to append to any existing data,
+ * "rw" for read and write access on any existing data, and "rwt" for read
+ * and write access that truncates any existing file.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new AssetFileDescriptor which you can use to access
+ * the file.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the file.
+ *
+ * @see #openFile(Uri, String)
+ * @see #openFileHelper(Uri, String)
+ * @see #getType(android.net.Uri)
+ */
+ public AssetFileDescriptor openAssetFile(Uri uri, String mode, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openAssetFile(uri, mode);
+ }
+
+ /**
* Convenience for subclasses that wish to implement {@link #openFile}
* by looking up a column named "_data" at the given URI.
*
@@ -1041,6 +1189,9 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
*
* <p>See {@link ClipData} for examples of the use and implementation
* of this method.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
*
* <p class="note">For better interoperability with other applications, it is recommended
* that for any URIs that can be opened, you also support queries on them
@@ -1086,6 +1237,64 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
throw new FileNotFoundException("Can't open " + uri + " as type " + mimeTypeFilter);
}
+
+ /**
+ * Called by a client to open a read-only stream containing data of a
+ * particular MIME type. This is like {@link #openAssetFile(Uri, String)},
+ * except the file can only be read-only and the content provider may
+ * perform data conversions to generate data of the desired type.
+ *
+ * <p>The default implementation compares the given mimeType against the
+ * result of {@link #getType(Uri)} and, if they match, simply calls
+ * {@link #openAssetFile(Uri, String)}.
+ *
+ * <p>See {@link ClipData} for examples of the use and implementation
+ * of this method.
+ * <p>
+ * The returned AssetFileDescriptor can be a pipe or socket pair to enable
+ * streaming of data.
+ *
+ * <p class="note">For better interoperability with other applications, it is recommended
+ * that for any URIs that can be opened, you also support queries on them
+ * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+ * You may also want to support other common columns if you have additional meta-data
+ * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+ * in {@link android.provider.MediaStore.MediaColumns}.</p>
+ *
+ * @param uri The data in the content provider being queried.
+ * @param mimeTypeFilter The type of data the client desires. May be
+ * a pattern, such as *\/*, if the caller does not have specific type
+ * requirements; in this case the content provider will pick its best
+ * type matching the pattern.
+ * @param opts Additional options from the client. The definitions of
+ * these are specific to the content provider being called.
+ * @param signal A signal to cancel the operation in progress, or
+ * {@code null} if none. For example, if you are downloading a
+ * file from the network to service a "rw" mode request, you
+ * should periodically call
+ * {@link CancellationSignal#throwIfCanceled()} to check whether
+ * the client has canceled the request and abort the download.
+ *
+ * @return Returns a new AssetFileDescriptor from which the client can
+ * read data of the desired type.
+ *
+ * @throws FileNotFoundException Throws FileNotFoundException if there is
+ * no file associated with the given URI or the mode is invalid.
+ * @throws SecurityException Throws SecurityException if the caller does
+ * not have permission to access the data.
+ * @throws IllegalArgumentException Throws IllegalArgumentException if the
+ * content provider does not support the requested MIME type.
+ *
+ * @see #getStreamTypes(Uri, String)
+ * @see #openAssetFile(Uri, String)
+ * @see ClipDescription#compareMimeTypes(String, String)
+ */
+ public AssetFileDescriptor openTypedAssetFile(
+ Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
+ throws FileNotFoundException {
+ return openTypedAssetFile(uri, mimeTypeFilter, opts);
+ }
+
/**
* Interface to write a stream of data to a pipe. Use with
* {@link ContentProvider#openPipeHelper}.
diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java
index 8dffac7..024a521 100644
--- a/core/java/android/content/ContentProviderClient.java
+++ b/core/java/android/content/ContentProviderClient.java
@@ -63,14 +63,7 @@ public class ContentProviderClient {
/** See {@link ContentProvider#query ContentProvider.query} */
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
- try {
- return query(url, projection, selection, selectionArgs, sortOrder, null);
- } catch (DeadObjectException e) {
- if (!mStable) {
- mContentResolver.unstableProviderDied(mContentProvider);
- }
- throw e;
- }
+ return query(url, projection, selection, selectionArgs, sortOrder, null);
}
/** See {@link ContentProvider#query ContentProvider.query} */
@@ -177,8 +170,25 @@ public class ContentProviderClient {
*/
public ParcelFileDescriptor openFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
+ return openFile(url, mode, null);
+ }
+
+ /**
+ * See {@link ContentProvider#openFile ContentProvider.openFile}. Note that
+ * this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openFileDescriptor
+ * ContentResolver.openFileDescriptor} API instead.
+ */
+ public ParcelFileDescriptor openFile(Uri url, String mode, CancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openFile(mPackageName, url, mode);
+ return mContentProvider.openFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -196,8 +206,25 @@ public class ContentProviderClient {
*/
public AssetFileDescriptor openAssetFile(Uri url, String mode)
throws RemoteException, FileNotFoundException {
+ return openAssetFile(url, mode, null);
+ }
+
+ /**
+ * See {@link ContentProvider#openAssetFile ContentProvider.openAssetFile}.
+ * Note that this <em>does not</em>
+ * take care of non-content: URIs such as file:. It is strongly recommended
+ * you use the {@link ContentResolver#openAssetFileDescriptor
+ * ContentResolver.openAssetFileDescriptor} API instead.
+ */
+ public AssetFileDescriptor openAssetFile(Uri url, String mode, CancellationSignal signal)
+ throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openAssetFile(mPackageName, url, mode);
+ return mContentProvider.openAssetFile(mPackageName, url, mode, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
@@ -208,10 +235,22 @@ public class ContentProviderClient {
/** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
- String mimeType, Bundle opts)
+ String mimeType, Bundle opts) throws RemoteException, FileNotFoundException {
+ return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+ }
+
+ /** See {@link ContentProvider#openTypedAssetFile ContentProvider.openTypedAssetFile} */
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
+ String mimeType, Bundle opts, CancellationSignal signal)
throws RemoteException, FileNotFoundException {
+ ICancellationSignal remoteSignal = null;
+ if (signal != null) {
+ remoteSignal = mContentProvider.createCancellationSignal();
+ signal.setRemote(remoteSignal);
+ }
try {
- return mContentProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ return mContentProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteSignal);
} catch (DeadObjectException e) {
if (!mStable) {
mContentResolver.unstableProviderDied(mContentProvider);
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index 6f822c1..744e68c 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -18,22 +18,20 @@ package android.content;
import android.content.res.AssetFileDescriptor;
import android.database.BulkCursorDescriptor;
-import android.database.BulkCursorNative;
import android.database.BulkCursorToCursorAdaptor;
import android.database.Cursor;
import android.database.CursorToBulkCursorAdaptor;
import android.database.DatabaseUtils;
-import android.database.IBulkCursor;
import android.database.IContentObserver;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.RemoteException;
import java.io.FileNotFoundException;
import java.util.ArrayList;
@@ -227,9 +225,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
ParcelFileDescriptor fd;
- fd = openFile(callingPkg, url, mode);
+ fd = openFile(callingPkg, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -247,9 +247,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
String callingPkg = data.readString();
Uri url = Uri.CREATOR.createFromParcel(data);
String mode = data.readString();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openAssetFile(callingPkg, url, mode);
+ fd = openAssetFile(callingPkg, url, mode, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -296,9 +298,11 @@ abstract public class ContentProviderNative extends Binder implements IContentPr
Uri url = Uri.CREATOR.createFromParcel(data);
String mimeType = data.readString();
Bundle opts = data.readBundle();
+ ICancellationSignal signal = ICancellationSignal.Stub.asInterface(
+ data.readStrongBinder());
AssetFileDescriptor fd;
- fd = openTypedAssetFile(callingPkg, url, mimeType, opts);
+ fd = openTypedAssetFile(callingPkg, url, mimeType, opts, signal);
reply.writeNoException();
if (fd != null) {
reply.writeInt(1);
@@ -538,7 +542,9 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
+ @Override
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -548,6 +554,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_FILE_TRANSACTION, data, reply, 0);
@@ -561,7 +568,9 @@ final class ContentProviderProxy implements IContentProvider
}
}
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
+ @Override
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -571,6 +580,7 @@ final class ContentProviderProxy implements IContentProvider
data.writeString(callingPkg);
url.writeToParcel(data, 0);
data.writeString(mode);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_ASSET_FILE_TRANSACTION, data, reply, 0);
@@ -629,8 +639,9 @@ final class ContentProviderProxy implements IContentProvider
}
}
+ @Override
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException {
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
@@ -640,6 +651,7 @@ final class ContentProviderProxy implements IContentProvider
url.writeToParcel(data, 0);
data.writeString(mimeType);
data.writeBundle(opts);
+ data.writeStrongBinder(signal != null ? signal.asBinder() : null);
mRemote.transact(IContentProvider.OPEN_TYPED_ASSET_FILE_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 5cabfee..1379d1b 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -55,7 +55,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
-
/**
* This class provides applications access to the content model.
*
@@ -328,7 +327,7 @@ public abstract class ContentResolver {
* content URL can be returned when opened as as stream with
* {@link #openTypedAssetFileDescriptor}. Note that the types here are
* not necessarily a superset of the type returned by {@link #getType} --
- * many content providers can not return a raw stream for the structured
+ * many content providers cannot return a raw stream for the structured
* data that they contain.
*
* @param url A Uri identifying content (either a list or specific type),
@@ -485,6 +484,9 @@ public abstract class ContentResolver {
if (qCursor != null) {
qCursor.close();
}
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (unstableProvider != null) {
releaseUnstableProvider(unstableProvider);
}
@@ -531,7 +533,7 @@ public abstract class ContentResolver {
// with sufficient testing.
return new FileInputStream(uri.getPath());
} else {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r");
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);
try {
return fd != null ? fd.createInputStream() : null;
} catch (IOException e) {
@@ -571,7 +573,7 @@ public abstract class ContentResolver {
*/
public final OutputStream openOutputStream(Uri uri, String mode)
throws FileNotFoundException {
- AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode);
+ AssetFileDescriptor fd = openAssetFileDescriptor(uri, mode, null);
try {
return fd != null ? fd.createOutputStream() : null;
} catch (IOException e) {
@@ -597,10 +599,64 @@ public abstract class ContentResolver {
*
* <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
* on these schemes.
+ * <p>
+ * If opening with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" mode implies a file on disk that supports
+ * seeking. If possible, always use an exclusive mode to give the underlying
+ * {@link ContentProvider} the most flexibility.
+ * <p>
+ * If you are writing a file, and need to communicate an error to the
+ * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
+ *
+ * @param uri The desired URI to open.
+ * @param mode The file mode to use, as per {@link ContentProvider#openFile
+ * ContentProvider.openFile}.
+ * @return Returns a new ParcelFileDescriptor pointing to the file. You
+ * own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException if no
+ * file exists under the URI or the mode is invalid.
+ * @see #openAssetFileDescriptor(Uri, String)
+ */
+ public final ParcelFileDescriptor openFileDescriptor(Uri uri, String mode)
+ throws FileNotFoundException {
+ return openFileDescriptor(uri, mode, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access data under a URI. This
+ * is like {@link #openAssetFileDescriptor(Uri, String)}, but uses the
+ * underlying {@link ContentProvider#openFile}
+ * ContentProvider.openFile()} method, so will <em>not</em> work with
+ * providers that return sub-sections of files. If at all possible,
+ * you should use {@link #openAssetFileDescriptor(Uri, String)}. You
+ * will receive a FileNotFoundException exception if the provider returns a
+ * sub-section of a file.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ *
+ * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information
+ * on these schemes.
+ * <p>
+ * If opening with the exclusive "r" or "w" modes, the returned
+ * ParcelFileDescriptor could be a pipe or socket pair to enable streaming
+ * of data. Opening with the "rw" mode implies a file on disk that supports
+ * seeking. If possible, always use an exclusive mode to give the underlying
+ * {@link ContentProvider} the most flexibility.
+ * <p>
+ * If you are writing a file, and need to communicate an error to the
+ * provider, use {@link ParcelFileDescriptor#closeWithError(String)}.
*
* @param uri The desired URI to open.
* @param mode The file mode to use, as per {@link ContentProvider#openFile
* ContentProvider.openFile}.
+ * @param signal A signal to cancel the operation in progress, or null if
+ * none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
* @return Returns a new ParcelFileDescriptor pointing to the file. You
* own this descriptor and are responsible for closing it when done.
* @throws FileNotFoundException Throws FileNotFoundException if no
@@ -608,8 +664,8 @@ public abstract class ContentResolver {
* @see #openAssetFileDescriptor(Uri, String)
*/
public final ParcelFileDescriptor openFileDescriptor(Uri uri,
- String mode) throws FileNotFoundException {
- AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode);
+ String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
+ AssetFileDescriptor afd = openAssetFileDescriptor(uri, mode, cancellationSignal);
if (afd == null) {
return null;
}
@@ -677,8 +733,64 @@ public abstract class ContentResolver {
* @throws FileNotFoundException Throws FileNotFoundException of no
* file exists under the URI or the mode is invalid.
*/
+ public final AssetFileDescriptor openAssetFileDescriptor(Uri uri, String mode)
+ throws FileNotFoundException {
+ return openAssetFileDescriptor(uri, mode, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access data under a URI. This
+ * interacts with the underlying {@link ContentProvider#openAssetFile}
+ * method of the provider associated with the given URI, to retrieve any file stored there.
+ *
+ * <h5>Accepts the following URI schemes:</h5>
+ * <ul>
+ * <li>content ({@link #SCHEME_CONTENT})</li>
+ * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li>
+ * <li>file ({@link #SCHEME_FILE})</li>
+ * </ul>
+ * <h5>The android.resource ({@link #SCHEME_ANDROID_RESOURCE}) Scheme</h5>
+ * <p>
+ * A Uri object can be used to reference a resource in an APK file. The
+ * Uri should be one of the following formats:
+ * <ul>
+ * <li><code>android.resource://package_name/id_number</code><br/>
+ * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+ * For example <code>com.example.myapp</code><br/>
+ * <code>id_number</code> is the int form of the ID.<br/>
+ * The easiest way to construct this form is
+ * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/" + R.raw.my_resource");</pre>
+ * </li>
+ * <li><code>android.resource://package_name/type/name</code><br/>
+ * <code>package_name</code> is your package name as listed in your AndroidManifest.xml.
+ * For example <code>com.example.myapp</code><br/>
+ * <code>type</code> is the string form of the resource type. For example, <code>raw</code>
+ * or <code>drawable</code>.
+ * <code>name</code> is the string form of the resource name. That is, whatever the file
+ * name was in your res directory, without the type extension.
+ * The easiest way to construct this form is
+ * <pre>Uri uri = Uri.parse("android.resource://com.example.myapp/raw/my_resource");</pre>
+ * </li>
+ * </ul>
+ *
+ * <p>Note that if this function is called for read-only input (mode is "r")
+ * on a content: URI, it will instead call {@link #openTypedAssetFileDescriptor}
+ * for you with a MIME type of "*\/*". This allows such callers to benefit
+ * from any built-in data conversion that a provider implements.
+ *
+ * @param uri The desired URI to open.
+ * @param mode The file mode to use, as per {@link ContentProvider#openAssetFile
+ * ContentProvider.openAssetFile}.
+ * @param signal A signal to cancel the operation in progress, or null if
+ * none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
+ * @return Returns a new ParcelFileDescriptor pointing to the file. You
+ * own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * file exists under the URI or the mode is invalid.
+ */
public final AssetFileDescriptor openAssetFileDescriptor(Uri uri,
- String mode) throws FileNotFoundException {
+ String mode, CancellationSignal cancellationSignal) throws FileNotFoundException {
String scheme = uri.getScheme();
if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {
if (!"r".equals(mode)) {
@@ -696,7 +808,7 @@ public abstract class ContentResolver {
return new AssetFileDescriptor(pfd, 0, -1);
} else {
if ("r".equals(mode)) {
- return openTypedAssetFileDescriptor(uri, "*/*", null);
+ return openTypedAssetFileDescriptor(uri, "*/*", null, cancellationSignal);
} else {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
@@ -706,8 +818,16 @@ public abstract class ContentResolver {
AssetFileDescriptor fd = null;
try {
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = unstableProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+
try {
- fd = unstableProvider.openAssetFile(mPackageName, uri, mode);
+ fd = unstableProvider.openAssetFile(
+ mPackageName, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -721,7 +841,8 @@ public abstract class ContentResolver {
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openAssetFile(mPackageName, uri, mode);
+ fd = stableProvider.openAssetFile(
+ mPackageName, uri, mode, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -749,6 +870,9 @@ public abstract class ContentResolver {
} catch (FileNotFoundException e) {
throw e;
} finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (stableProvider != null) {
releaseProvider(stableProvider);
}
@@ -788,8 +912,45 @@ public abstract class ContentResolver {
* @throws FileNotFoundException Throws FileNotFoundException of no
* data of the desired type exists under the URI.
*/
+ public final AssetFileDescriptor openTypedAssetFileDescriptor(
+ Uri uri, String mimeType, Bundle opts) throws FileNotFoundException {
+ return openTypedAssetFileDescriptor(uri, mimeType, opts, null);
+ }
+
+ /**
+ * Open a raw file descriptor to access (potentially type transformed)
+ * data from a "content:" URI. This interacts with the underlying
+ * {@link ContentProvider#openTypedAssetFile} method of the provider
+ * associated with the given URI, to retrieve retrieve any appropriate
+ * data stream for the data stored there.
+ *
+ * <p>Unlike {@link #openAssetFileDescriptor}, this function only works
+ * with "content:" URIs, because content providers are the only facility
+ * with an associated MIME type to ensure that the returned data stream
+ * is of the desired type.
+ *
+ * <p>All text/* streams are encoded in UTF-8.
+ *
+ * @param uri The desired URI to open.
+ * @param mimeType The desired MIME type of the returned data. This can
+ * be a pattern such as *\/*, which will allow the content provider to
+ * select a type, though there is no way for you to determine what type
+ * it is returning.
+ * @param opts Additional provider-dependent options.
+ * @param signal A signal to cancel the operation in progress, or null if
+ * none. If the operation is canceled, then
+ * {@link OperationCanceledException} will be thrown.
+ * @return Returns a new ParcelFileDescriptor from which you can read the
+ * data stream from the provider. Note that this may be a pipe, meaning
+ * you can't seek in it. The only seek you should do is if the
+ * AssetFileDescriptor contains an offset, to move to that offset before
+ * reading. You own this descriptor and are responsible for closing it when done.
+ * @throws FileNotFoundException Throws FileNotFoundException of no
+ * data of the desired type exists under the URI.
+ */
public final AssetFileDescriptor openTypedAssetFileDescriptor(Uri uri,
- String mimeType, Bundle opts) throws FileNotFoundException {
+ String mimeType, Bundle opts, CancellationSignal cancellationSignal)
+ throws FileNotFoundException {
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
@@ -798,8 +959,16 @@ public abstract class ContentResolver {
AssetFileDescriptor fd = null;
try {
+ ICancellationSignal remoteCancellationSignal = null;
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ remoteCancellationSignal = unstableProvider.createCancellationSignal();
+ cancellationSignal.setRemote(remoteCancellationSignal);
+ }
+
try {
- fd = unstableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ fd = unstableProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -813,7 +982,8 @@ public abstract class ContentResolver {
if (stableProvider == null) {
throw new FileNotFoundException("No content provider: " + uri);
}
- fd = stableProvider.openTypedAssetFile(mPackageName, uri, mimeType, opts);
+ fd = stableProvider.openTypedAssetFile(
+ mPackageName, uri, mimeType, opts, remoteCancellationSignal);
if (fd == null) {
// The provider will be released by the finally{} clause
return null;
@@ -841,6 +1011,9 @@ public abstract class ContentResolver {
} catch (FileNotFoundException e) {
throw e;
} finally {
+ if (cancellationSignal != null) {
+ cancellationSignal.setRemote(null);
+ }
if (stableProvider != null) {
releaseProvider(stableProvider);
}
diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java
index 62b79f0..6ea8876 100644
--- a/core/java/android/content/IContentProvider.java
+++ b/core/java/android/content/IContentProvider.java
@@ -46,9 +46,11 @@ public interface IContentProvider extends IInterface {
throws RemoteException;
public int update(String callingPkg, Uri url, ContentValues values, String selection,
String[] selectionArgs) throws RemoteException;
- public ParcelFileDescriptor openFile(String callingPkg, Uri url, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
- public AssetFileDescriptor openAssetFile(String callingPkg, Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPkg, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException;
public ContentProviderResult[] applyBatch(String callingPkg,
ArrayList<ContentProviderOperation> operations)
@@ -60,7 +62,7 @@ public interface IContentProvider extends IInterface {
// Data interchange.
public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException;
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
/* IPC constants */
static final String descriptor = "android.content.IContentProvider";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 017ad98..be13620 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -35,6 +35,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.StrictMode;
+import android.provider.DocumentsContract;
import android.util.AttributeSet;
import android.util.Log;
@@ -2650,11 +2651,16 @@ public class Intent implements Parcelable, Cloneable {
* multiple selection), then it can specify {@link #EXTRA_ALLOW_MULTIPLE} to
* indicate this.
* <p>
- * All returned URIs can be opened as a stream with
- * {@link ContentResolver#openInputStream(Uri)}.
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
+ * returned URIs can be opened with
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was picked. This must be a content: URI
- * so that any receiver can access it.
+ * so that any receiver can access it. If multiple documents were selected,
+ * they are returned in {@link #getClipData()}.
+ *
+ * @see DocumentsContract
+ * @see DocumentsContract#getOpenDocuments(Context)
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
@@ -2669,11 +2675,14 @@ public class Intent implements Parcelable, Cloneable {
* optionally hint at the MIME type being created by setting
* {@link #setType(String)}.
* <p>
- * All returned URIs can be opened as a stream with
- * {@link ContentResolver#openOutputStream(Uri)}.
+ * Callers must include {@link #CATEGORY_OPENABLE} in the Intent so that
+ * returned URIs can be opened with
+ * {@link ContentResolver#openFileDescriptor(Uri, String)}.
* <p>
* Output: The URI of the item that was created. This must be a content: URI
* so that any receiver can access it.
+ *
+ * @see DocumentsContract
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CREATE_DOCUMENT = "android.intent.action.CREATE_DOCUMENT";
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index da7647a..0d1a740 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -30,6 +30,8 @@ import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.OnCloseListener;
import android.util.Log;
import com.google.android.collect.Lists;
@@ -41,8 +43,33 @@ import java.io.IOException;
import java.util.List;
/**
- * The contract between a storage backend and the platform. Contains definitions
- * for the supported URIs and columns.
+ * Defines the contract between a documents provider and the platform.
+ * <p>
+ * A document provider is a {@link ContentProvider} that presents a set of
+ * documents in a hierarchical structure. The system provides UI that visualizes
+ * all available document providers, offering users the ability to open existing
+ * documents or create new documents.
+ * <p>
+ * Each provider expresses one or more "roots" which each serve as the top-level
+ * of a tree. For example, a root could represent an account, or a physical
+ * storage device. Under each root, documents are referenced by a unique
+ * {@link DocumentColumns#DOC_ID}, and each root starts at the
+ * {@link Documents#DOC_ID_ROOT} document.
+ * <p>
+ * Documents can be either an openable file (with a specific MIME type), or a
+ * directory containing additional documents (with the
+ * {@link Documents#MIME_TYPE_DIR} MIME type). Each document can have different
+ * capabilities, as described by {@link DocumentColumns#FLAGS}. The same
+ * {@link DocumentColumns#DOC_ID} can be included in multiple directories.
+ * <p>
+ * Document providers must be protected with the
+ * {@link android.Manifest.permission#MANAGE_DOCUMENTS} permission, which can
+ * only be requested by the system. The system-provided UI then issues narrow
+ * Uri permission grants for individual documents when the user explicitly picks
+ * documents.
+ *
+ * @see Intent#ACTION_OPEN_DOCUMENT
+ * @see Intent#ACTION_CREATE_DOCUMENT
*/
public final class DocumentsContract {
private static final String TAG = "Documents";
@@ -59,6 +86,9 @@ public final class DocumentsContract {
/** {@hide} */
public static final String ACTION_DOCUMENT_CHANGED = "android.provider.action.DOCUMENT_CHANGED";
+ /**
+ * Constants for individual documents.
+ */
public static class Documents {
private Documents() {
}
@@ -73,7 +103,7 @@ public final class DocumentsContract {
/**
* {@link DocumentColumns#DOC_ID} value representing the root directory of a
- * storage root.
+ * documents root.
*/
public static final String DOC_ID_ROOT = "0";
@@ -144,8 +174,8 @@ public final class DocumentsContract {
/**
* Extra boolean flag included in a directory {@link Cursor#getExtras()}
- * indicating that the backend can provide additional data if requested,
- * such as additional search results.
+ * indicating that the document provider can provide additional data if
+ * requested, such as additional search results.
*/
public static final String EXTRA_HAS_MORE = "has_more";
@@ -170,21 +200,24 @@ public final class DocumentsContract {
private static final String PARAM_LOCAL_ONLY = "localOnly";
/**
- * Build URI representing the roots in a storage backend.
+ * Build Uri representing the roots offered by a document provider.
*/
public static Uri buildRootsUri(String authority) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).appendPath(PATH_ROOTS).build();
}
+ /**
+ * Build Uri representing a specific root offered by a document provider.
+ */
public static Uri buildRootUri(String authority, String rootId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
.authority(authority).appendPath(PATH_ROOTS).appendPath(rootId).build();
}
/**
- * Build URI representing the given {@link DocumentColumns#DOC_ID} in a
- * storage root.
+ * Build Uri representing the given {@link DocumentColumns#DOC_ID} in a
+ * document provider.
*/
public static Uri buildDocumentUri(String authority, String rootId, String docId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -193,8 +226,8 @@ public final class DocumentsContract {
}
/**
- * Build URI representing the contents of the given directory in a storage
- * backend. The given document must be {@link Documents#MIME_TYPE_DIR}.
+ * Build Uri representing the contents of the given directory in a document
+ * provider. The given document must be {@link Documents#MIME_TYPE_DIR}.
*/
public static Uri buildContentsUri(String authority, String rootId, String docId) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -203,8 +236,9 @@ public final class DocumentsContract {
}
/**
- * Build URI representing a search for matching documents under a directory
- * in a storage backend.
+ * Build Uri representing a search for matching documents under a specific
+ * directory in a document provider. The given document must have
+ * {@link Documents#FLAG_SUPPORTS_SEARCH}.
*/
public static Uri buildSearchUri(String authority, String rootId, String docId, String query) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
@@ -212,20 +246,36 @@ public final class DocumentsContract {
.appendPath(PATH_SEARCH).appendQueryParameter(PARAM_QUERY, query).build();
}
+ /**
+ * Convenience method for {@link #buildDocumentUri(String, String, String)},
+ * extracting authority and root from the given Uri.
+ */
public static Uri buildDocumentUri(Uri relatedUri, String docId) {
return buildDocumentUri(relatedUri.getAuthority(), getRootId(relatedUri), docId);
}
+ /**
+ * Convenience method for {@link #buildContentsUri(String, String, String)},
+ * extracting authority and root from the given Uri.
+ */
public static Uri buildContentsUri(Uri relatedUri) {
return buildContentsUri(
relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri));
}
+ /**
+ * Convenience method for
+ * {@link #buildSearchUri(String, String, String, String)}, extracting
+ * authority and root from the given Uri.
+ */
public static Uri buildSearchUri(Uri relatedUri, String query) {
return buildSearchUri(
relatedUri.getAuthority(), getRootId(relatedUri), getDocId(relatedUri), query);
}
+ /**
+ * Extract the {@link RootColumns#ROOT_ID} from the given Uri.
+ */
public static String getRootId(Uri documentUri) {
final List<String> paths = documentUri.getPathSegments();
if (paths.size() < 2) {
@@ -237,6 +287,9 @@ public final class DocumentsContract {
return paths.get(1);
}
+ /**
+ * Extract the {@link DocumentColumns#DOC_ID} from the given Uri.
+ */
public static String getDocId(Uri documentUri) {
final List<String> paths = documentUri.getPathSegments();
if (paths.size() < 4) {
@@ -252,15 +305,17 @@ public final class DocumentsContract {
}
/**
- * Return requested search query from the given Uri.
+ * Return requested search query from the given Uri, as constructed by
+ * {@link #buildSearchUri(String, String, String, String)}.
*/
public static String getSearchQuery(Uri documentUri) {
return documentUri.getQueryParameter(PARAM_QUERY);
}
/**
- * Mark the given Uri to indicate that only locally-available contents
- * should be returned.
+ * Mark the given Uri to indicate that only locally-available data should be
+ * returned. That is, no network connections should be initiated to provide
+ * the metadata or content.
*/
public static Uri setLocalOnly(Uri documentUri) {
return documentUri.buildUpon()
@@ -268,20 +323,21 @@ public final class DocumentsContract {
}
/**
- * Return if the given Uri is requesting that only locally-available content
- * be returned. That is, no network connections should be initiated to
- * provide the metadata or content.
+ * Return if the given Uri is requesting that only locally-available data be
+ * returned. That is, no network connections should be initiated to provide
+ * the metadata or content.
*/
public static boolean isLocalOnly(Uri documentUri) {
return documentUri.getBooleanQueryParameter(PARAM_LOCAL_ONLY, false);
}
/**
- * These are standard columns for document URIs. Storage backend providers
- * <em>must</em> support at least these columns when queried.
+ * Standard columns for document queries. Document providers <em>must</em>
+ * support at least these columns when queried.
*
- * @see Intent#ACTION_OPEN_DOCUMENT
- * @see Intent#ACTION_CREATE_DOCUMENT
+ * @see DocumentsContract#buildDocumentUri(String, String, String)
+ * @see DocumentsContract#buildContentsUri(String, String, String)
+ * @see DocumentsContract#buildSearchUri(String, String, String, String)
*/
public interface DocumentColumns extends OpenableColumns {
/**
@@ -296,8 +352,8 @@ public final class DocumentsContract {
/**
* MIME type of a document, matching the value returned by
* {@link ContentResolver#getType(android.net.Uri)}. This field must be
- * provided when a new document is created, but after that the field is
- * read-only.
+ * provided when a new document is created. This field is read-only to
+ * document clients.
* <p>
* Type: STRING
*
@@ -308,7 +364,9 @@ public final class DocumentsContract {
/**
* Timestamp when a document was last modified, in milliseconds since
* January 1, 1970 00:00:00.0 UTC. This field is read-only to document
- * clients.
+ * clients. Document providers can update this field using events from
+ * {@link OnCloseListener} or other reliable
+ * {@link ParcelFileDescriptor} transport.
* <p>
* Type: INTEGER (long)
*
@@ -325,13 +383,17 @@ public final class DocumentsContract {
public static final String FLAGS = "flags";
/**
- * Summary for this document, or {@code null} to omit.
+ * Summary for this document, or {@code null} to omit. This field is
+ * read-only to document clients.
* <p>
* Type: STRING
*/
public static final String SUMMARY = "summary";
}
+ /**
+ * Constants for individual document roots.
+ */
public static class Roots {
private Roots() {
}
@@ -340,7 +402,8 @@ public final class DocumentsContract {
public static final String MIME_TYPE_ITEM = "vnd.android.cursor.item/root";
/**
- * Root that represents a cloud-based storage service.
+ * Root that represents a storage service, such as a cloud-based
+ * service.
*
* @see RootColumns#ROOT_TYPE
*/
@@ -371,15 +434,17 @@ public final class DocumentsContract {
}
/**
- * These are standard columns for the roots URI.
+ * Standard columns for document root queries.
*
* @see DocumentsContract#buildRootsUri(String)
+ * @see DocumentsContract#buildRootUri(String, String)
*/
public interface RootColumns {
public static final String ROOT_ID = "root_id";
/**
- * Storage root type, use for clustering.
+ * Storage root type, use for clustering. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (int)
*
@@ -389,8 +454,9 @@ public final class DocumentsContract {
public static final String ROOT_TYPE = "root_type";
/**
- * Icon resource ID for this storage root, or {@code 0} to use the
- * default {@link ProviderInfo#icon}.
+ * Icon resource ID for this storage root, or {@code null} to use the
+ * default {@link ProviderInfo#icon}. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (int)
*/
@@ -398,22 +464,25 @@ public final class DocumentsContract {
/**
* Title for this storage root, or {@code null} to use the default
- * {@link ProviderInfo#labelRes}.
+ * {@link ProviderInfo#labelRes}. This field is read-only to document
+ * clients.
* <p>
* Type: STRING
*/
public static final String TITLE = "title";
/**
- * Summary for this storage root, or {@code null} to omit.
+ * Summary for this storage root, or {@code null} to omit. This field is
+ * read-only to document clients.
* <p>
* Type: STRING
*/
public static final String SUMMARY = "summary";
/**
- * Number of free bytes of available in this storage root, or -1 if
- * unknown or unbounded.
+ * Number of free bytes of available in this storage root, or
+ * {@code null} if unknown or unbounded. This field is read-only to
+ * document clients.
* <p>
* Type: INTEGER (long)
*/
@@ -452,7 +521,7 @@ public final class DocumentsContract {
/**
* Return thumbnail representing the document at the given URI. Callers are
- * responsible for their own caching. Given document must have
+ * responsible for their own in-memory caching. Given document must have
* {@link Documents#FLAG_SUPPORTS_THUMBNAIL} set.
*
* @return decoded thumbnail, or {@code null} if problem was encountered.
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 091737d..22e3b98 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -584,7 +584,6 @@ public class DocumentsActivity extends Activity {
SaveFragment.get(fm).setReplaceTarget(doc);
} else if (mAction == ACTION_MANAGE) {
// Open the document
- // TODO: trampoline activity for launching downloaded APKs
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(doc.uri);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 8f4b6c2..4678b85 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -7644,7 +7644,7 @@ public final class ActivityManagerService extends ActivityManagerNative
sCallerIdentity.set(new Identity(
Binder.getCallingPid(), Binder.getCallingUid()));
try {
- pfd = cph.provider.openFile(null, uri, "r");
+ pfd = cph.provider.openFile(null, uri, "r", null);
} catch (FileNotFoundException e) {
// do nothing; pfd will be returned null
} finally {
diff --git a/test-runner/src/android/test/mock/MockContentProvider.java b/test-runner/src/android/test/mock/MockContentProvider.java
index 373f24a..596ea0a 100644
--- a/test-runner/src/android/test/mock/MockContentProvider.java
+++ b/test-runner/src/android/test/mock/MockContentProvider.java
@@ -83,13 +83,15 @@ public class MockContentProvider extends ContentProvider {
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri url, String mode)
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openAssetFile(url, mode);
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode)
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openFile(url, mode);
}
@@ -126,7 +128,7 @@ public class MockContentProvider extends ContentProvider {
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url,
- String mimeType, Bundle opts)
+ String mimeType, Bundle opts, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
return MockContentProvider.this.openTypedAssetFile(url, mimeType, opts);
}
diff --git a/test-runner/src/android/test/mock/MockIContentProvider.java b/test-runner/src/android/test/mock/MockIContentProvider.java
index 2b4fce6..b14ce49 100644
--- a/test-runner/src/android/test/mock/MockIContentProvider.java
+++ b/test-runner/src/android/test/mock/MockIContentProvider.java
@@ -61,11 +61,13 @@ public class MockIContentProvider implements IContentProvider {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public ParcelFileDescriptor openFile(String callingPackage, Uri url, String mode) {
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri url, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri uri, String mode) {
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri uri, String mode, ICancellationSignal signal) {
throw new UnsupportedOperationException("unimplemented mock method");
}
@@ -104,7 +106,7 @@ public class MockIContentProvider implements IContentProvider {
}
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri url, String mimeType,
- Bundle opts) throws RemoteException, FileNotFoundException {
+ Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
throw new UnsupportedOperationException("unimplemented mock method");
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 4aea38f..688cc87 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -81,14 +81,16 @@ public final class BridgeContentProvider implements IContentProvider {
}
@Override
- public AssetFileDescriptor openAssetFile(String callingPackage, Uri arg0, String arg1)
+ public AssetFileDescriptor openAssetFile(
+ String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}
@Override
- public ParcelFileDescriptor openFile(String callingPackage, Uri arg0, String arg1)
+ public ParcelFileDescriptor openFile(
+ String callingPackage, Uri arg0, String arg1, ICancellationSignal signal)
throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
@@ -122,7 +124,7 @@ public final class BridgeContentProvider implements IContentProvider {
@Override
public AssetFileDescriptor openTypedAssetFile(String callingPackage, Uri arg0, String arg1,
- Bundle arg2) throws RemoteException, FileNotFoundException {
+ Bundle arg2, ICancellationSignal signal) throws RemoteException, FileNotFoundException {
// TODO Auto-generated method stub
return null;
}