diff options
5 files changed, 128 insertions, 71 deletions
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java index d4c4331267a0..0d4265a6417c 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/DocumentLoader.java @@ -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()) { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java index 78ed161d47f4..a1a43c18a72f 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java @@ -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); + } + } } diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java index ca78a3e1b775..7cc7413bab3d 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -42,7 +42,7 @@ class MtpManager { private final SparseArray<MtpDevice> mDevices = new SparseArray<>(); MtpManager(Context context) { - mManager = (UsbManager)context.getSystemService(Context.USB_SERVICE); + mManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); } synchronized void openDevice(int deviceId) throws IOException { @@ -96,78 +96,96 @@ class MtpManager { return result; } - synchronized MtpRoot[] getRoots(int deviceId) throws IOException { + MtpRoot[] getRoots(int deviceId) throws IOException { final MtpDevice device = getDevice(deviceId); - final int[] storageIds = device.getStorageIds(); - if (storageIds == null) { - throw new IOException("Failed to obtain storage IDs."); - } - final MtpRoot[] results = new MtpRoot[storageIds.length]; - for (int i = 0; i < storageIds.length; i++) { - results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i])); + synchronized (device) { + final int[] storageIds = device.getStorageIds(); + if (storageIds == null) { + throw new IOException("Failed to obtain storage IDs."); + } + final MtpRoot[] results = new MtpRoot[storageIds.length]; + for (int i = 0; i < storageIds.length; i++) { + results[i] = new MtpRoot(deviceId, device.getStorageInfo(storageIds[i])); + } + return results; } - return results; } - synchronized MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) + MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException { final MtpDevice device = getDevice(deviceId); - return device.getObjectInfo(objectHandle); + 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); - return device.getObjectHandles(storageId, 0 /* all format */, parentObjectHandle); + 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); - return device.getObject(objectHandle, expectedSize); + 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); - return device.getThumbnail(objectHandle); + 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); - if (!device.deleteObject(objectHandle)) { - throw new IOException("Failed to delete document"); + 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); - final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo); - if (sendObjectInfoResult == null) { - throw new IOException("Failed to create a document"); - } - if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) { - if (!device.sendObject(sendObjectInfoResult.getObjectHandle(), - sendObjectInfoResult.getCompressedSize(), source)) { - throw new IOException("Failed to send contents of a document"); + synchronized (device) { + final MtpObjectInfo sendObjectInfoResult = device.sendObjectInfo(objectInfo); + if (sendObjectInfoResult == null) { + throw new IOException("Failed to create a document"); + } + if (objectInfo.getFormat() != MtpConstants.FORMAT_ASSOCIATION) { + if (!device.sendObject(sendObjectInfoResult.getObjectHandle(), + sendObjectInfoResult.getCompressedSize(), source)) { + throw new IOException("Failed to send contents of a document"); + } } + return sendObjectInfoResult.getObjectHandle(); } - 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); - final int result = (int) device.getParent(objectHandle); - if (result < 0) { - throw new FileNotFoundException("Not found parent object"); + synchronized (device) { + final int result = (int) device.getParent(objectHandle); + if (result < 0) { + throw new FileNotFoundException("Not found parent object"); + } + return result; } - 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); - device.importFile(objectHandle, target); + synchronized (device) { + device.importFile(objectHandle, target); + } } private MtpDevice getDevice(int deviceId) throws IOException { diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java index cd38f1e852b5..affaebd05c16 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/PipeManager.java @@ -33,7 +33,7 @@ class PipeManager { final ExecutorService mExecutor; PipeManager() { - this(Executors.newCachedThreadPool()); + this(Executors.newSingleThreadExecutor()); } PipeManager(ExecutorService executor) { diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java index 9b316be48a15..4b3a5cd061bf 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java @@ -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()); |