diff options
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; } |