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

Commit b9ffa2a1 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Add findPath method to MtpDocumentsProvider.

Bug: 32515515
Test: adb shell am instrument -w -e class com.android.mtp.MtpDocumentsProviderTest com.android.mtp.tests/com.android.mtp.TestResultInstrumentation
Change-Id: Ib87e8f5258de8fd48a099adbf077b3b9ff85773c
parent 03c8e3f7
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]);
            }
        }
    }
}