Add API for streamed reading on MTP devices.

The existing APIs required to copy all bytes to memory, which will fail in
case of very large files, like movies.

Bug: 22908937
Change-Id: I23bdcbdbf08b7c1b1017591799bbb94e53249a57
diff --git a/api/current.txt b/api/current.txt
index d468831..ea7963c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18088,6 +18088,7 @@
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
     method public byte[] getThumbnail(int);
     method public boolean importFile(int, java.lang.String);
+    method public boolean importFile(int, android.os.ParcelFileDescriptor);
     method public boolean open(android.hardware.usb.UsbDeviceConnection);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 4dd85e6..b8cd453 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19602,6 +19602,7 @@
     method public android.mtp.MtpStorageInfo getStorageInfo(int);
     method public byte[] getThumbnail(int);
     method public boolean importFile(int, java.lang.String);
+    method public boolean importFile(int, android.os.ParcelFileDescriptor);
     method public boolean open(android.hardware.usb.UsbDeviceConnection);
   }
 
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index a68361b..a9611c5 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -18,6 +18,7 @@
 
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbDeviceConnection;
+import android.os.ParcelFileDescriptor;
 
 /**
  * This class represents an MTP or PTP device connected on the USB host bus. An application can
@@ -235,6 +236,20 @@
         return native_import_file(objectHandle, destPath);
     }
 
+    /**
+     * Copies the data for an object to a file descriptor.
+     * This call may block for an arbitrary amount of time depending on the size
+     * of the data and speed of the devices. The file descriptor is not closed
+     * on completion, and must be done by the caller.
+     *
+     * @param objectHandle handle of the object to read
+     * @param descriptor file descriptor to write the data to for the file transfer.
+     * @return true if the file transfer succeeds
+     */
+    public boolean importFile(int objectHandle, ParcelFileDescriptor descriptor) {
+        return native_import_file(objectHandle, descriptor.getFd());
+    }
+
     // used by the JNI code
     private long mNativeContext;
 
@@ -251,4 +266,5 @@
     private native long native_get_parent(int objectHandle);
     private native long native_get_storage_id(int objectHandle);
     private native boolean native_import_file(int objectHandle, String destPath);
+    private native boolean native_import_file(int objectHandle, int fd);
 }
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 2dbd7dc..6652a54 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -393,6 +393,16 @@
     return JNI_FALSE;
 }
 
+static jboolean
+android_mtp_MtpDevice_import_file_to_fd(JNIEnv *env, jobject thiz, jint object_id, jint fd)
+{
+    MtpDevice* device = get_device_from_object(env, thiz);
+    if (device)
+        return device->readObject(object_id, fd);
+    else
+        return JNI_FALSE;
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -413,8 +423,9 @@
     {"native_delete_object",    "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
     {"native_get_parent",       "(I)J", (void *)android_mtp_MtpDevice_get_parent},
     {"native_get_storage_id",   "(I)J", (void *)android_mtp_MtpDevice_get_storage_id},
-    {"native_import_file",     "(ILjava/lang/String;)Z",
+    {"native_import_file",      "(ILjava/lang/String;)Z",
                                         (void *)android_mtp_MtpDevice_import_file},
+    {"native_import_file",      "(II)Z", (void *)android_mtp_MtpDevice_import_file_to_fd}
 };
 
 int register_android_mtp_MtpDevice(JNIEnv *env)