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

Commit 4c1d3dde authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski
Browse files

Create PipeManager and DocumentLoader per device.

Also, reduce MtpManager's synchronization to per device synchronization.

Bug: 23733078
Change-Id: Ieedc7d871f2a4d260ca4287c6fa05d67f54fb4c5
parent ca9f413b
Loading
Loading
Loading
Loading
+4 −15
Original line number Diff line number Diff line
@@ -91,12 +91,12 @@ class DocumentLoader {
        return task.createCursor(mResolver, columnNames);
    }

    synchronized void clearTasks(int deviceId) {
        mTaskList.clearTaskForDevice(deviceId);
    synchronized void clearTasks() {
        mTaskList.clear();
    }

    synchronized void clearCompletedTasks() {
        mTaskList.clearCompletedTask();
        mTaskList.clearCompletedTasks();
    }

    synchronized void clearTask(Identifier parentIdentifier) {
@@ -162,18 +162,7 @@ class DocumentLoader {
            return null;
        }

        void clearTaskForDevice(int deviceId) {
            int i = 0;
            while (i < size()) {
                if (get(i).mIdentifier.mDeviceId == deviceId) {
                    remove(i);
                } else {
                    i++;
                }
            }
        }

        void clearCompletedTask() {
        void clearCompletedTasks() {
            int i = 0;
            while (i < size()) {
                if (get(i).completed()) {
+48 −14
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@ import com.android.internal.annotations.VisibleForTesting;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * DocumentsProvider for MTP devices.
@@ -56,8 +58,8 @@ public class MtpDocumentsProvider extends DocumentsProvider {

    private MtpManager mMtpManager;
    private ContentResolver mResolver;
    private PipeManager mPipeManager;
    private DocumentLoader mDocumentLoader;
    private Map<Integer, DeviceToolkit> mDeviceToolkits;
    private DocumentLoader mDocumentLoaders;
    private RootScanner mRootScanner;

    /**
@@ -72,8 +74,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        sSingleton = this;
        mMtpManager = new MtpManager(getContext());
        mResolver = getContext().getContentResolver();
        mPipeManager = new PipeManager();
        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mRootScanner = new RootScanner(mResolver, mMtpManager);
        return true;
    }
@@ -82,7 +83,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
    void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
        mMtpManager = mtpManager;
        mResolver = resolver;
        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mRootScanner = new RootScanner(mResolver, mMtpManager);
    }

@@ -158,7 +159,8 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        }
        final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
        try {
            return mDocumentLoader.queryChildDocuments(projection, parentIdentifier);
            return getDocumentLoader(parentIdentifier).queryChildDocuments(
                    projection, parentIdentifier);
        } catch (IOException exception) {
            throw new FileNotFoundException(exception.getMessage());
        }
@@ -172,11 +174,12 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        try {
            switch (mode) {
                case "r":
                    return mPipeManager.readDocument(mMtpManager, identifier);
                    return getPipeManager(identifier).readDocument(mMtpManager, identifier);
                case "w":
                    // TODO: Clear the parent document loader task (if exists) and call notify
                    // when writing is completed.
                    return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
                    return getPipeManager(identifier).writeDocument(
                            getContext(), mMtpManager, identifier);
                default:
                    // TODO: Add support for seekable files.
                    throw new UnsupportedOperationException(
@@ -195,7 +198,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        final Identifier identifier = Identifier.createFromDocumentId(documentId);
        try {
            return new AssetFileDescriptor(
                    mPipeManager.readThumbnail(mMtpManager, identifier),
                    getPipeManager(identifier).readThumbnail(mMtpManager, identifier),
                    0,  // Start offset.
                    AssetFileDescriptor.UNKNOWN_LENGTH);
        } catch (IOException error) {
@@ -212,7 +215,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
            mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
            final Identifier parentIdentifier = new Identifier(
                    identifier.mDeviceId, identifier.mStorageId, parentHandle);
            mDocumentLoader.clearTask(parentIdentifier);
            getDocumentLoader(parentIdentifier).clearTask(parentIdentifier);
            notifyChildDocumentsChange(parentIdentifier.toDocumentId());
        } catch (IOException error) {
            throw new FileNotFoundException(error.getMessage());
@@ -221,7 +224,9 @@ public class MtpDocumentsProvider extends DocumentsProvider {

    @Override
    public void onTrimMemory(int level) {
        mDocumentLoader.clearCompletedTasks();
      for (final DeviceToolkit toolkit : mDeviceToolkits.values()) {
          toolkit.mDocumentLoader.clearCompletedTasks();
      }
    }

    @Override
@@ -241,7 +246,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
                            .build(), pipe[1]);
            final String documentId = new Identifier(parentId.mDeviceId, parentId.mStorageId,
                   objectHandle).toDocumentId();
            mDocumentLoader.clearTask(parentId);
            getDocumentLoader(parentId).clearTask(parentId);
            notifyChildDocumentsChange(parentDocumentId);
            return documentId;
        } catch (IOException error) {
@@ -252,12 +257,15 @@ public class MtpDocumentsProvider extends DocumentsProvider {

    void openDevice(int deviceId) throws IOException {
        mMtpManager.openDevice(deviceId);
        mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver));
        mRootScanner.scanNow();
    }

    void closeDevice(int deviceId) throws IOException {
        // TODO: Flush the device before closing (if not closed externally).
        getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
        mDeviceToolkits.remove(deviceId);
        mMtpManager.closeDevice(deviceId);
        mDocumentLoader.clearTasks(deviceId);
        mRootScanner.scanNow();
    }

@@ -266,7 +274,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
            try {
                mMtpManager.closeDevice(deviceId);
                mDocumentLoader.clearTasks(deviceId);
                getDeviceToolkit(deviceId).mDocumentLoader.clearTasks();
                closed = true;
            } catch (IOException d) {
                Log.d(TAG, "Failed to close the MTP device: " + deviceId);
@@ -287,4 +295,30 @@ public class MtpDocumentsProvider extends DocumentsProvider {
                null,
                false);
    }

    private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException {
        final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId);
        if (toolkit == null) {
            throw new FileNotFoundException();
        }
        return toolkit;
    }

    private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException {
        return getDeviceToolkit(identifier.mDeviceId).mPipeManager;
    }

    private DocumentLoader getDocumentLoader(Identifier identifier) throws FileNotFoundException {
        return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader;
    }

    private static class DeviceToolkit {
        public final PipeManager mPipeManager;
        public final DocumentLoader mDocumentLoader;

        public DeviceToolkit(MtpManager manager, ContentResolver resolver) {
            mPipeManager = new PipeManager();
            mDocumentLoader = new DocumentLoader(manager, resolver);
        }
    }
}
+56 −38
Original line number Diff line number Diff line
@@ -96,8 +96,9 @@ class MtpManager {
        return result;
    }

    synchronized MtpRoot[] getRoots(int deviceId) throws IOException {
    MtpRoot[] getRoots(int deviceId) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            final int[] storageIds = device.getStorageIds();
            if (storageIds == null) {
                throw new IOException("Failed to obtain storage IDs.");
@@ -108,40 +109,52 @@ class MtpManager {
            }
            return results;
        }
    }

    synchronized MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
    MtpObjectInfo getObjectInfo(int deviceId, int objectHandle)
            throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            return device.getObjectInfo(objectHandle);
        }
    }

    synchronized int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
    int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
            throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle);
        }
    }

    synchronized byte[] getObject(int deviceId, int objectHandle, int expectedSize)
    byte[] getObject(int deviceId, int objectHandle, int expectedSize)
            throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            return device.getObject(objectHandle, expectedSize);
        }
    }

    synchronized byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
    byte[] getThumbnail(int deviceId, int objectHandle) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            return device.getThumbnail(objectHandle);
        }
    }

    synchronized void deleteDocument(int deviceId, int objectHandle) throws IOException {
    void deleteDocument(int deviceId, int objectHandle) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            if (!device.deleteObject(objectHandle)) {
                throw new IOException("Failed to delete document");
            }
        }
    }

    synchronized int createDocument(int deviceId, MtpObjectInfo objectInfo,
    int createDocument(int deviceId, MtpObjectInfo objectInfo,
            ParcelFileDescriptor source) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo);
            if (sendObjectInfoResult == null) {
                throw new IOException("Failed to create a document");
@@ -154,21 +167,26 @@ class MtpManager {
            }
            return sendObjectInfoResult.getObjectHandle();
        }
    }

    synchronized int getParent(int deviceId, int objectHandle) throws IOException {
    int getParent(int deviceId, int objectHandle) throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            final int result = (int) device.getParent(objectHandle);
            if (result < 0) {
                throw new FileNotFoundException("Not found parent object");
            }
            return result;
        }
    }

    synchronized void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
    void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
            throws IOException {
        final MtpDevice device = getDevice(deviceId);
        synchronized (device) {
            device.importFile(objectHandle, target);
        }
    }

    private MtpDevice getDevice(int deviceId) throws IOException {
        final MtpDevice device = mDevices.get(deviceId);
+1 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ class PipeManager {
    final ExecutorService mExecutor;

    PipeManager() {
        this(Executors.newCachedThreadPool());
        this(Executors.newSingleThreadExecutor());
    }

    PipeManager(ExecutorService executor) {
+19 −3
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    private TestMtpManager mMtpManager;

    @Override
    public void setUp() {
    public void setUp() throws IOException {
        mResolver = new TestContentResolver();
        mMtpManager = new TestMtpManager(getContext());
        mProvider = new MtpDocumentsProvider();
@@ -207,6 +207,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryDocument() throws IOException {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
                .setObjectHandle(2)
                .setFormat(MtpConstants.FORMAT_EXIF_JPEG)
@@ -232,6 +234,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryDocument_directory() throws IOException {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
                .setObjectHandle(2)
                .setFormat(MtpConstants.FORMAT_ASSOCIATION)
@@ -255,6 +259,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryDocument_forRoot() throws IOException {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setRoots(0, new MtpRoot[] {
                new MtpRoot(
                        0 /* deviceId */,
@@ -277,6 +283,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryChildDocuments() throws Exception {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });

        mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
@@ -303,6 +311,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryChildDocuments_cursorError() throws Exception {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        try {
            mProvider.queryChildDocuments("0_0_0", null, null);
            fail();
@@ -312,6 +322,8 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
    }

    public void testQueryChildDocuments_documentError() throws Exception {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectHandles(0, 0, -1, new int[] { 1 });
        try {
            mProvider.queryChildDocuments("0_0_0", null, null);
@@ -321,7 +333,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
        }
    }

    public void testDeleteDocument() throws FileNotFoundException {
    public void testDeleteDocument() throws IOException {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
                .setObjectHandle(1)
                .setParent(2)
@@ -332,7 +346,9 @@ public class MtpDocumentsProviderTest extends AndroidTestCase {
                        MtpDocumentsProvider.AUTHORITY, "0_0_2")));
    }

    public void testDeleteDocument_error() {
    public void testDeleteDocument_error() throws IOException {
        mMtpManager.addValidDevice(0);
        mProvider.openDevice(0);
        mMtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
                .setObjectHandle(2)
                .build());