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

Commit c4e678e7 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add findPath method to MtpDocumentsProvider."

parents 5974b4df b9ffa2a1
Loading
Loading
Loading
Loading
+47 −4
Original line number Diff line number Diff line
@@ -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)) {
+122 −2
Original line number Diff line number Diff line
@@ -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]);
            }
        }
    }
}