Merge "Add findPath method to MtpDocumentsProvider."

This commit is contained in:
TreeHugger Robot
2016-11-02 05:18:26 +00:00
committed by Android (Google) Code Review
2 changed files with 169 additions and 6 deletions

View File

@@ -35,23 +35,21 @@ import android.os.FileUtils;
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 @@ public class MtpDocumentsProvider extends DocumentsProvider {
}
}
@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)) {

View File

@@ -21,9 +21,9 @@ import android.mtp.MtpConstants;
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 android.test.suitebuilder.annotation.MediumTest;
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 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
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 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
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]);
}
}
}
}