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

Commit b36b1558 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Fix bugs that prevent from using AppFuse.

 * Allow buffer size that is greater than requested read size in JNI,
   because we reuse fixed-size buffer among multiple requests.
 * Fix condition to check if the file size is greater than 4GB or
   not. We need to use 0xffffffffl instead of 0xffffffff because
   0xffffffff is int and its value is -1.

BUG=None
Change-Id: I155916e139353b15dc1ab535234faf50d942996d
parent e7695829
Loading
Loading
Loading
Loading
+3 −6
Original line number Diff line number Diff line
@@ -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;
    }
+20 −7
Original line number Diff line number Diff line
@@ -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;

@@ -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
@@ -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);
@@ -305,6 +315,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
                throw new RuntimeException(e);
            } finally {
                mDatabase.close();
                mAppFuse.close();
                super.shutdown();
            }
        }
@@ -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
+71 −3
Original line number Diff line number Diff line
@@ -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);
@@ -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) {
+12 −0
Original line number Diff line number Diff line
@@ -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;
    }
}