diff --git a/api/current.txt b/api/current.txt index 7df132e2caafd..c337ffbb38eae 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15954,7 +15954,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 android.hardware.usb.UsbRequest requestWait(long) throws java.util.concurrent.TimeoutException; 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 39f58477a10d8..1a96933d9c23c 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -17446,7 +17446,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 android.hardware.usb.UsbRequest requestWait(long) throws java.util.concurrent.TimeoutException; 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 93b320538e8f2..35fb7c946e678 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -16010,7 +16010,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 android.hardware.usb.UsbRequest requestWait(long) throws java.util.concurrent.TimeoutException; 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 1bf2137885b29..9b5d0d3deb8c7 100644 --- a/core/java/android/hardware/usb/UsbDeviceConnection.java +++ b/core/java/android/hardware/usb/UsbDeviceConnection.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.Context; +import android.os.Build; import android.os.ParcelFileDescriptor; import com.android.internal.util.Preconditions; @@ -27,7 +28,9 @@ import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import java.io.FileDescriptor; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; +import java.util.concurrent.TimeoutException; /** * This class is used for sending and receiving data and control messages to a USB device. @@ -268,16 +271,29 @@ public class UsbDeviceConnection { * * @return a completed USB request, or null if an error 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 + * @throws IllegalArgumentException Before API {@value Build.VERSION_CODES#O}: 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)} + * @throws BufferOverflowException In API {@value Build.VERSION_CODES#O} and after: 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() { - // -1 is special value indicating infinite wait - UsbRequest request = native_request_wait(-1); + UsbRequest request = null; + try { + // -1 is special value indicating infinite wait + request = native_request_wait(-1); + } catch (TimeoutException e) { + // Does not happen, infinite timeout + } + if (request != null) { - request.dequeue(); + request.dequeue( + mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O); } return request; } @@ -290,24 +306,25 @@ public class UsbDeviceConnection { * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process * the result of this function.

*

Android processes {@link UsbRequest UsbRequests} asynchronously. Hence it is not - * guaranteed that {@link #requestWait(int) requestWait(0)} returns a request that has been + * guaranteed that {@link #requestWait(long) 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 + * @return a completed USB request, or {@code null} if an error 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)} + * @throws BufferOverflowException 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)} + * @throws TimeoutException if no request was received in {@code timeout} milliseconds. */ - public UsbRequest requestWait(int timeout) { + public UsbRequest requestWait(long timeout) throws TimeoutException { timeout = Preconditions.checkArgumentNonnegative(timeout, "timeout"); UsbRequest request = native_request_wait(timeout); if (request != null) { - request.dequeue(); + request.dequeue(true); } return request; } @@ -352,7 +369,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(int timeout); + private native UsbRequest native_request_wait(long timeout) throws TimeoutException; private native String native_get_serial(); private native boolean native_reset_device(); } diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java index 3ff71ffb90fde..2e8f8e12b5087 100644 --- a/core/java/android/hardware/usb/UsbRequest.java +++ b/core/java/android/hardware/usb/UsbRequest.java @@ -17,11 +17,13 @@ package android.hardware.usb; import android.annotation.Nullable; +import android.util.Log; import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; /** @@ -279,7 +281,7 @@ public class UsbRequest { return wasQueued; } - /* package */ void dequeue() { + /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) { boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); int bytesTransferred; @@ -316,7 +318,18 @@ public class UsbRequest { bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend); } if (bytesTransferred >= 0) { - mBuffer.position(Math.min(bytesTransferred, mLength)); + int bytesToStore = Math.min(bytesTransferred, mLength); + try { + mBuffer.position(bytesToStore); + } catch (IllegalArgumentException e) { + if (useBufferOverflowInsteadOfIllegalArg) { + Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read " + + bytesToStore + " bytes", e); + throw new BufferOverflowException(); + } else { + throw e; + } + } } } diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp index 6814506ad7974..ba08bce7d17b7 100644 --- a/core/jni/android_hardware_UsbDeviceConnection.cpp +++ b/core/jni/android_hardware_UsbDeviceConnection.cpp @@ -220,7 +220,7 @@ android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz, } static jobject -android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jint timeoutMillis) +android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jlong timeoutMillis) { struct usb_device* device = get_device_from_object(env, thiz); if (!device) { @@ -243,8 +243,17 @@ android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jin - currentTime).count()); int error = errno; + if (request != NULL) { + break; + } + currentTime = steady_clock::now(); - if (request != NULL || error != EAGAIN || currentTime >= endTime) { + if (currentTime >= endTime) { + jniThrowException(env, "java/util/concurrent/TimeoutException", ""); + break; + } + + if (error != EAGAIN) { break; } }; @@ -300,7 +309,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", "(I)Landroid/hardware/usb/UsbRequest;", + {"native_request_wait", "(J)Landroid/hardware/usb/UsbRequest;", (void *)android_hardware_UsbDeviceConnection_request_wait}, { "native_get_serial", "()Ljava/lang/String;", (void*)android_hardware_UsbDeviceConnection_get_serial },