summaryrefslogtreecommitdiffstats
path: root/core/java/android/provider/DocumentsProvider.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/provider/DocumentsProvider.java')
-rw-r--r--core/java/android/provider/DocumentsProvider.java229
1 files changed, 132 insertions, 97 deletions
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index eeb8c41..09f4866 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -16,16 +16,12 @@
package android.provider;
-import static android.provider.DocumentsContract.ACTION_DOCUMENT_ROOT_CHANGED;
-import static android.provider.DocumentsContract.EXTRA_AUTHORITY;
-import static android.provider.DocumentsContract.EXTRA_ROOTS;
import static android.provider.DocumentsContract.EXTRA_THUMBNAIL_SIZE;
import static android.provider.DocumentsContract.METHOD_CREATE_DOCUMENT;
import static android.provider.DocumentsContract.METHOD_DELETE_DOCUMENT;
-import static android.provider.DocumentsContract.METHOD_GET_ROOTS;
-import static android.provider.DocumentsContract.METHOD_RENAME_DOCUMENT;
-import static android.provider.DocumentsContract.getDocId;
-import static android.provider.DocumentsContract.getSearchQuery;
+import static android.provider.DocumentsContract.getDocumentId;
+import static android.provider.DocumentsContract.getRootId;
+import static android.provider.DocumentsContract.getSearchDocumentsQuery;
import android.content.ContentProvider;
import android.content.ContentValues;
@@ -41,15 +37,12 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.OnCloseListener;
-import android.provider.DocumentsContract.DocumentColumns;
-import android.provider.DocumentsContract.DocumentRoot;
-import android.provider.DocumentsContract.Documents;
+import android.provider.DocumentsContract.Document;
import android.util.Log;
import libcore.io.IoUtils;
import java.io.FileNotFoundException;
-import java.util.List;
/**
* Base class for a document provider. A document provider should extend this
@@ -58,13 +51,13 @@ import java.util.List;
* Each document 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
- * {@link DocumentColumns#DOC_ID}, which must not change once returned.
+ * {@link Document#COLUMN_DOCUMENT_ID}, which must not change once returned.
* <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.
+ * {@link Document#MIME_TYPE_DIR} MIME type). Each document can have different
+ * capabilities, as described by {@link Document#COLUMN_FLAGS}. The same
+ * {@link Document#COLUMN_DOCUMENT_ID} can be included in multiple directories.
* <p>
* Document providers must be protected with the
* {@link android.Manifest.permission#MANAGE_DOCUMENTS} permission, which can
@@ -78,22 +71,29 @@ import java.util.List;
public abstract class DocumentsProvider extends ContentProvider {
private static final String TAG = "DocumentsProvider";
- private static final int MATCH_DOCUMENT = 1;
- private static final int MATCH_CHILDREN = 2;
- private static final int MATCH_SEARCH = 3;
+ private static final int MATCH_ROOT = 1;
+ private static final int MATCH_RECENT = 2;
+ private static final int MATCH_DOCUMENT = 3;
+ private static final int MATCH_CHILDREN = 4;
+ private static final int MATCH_SEARCH = 5;
private String mAuthority;
private UriMatcher mMatcher;
+ /**
+ * Implementation is provided by the parent class.
+ */
@Override
public void attachInfo(Context context, ProviderInfo info) {
mAuthority = info.authority;
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
- mMatcher.addURI(mAuthority, "docs/*", MATCH_DOCUMENT);
- mMatcher.addURI(mAuthority, "docs/*/children", MATCH_CHILDREN);
- mMatcher.addURI(mAuthority, "docs/*/search", MATCH_SEARCH);
+ mMatcher.addURI(mAuthority, "root", MATCH_ROOT);
+ mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
+ mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
+ mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
+ mMatcher.addURI(mAuthority, "document/*/search", MATCH_SEARCH);
// Sanity check our setup
if (!info.exported) {
@@ -111,83 +111,80 @@ public abstract class DocumentsProvider extends ContentProvider {
}
/**
- * Return list of all document roots provided by this document provider.
- * When this list changes, a provider must call
- * {@link #notifyDocumentRootsChanged()}.
- */
- public abstract List<DocumentRoot> getDocumentRoots();
-
- /**
- * Create and return a new document. A provider must allocate a new
- * {@link DocumentColumns#DOC_ID} to represent the document, which must not
- * change once returned.
+ * Create a new document and return its {@link Document#COLUMN_DOCUMENT_ID}.
+ * A provider must allocate a new {@link Document#COLUMN_DOCUMENT_ID} to
+ * represent the document, which must not change once returned.
*
- * @param docId the parent directory to create the new document under.
+ * @param documentId the parent directory to create the new document under.
* @param mimeType the MIME type associated with the new document.
* @param displayName the display name of the new document.
*/
@SuppressWarnings("unused")
- public String createDocument(String docId, String mimeType, String displayName)
+ public String createDocument(String documentId, String mimeType, String displayName)
throws FileNotFoundException {
throw new UnsupportedOperationException("Create not supported");
}
/**
- * Rename the given document.
+ * Delete the given document. Upon returning, any Uri permission grants for
+ * the given document will be revoked. If additional documents were deleted
+ * as a side effect of this call, such as documents inside a directory, the
+ * implementor is responsible for revoking those permissions.
*
- * @param docId the document to rename.
- * @param displayName the new display name.
+ * @param documentId the document to delete.
*/
@SuppressWarnings("unused")
- public void renameDocument(String docId, String displayName) throws FileNotFoundException {
- throw new UnsupportedOperationException("Rename not supported");
+ public void deleteDocument(String documentId) throws FileNotFoundException {
+ throw new UnsupportedOperationException("Delete not supported");
}
- /**
- * Delete the given document.
- *
- * @param docId the document to delete.
- */
+ public abstract Cursor queryRoots(String[] projection) throws FileNotFoundException;
+
@SuppressWarnings("unused")
- public void deleteDocument(String docId) throws FileNotFoundException {
- throw new UnsupportedOperationException("Delete not supported");
+ public Cursor queryRecentDocuments(String rootId, String[] projection)
+ throws FileNotFoundException {
+ throw new UnsupportedOperationException("Recent not supported");
}
/**
* Return metadata for the given document. A provider should avoid making
* network requests to keep this request fast.
*
- * @param docId the document to return.
+ * @param documentId the document to return.
*/
- public abstract Cursor queryDocument(String docId) throws FileNotFoundException;
+ public abstract Cursor queryDocument(String documentId, String[] projection)
+ throws FileNotFoundException;
/**
* Return the children of the given document which is a directory.
*
- * @param docId the directory to return children for.
+ * @param parentDocumentId the directory to return children for.
*/
- public abstract Cursor queryDocumentChildren(String docId) throws FileNotFoundException;
+ public abstract Cursor queryChildDocuments(
+ String parentDocumentId, String[] projection, String sortOrder)
+ throws FileNotFoundException;
/**
* Return documents that that match the given query, starting the search at
* the given directory.
*
- * @param docId the directory to start search at.
+ * @param parentDocumentId the directory to start search at.
*/
@SuppressWarnings("unused")
- public Cursor querySearch(String docId, String query) throws FileNotFoundException {
+ public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection)
+ throws FileNotFoundException {
throw new UnsupportedOperationException("Search not supported");
}
/**
* Return MIME type for the given document. Must match the value of
- * {@link DocumentColumns#MIME_TYPE} for this document.
+ * {@link Document#COLUMN_MIME_TYPE} for this document.
*/
- public String getType(String docId) throws FileNotFoundException {
- final Cursor cursor = queryDocument(docId);
+ public String getDocumentType(String documentId) throws FileNotFoundException {
+ final Cursor cursor = queryDocument(documentId, null);
try {
if (cursor.moveToFirst()) {
- return cursor.getString(cursor.getColumnIndexOrThrow(DocumentColumns.MIME_TYPE));
+ return cursor.getString(cursor.getColumnIndexOrThrow(Document.COLUMN_MIME_TYPE));
} else {
return null;
}
@@ -233,7 +230,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* @param sizeHint hint of the optimal thumbnail dimensions.
* @param signal used by the caller to signal if the request should be
* cancelled.
- * @see Documents#FLAG_SUPPORTS_THUMBNAIL
+ * @see Document#FLAG_SUPPORTS_THUMBNAIL
*/
@SuppressWarnings("unused")
public AssetFileDescriptor openDocumentThumbnail(
@@ -241,17 +238,31 @@ public abstract class DocumentsProvider extends ContentProvider {
throw new UnsupportedOperationException("Thumbnails not supported");
}
+ /**
+ * Implementation is provided by the parent class. Cannot be overriden.
+ *
+ * @see #queryRoots(String[])
+ * @see #queryRecentDocuments(String, String[])
+ * @see #queryDocument(String, String[])
+ * @see #queryChildDocuments(String, String[], String)
+ * @see #querySearchDocuments(String, String, String[])
+ */
@Override
- public final Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
+ public final Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
try {
switch (mMatcher.match(uri)) {
+ case MATCH_ROOT:
+ return queryRoots(projection);
+ case MATCH_RECENT:
+ return queryRecentDocuments(getRootId(uri), projection);
case MATCH_DOCUMENT:
- return queryDocument(getDocId(uri));
+ return queryDocument(getDocumentId(uri), projection);
case MATCH_CHILDREN:
- return queryDocumentChildren(getDocId(uri));
+ return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
case MATCH_SEARCH:
- return querySearch(getDocId(uri), getSearchQuery(uri));
+ return querySearchDocuments(
+ getDocumentId(uri), getSearchDocumentsQuery(uri), projection);
default:
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
@@ -261,12 +272,17 @@ public abstract class DocumentsProvider extends ContentProvider {
}
}
+ /**
+ * Implementation is provided by the parent class. Cannot be overriden.
+ *
+ * @see #getDocumentType(String)
+ */
@Override
public final String getType(Uri uri) {
try {
switch (mMatcher.match(uri)) {
case MATCH_DOCUMENT:
- return getType(getDocId(uri));
+ return getDocumentType(getDocumentId(uri));
default:
return null;
}
@@ -276,22 +292,39 @@ public abstract class DocumentsProvider extends ContentProvider {
}
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ *
+ * @see #createDocument(String, String, String)
+ */
@Override
public final Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("Insert not supported");
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ *
+ * @see #deleteDocument(String)
+ */
@Override
public final int delete(Uri uri, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Delete not supported");
}
+ /**
+ * Implementation is provided by the parent class. Throws by default, and
+ * cannot be overriden.
+ */
@Override
public final int update(
Uri uri, ContentValues values, String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Update not supported");
}
+ /** {@hide} */
@Override
public final Bundle callFromPackage(
String callingPackage, String method, String arg, Bundle extras) {
@@ -300,33 +333,25 @@ public abstract class DocumentsProvider extends ContentProvider {
return super.callFromPackage(callingPackage, method, arg, extras);
}
- // Platform operations require the caller explicitly hold manage
- // permission; Uri permissions don't extend management operations.
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_DOCUMENTS, "Document management");
+ // Require that caller can manage given document
+ final String documentId = extras.getString(Document.COLUMN_DOCUMENT_ID);
+ final Uri documentUri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
+ getContext().enforceCallingOrSelfUriPermission(
+ documentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, method);
final Bundle out = new Bundle();
try {
- if (METHOD_GET_ROOTS.equals(method)) {
- final List<DocumentRoot> roots = getDocumentRoots();
- out.putParcelableList(EXTRA_ROOTS, roots);
-
- } else if (METHOD_CREATE_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
- final String mimeType = extras.getString(DocumentColumns.MIME_TYPE);
- final String displayName = extras.getString(DocumentColumns.DISPLAY_NAME);
+ if (METHOD_CREATE_DOCUMENT.equals(method)) {
+ final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
+ final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
- // TODO: issue Uri grant towards caller
- final String newDocId = createDocument(docId, mimeType, displayName);
- out.putString(DocumentColumns.DOC_ID, newDocId);
-
- } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
- final String displayName = extras.getString(DocumentColumns.DISPLAY_NAME);
- renameDocument(docId, displayName);
+ // TODO: issue Uri grant towards calling package
+ // TODO: enforce that package belongs to caller
+ final String newDocumentId = createDocument(documentId, mimeType, displayName);
+ out.putString(Document.COLUMN_DOCUMENT_ID, newDocumentId);
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
- final String docId = extras.getString(DocumentColumns.DOC_ID);
+ final String docId = extras.getString(Document.COLUMN_DOCUMENT_ID);
deleteDocument(docId);
} else {
@@ -338,47 +363,57 @@ public abstract class DocumentsProvider extends ContentProvider {
return out;
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocument(String, String, CancellationSignal)
+ */
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
- return openDocument(getDocId(uri), mode, null);
+ return openDocument(getDocumentId(uri), mode, null);
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocument(String, String, CancellationSignal)
+ */
@Override
public final ParcelFileDescriptor openFile(Uri uri, String mode, CancellationSignal signal)
throws FileNotFoundException {
- return openDocument(getDocId(uri), mode, signal);
+ return openDocument(getDocumentId(uri), mode, signal);
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ */
@Override
public final AssetFileDescriptor openTypedAssetFile(Uri uri, String mimeTypeFilter, Bundle opts)
throws FileNotFoundException {
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
- return openDocumentThumbnail(getDocId(uri), sizeHint, null);
+ return openDocumentThumbnail(getDocumentId(uri), sizeHint, null);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts);
}
}
+ /**
+ * Implementation is provided by the parent class.
+ *
+ * @see #openDocumentThumbnail(String, Point, CancellationSignal)
+ */
@Override
public final AssetFileDescriptor openTypedAssetFile(
Uri uri, String mimeTypeFilter, Bundle opts, CancellationSignal signal)
throws FileNotFoundException {
if (opts != null && opts.containsKey(EXTRA_THUMBNAIL_SIZE)) {
final Point sizeHint = opts.getParcelable(EXTRA_THUMBNAIL_SIZE);
- return openDocumentThumbnail(getDocId(uri), sizeHint, signal);
+ return openDocumentThumbnail(getDocumentId(uri), sizeHint, signal);
} else {
return super.openTypedAssetFile(uri, mimeTypeFilter, opts, signal);
}
}
-
- /**
- * Notify system that {@link #getDocumentRoots()} has changed, usually due to an
- * account or device change.
- */
- public void notifyDocumentRootsChanged() {
- final Intent intent = new Intent(ACTION_DOCUMENT_ROOT_CHANGED);
- intent.putExtra(EXTRA_AUTHORITY, mAuthority);
- getContext().sendBroadcast(intent);
- }
}