Add support for multi-parents to DocumentsProvider::moveDocument.

Bug: 26481380
Change-Id: I96290268fd49072a334bde9c04c5313de8540f56
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 88cc8a2..414c608 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -342,8 +342,8 @@
          * within the same document provider.
          *
          * @see #COLUMN_FLAGS
-         * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri)
-         * @see DocumentsProvider#moveDocument(String, String)
+         * @see DocumentsContract#moveDocument(ContentProviderClient, Uri, Uri, Uri)
+         * @see DocumentsProvider#moveDocument(String, String, String)
          */
         public static final int FLAG_SUPPORTS_MOVE = 1 << 8;
 
@@ -614,6 +614,8 @@
     public static final String METHOD_IS_CHILD_DOCUMENT = "android:isChildDocument";
 
     /** {@hide} */
+    public static final String EXTRA_PARENT_URI = "parentUri";
+    /** {@hide} */
     public static final String EXTRA_URI = "uri";
 
     private static final String PATH_ROOT = "root";
@@ -1170,17 +1172,19 @@
      * Moves the given document under a new parent.
      *
      * @param sourceDocumentUri document with {@link Document#FLAG_SUPPORTS_MOVE}
+     * @param sourceParentDocumentUri parent document of the document to move.
      * @param targetParentDocumentUri document which will become a new parent of the source
      *         document.
      * @return the moved document, or {@code null} if failed.
      * @hide
      */
     public static Uri moveDocument(ContentResolver resolver, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) {
+            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) {
         final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
                 sourceDocumentUri.getAuthority());
         try {
-            return moveDocument(client, sourceDocumentUri, targetParentDocumentUri);
+            return moveDocument(client, sourceParentDocumentUri, sourceDocumentUri,
+                    targetParentDocumentUri);
         } catch (Exception e) {
             Log.w(TAG, "Failed to move document", e);
             return null;
@@ -1191,9 +1195,10 @@
 
     /** {@hide} */
     public static Uri moveDocument(ContentProviderClient client, Uri sourceDocumentUri,
-            Uri targetParentDocumentUri) throws RemoteException {
+            Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws RemoteException {
         final Bundle in = new Bundle();
         in.putParcelable(DocumentsContract.EXTRA_URI, sourceDocumentUri);
+        in.putParcelable(DocumentsContract.EXTRA_PARENT_URI, sourceParentDocumentUri);
         in.putParcelable(DocumentsContract.EXTRA_TARGET_URI, targetParentDocumentUri);
 
         final Bundle out = client.call(METHOD_MOVE_DOCUMENT, null, in);
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 94b4157..bae928d 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -289,12 +289,14 @@
      * be returned.
      *
      * @param sourceDocumentId the document to move.
+     * @param sourceParentDocumentId the parent of the document to move.
      * @param targetParentDocumentId the target document to be a new parent of the
      *     source document.
      * @hide
      */
     @SuppressWarnings("unused")
-    public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+    public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+            String targetParentDocumentId)
             throws FileNotFoundException {
         throw new UnsupportedOperationException("Move not supported");
     }
@@ -759,7 +761,7 @@
 
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
 
-                // Original document no longer exists, clean up any grants
+                // Original document no longer exists, clean up any grants.
                 revokeDocumentPermission(documentId);
             }
 
@@ -767,7 +769,7 @@
             enforceWritePermissionInner(documentUri, getCallingPackage(), null);
             deleteDocument(documentId);
 
-            // Document no longer exists, clean up any grants
+            // Document no longer exists, clean up any grants.
             revokeDocumentPermission(documentId);
 
         } else if (METHOD_COPY_DOCUMENT.equals(method)) {
@@ -793,13 +795,16 @@
             }
 
         } else if (METHOD_MOVE_DOCUMENT.equals(method)) {
+            final Uri parentSourceUri = extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI);
+            final String parentSourceId = DocumentsContract.getDocumentId(parentSourceUri);
             final Uri targetUri = extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI);
             final String targetId = DocumentsContract.getDocumentId(targetUri);
 
-            enforceReadPermissionInner(documentUri, getCallingPackage(), null);
+            enforceWritePermissionInner(documentUri, getCallingPackage(), null);
+            enforceReadPermissionInner(parentSourceUri, getCallingPackage(), null);
             enforceWritePermissionInner(targetUri, getCallingPackage(), null);
 
-            final String newDocumentId = moveDocument(documentId, targetId);
+            final String newDocumentId = moveDocument(documentId, parentSourceId, targetId);
 
             if (newDocumentId != null) {
                 final Uri newDocumentUri = buildDocumentUriMaybeUsingTree(documentUri,
@@ -814,7 +819,7 @@
                 out.putParcelable(DocumentsContract.EXTRA_URI, newDocumentUri);
             }
 
-            // Original document no longer exists, clean up any grants
+            // Original document no longer exists, clean up any grants.
             revokeDocumentPermission(documentId);
 
         } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
index 7a238bd..2a0262c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
@@ -21,6 +21,7 @@
 import android.app.Notification;
 import android.app.Notification.Builder;
 import android.content.Context;
+import android.net.Uri;
 import android.os.RemoteException;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -85,7 +86,7 @@
         if (src.authority.equals(dest.authority)) {
             if ((src.flags & Document.FLAG_SUPPORTS_MOVE) != 0) {
                 if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
-                        dest.derivedUri) == null) {
+                        Uri.EMPTY /* Not used yet */, dest.derivedUri) == null) {
                     onFileFailed(src);
                     return false;
                 }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index c6e5531..43527a2 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -463,7 +463,8 @@
     }
 
     @Override
-    public String moveDocument(String sourceDocumentId, String targetParentDocumentId)
+    public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
+            String targetParentDocumentId)
             throws FileNotFoundException {
         final File before = getFileForDocId(sourceDocumentId);
         final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());