Merge "Add findPath method to MtpDocumentsProvider."
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index 1823711..96f91d9 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -35,23 +35,21 @@
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.provider.Settings;
import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import java.io.File;
-import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
@@ -414,6 +412,51 @@
}
}
+ @Override
+ public Path findPath(String childDocumentId, String parentDocumentId)
+ throws FileNotFoundException {
+ final LinkedList<String> ids = new LinkedList<>();
+ final Identifier childIdentifier = mDatabase.createIdentifier(childDocumentId);
+
+ Identifier i = childIdentifier;
+ outer: while (true) {
+ if (i.mDocumentId.equals(parentDocumentId)) {
+ ids.addFirst(i.mDocumentId);
+ break;
+ }
+ switch (i.mDocumentType) {
+ case MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT:
+ ids.addFirst(i.mDocumentId);
+ i = mDatabase.getParentIdentifier(i.mDocumentId);
+ break;
+ case MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE: {
+ // Check if there is the multiple storage.
+ final Identifier deviceIdentifier =
+ mDatabase.getParentIdentifier(i.mDocumentId);
+ final String[] storageIds =
+ mDatabase.getStorageDocumentIds(deviceIdentifier.mDocumentId);
+ // Add storage's document ID to the path only when the device has multiple
+ // storages.
+ if (storageIds.length > 1) {
+ ids.addFirst(i.mDocumentId);
+ break outer;
+ }
+ i = deviceIdentifier;
+ break;
+ }
+ case MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE:
+ ids.addFirst(i.mDocumentId);
+ break outer;
+ }
+ }
+
+ if (parentDocumentId != null) {
+ return new Path(null, ids);
+ } else {
+ return new Path(/* Should be same with root ID */ i.mDocumentId, ids);
+ }
+ }
+
void openDevice(int deviceId) throws IOException {
synchronized (mDeviceListLock) {
if (mDeviceToolkits.containsKey(deviceId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 3f7e8b6..a29e27d 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -21,9 +21,9 @@
import android.mtp.MtpObjectInfo;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
-import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Path;
import android.provider.DocumentsContract.Root;
import android.system.Os;
import android.system.OsConstants;
@@ -34,6 +34,9 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
import java.util.concurrent.TimeoutException;
import static com.android.mtp.MtpDatabase.strings;
@@ -770,6 +773,64 @@
assertEquals(0x400000000L, cursor.getLong(0));
}
+ public void testFindPath_singleStorage_toRoot() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
+ setupHierarchyDocuments("1");
+
+ final Path path = mProvider.findPath("15", null);
+ assertEquals("1", path.getRootId());
+ assertEquals(4, path.getPath().size());
+ assertEquals("1", path.getPath().get(0));
+ assertEquals("3", path.getPath().get(1));
+ assertEquals("6", path.getPath().get(2));
+ assertEquals("15", path.getPath().get(3));
+ }
+
+ public void testFindPath_singleStorage_toDoc() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Storage", 1000, 1000, "") });
+ setupHierarchyDocuments("1");
+
+ final Path path = mProvider.findPath("18", "3");
+ assertNull(path.getRootId());
+ assertEquals(3, path.getPath().size());
+ assertEquals("3", path.getPath().get(0));
+ assertEquals("7", path.getPath().get(1));
+ assertEquals("18", path.getPath().get(2));
+ }
+
+ public void testFindPath_multiStorage_toRoot() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 0, "Storage A", 1000, 1000, ""),
+ new MtpRoot(0, 1, "Storage B", 1000, 1000, "") });
+ setupHierarchyDocuments("2");
+
+ final Path path = mProvider.findPath("16", null);
+ assertEquals("2", path.getRootId());
+ assertEquals(4, path.getPath().size());
+ assertEquals("2", path.getPath().get(0));
+ assertEquals("4", path.getPath().get(1));
+ assertEquals("7", path.getPath().get(2));
+ assertEquals("16", path.getPath().get(3));
+ }
+
+ public void testFindPath_multiStorage_toDoc() throws Exception {
+ setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
+ setupRoots(0, new MtpRoot[] {
+ new MtpRoot(0, 0, "Storage A", 1000, 1000, ""),
+ new MtpRoot(0, 1, "Storage B", 1000, 1000, "") });
+ setupHierarchyDocuments("2");
+
+ final Path path = mProvider.findPath("19", "4");
+ assertNull(path.getRootId());
+ assertEquals(3, path.getPath().size());
+ assertEquals("4", path.getPath().get(0));
+ assertEquals("8", path.getPath().get(1));
+ assertEquals("19", path.getPath().get(2));
+ }
+
private void setupProvider(int flag) {
mDatabase = new MtpDatabase(getContext(), flag);
mProvider = new MtpDocumentsProvider();
@@ -816,11 +877,70 @@
final int[] handles = new int[objects.length];
int i = 0;
for (final MtpObjectInfo info : objects) {
- handles[i] = info.getObjectHandle();
+ handles[i++] = info.getObjectHandle();
mMtpManager.setObjectInfo(deviceId, info);
}
mMtpManager.setObjectHandles(deviceId, storageId, parentHandle, handles);
return getStrings(mProvider.queryChildDocuments(
parentDocumentId, strings(DocumentsContract.Document.COLUMN_DOCUMENT_ID), null));
}
+
+ static class HierarchyDocument {
+ int depth;
+ String documentId;
+ int objectHandle;
+ int parentHandle;
+
+ HierarchyDocument createChildDocument(int newHandle) {
+ final HierarchyDocument doc = new HierarchyDocument();
+ doc.depth = depth - 1;
+ doc.objectHandle = newHandle;
+ doc.parentHandle = objectHandle;
+ return doc;
+ }
+
+ MtpObjectInfo toObjectInfo() {
+ return new MtpObjectInfo.Builder()
+ .setName("doc_" + documentId)
+ .setFormat(depth > 0 ?
+ MtpConstants.FORMAT_ASSOCIATION : MtpConstants.FORMAT_TEXT)
+ .setObjectHandle(objectHandle)
+ .setParent(parentHandle)
+ .build();
+ }
+ }
+
+ private void setupHierarchyDocuments(String documentId) throws Exception {
+ final Queue<HierarchyDocument> ids = new LinkedList<>();
+ final HierarchyDocument firstDocument = new HierarchyDocument();
+ firstDocument.depth = 3;
+ firstDocument.documentId = documentId;
+ firstDocument.objectHandle = MtpManager.OBJECT_HANDLE_ROOT_CHILDREN;
+ ids.add(firstDocument);
+
+ int objectHandle = 100;
+ while (!ids.isEmpty()) {
+ final HierarchyDocument document = ids.remove();
+ final HierarchyDocument[] children = new HierarchyDocument[] {
+ document.createChildDocument(objectHandle++),
+ document.createChildDocument(objectHandle++),
+ document.createChildDocument(objectHandle++),
+ };
+ final String[] childDocIds = setupDocuments(
+ 0, 0, document.objectHandle, document.documentId, new MtpObjectInfo[] {
+ children[0].toObjectInfo(),
+ children[1].toObjectInfo(),
+ children[2].toObjectInfo(),
+ });
+ children[0].documentId = childDocIds[0];
+ children[1].documentId = childDocIds[1];
+ children[2].documentId = childDocIds[2];
+
+ if (children[0].depth > 0) {
+ ids.add(children[0]);
+ ids.add(children[1]);
+ ids.add(children[2]);
+ }
+ }
+ }
}