From 927fefe8133289d4b17582b1d700f2f2adff6ba2 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 18 Oct 2016 12:57:34 -0700 Subject: [PATCH] Add UsbDeviceConnection.requestWait with a timeout Test: Test is submitted alongside this change Change-Id: I1c46143030a2822ee76676ddc80d3b5c8c62ee80 Fixes: 31288102 --- api/current.txt | 1 + api/system-current.txt | 1 + api/test-current.txt | 1 + .../hardware/usb/UsbDeviceConnection.java | 40 ++++++++++++++++++- .../android_hardware_UsbDeviceConnection.cpp | 35 +++++++++++++--- 5 files changed, 71 insertions(+), 7 deletions(-) diff --git a/api/current.txt b/api/current.txt index 9aa8c59dbf707..0afbaa0b7882a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14778,6 +14778,7 @@ package android.hardware.usb { method public java.lang.String getSerial(); method public boolean releaseInterface(android.hardware.usb.UsbInterface); method public android.hardware.usb.UsbRequest requestWait(); + method public android.hardware.usb.UsbRequest requestWait(int); method public boolean setConfiguration(android.hardware.usb.UsbConfiguration); method public boolean setInterface(android.hardware.usb.UsbInterface); } diff --git a/api/system-current.txt b/api/system-current.txt index e6e5d38356574..cdfc26baae3a4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -15988,6 +15988,7 @@ package android.hardware.usb { method public java.lang.String getSerial(); method public boolean releaseInterface(android.hardware.usb.UsbInterface); method public android.hardware.usb.UsbRequest requestWait(); + method public android.hardware.usb.UsbRequest requestWait(int); method public boolean resetDevice(); method public boolean setConfiguration(android.hardware.usb.UsbConfiguration); method public boolean setInterface(android.hardware.usb.UsbInterface); diff --git a/api/test-current.txt b/api/test-current.txt index ee3cb0d66e4d0..9905d4b059fac 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14795,6 +14795,7 @@ package android.hardware.usb { method public java.lang.String getSerial(); method public boolean releaseInterface(android.hardware.usb.UsbInterface); method public android.hardware.usb.UsbRequest requestWait(); + method public android.hardware.usb.UsbRequest requestWait(int); method public boolean setConfiguration(android.hardware.usb.UsbConfiguration); method public boolean setInterface(android.hardware.usb.UsbInterface); } diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java index 160e261de5308..ca2b1f87d8735 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -21,6 +21,9 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; import android.os.ParcelFileDescriptor; + +import com.android.internal.util.Preconditions; + import dalvik.system.CloseGuard; import java.io.FileDescriptor; @@ -273,7 +276,40 @@ public class UsbDeviceConnection { * {@link UsbRequest#queue(ByteBuffer, int)} */ public UsbRequest requestWait() { - UsbRequest request = native_request_wait(); + // -1 is special value indicating infinite wait + UsbRequest request = native_request_wait(-1); + if (request != null) { + request.dequeue(); + } + return request; + } + + /** + * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation + *

Note that this may return requests queued on multiple + * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use, + * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link + * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process + * the result of this function.

+ *

Position and array offset of the request's buffer are ignored and assumed to be 0. The + * position will be set to the number of bytes read/written.

+ *

Android processes {@link UsbRequest UsbRequests} asynchronously. Hence it is not + * guaranteed that {@link #requestWait(int) requestWait(0)} returns a request that has been + * queued right before even if the request could have been processed immediately.

+ * + * @param timeout timeout in milliseconds. If 0 this method does not wait. + * + * @return a completed USB request, or {@code null} if an error or time out occurred + * + * @throws IllegalArgumentException if the number of bytes read or written is more than the + * limit of the request's buffer. The number of bytes is + * determined by the {@code length} parameter of + * {@link UsbRequest#queue(ByteBuffer, int)} + */ + public UsbRequest requestWait(int timeout) { + timeout = Preconditions.checkArgumentNonnegative(timeout); + + UsbRequest request = native_request_wait(timeout); if (request != null) { request.dequeue(); } @@ -318,7 +354,7 @@ public class UsbDeviceConnection { int index, byte[] buffer, int offset, int length, int timeout); private native int native_bulk_request(int endpoint, byte[] buffer, int offset, int length, int timeout); - private native UsbRequest native_request_wait(); + private native UsbRequest native_request_wait(int timeout); private native String native_get_serial(); private native boolean native_reset_device(); } diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index 7ec17bf1d85eb..341b6a22ef894 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -24,12 +24,15 @@ #include +#include + #include #include #include #include using namespace android; +using namespace std::chrono; static jfieldID field_context; @@ -215,7 +218,7 @@ android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz, } static jobject -android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz) +android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jint timeoutMillis) { struct usb_device* device = get_device_from_object(env, thiz); if (!device) { @@ -223,11 +226,33 @@ android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz) return NULL; } - struct usb_request* request = usb_request_wait(device); - if (request) + struct usb_request* request; + if (timeoutMillis == -1) { + request = usb_request_wait(device, -1); + } else { + steady_clock::time_point currentTime = steady_clock::now(); + steady_clock::time_point endTime = currentTime + std::chrono::milliseconds(timeoutMillis); + + // Poll the existence of a request via usb_request_wait until we get a result, an unexpected + // error or time out. As several threads can listen on the same fd, we might get wakeups + // without data. + while (1) { + request = usb_request_wait(device, duration_cast(endTime + - currentTime).count()); + + int error = errno; + currentTime = steady_clock::now(); + if (request != NULL || error != EAGAIN || currentTime >= endTime) { + break; + } + }; + } + + if (request) { return (jobject)request->client_data; - else + } else { return NULL; + } } static jstring @@ -272,7 +297,7 @@ static const JNINativeMethod method_table[] = { (void *)android_hardware_UsbDeviceConnection_control_request}, {"native_bulk_request", "(I[BIII)I", (void *)android_hardware_UsbDeviceConnection_bulk_request}, - {"native_request_wait", "()Landroid/hardware/usb/UsbRequest;", + {"native_request_wait", "(I)Landroid/hardware/usb/UsbRequest;", (void *)android_hardware_UsbDeviceConnection_request_wait}, { "native_get_serial", "()Ljava/lang/String;", (void*)android_hardware_UsbDeviceConnection_get_serial },