Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 74fe1816 authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski
Browse files

Add support for efficient copy within a document provider.

Currently copying files even within the same document provider causes
reading and writing all of the bytes, which is very inefficient for all
network based solutions, eg. Drive where copying even huge files can be
instant.

This CL adds an optional copyDocument method to the DocumentProvider as
well as DocumentContract so providers can implement an efficient way of
copying things. If not implemented, we would fallback to copying by
streaming.

BUG=None

Change-Id: I8f2b2c0c834717f07d42f8247cd1fc025e82239a
parent 3b409d01
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -26014,6 +26014,7 @@ package android.provider {
    field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
    field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
    field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
    field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
    field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
    field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
    field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
+1 −0
Original line number Diff line number Diff line
@@ -27988,6 +27988,7 @@ package android.provider {
    field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
    field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
    field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
    field public static final int FLAG_SUPPORTS_COPY = 128; // 0x80
    field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
    field public static final int FLAG_SUPPORTS_RENAME = 64; // 0x40
    field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
+50 −0
Original line number Diff line number Diff line
@@ -92,6 +92,9 @@ public final class DocumentsContract {
    /** {@hide} */
    public static final String EXTRA_SHOW_ADVANCED = "android.content.extra.SHOW_ADVANCED";

    /** {@hide} */
    public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";

    /**
     * Set this in a DocumentsUI intent to cause a package's own roots to be
     * excluded from the roots list.
@@ -316,6 +319,17 @@ public final class DocumentsContract {
         */
        public static final int FLAG_SUPPORTS_RENAME = 1 << 6;

        /**
         * Flag indicating that a document can be copied to another location
         * within the same document provider.
         *
         * @see #COLUMN_FLAGS
         * @see DocumentsContract#copyDocument(ContentProviderClient, Uri,
         *      String)
         * @see DocumentsProvider#copyDocument(String, String, String)
         */
        public static final int FLAG_SUPPORTS_COPY = 1 << 7;

        /**
         * Flag indicating that document titles should be hidden when viewing
         * this directory in a larger format grid. For example, a directory
@@ -537,6 +551,8 @@ public final class DocumentsContract {
    public static final String METHOD_RENAME_DOCUMENT = "android:renameDocument";
    /** {@hide} */
    public static final String METHOD_DELETE_DOCUMENT = "android:deleteDocument";
    /** {@hide} */
    public static final String METHOD_COPY_DOCUMENT = "android:copyDocument";

    /** {@hide} */
    public static final String EXTRA_URI = "uri";
@@ -1015,6 +1031,40 @@ public final class DocumentsContract {
        client.call(METHOD_DELETE_DOCUMENT, null, in);
    }

    /**
     * Copies the given document.
     *
     * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_COPY}
     * @param targetParentDocumentUri document which will become a parent of the source
     *         document's copy.
     * @return the copied document, or {@code null} if failed.
     * @hide
     */
    public static Uri copyDocument(ContentResolver resolver, Uri sourceDocumentUri,
            Uri targetParentDocumentUri) {
        final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                sourceDocumentUri.getAuthority());
        try {
            return copyDocument(client, sourceDocumentUri, targetParentDocumentUri);
        } catch (Exception e) {
            Log.w(TAG, "Failed to copy document", e);
            return null;
        } finally {
            ContentProviderClient.releaseQuietly(client);
        }
    }

    /** {@hide} */
    public static Uri copyDocument(ContentProviderClient client, Uri sourceDocumentUri,
            Uri targetParentDocumentUri) throws RemoteException {
        final Bundle in = new Bundle();
        in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
        in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);

        final Bundle out = client.call(METHOD_COPY_DOCUMENT, null, in);
        return out.getParcelable(DocumentsContract.EXTRA_URI);
    }

    /**
     * Open the given image for thumbnail purposes, using any embedded EXIF
     * thumbnail if available, and providing orientation hints from the parent
+41 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.provider;
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.METHOD_COPY_DOCUMENT;
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
import static android.provider.DocumentsContract.buildTreeDocumentUri;
@@ -258,6 +259,24 @@ public abstract class DocumentsProvider extends ContentProvider {
        throw new UnsupportedOperationException("Delete not supported");
    }

    /**
     * Copy the requested document or a document tree.
     * <p>
     * Copies a document including all child documents to another location within
     * the same document provider. Upon completion returns the document id of
     * the copied document at the target destination. {@code null} must never
     * be returned.
     *
     * @param sourceDocumentId the document to copy.
     * @param targetParentDocumentId the target document to be copied into as a child.
     * @hide
     */
    @SuppressWarnings("unused")
    public String copyDocument(String sourceDocumentId, String targetParentDocumentId)
            throws FileNotFoundException {
        throw new UnsupportedOperationException("Copy not supported");
    }

    /**
     * Return all roots currently provided. To display to users, you must define
     * at least one root. You should avoid making network requests to keep this
@@ -684,6 +703,28 @@ public abstract class DocumentsProvider extends ContentProvider {
                // Document no longer exists, clean up any grants
                revokeDocumentPermission(documentId);

            } else if (METHOD_COPY_DOCUMENT.equals(method)) {
                final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
                final String targetId = DocumentsContract.getDocumentId(targetUri);

                enforceReadPermissionInner(documentUri, null);
                enforceWritePermissionInner(targetUri, null);

                final String newDocumentId = copyDocument(documentId, targetId);

                if (newDocumentId != null) {
                    final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
                            newDocumentId);

                    if (!isTreeUri(newDocumentUri)) {
                        final int modeFlags = getCallingOrSelfUriPermissionModeFlags(context,
                                documentUri);
                        context.grantUriPermission(getCallingPackage(), newDocumentUri, modeFlags);
                    }

                    out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
                }

            } else {
                throw new UnsupportedOperationException("Method not supported " + method);
            }