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

Commit 4ba6303a authored by Daichi Hirono's avatar Daichi Hirono Committed by Android (Google) Code Review
Browse files

Merge "Start to use app fuse in MtpDocumentsProvider."

parents d3d9ddfa f52ef008
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ LOCAL_PACKAGE_NAME := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
LOCAL_PRIVILEGED_MODULE := true
LOCAL_JNI_SHARED_LIBRARIES := libappfuse_jni
LOCAL_PROGUARD_FLAG_FILES := proguard.flags

include $(BUILD_PACKAGE)
include $(call all-makefiles-under, $(LOCAL_PATH))
+4 −0
Original line number Diff line number Diff line
# Keeps methods that are invoked by JNI.
-keepclassmembers class * {
  @com.android.mtp.annotations.UsedByNative *;
}
+12 −13
Original line number Diff line number Diff line
@@ -21,25 +21,27 @@ import android.os.Process;
import android.os.storage.StorageManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.mtp.annotations.UsedByNative;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * TODO: Remove VisibleForTesting class.
 */
@VisibleForTesting
public class AppFuse {
    static {
        System.loadLibrary("appfuse_jni");
    }

    /**
     * Max read amount specified at the FUSE kernel implementation.
     * The value is copied from sdcard.c.
     */
    static final int MAX_READ = 128 * 1024;

    private final String mName;
    private final Callback mCallback;
    private final Thread mMessageThread;
    private ParcelFileDescriptor mDeviceFd;

    @VisibleForTesting
    AppFuse(String name, Callback callback) {
        mName = name;
        mCallback = callback;
@@ -51,7 +53,6 @@ public class AppFuse {
        });
    }

    @VisibleForTesting
    void mount(StorageManager storageManager) {
        mDeviceFd = storageManager.mountAppFuse(mName);
        mMessageThread.start();
@@ -72,11 +73,6 @@ public class AppFuse {
        }
    }

    /**
     * @param i
     * @throws FileNotFoundException
     */
    @VisibleForTesting
    public ParcelFileDescriptor openFile(int i) throws FileNotFoundException {
        return ParcelFileDescriptor.open(new File(
                getMountPoint(),
@@ -94,7 +90,7 @@ public class AppFuse {
        byte[] getObjectBytes(int inode, long offset, int size) throws IOException;
    }

    @VisibleForTesting
    @UsedByNative("com_android_mtp_AppFuse.cpp")
    private long getFileSize(int inode) {
        try {
            return mCallback.getFileSize(inode);
@@ -103,8 +99,11 @@ public class AppFuse {
        }
    }

    @VisibleForTesting
    @UsedByNative("com_android_mtp_AppFuse.cpp")
    private byte[] getObjectBytes(int inode, long offset, int size) {
        if (offset < 0 || size < 0 || size > MAX_READ) {
            return null;
        }
        try {
            return mCallback.getObjectBytes(inode, offset, size);
        } catch (IOException e) {
+55 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.mtp;

import static com.android.internal.util.Preconditions.checkArgument;

import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
@@ -26,6 +28,7 @@ import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.os.storage.StorageManager;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsContract;
@@ -34,6 +37,7 @@ 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;
@@ -68,6 +72,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
    private RootScanner mRootScanner;
    private Resources mResources;
    private MtpDatabase mDatabase;
    private AppFuse mAppFuse;

    /**
     * Provides singleton instance to MtpDocumentsService.
@@ -85,6 +90,9 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
        mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
        mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase);
        mAppFuse = new AppFuse(TAG, new AppFuseCallback());
        // TODO: Mount AppFuse on demands.
        mAppFuse.mount(getContext().getSystemService(StorageManager.class));
        resume();
        return true;
    }
@@ -147,16 +155,27 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        try {
            switch (mode) {
                case "r":
                    final long fileSize = getFileSize(documentId);
                    // MTP getPartialObject operation does not support files that are larger than 4GB.
                    // Fallback to non-seekable file descriptor.
                    // TODO: Use getPartialObject64 for MTP devices that support Android vendor
                    // extension.
                    if (fileSize <= 0xffffffff) {
                        return mAppFuse.openFile(Integer.parseInt(documentId));
                    } else {
                        return getPipeManager(identifier).readDocument(mMtpManager, identifier);
                    }
                case "w":
                    // TODO: Clear the parent document loader task (if exists) and call notify
                    // when writing is completed.
                    return getPipeManager(identifier).writeDocument(
                            getContext(), mMtpManager, identifier);
                default:
                    // TODO: Add support for seekable files.
                case "rw":
                    // TODO: Add support for "rw" mode.
                    throw new UnsupportedOperationException(
                            "The provider does not support seekable file.");
                            "The provider does not support 'rw' mode.");
                default:
                    throw new IllegalArgumentException("Unknown mode for openDocument: " + mode);
            }
        } catch (IOException error) {
            throw new FileNotFoundException(error.getMessage());
@@ -330,6 +349,21 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader;
    }

    private long getFileSize(String documentId) throws FileNotFoundException {
        final Cursor cursor = mDatabase.queryDocument(
                documentId,
                MtpDatabase.strings(Document.COLUMN_SIZE, Document.COLUMN_DISPLAY_NAME));
        try {
            if (cursor.moveToNext()) {
                return cursor.getLong(0);
            } else {
                throw new FileNotFoundException();
            }
        } finally {
            cursor.close();
        }
    }

    private static class DeviceToolkit {
        public final PipeManager mPipeManager;
        public final DocumentLoader mDocumentLoader;
@@ -339,4 +373,21 @@ public class MtpDocumentsProvider extends DocumentsProvider {
            mDocumentLoader = new DocumentLoader(manager, resolver, database);
        }
    }

    private class AppFuseCallback implements AppFuse.Callback {
        final byte[] mBytes = new byte[AppFuse.MAX_READ];

        @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;
        }

        @Override
        public long getFileSize(int inode) throws FileNotFoundException {
            return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode));
        }
    }
}
+31 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.mtp.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
 * Annotation that shows the method is used by JNI.
 */
@Target(ElementType.METHOD)
public @interface UsedByNative {
    /**
     * JNI file name that uses the method.
     */
    String value();
}
Loading