Loading packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java +2 −0 Original line number Diff line number Diff line Loading @@ -133,6 +133,8 @@ public class AppFuse { return mCallback.readObjectBytes(inode, offset, size, mBuffer); } catch (IOException e) { return -OsConstants.EIO; } catch (UnsupportedOperationException e) { return -OsConstants.ENOTSUP; } } Loading packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +7 −2 Original line number Diff line number Diff line Loading @@ -89,7 +89,8 @@ class Mapper { * @return If roots are added or removed from the database. * @throws FileNotFoundException */ synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots) synchronized boolean putStorageDocuments( String parentDocumentId, int[] operationsSupported, MtpRoot[] roots) throws FileNotFoundException { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); Loading @@ -100,7 +101,11 @@ class Mapper { valuesList[i] = new ContentValues(); extraValuesList[i] = new ContentValues(); MtpDatabase.getStorageDocumentValues( valuesList[i], extraValuesList[i], parentDocumentId, roots[i]); valuesList[i], extraValuesList[i], parentDocumentId, operationsSupported, roots[i]); } final boolean changed = putDocuments( parentDocumentId, Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +14 −6 Original line number Diff line number Diff line Loading @@ -689,9 +689,7 @@ class MtpDatabase { values.putNull(Document.COLUMN_SIZE); extraValues.clear(); extraValues.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); extraValues.put(Root.COLUMN_FLAGS, getRootFlags(device.operationsSupported)); extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES); extraValues.putNull(Root.COLUMN_CAPACITY_BYTES); extraValues.put(Root.COLUMN_MIME_TYPES, ""); Loading @@ -700,12 +698,16 @@ class MtpDatabase { /** * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. * @param extraValues {@link ContentValues} that receives extra values for roots. * @param parentDocumentId Parent document ID. * @param supportedOperations Array of Operation code supported by the device. * @param root Root to be converted {@link ContentValues}. */ static void getStorageDocumentValues( ContentValues values, ContentValues extraValues, String parentDocumentId, int[] operationsSupported, MtpRoot root) { values.clear(); values.put(COLUMN_DEVICE_ID, root.mDeviceId); Loading @@ -722,9 +724,7 @@ class MtpDatabase { values.put(Document.COLUMN_FLAGS, 0); values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace); extraValues.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported)); extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace); extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity); extraValues.put(Root.COLUMN_MIME_TYPES, ""); Loading Loading @@ -785,6 +785,14 @@ class MtpDatabase { return "application/octet-stream"; } private static int getRootFlags(int[] operationsSupported) { int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD; if (MtpDeviceRecord.isWritingSupported(operationsSupported)) { rootFlag |= Root.FLAG_SUPPORTS_CREATE; } return rootFlag; } static String[] strings(Object... args) { final String[] results = new String[args.length]; for (int i = 0; i < args.length; i++) { Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java +26 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.mtp; import android.annotation.Nullable; import android.mtp.MtpConstants; class MtpDeviceRecord { public final int deviceId; Loading @@ -38,4 +39,29 @@ class MtpDeviceRecord { this.operationsSupported = operationsSupported; this.eventsSupported = eventsSupported; } /** * Helper method to check operations/events are supported by the device or not. */ static boolean isSupported(@Nullable int[] supportedList, int code) { if (supportedList == null) { return false; } for (int i = 0; i < supportedList.length; i++) { if (supportedList[i] == code) { return true; } } return false; } static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) { return fileSize <= 0xffffffffl && isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT); } static boolean isWritingSupported(@Nullable int[] supportedList) { return isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT_INFO) && isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT); } } packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +39 −22 Original line number Diff line number Diff line Loading @@ -201,6 +201,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final Identifier identifier = mDatabase.createIdentifier(documentId); try { openDevice(identifier.mDeviceId); final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; switch (mode) { case "r": final long fileSize = getFileSize(documentId); Loading @@ -208,7 +209,8 @@ public class MtpDocumentsProvider extends DocumentsProvider { // 4GB. Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. if (fileSize <= 0xffffffffl) { if (MtpDeviceRecord.isPartialReadSupported( device.operationsSupported, fileSize)) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); Loading @@ -216,8 +218,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { case "w": // TODO: Clear the parent document loader task (if exists) and call notify // when writing is completed. if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) { return getPipeManager(identifier).writeDocument( getContext(), mMtpManager, identifier); } else { throw new UnsupportedOperationException( "The device does not support writing operation."); } case "rw": // TODO: Add support for "rw" mode. throw new UnsupportedOperationException( Loading Loading @@ -290,6 +297,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { try { final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); openDevice(parentId.mDeviceId); final MtpDeviceRecord record = getDeviceToolkit(parentId.mDeviceId).mDeviceRecord; if (!MtpDeviceRecord.isWritingSupported(record.operationsSupported)) { throw new UnsupportedOperationException(); } final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe(); pipe[0].close(); // 0 bytes for a new document. final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ? Loading Loading @@ -323,9 +334,9 @@ public class MtpDocumentsProvider extends DocumentsProvider { if (DEBUG) { Log.d(TAG, "Open device " + deviceId); } mMtpManager.openDevice(deviceId); final MtpDeviceRecord device = mMtpManager.openDevice(deviceId); final DeviceToolkit toolkit = new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase); new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase, device); mDeviceToolkits.put(deviceId, toolkit); mIntentSender.sendUpdateNotificationIntent(); try { Loading @@ -347,20 +358,15 @@ public class MtpDocumentsProvider extends DocumentsProvider { mIntentSender.sendUpdateNotificationIntent(); } int[] getOpenedDeviceIds() { synchronized (mDeviceListLock) { return mMtpManager.getOpenedDeviceIds(); } } String getDeviceName(int deviceId) throws IOException { MtpDeviceRecord[] getOpenedDeviceRecordsCache() { synchronized (mDeviceListLock) { for (final MtpDeviceRecord device : mMtpManager.getDevices()) { if (device.deviceId == deviceId) { return device.name; } final MtpDeviceRecord[] records = new MtpDeviceRecord[mDeviceToolkits.size()]; int i = 0; for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { records[i] = toolkit.mDeviceRecord; i++; } throw new IOException("Not found the device: " + Integer.toString(deviceId)); return records; } } Loading Loading @@ -391,7 +397,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { public void shutdown() { synchronized (mDeviceListLock) { try { for (final int id : mMtpManager.getOpenedDeviceIds()) { // Copy the opened key set because it will be modified when closing devices. final Integer[] keySet = mDeviceToolkits.keySet().toArray(new Integer[mDeviceToolkits.size()]); for (final int id : keySet) { closeDeviceInternal(id); } } catch (InterruptedException|IOException e) { Loading Loading @@ -432,7 +441,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { getDeviceToolkit(deviceId).mDocumentLoader.close(); mDeviceToolkits.remove(deviceId); mMtpManager.closeDevice(deviceId); if (getOpenedDeviceIds().length == 0) { if (mDeviceToolkits.size() == 0) { mRootScanner.pause(); } } Loading Loading @@ -488,11 +497,14 @@ public class MtpDocumentsProvider extends DocumentsProvider { private static class DeviceToolkit { public final PipeManager mPipeManager; public final DocumentLoader mDocumentLoader; public final MtpDeviceRecord mDeviceRecord; public DeviceToolkit( int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database) { int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database, MtpDeviceRecord record) { mPipeManager = new PipeManager(database); mDocumentLoader = new DocumentLoader(deviceId, manager, resolver, database); mDeviceRecord = record; } } Loading @@ -501,8 +513,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { public long readObjectBytes( int inode, long offset, long size, byte[] buffer) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) { return mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); } else { throw new UnsupportedOperationException(); } } @Override Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java +2 −0 Original line number Diff line number Diff line Loading @@ -133,6 +133,8 @@ public class AppFuse { return mCallback.readObjectBytes(inode, offset, size, mBuffer); } catch (IOException e) { return -OsConstants.EIO; } catch (UnsupportedOperationException e) { return -OsConstants.ENOTSUP; } } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java +7 −2 Original line number Diff line number Diff line Loading @@ -89,7 +89,8 @@ class Mapper { * @return If roots are added or removed from the database. * @throws FileNotFoundException */ synchronized boolean putStorageDocuments(String parentDocumentId, MtpRoot[] roots) synchronized boolean putStorageDocuments( String parentDocumentId, int[] operationsSupported, MtpRoot[] roots) throws FileNotFoundException { final SQLiteDatabase database = mDatabase.getSQLiteDatabase(); database.beginTransaction(); Loading @@ -100,7 +101,11 @@ class Mapper { valuesList[i] = new ContentValues(); extraValuesList[i] = new ContentValues(); MtpDatabase.getStorageDocumentValues( valuesList[i], extraValuesList[i], parentDocumentId, roots[i]); valuesList[i], extraValuesList[i], parentDocumentId, operationsSupported, roots[i]); } final boolean changed = putDocuments( parentDocumentId, Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +14 −6 Original line number Diff line number Diff line Loading @@ -689,9 +689,7 @@ class MtpDatabase { values.putNull(Document.COLUMN_SIZE); extraValues.clear(); extraValues.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); extraValues.put(Root.COLUMN_FLAGS, getRootFlags(device.operationsSupported)); extraValues.putNull(Root.COLUMN_AVAILABLE_BYTES); extraValues.putNull(Root.COLUMN_CAPACITY_BYTES); extraValues.put(Root.COLUMN_MIME_TYPES, ""); Loading @@ -700,12 +698,16 @@ class MtpDatabase { /** * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. * @param extraValues {@link ContentValues} that receives extra values for roots. * @param parentDocumentId Parent document ID. * @param supportedOperations Array of Operation code supported by the device. * @param root Root to be converted {@link ContentValues}. */ static void getStorageDocumentValues( ContentValues values, ContentValues extraValues, String parentDocumentId, int[] operationsSupported, MtpRoot root) { values.clear(); values.put(COLUMN_DEVICE_ID, root.mDeviceId); Loading @@ -722,9 +724,7 @@ class MtpDatabase { values.put(Document.COLUMN_FLAGS, 0); values.put(Document.COLUMN_SIZE, root.mMaxCapacity - root.mFreeSpace); extraValues.put( Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE); extraValues.put(Root.COLUMN_FLAGS, getRootFlags(operationsSupported)); extraValues.put(Root.COLUMN_AVAILABLE_BYTES, root.mFreeSpace); extraValues.put(Root.COLUMN_CAPACITY_BYTES, root.mMaxCapacity); extraValues.put(Root.COLUMN_MIME_TYPES, ""); Loading Loading @@ -785,6 +785,14 @@ class MtpDatabase { return "application/octet-stream"; } private static int getRootFlags(int[] operationsSupported) { int rootFlag = Root.FLAG_SUPPORTS_IS_CHILD; if (MtpDeviceRecord.isWritingSupported(operationsSupported)) { rootFlag |= Root.FLAG_SUPPORTS_CREATE; } return rootFlag; } static String[] strings(Object... args) { final String[] results = new String[args.length]; for (int i = 0; i < args.length; i++) { Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDeviceRecord.java +26 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.mtp; import android.annotation.Nullable; import android.mtp.MtpConstants; class MtpDeviceRecord { public final int deviceId; Loading @@ -38,4 +39,29 @@ class MtpDeviceRecord { this.operationsSupported = operationsSupported; this.eventsSupported = eventsSupported; } /** * Helper method to check operations/events are supported by the device or not. */ static boolean isSupported(@Nullable int[] supportedList, int code) { if (supportedList == null) { return false; } for (int i = 0; i < supportedList.length; i++) { if (supportedList[i] == code) { return true; } } return false; } static boolean isPartialReadSupported(@Nullable int[] supportedList, long fileSize) { return fileSize <= 0xffffffffl && isSupported(supportedList, MtpConstants.OPERATION_GET_PARTIAL_OBJECT); } static boolean isWritingSupported(@Nullable int[] supportedList) { return isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT_INFO) && isSupported(supportedList, MtpConstants.OPERATION_SEND_OBJECT); } }
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +39 −22 Original line number Diff line number Diff line Loading @@ -201,6 +201,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { final Identifier identifier = mDatabase.createIdentifier(documentId); try { openDevice(identifier.mDeviceId); final MtpDeviceRecord device = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; switch (mode) { case "r": final long fileSize = getFileSize(documentId); Loading @@ -208,7 +209,8 @@ public class MtpDocumentsProvider extends DocumentsProvider { // 4GB. Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. if (fileSize <= 0xffffffffl) { if (MtpDeviceRecord.isPartialReadSupported( device.operationsSupported, fileSize)) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); Loading @@ -216,8 +218,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { case "w": // TODO: Clear the parent document loader task (if exists) and call notify // when writing is completed. if (MtpDeviceRecord.isWritingSupported(device.operationsSupported)) { return getPipeManager(identifier).writeDocument( getContext(), mMtpManager, identifier); } else { throw new UnsupportedOperationException( "The device does not support writing operation."); } case "rw": // TODO: Add support for "rw" mode. throw new UnsupportedOperationException( Loading Loading @@ -290,6 +297,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { try { final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); openDevice(parentId.mDeviceId); final MtpDeviceRecord record = getDeviceToolkit(parentId.mDeviceId).mDeviceRecord; if (!MtpDeviceRecord.isWritingSupported(record.operationsSupported)) { throw new UnsupportedOperationException(); } final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe(); pipe[0].close(); // 0 bytes for a new document. final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ? Loading Loading @@ -323,9 +334,9 @@ public class MtpDocumentsProvider extends DocumentsProvider { if (DEBUG) { Log.d(TAG, "Open device " + deviceId); } mMtpManager.openDevice(deviceId); final MtpDeviceRecord device = mMtpManager.openDevice(deviceId); final DeviceToolkit toolkit = new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase); new DeviceToolkit(deviceId, mMtpManager, mResolver, mDatabase, device); mDeviceToolkits.put(deviceId, toolkit); mIntentSender.sendUpdateNotificationIntent(); try { Loading @@ -347,20 +358,15 @@ public class MtpDocumentsProvider extends DocumentsProvider { mIntentSender.sendUpdateNotificationIntent(); } int[] getOpenedDeviceIds() { synchronized (mDeviceListLock) { return mMtpManager.getOpenedDeviceIds(); } } String getDeviceName(int deviceId) throws IOException { MtpDeviceRecord[] getOpenedDeviceRecordsCache() { synchronized (mDeviceListLock) { for (final MtpDeviceRecord device : mMtpManager.getDevices()) { if (device.deviceId == deviceId) { return device.name; } final MtpDeviceRecord[] records = new MtpDeviceRecord[mDeviceToolkits.size()]; int i = 0; for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { records[i] = toolkit.mDeviceRecord; i++; } throw new IOException("Not found the device: " + Integer.toString(deviceId)); return records; } } Loading Loading @@ -391,7 +397,10 @@ public class MtpDocumentsProvider extends DocumentsProvider { public void shutdown() { synchronized (mDeviceListLock) { try { for (final int id : mMtpManager.getOpenedDeviceIds()) { // Copy the opened key set because it will be modified when closing devices. final Integer[] keySet = mDeviceToolkits.keySet().toArray(new Integer[mDeviceToolkits.size()]); for (final int id : keySet) { closeDeviceInternal(id); } } catch (InterruptedException|IOException e) { Loading Loading @@ -432,7 +441,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { getDeviceToolkit(deviceId).mDocumentLoader.close(); mDeviceToolkits.remove(deviceId); mMtpManager.closeDevice(deviceId); if (getOpenedDeviceIds().length == 0) { if (mDeviceToolkits.size() == 0) { mRootScanner.pause(); } } Loading Loading @@ -488,11 +497,14 @@ public class MtpDocumentsProvider extends DocumentsProvider { private static class DeviceToolkit { public final PipeManager mPipeManager; public final DocumentLoader mDocumentLoader; public final MtpDeviceRecord mDeviceRecord; public DeviceToolkit( int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database) { int deviceId, MtpManager manager, ContentResolver resolver, MtpDatabase database, MtpDeviceRecord record) { mPipeManager = new PipeManager(database); mDocumentLoader = new DocumentLoader(deviceId, manager, resolver, database); mDeviceRecord = record; } } Loading @@ -501,8 +513,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { public long readObjectBytes( int inode, long offset, long size, byte[] buffer) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); final MtpDeviceRecord record = getDeviceToolkit(identifier.mDeviceId).mDeviceRecord; if (MtpDeviceRecord.isPartialReadSupported(record.operationsSupported, offset)) { return mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); } else { throw new UnsupportedOperationException(); } } @Override Loading