Loading packages/MtpDocumentsProvider/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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)) packages/MtpDocumentsProvider/proguard.flags 0 → 100644 +4 −0 Original line number Diff line number Diff line # Keeps methods that are invoked by JNI. -keepclassmembers class * { @com.android.mtp.annotations.UsedByNative *; } packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java +12 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -51,7 +53,6 @@ public class AppFuse { }); } @VisibleForTesting void mount(StorageManager storageManager) { mDeviceFd = storageManager.mountAppFuse(mName); mMessageThread.start(); Loading @@ -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(), Loading @@ -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); Loading @@ -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) { Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +55 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; } Loading Loading @@ -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()); Loading Loading @@ -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; Loading @@ -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)); } } } packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java 0 → 100644 +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
packages/MtpDocumentsProvider/Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -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))
packages/MtpDocumentsProvider/proguard.flags 0 → 100644 +4 −0 Original line number Diff line number Diff line # Keeps methods that are invoked by JNI. -keepclassmembers class * { @com.android.mtp.annotations.UsedByNative *; }
packages/MtpDocumentsProvider/src/com/android/mtp/AppFuse.java +12 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -51,7 +53,6 @@ public class AppFuse { }); } @VisibleForTesting void mount(StorageManager storageManager) { mDeviceFd = storageManager.mountAppFuse(mName); mMessageThread.start(); Loading @@ -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(), Loading @@ -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); Loading @@ -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) { Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +55 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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. Loading @@ -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; } Loading Loading @@ -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()); Loading Loading @@ -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; Loading @@ -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)); } } }
packages/MtpDocumentsProvider/src/com/android/mtp/annotations/UsedByNative.java 0 → 100644 +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(); }