Loading packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp +3 −6 Original line number Diff line number Diff line Loading @@ -332,23 +332,20 @@ private: uint64_t offset, uint32_t size, void* buf) { const uint32_t read_size = static_cast<uint32_t>(std::min( static_cast<uint64_t>(size), get_file_size(inode) - offset)); ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod( self_, app_fuse_get_object_bytes, inode, offset, read_size))); size))); if (array.get() == nullptr) { return -1; } ScopedByteArrayRO bytes(env_, array.get()); if (bytes.size() != read_size || bytes.get() == nullptr) { if (bytes.get() == nullptr) { return -1; } const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size); memcpy(buf, bytes.get(), read_size); return read_size; } Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +20 −7 Original line number Diff line number Diff line Loading @@ -37,10 +37,10 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; Loading Loading @@ -103,18 +103,28 @@ public class MtpDocumentsProvider extends DocumentsProvider { } @VisibleForTesting void onCreateForTesting( boolean onCreateForTesting( Resources resources, MtpManager mtpManager, ContentResolver resolver, MtpDatabase database) { MtpDatabase database, StorageManager storageManager) { mResources = resources; mMtpManager = mtpManager; mResolver = resolver; mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = database; mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); mAppFuse = new AppFuse(TAG, new AppFuseCallback()); // TODO: Mount AppFuse on demands. try { mAppFuse.mount(storageManager); } catch (IOException e) { Log.e(TAG, "Failed to start app fuse.", e); return false; } resume(); return true; } @Override Loading Loading @@ -165,7 +175,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { // Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. if (fileSize <= 0xffffffff) { if (fileSize <= 0xffffffffl) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); Loading Loading @@ -305,6 +315,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new RuntimeException(e); } finally { mDatabase.close(); mAppFuse.close(); super.shutdown(); } } Loading Loading @@ -385,9 +396,11 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public byte[] getObjectBytes(int inode, long offset, int size) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, (int) offset, size, mBytes); return mBytes; final long readSize = mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes); // TODO: Change signature so that getObjectBytes can return read size without copying // bytes. return Arrays.copyOf(mBytes, (int) readSize); } @Override Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +71 −3 Original line number Diff line number Diff line Loading @@ -20,18 +20,25 @@ import android.database.Cursor; import android.mtp.MtpConstants; import android.mtp.MtpObjectInfo; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.provider.DocumentsContract.Root; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.provider.DocumentsContract; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.TimeoutException; import static com.android.mtp.MtpDatabase.strings; @SmallTest @MediumTest public class MtpDocumentsProviderTest extends AndroidTestCase { private final static Uri ROOTS_URI = DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY); Loading Loading @@ -421,10 +428,71 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { MtpDocumentsProvider.AUTHORITY, "1"))); } public void testOpenDocument() throws Exception { setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") }); final byte[] bytes = "Hello world".getBytes(); setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { new MtpObjectInfo.Builder() .setName("test.txt") .setObjectHandle(1) .setCompressedSize(bytes.length) .setParent(-1) .build() }); mMtpManager.setImportFileBytes(0, 1, bytes); try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { final byte[] readBytes = new byte[5]; assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET)); assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); assertTrue(Arrays.equals("world".getBytes(), readBytes)); assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET)); assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); assertTrue(Arrays.equals("Hello".getBytes(), readBytes)); } } public void testOpenDocument_shortBytes() throws Exception { mMtpManager = new TestMtpManager(getContext()) { @Override MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException { if (objectHandle == 1) { return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle)) .setObjectHandle(1).setCompressedSize(1024 * 1024).build(); } return super.getObjectInfo(deviceId, objectHandle); } }; setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") }); final byte[] bytes = "Hello world".getBytes(); setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { new MtpObjectInfo.Builder() .setName("test.txt") .setObjectHandle(1) .setCompressedSize(bytes.length) .setParent(-1) .build() }); mMtpManager.setImportFileBytes(0, 1, bytes); try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { final byte[] readBytes = new byte[1024 * 1024]; assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length)); } } private void setupProvider(int flag) { mDatabase = new MtpDatabase(getContext(), flag); mProvider = new MtpDocumentsProvider(); mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase); final StorageManager storageManager = getContext().getSystemService(StorageManager.class); assertTrue(mProvider.onCreateForTesting( mResources, mMtpManager, mResolver, mDatabase, storageManager)); } private String[] getStrings(Cursor cursor) { Loading packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +12 −0 Original line number Diff line number Diff line Loading @@ -208,4 +208,16 @@ public class TestMtpManager extends MtpManager { byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { return mImportFileBytes.get(pack(deviceId, objectHandle)); } @Override long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer) throws IOException { final byte[] bytes = mImportFileBytes.get(pack(deviceId, objectHandle)); int i = 0; while (i < size && i + offset < bytes.length) { buffer[i] = bytes[(int) (i + offset)]; i++; } return i; } } Loading
packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp +3 −6 Original line number Diff line number Diff line Loading @@ -332,23 +332,20 @@ private: uint64_t offset, uint32_t size, void* buf) { const uint32_t read_size = static_cast<uint32_t>(std::min( static_cast<uint64_t>(size), get_file_size(inode) - offset)); ScopedLocalRef<jbyteArray> array(env_, static_cast<jbyteArray>(env_->CallObjectMethod( self_, app_fuse_get_object_bytes, inode, offset, read_size))); size))); if (array.get() == nullptr) { return -1; } ScopedByteArrayRO bytes(env_, array.get()); if (bytes.size() != read_size || bytes.get() == nullptr) { if (bytes.get() == nullptr) { return -1; } const uint32_t read_size = std::min(static_cast<uint32_t>(bytes.size()), size); memcpy(buf, bytes.get(), read_size); return read_size; } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +20 −7 Original line number Diff line number Diff line Loading @@ -37,10 +37,10 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; Loading Loading @@ -103,18 +103,28 @@ public class MtpDocumentsProvider extends DocumentsProvider { } @VisibleForTesting void onCreateForTesting( boolean onCreateForTesting( Resources resources, MtpManager mtpManager, ContentResolver resolver, MtpDatabase database) { MtpDatabase database, StorageManager storageManager) { mResources = resources; mMtpManager = mtpManager; mResolver = resolver; mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = database; mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); mAppFuse = new AppFuse(TAG, new AppFuseCallback()); // TODO: Mount AppFuse on demands. try { mAppFuse.mount(storageManager); } catch (IOException e) { Log.e(TAG, "Failed to start app fuse.", e); return false; } resume(); return true; } @Override Loading Loading @@ -165,7 +175,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { // Fallback to non-seekable file descriptor. // TODO: Use getPartialObject64 for MTP devices that support Android vendor // extension. if (fileSize <= 0xffffffff) { if (fileSize <= 0xffffffffl) { return mAppFuse.openFile(Integer.parseInt(documentId)); } else { return getPipeManager(identifier).readDocument(mMtpManager, identifier); Loading Loading @@ -305,6 +315,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new RuntimeException(e); } finally { mDatabase.close(); mAppFuse.close(); super.shutdown(); } } Loading Loading @@ -385,9 +396,11 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public byte[] getObjectBytes(int inode, long offset, int size) throws IOException { final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, (int) offset, size, mBytes); return mBytes; final long readSize = mMtpManager.getPartialObject( identifier.mDeviceId, identifier.mObjectHandle, offset, size, mBytes); // TODO: Change signature so that getObjectBytes can return read size without copying // bytes. return Arrays.copyOf(mBytes, (int) readSize); } @Override Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +71 −3 Original line number Diff line number Diff line Loading @@ -20,18 +20,25 @@ import android.database.Cursor; import android.mtp.MtpConstants; import android.mtp.MtpObjectInfo; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.provider.DocumentsContract.Root; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; import android.provider.DocumentsContract; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.TimeoutException; import static com.android.mtp.MtpDatabase.strings; @SmallTest @MediumTest public class MtpDocumentsProviderTest extends AndroidTestCase { private final static Uri ROOTS_URI = DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY); Loading Loading @@ -421,10 +428,71 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { MtpDocumentsProvider.AUTHORITY, "1"))); } public void testOpenDocument() throws Exception { setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") }); final byte[] bytes = "Hello world".getBytes(); setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { new MtpObjectInfo.Builder() .setName("test.txt") .setObjectHandle(1) .setCompressedSize(bytes.length) .setParent(-1) .build() }); mMtpManager.setImportFileBytes(0, 1, bytes); try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { final byte[] readBytes = new byte[5]; assertEquals(6, Os.lseek(fd.getFileDescriptor(), 6, OsConstants.SEEK_SET)); assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); assertTrue(Arrays.equals("world".getBytes(), readBytes)); assertEquals(0, Os.lseek(fd.getFileDescriptor(), 0, OsConstants.SEEK_SET)); assertEquals(5, Os.read(fd.getFileDescriptor(), readBytes, 0, 5)); assertTrue(Arrays.equals("Hello".getBytes(), readBytes)); } } public void testOpenDocument_shortBytes() throws Exception { mMtpManager = new TestMtpManager(getContext()) { @Override MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException { if (objectHandle == 1) { return new MtpObjectInfo.Builder(super.getObjectInfo(deviceId, objectHandle)) .setObjectHandle(1).setCompressedSize(1024 * 1024).build(); } return super.getObjectInfo(deviceId, objectHandle); } }; setupProvider(MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY); setupRoots(0, new MtpRoot[] { new MtpRoot(0, 0, "Device", "Storage", 0, 0, "") }); final byte[] bytes = "Hello world".getBytes(); setupDocuments(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, "1", new MtpObjectInfo[] { new MtpObjectInfo.Builder() .setName("test.txt") .setObjectHandle(1) .setCompressedSize(bytes.length) .setParent(-1) .build() }); mMtpManager.setImportFileBytes(0, 1, bytes); try (final ParcelFileDescriptor fd = mProvider.openDocument("3", "r", null)) { final byte[] readBytes = new byte[1024 * 1024]; assertEquals(11, Os.read(fd.getFileDescriptor(), readBytes, 0, readBytes.length)); } } private void setupProvider(int flag) { mDatabase = new MtpDatabase(getContext(), flag); mProvider = new MtpDocumentsProvider(); mProvider.onCreateForTesting(mResources, mMtpManager, mResolver, mDatabase); final StorageManager storageManager = getContext().getSystemService(StorageManager.class); assertTrue(mProvider.onCreateForTesting( mResources, mMtpManager, mResolver, mDatabase, storageManager)); } private String[] getStrings(Cursor cursor) { Loading
packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestMtpManager.java +12 −0 Original line number Diff line number Diff line Loading @@ -208,4 +208,16 @@ public class TestMtpManager extends MtpManager { byte[] getObject(int deviceId, int objectHandle, int expectedSize) throws IOException { return mImportFileBytes.get(pack(deviceId, objectHandle)); } @Override long getPartialObject(int deviceId, int objectHandle, long offset, long size, byte[] buffer) throws IOException { final byte[] bytes = mImportFileBytes.get(pack(deviceId, objectHandle)); int i = 0; while (i < size && i + offset < bytes.length) { buffer[i] = bytes[(int) (i + offset)]; i++; } return i; } }