Merge "Add getPartialObject to Java MtpDevice class."
diff --git a/api/current.txt b/api/current.txt
index d15092f..4bc700c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22452,6 +22452,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
+ method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 93fd7d2..ca6aa93 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -23998,6 +23998,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
+ method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index a2c2076..81e7449 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -22460,6 +22460,7 @@
method public int[] getObjectHandles(int, int, int);
method public android.mtp.MtpObjectInfo getObjectInfo(int);
method public long getParent(int);
+ method public int getPartialObject(int, int, int, byte[]) throws java.io.IOException;
method public long getStorageId(int);
method public int[] getStorageIds();
method public android.mtp.MtpStorageInfo getStorageInfo(int);
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
index 95cb520..d24c5e8 100644
--- a/media/java/android/mtp/MtpDevice.java
+++ b/media/java/android/mtp/MtpDevice.java
@@ -19,9 +19,10 @@
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
import android.os.ParcelFileDescriptor;
+import java.io.IOException;
+
/**
* This class represents an MTP or PTP device connected on the USB host bus. An application can
* instantiate an object of this type, by referencing an attached {@link
@@ -158,6 +159,22 @@
}
/**
+ * Obtains object bytes in the specified range and writes it to an array.
+ * This call may block for an arbitrary amount of time depending on the size
+ * of the data and speed of the devices.
+ *
+ * @param objectHandle handle of the object to read
+ * @param offset Start index of reading range.
+ * @param size Size of reading range.
+ * @param buffer Array to write data.
+ * @return Size of bytes that are actually read.
+ */
+ public int getPartialObject(int objectHandle, int offset, int size, byte[] buffer)
+ throws IOException {
+ return native_get_partial_object(objectHandle, offset, size, buffer);
+ }
+
+ /**
* Returns the thumbnail data for an object as a byte array.
* The size and format of the thumbnail data can be determined via
* {@link MtpObjectInfo#getThumbCompressedSize} and
@@ -323,6 +340,8 @@
private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
private native MtpObjectInfo native_get_object_info(int objectHandle);
private native byte[] native_get_object(int objectHandle, int objectSize);
+ private native int native_get_partial_object(
+ int objectHandle, int offset, int objectSize, byte[] buffer) throws IOException;
private native byte[] native_get_thumbnail(int objectHandle);
private native boolean native_delete_object(int objectHandle);
private native long native_get_parent(int objectHandle);
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index 3f4d183..ee73a05 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -29,6 +29,7 @@
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include "nativehelper/ScopedLocalRef.h"
#include "private/android_filesystem_config.h"
#include "MtpTypes.h"
@@ -41,6 +42,8 @@
// ----------------------------------------------------------------------------
+namespace {
+
static jfieldID field_context;
jclass clazz_deviceInfo;
@@ -93,6 +96,28 @@
// MtpEvent fields
static jfieldID field_event_eventCode;
+class JavaArrayWriter {
+ JNIEnv* mEnv;
+ jbyteArray mArray;
+ jsize mSize;
+
+public:
+ JavaArrayWriter(JNIEnv* env, jbyteArray array) :
+ mEnv(env), mArray(array), mSize(mEnv->GetArrayLength(mArray)) {}
+ bool write(void* data, uint32_t offset, uint32_t length) {
+ if (static_cast<uint32_t>(mSize) < offset + length) {
+ return false;
+ }
+ mEnv->SetByteArrayRegion(mArray, offset, length, static_cast<jbyte*>(data));
+ return true;
+ }
+ static bool writeTo(void* data, uint32_t offset, uint32_t length, void* clientData) {
+ return static_cast<JavaArrayWriter*>(clientData)->write(data, offset, length);
+ }
+};
+
+}
+
MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
{
return (MtpDevice*)env->GetLongField(javaDevice, field_context);
@@ -307,38 +332,57 @@
return info;
}
-struct get_object_callback_data {
- JNIEnv *env;
- jbyteArray array;
-};
-
-static bool get_object_callback(void* data, int offset, int length, void* clientData)
-{
- get_object_callback_data* cbData = (get_object_callback_data *)clientData;
- cbData->env->SetByteArrayRegion(cbData->array, offset, length, (jbyte *)data);
- return true;
-}
-
static jbyteArray
android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize)
{
MtpDevice* device = get_device_from_object(env, thiz);
- if (!device)
- return NULL;
-
- jbyteArray array = env->NewByteArray(objectSize);
- if (!array) {
- jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
- return NULL;
+ if (!device) {
+ return nullptr;
}
- get_object_callback_data data;
- data.env = env;
- data.array = array;
+ ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(objectSize));
+ if (!array.get()) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return nullptr;
+ }
- if (device->readObject(objectID, get_object_callback, objectSize, &data))
- return array;
- return NULL;
+ JavaArrayWriter writer(env, array.get());
+
+ if (device->readObject(objectID, JavaArrayWriter::writeTo, objectSize, &writer)) {
+ return array.release();
+ }
+ return nullptr;
+}
+
+static jint
+android_mtp_MtpDevice_get_partial_object(JNIEnv *env,
+ jobject thiz,
+ jint objectID,
+ jint offset,
+ jint size,
+ jbyteArray array)
+{
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Array must not be null.");
+ return -1;
+ }
+
+ MtpDevice* const device = get_device_from_object(env, thiz);
+ if (!device) {
+ jniThrowException(env, "java/io/IOException", "Failed to obtain MtpDevice.");
+ return -1;
+ }
+
+ JavaArrayWriter writer(env, array);
+ const int64_t result = device->readPartialObject(
+ objectID, offset, size, JavaArrayWriter::writeTo, &writer);
+
+ if (result >= 0) {
+ return static_cast<jint>(result);
+ } else {
+ jniThrowException(env, "java/io/IOException", "Failed to read data.");
+ return -1;
+ }
}
static jbyteArray
@@ -547,6 +591,7 @@
{"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;",
(void *)android_mtp_MtpDevice_get_object_info},
{"native_get_object", "(II)[B",(void *)android_mtp_MtpDevice_get_object},
+ {"native_get_partial_object", "(III[B)I", (void *) android_mtp_MtpDevice_get_partial_object},
{"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
{"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
{"native_get_parent", "(I)J", (void *)android_mtp_MtpDevice_get_parent},