OSDN Git Service

Fallback to byte-by-byte copy/move if optimized fails.
authorTomasz Mikolajewski <mtomasz@google.com>
Wed, 2 Mar 2016 08:41:40 +0000 (17:41 +0900)
committerTomasz Mikolajewski <mtomasz@google.com>
Thu, 10 Mar 2016 07:48:29 +0000 (16:48 +0900)
Sometimes providers may refuse to do a provider-side copy/move
even though the documents had the SUPPORTS_COPY/MOVE flags set.

This is because optimized copy/move may be only supported within
the same root.

Since there is no easy way to check if documents are on the same
roots from DocumentsUI, as well as whether the provider can actually
copy/move efficiently between different roots, we should tolerate the
failure and do a fallback to byte-by-byte operation.

Bug: 27436368
Change-Id: Ia1a0fbdba26c06b2151afc25d8513c01d60d31db

packages/DocumentsUI/src/com/android/documentsui/services/CopyJob.java
packages/DocumentsUI/src/com/android/documentsui/services/MoveJob.java
packages/DocumentsUI/tests/src/com/android/documentsui/DocumentsProviderHelper.java
packages/DocumentsUI/tests/src/com/android/documentsui/StubProvider.java
packages/DocumentsUI/tests/src/com/android/documentsui/services/CopyJobTest.java
packages/DocumentsUI/tests/src/com/android/documentsui/services/MoveJobTest.java

index ad48a70..9ed2abf 100644 (file)
@@ -299,18 +299,15 @@ class CopyJob extends Job {
             if ((src.flags & Document.FLAG_SUPPORTS_COPY) != 0) {
                 try {
                     if (DocumentsContract.copyDocument(getClient(src), src.derivedUri,
-                            dstDirInfo.derivedUri) == null) {
-                        throw new ResourceException("Provider side copy failed for document %s.",
-                                src.derivedUri);
+                            dstDirInfo.derivedUri) != null) {
+                        return;
                     }
-                } catch (ResourceException e) {
-                    throw e;
                 } catch (RemoteException | RuntimeException e) {
-                    throw new ResourceException(
-                            "Provider side copy failed for document %s due to an exception.",
-                            src.derivedUri, e);
+                    Log.e(TAG, "Provider side copy failed for: " + src.derivedUri
+                            + " due to an exception: " + e);
                 }
-                return;
+                // If optimized copy fails, then fallback to byte-by-byte copy.
+                if (DEBUG) Log.d(TAG, "Fallback to byte-by-byte copy for: " + src.derivedUri);
             }
         }
 
index dc39235..ceae50c 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.documentsui.services;
 
+import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.services.FileOperationService.OPERATION_MOVE;
 
 import android.app.Notification;
@@ -24,6 +25,7 @@ import android.content.Context;
 import android.os.RemoteException;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
+import android.util.Log;
 
 import com.android.documentsui.R;
 import com.android.documentsui.model.DocumentInfo;
@@ -34,6 +36,8 @@ import java.util.List;
 // TODO: Stop extending CopyJob.
 final class MoveJob extends CopyJob {
 
+    private static final String TAG = "MoveJob";
+
     final DocumentInfo mSrcParent;
 
     /**
@@ -89,16 +93,15 @@ final class MoveJob extends CopyJob {
                 try {
                     if (DocumentsContract.moveDocument(getClient(src), src.derivedUri,
                             srcParent != null ? srcParent.derivedUri : mSrcParent.derivedUri,
-                            dest.derivedUri) == null) {
-                        throw new ResourceException("Provider side move failed for document %s.",
-                                src.derivedUri);
+                            dest.derivedUri) != null) {
+                        return;
                     }
-                } catch (RuntimeException | RemoteException e) {
-                    throw new ResourceException(
-                            "Provider side move failed for document %s due to an exception.",
-                            src.derivedUri, e);
+                } catch (RemoteException | RuntimeException e) {
+                    Log.e(TAG, "Provider side move failed for: " + src.derivedUri
+                            + " due to an exception: " + e);
                 }
-                return;
+                // If optimized move fails, then fallback to byte-by-byte copy.
+                if (DEBUG) Log.d(TAG, "Fallback to byte-by-byte move for: " + src.derivedUri);
             }
         }
 
index af478ea..16ed2d9 100644 (file)
@@ -106,13 +106,15 @@ public class DocumentsProviderHelper {
         return createDocument(root.documentId, mimeType, name);
     }
 
-    public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags)
+    public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags,
+            String... streamTypes)
             throws RemoteException {
         Bundle in = new Bundle();
         in.putInt(StubProvider.EXTRA_FLAGS, flags);
         in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
         in.putString(Document.COLUMN_MIME_TYPE, mimeType);
         in.putString(Document.COLUMN_DISPLAY_NAME, name);
+        in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
 
         Bundle out = mClient.call("createDocumentWithFlags", null, in);
         Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
index 2527650..f71ce5d 100644 (file)
@@ -518,28 +518,29 @@ public class StubProvider extends DocumentsProvider {
         String rootId = extras.getString(EXTRA_PARENT_ID);
         String mimeType = extras.getString(Document.COLUMN_MIME_TYPE);
         String name = extras.getString(Document.COLUMN_DISPLAY_NAME);
+        List<String> streamTypes = extras.getStringArrayList(EXTRA_STREAM_TYPES);
         int flags = extras.getInt(EXTRA_FLAGS);
 
         Bundle out = new Bundle();
         String documentId = null;
         try {
-            documentId = createDocument(rootId, mimeType, name, flags);
+            documentId = createDocument(rootId, mimeType, name, flags, streamTypes);
             Uri uri = DocumentsContract.buildDocumentUri(mAuthority, documentId);
             out.putParcelable(DocumentsContract.EXTRA_URI, uri);
         } catch (FileNotFoundException e) {
-            Log.d(TAG, "Cretaing document with flags failed" + name);
+            Log.d(TAG, "Creating document with flags failed" + name);
         }
         return out;
     }
 
-    public String createDocument(String parentId, String mimeType, String displayName, int flags)
-            throws FileNotFoundException {
+    public String createDocument(String parentId, String mimeType, String displayName, int flags,
+            List<String> streamTypes) throws FileNotFoundException {
 
         StubDocument parent = mStorage.get(parentId);
         File file = createFile(parent, mimeType, displayName);
 
         final StubDocument document = StubDocument.createDocumentWithFlags(file, mimeType, parent,
-                flags);
+                flags, streamTypes);
         mStorage.put(document.documentId, document);
         Log.d(TAG, "Created document " + document.documentId);
         notifyParentChanged(document.parentId);
@@ -787,8 +788,9 @@ public class StubProvider extends DocumentsProvider {
         }
 
         public static StubDocument createDocumentWithFlags(
-                File file, String mimeType, StubDocument parent, int flags) {
-            return new StubDocument(file, mimeType, new ArrayList<String>(), flags, parent);
+                File file, String mimeType, StubDocument parent, int flags,
+                List<String> streamTypes) {
+            return new StubDocument(file, mimeType, streamTypes, flags, parent);
         }
 
         public static StubDocument createVirtualDocument(
index 543396e..bb7c01a 100644 (file)
 
 package com.android.documentsui.services;
 
+import static com.google.common.collect.Lists.newArrayList;
+
+import android.net.Uri;
+import android.provider.DocumentsContract.Document;
 import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.documentsui.model.DocumentInfo;
@@ -38,6 +42,21 @@ public class CopyJobTest extends AbstractCopyJobTest<CopyJob> {
         runCopyVirtualNonTypedFileTest();
     }
 
+    public void testCopy_BackendSideVirtualTypedFile_Fallback() throws Exception {
+        mDocs.assertChildCount(mDestRoot, 0);
+
+        Uri testFile = mDocs.createDocumentWithFlags(
+                mSrcRoot.documentId, "virtual/mime-type", "tokyo.sth",
+                Document.FLAG_VIRTUAL_DOCUMENT | Document.FLAG_SUPPORTS_COPY
+                        | Document.FLAG_SUPPORTS_MOVE, "application/pdf");
+
+        createJob(newArrayList(testFile)).run();
+
+        mJobListener.waitForFinished();
+        mDocs.assertChildCount(mDestRoot, 1);
+        mDocs.assertHasFile(mDestRoot, "tokyo.sth.pdf");  // Copy should convert file to PDF.
+    }
+
     public void testCopyEmptyDir() throws Exception {
         runCopyEmptyDirTest();
     }
index 749264a..24181d6 100644 (file)
@@ -59,6 +59,21 @@ public class MoveJobTest extends AbstractCopyJobTest<MoveJob> {
         mDocs.assertChildCount(mSrcRoot, 1);
     }
 
+    public void testMove_BackendSideVirtualTypedFile_Fallback() throws Exception {
+        Uri testFile = mDocs.createDocumentWithFlags(
+                mSrcRoot.documentId, "virtual/mime-type", "tokyo.sth",
+                Document.FLAG_VIRTUAL_DOCUMENT | Document.FLAG_SUPPORTS_COPY
+                        | Document.FLAG_SUPPORTS_MOVE, "application/pdf");
+
+        createJob(newArrayList(testFile)).run();
+        mJobListener.waitForFinished();
+
+        // Should have failed, source not deleted. Moving by bytes for virtual files
+        // is not supported.
+        mDocs.assertChildCount(mDestRoot, 0);
+        mDocs.assertChildCount(mSrcRoot, 1);
+    }
+
     public void testMoveEmptyDir() throws Exception {
         runCopyEmptyDirTest();