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

Commit 78033b77 authored by tomm's avatar tomm Committed by Tom Mulcahy
Browse files

usb: Use Get/SetByteArrayRegion in controlTransfer

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 controlTransfer, we call usb_device_control_transfer between
GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical.
usb_device_control_transfer is a blocking call so this can lead to GC
hangs.

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

This change is analogous to
https://android-review.googlesource.com/c/platform/frameworks/base/+/3056082
which fixed the same problem in bulkTransfer.

Test: I ran the USB Accessory Test - https://source.android.com/docs/compatibility/cts/verifier#testing-accessory-8 - both directions (this change on the DUT and this change on the companion device)

Change-Id: I3f670121fc1c65f046376aa819d638edb446658a
parent 2e1a903f
Loading
Loading
Loading
Loading
+14 −8
Original line number Diff line number Diff line
@@ -165,19 +165,25 @@ android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz,
        return -1;
    }

    jbyte* bufferBytes = NULL;
    if (buffer) {
        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
    bool is_dir_in = (requestType & 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_control_transfer(device, requestType, request,
            value, index, bufferBytes + start, length, timeout);
    if (!is_dir_in && buffer) {
        env->GetByteArrayRegion(buffer, start, length, bufferBytes.get());
    }

    if (bufferBytes) {
        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
    jint bytes_transferred = usb_device_control_transfer(device, requestType, request,
            value, index, bufferBytes.get(), length, timeout);

    if (bytes_transferred > 0 && is_dir_in) {
        env->SetByteArrayRegion(buffer, start, bytes_transferred, bufferBytes.get());
    }

    return result;
    return bytes_transferred;
}

static jint