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

Commit 52652ac7 authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski
Browse files

Make reading files streamed in MtpDocumentsProvider.

Change-Id: If5ca4fc71cf27a2eccb46865421235790623985a
parent ca222781
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -170,7 +170,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        try {
            final MtpDocument document =
                    mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
            return mPipeManager.readDocument(mMtpManager, identifier, document.getSize());
            return mPipeManager.readDocument(mMtpManager, identifier);
        } catch (IOException error) {
            throw new FileNotFoundException(error.getMessage());
        }
+6 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.mtp.MtpDevice;
import android.os.ParcelFileDescriptor;
import android.util.SparseArray;

import java.io.FileNotFoundException;
@@ -126,6 +127,11 @@ class MtpManager {
        return result;
    }

    synchronized void importFile(int deviceId, int objectHandle, ParcelFileDescriptor target)
            throws IOException {
        throw new UnsupportedOperationException("Importing files is not supported.");
    }

    private MtpDevice getDevice(int deviceId) throws IOException {
        final MtpDevice device = mDevices.get(deviceId);
        if (device == null) {
+24 −26
Original line number Diff line number Diff line
@@ -36,47 +36,45 @@ class PipeManager {

    ParcelFileDescriptor readDocument(
            final MtpManager model,
            final Identifier identifier,
            final int expectedSize) throws IOException {
        final Task task = new Task() {
            @Override
            byte[] getBytes() throws IOException {
                // TODO: Use importFile to ParcelFileDescripter after implementing this.
                return model.getObject(
                        identifier.mDeviceId, identifier.mObjectHandle, expectedSize);
            }
        };
            final Identifier identifier) throws IOException {
        final Task task = new ImportFileTask(model, identifier);
        mExecutor.execute(task);
        return task.getReadingFileDescriptor();
    }

    private static abstract class Task implements Runnable {
        private final ParcelFileDescriptor[] mDescriptors;
        protected final MtpManager mModel;
        protected final Identifier mIdentifier;
        protected final ParcelFileDescriptor[] mDescriptors;

        Task() throws IOException {
        Task(MtpManager model, Identifier identifier) throws IOException {
            mModel = model;
            mIdentifier = identifier;
            mDescriptors = ParcelFileDescriptor.createReliablePipe();
        }

        abstract byte[] getBytes() throws IOException;
        ParcelFileDescriptor getReadingFileDescriptor() {
            return mDescriptors[0];
        }
    }

    private static class ImportFileTask extends Task {
        ImportFileTask(MtpManager model, Identifier identifier) throws IOException {
            super(model, identifier);
        }

        @Override
        public void run() {
            try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
                    new ParcelFileDescriptor.AutoCloseOutputStream(mDescriptors[1])) {
            try {
                    final byte[] bytes = getBytes();
                    stream.write(bytes);
                mModel.importFile(mIdentifier.mDeviceId, mIdentifier.mObjectHandle, mDescriptors[1]);
                mDescriptors[1].close();
            } catch (IOException error) {
                    mDescriptors[1].closeWithError("Failed to load bytes.");
                    return;
                }
                try {
                    mDescriptors[1].closeWithError("Failed to stream a file.");
                } catch (IOException closeError) {
                Log.d(MtpDocumentsProvider.TAG, closeError.getMessage());
                    Log.w(MtpDocumentsProvider.TAG, closeError.getMessage());
                }
            }

        ParcelFileDescriptor getReadingFileDescriptor() {
            return mDescriptors[0];
        }
    }

+5 −5
Original line number Diff line number Diff line
@@ -30,16 +30,16 @@ public class PipeManagerTest extends AndroidTestCase {
    public void testReadDocument_basic() throws Exception {
        final TestMtpManager mtpManager = new TestMtpManager(getContext());
        final byte[] expectedBytes = new byte[] { 'h', 'e', 'l', 'l', 'o' };
        mtpManager.setObjectBytes(0, 1, 5, expectedBytes);
        mtpManager.setImportFileBytes(0, 1, expectedBytes);
        final ExecutorService executor = Executors.newSingleThreadExecutor();
        final PipeManager pipeManager = new PipeManager(executor);
        final ParcelFileDescriptor descriptor = pipeManager.readDocument(
                mtpManager, new Identifier(0, 0, 1), 5);
                mtpManager, new Identifier(0, 0, 1));
        try (final ParcelFileDescriptor.AutoCloseInputStream stream =
                new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
            final byte[] results = new byte[100];
            assertEquals(5, stream.read(results));
            for (int i = 0; i < 5; i++) {
            assertEquals(expectedBytes.length, stream.read(results));
            for (int i = 0; i < expectedBytes.length; i++) {
                assertEquals(expectedBytes[i], results[i]);
            }
        }
@@ -50,7 +50,7 @@ public class PipeManagerTest extends AndroidTestCase {
        final ExecutorService executor = Executors.newSingleThreadExecutor();
        final PipeManager pipeManager = new PipeManager(executor);
        final ParcelFileDescriptor descriptor =
                pipeManager.readDocument(mtpManager, new Identifier(0, 0, 1), 5);
                pipeManager.readDocument(mtpManager, new Identifier(0, 0, 1));
        executor.awaitTermination(1000, TimeUnit.MILLISECONDS);
        try {
            descriptor.checkError();
+12 −8
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.mtp;

import android.content.Context;
import android.os.ParcelFileDescriptor;

import java.io.IOException;
import java.util.Arrays;
@@ -35,8 +36,8 @@ public class TestMtpManager extends MtpManager {
    private final Set<Integer> mOpenedDevices = new TreeSet<Integer>();
    private final Map<Integer, MtpRoot[]> mRoots = new HashMap<Integer, MtpRoot[]>();
    private final Map<String, MtpDocument> mDocuments = new HashMap<String, MtpDocument>();
    private final Map<String, byte[]> mObjectBytes = new HashMap<String, byte[]>();
    private final Map<String, Integer> mParents = new HashMap<String, Integer>();
    private final Map<String, byte[]> mImportFileBytes = new HashMap<String, byte[]>();

    TestMtpManager(Context context) {
        super(context);
@@ -54,8 +55,8 @@ public class TestMtpManager extends MtpManager {
        mDocuments.put(pack(deviceId, objectHandle), document);
    }

    void setObjectBytes(int deviceId, int objectHandle, int expectedSize, byte[] bytes) {
        mObjectBytes.put(pack(deviceId, objectHandle, expectedSize), bytes);
    void setImportFileBytes(int deviceId, int objectHandle, byte[] bytes) {
        mImportFileBytes.put(pack(deviceId, objectHandle), bytes);
    }

    void setParent(int deviceId, int objectHandle, int parentObjectHandle) {
@@ -93,12 +94,15 @@ public class TestMtpManager extends MtpManager {
    }

    @Override
    byte[] getObject(int deviceId, int storageId, int expectedSize) throws IOException {
        final String key = pack(deviceId, storageId, expectedSize);
        if (mObjectBytes.containsKey(key)) {
            return mObjectBytes.get(key);
    void importFile(int deviceId, int storageId, ParcelFileDescriptor target) throws IOException {
        final String key = pack(deviceId, storageId);
        if (mImportFileBytes.containsKey(key)) {
            try (final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
                    new ParcelFileDescriptor.AutoCloseOutputStream(target)) {
                outputStream.write(mImportFileBytes.get(key));
            }
        } else {
            throw new IOException("getObject error: " + key);
            throw new IOException("importFile error: " + key);
        }
    }