summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/provider/DocumentsContract.java51
-rw-r--r--core/java/android/provider/DocumentsProvider.java97
2 files changed, 127 insertions, 21 deletions
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index b907375..6b8e2de 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -287,6 +287,16 @@ public final class DocumentsContract {
public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 1 << 5;
/**
+ * Flag indicating that a document can be renamed.
+ *
+ * @see #COLUMN_FLAGS
+ * @see DocumentsContract#renameDocument(ContentProviderClient, Uri,
+ * String)
+ * @see DocumentsProvider#renameDocument(String, String)
+ */
+ public static final int FLAG_SUPPORTS_RENAME = 1 << 6;
+
+ /**
* Flag indicating that document titles should be hidden when viewing
* this directory in a larger format grid. For example, a directory
* containing only images may want the image thumbnails to speak for
@@ -494,6 +504,8 @@ public final class DocumentsContract {
/** {@hide} */
public static final String METHOD_CREATE_DOCUMENT = "android:createDocument";
/** {@hide} */
+ public static final String METHOD_RENAME_DOCUMENT = "android:renameDocument";
+ /** {@hide} */
public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
/** {@hide} */
@@ -898,6 +910,45 @@ public final class DocumentsContract {
}
/**
+ * Change the display name of an existing document.
+ * <p>
+ * If the underlying provider needs to create a new
+ * {@link Document#COLUMN_DOCUMENT_ID} to represent the updated display
+ * name, that new document is returned and the original document is no
+ * longer valid. Otherwise, the original document is returned.
+ *
+ * @param documentUri document with {@link Document#FLAG_SUPPORTS_RENAME}
+ * @param displayName updated name for document
+ * @return the existing or new document after the rename, or {@code null} if
+ * failed.
+ */
+ public static Uri renameDocument(ContentResolver resolver, Uri documentUri,
+ String displayName) {
+ final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
+ documentUri.getAuthority());
+ try {
+ return renameDocument(client, documentUri, displayName);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to rename document", e);
+ return null;
+ } finally {
+ ContentProviderClient.releaseQuietly(client);
+ }
+ }
+
+ /** {@hide} */
+ public static Uri renameDocument(ContentProviderClient client, Uri documentUri,
+ String displayName) throws RemoteException {
+ final Bundle in = new Bundle();
+ in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
+ in.putString(Document.COLUMN_DISPLAY_NAME, displayName);
+
+ final Bundle out = client.call(METHOD_RENAME_DOCUMENT, null, in);
+ final Uri outUri = out.getParcelable(DocumentsContract.EXTRA_URI);
+ return (outUri != null) ? outUri : documentUri;
+ }
+
+ /**
* Delete the given document.
*
* @param documentUri document with {@link Document#FLAG_SUPPORTS_DELETE}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 1a7a00f2..066b4aa 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -19,9 +19,11 @@ package android.provider;
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_RENAME_DOCUMENT;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.getRootId;
import static android.provider.DocumentsContract.getSearchDocumentsQuery;
+import static android.provider.DocumentsContract.isViaUri;
import android.content.ContentProvider;
import android.content.ContentResolver;
@@ -206,7 +208,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* If the MIME type is not supported, the provider must throw.
* @param displayName the display name of the new document. The provider may
* alter this name to meet any internal constraints, such as
- * conflicting names.
+ * avoiding conflicting names.
*/
@SuppressWarnings("unused")
public String createDocument(String parentDocumentId, String mimeType, String displayName)
@@ -215,11 +217,33 @@ public abstract class DocumentsProvider extends ContentProvider {
}
/**
- * Delete the requested 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
- * using {@link #revokeDocumentPermission(String)}.
+ * Rename an existing document.
+ * <p>
+ * If a different {@link Document#COLUMN_DOCUMENT_ID} must be used to
+ * represent the renamed document, generate and return it. Any outstanding
+ * URI permission grants will be updated to point at the new document. If
+ * the original {@link Document#COLUMN_DOCUMENT_ID} is still valid after the
+ * rename, return {@code null}.
+ *
+ * @param documentId the document to rename.
+ * @param displayName the updated display name of the document. The provider
+ * may alter this name to meet any internal constraints, such as
+ * avoiding conflicting names.
+ */
+ @SuppressWarnings("unused")
+ public String renameDocument(String documentId, String displayName)
+ throws FileNotFoundException {
+ throw new UnsupportedOperationException("Rename not supported");
+ }
+
+ /**
+ * Delete the requested document.
+ * <p>
+ * 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 using
+ * {@link #revokeDocumentPermission(String)}.
*
* @param documentId the document to delete.
*/
@@ -523,26 +547,33 @@ public abstract class DocumentsProvider extends ContentProvider {
DocumentsContract.getDocumentId(uri));
// Caller may only have prefix grant, so extend them a grant to
- // the narrow Uri. Caller already holds read grant to get here,
- // so check for any other modes we should extend.
- int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION;
- if (context.checkCallingOrSelfUriPermission(uri,
- Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- == PackageManager.PERMISSION_GRANTED) {
- modeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
- }
- if (context.checkCallingOrSelfUriPermission(uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION
- | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
- == PackageManager.PERMISSION_GRANTED) {
- modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
- }
+ // the narrow URI.
+ final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context, uri);
context.grantUriPermission(getCallingPackage(), narrowUri, modeFlags);
return narrowUri;
}
return null;
}
+ private static int getCallingOrSelfUriPermissionModeFlags(Context context, Uri uri) {
+ // TODO: move this to a direct AMS call
+ int modeFlags = 0;
+ if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ modeFlags |= Intent.FLAG_GRANT_READ_URI_PERMISSION;
+ }
+ if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ modeFlags |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+ }
+ if (context.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ modeFlags |= Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION;
+ }
+ return modeFlags;
+ }
+
/**
* Implementation is provided by the parent class. Throws by default, and
* cannot be overriden.
@@ -588,6 +619,7 @@ public abstract class DocumentsProvider extends ContentProvider {
return super.call(method, arg, extras);
}
+ final Context context = getContext();
final Uri documentUri = extras.getParcelable(DocumentsContract.EXTRA_URI);
final String authority = documentUri.getAuthority();
final String documentId = DocumentsContract.getDocumentId(documentUri);
@@ -605,7 +637,6 @@ public abstract class DocumentsProvider extends ContentProvider {
final String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
-
final String newDocumentId = createDocument(documentId, mimeType, displayName);
// No need to issue new grants here, since caller either has
@@ -615,6 +646,30 @@ public abstract class DocumentsProvider extends ContentProvider {
newDocumentId);
out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
+ } else if (METHOD_RENAME_DOCUMENT.equals(method)) {
+ enforceWritePermissionInner(documentUri);
+
+ final String displayName = extras.getString(Document.COLUMN_DISPLAY_NAME);
+ final String newDocumentId = renameDocument(documentId, displayName);
+
+ if (newDocumentId != null) {
+ final Uri newDocumentUri = DocumentsContract.buildDocumentMaybeViaUri(
+ documentUri, newDocumentId);
+
+ // If caller came in with a narrow grant, issue them a
+ // narrow grant for the newly renamed document.
+ if (!isViaUri(newDocumentUri)) {
+ final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
+ documentUri);
+ context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
+ }
+
+ out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
+
+ // Original document no longer exists, clean up any grants
+ revokeDocumentPermission(documentId);
+ }
+
} else if (METHOD_DELETE_DOCUMENT.equals(method)) {
enforceWritePermissionInner(documentUri);
deleteDocument(documentId);