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

Commit ebcfc90c authored by Tomasz Mikolajewski's avatar Tomasz Mikolajewski Committed by Android (Google) Code Review
Browse files

Merge "Make reading files streamed in MtpDocumentsProvider."

parents 2dbd0c7e 52652ac7
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);
        }
    }