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

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

Start to use app fuse in MtpDocumentsProvider.

BUG=25756419

Change-Id: I050e7cf7523926710291875737602e95c47be088
parent 90131a91
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