diff --git a/api/current.txt b/api/current.txt index 564653ae1824d..4c982638e3c6e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14781,6 +14781,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 fd51a6a7eb529..ce115d85e6ad3 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -15990,6 +15990,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 150fbfeb226c3..92e5776a5cc2b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14798,6 +14798,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 9ceb25a3dfdf3..6814506ad7974 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 const int USB_CONTROL_READ_TIMEOUT_MS = 200; @@ -217,7 +220,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) { @@ -225,11 +228,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 @@ -275,7 +300,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 },