usb: Use Get/SetByteArrayRegion in bulkTransfer

This change fixes https://issuetracker.google.com/issues/335003907

This change replaces
GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical with
GetByteArrayRegion/SetByteArrayRegion.

This change is needed because the critical API variants are not
appropriate for this context. See the JNI docs:

After calling GetPrimitiveArrayCritical, the native code should not run
for an extended period of time before it calls
ReleasePrimitiveArrayCritical.

In bulkTransfer, we call usb_device_bulk_transfer between
GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical.
usb_device_bulk_transfer is a blocking call so this can lead to GC
hangs.

Instead, we allocate a new buffer with malloc and then call
GetByteArrayRegion/SetByteArrayRegion before/after
usb_device_bulk_transfer, depending on the direction of the endpoint.

Test: I ran UsbTests. I also sent a "TEST UNIT READY" to a USB drive and
verified the response.

Change-Id: I5c82aac3fe99e1f11044f20091bf3a940bc8f6a7
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index a022842..b1221ee 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -190,18 +190,25 @@
         return -1;
     }
 
-    jbyte* bufferBytes = NULL;
-    if (buffer) {
-        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
+    bool is_dir_in = (endpoint & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN;
+    std::unique_ptr<jbyte[]> bufferBytes(new (std::nothrow) jbyte[length]);
+    if (!bufferBytes) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+        return -1;
     }
 
-    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout);
-
-    if (bufferBytes) {
-        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
+    if (!is_dir_in && buffer) {
+        env->GetByteArrayRegion(buffer, start, length, bufferBytes.get());
     }
 
-    return result;
+    jint bytes_transferred =
+            usb_device_bulk_transfer(device, endpoint, bufferBytes.get(), length, timeout);
+
+    if (bytes_transferred > 0 && is_dir_in) {
+        env->SetByteArrayRegion(buffer, start, bytes_transferred, bufferBytes.get());
+    }
+
+    return bytes_transferred;
 }
 
 static jobject