1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.usb; 18 19 import android.annotation.Nullable; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.os.Build; 22 import android.util.Log; 23 24 import com.android.internal.util.Preconditions; 25 26 import dalvik.system.CloseGuard; 27 28 import java.nio.BufferOverflowException; 29 import java.nio.ByteBuffer; 30 import java.util.Objects; 31 32 /** 33 * A class representing USB request packet. 34 * This can be used for both reading and writing data to or from a 35 * {@link android.hardware.usb.UsbDeviceConnection}. 36 * UsbRequests can be used to transfer data on bulk and interrupt endpoints. 37 * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer} 38 * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}. 39 * Requests on interrupt endpoints are only send and received asynchronously. 40 * 41 * <p>Requests on endpoint zero are not supported by this class; 42 * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead. 43 */ 44 public class UsbRequest { 45 46 private static final String TAG = "UsbRequest"; 47 48 // From drivers/usb/core/devio.c 49 static final int MAX_USBFS_BUFFER_SIZE = 16384; 50 51 // used by the JNI code 52 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 53 private long mNativeContext; 54 55 private UsbEndpoint mEndpoint; 56 57 /** The buffer that is currently being read / written */ 58 @UnsupportedAppUsage 59 private ByteBuffer mBuffer; 60 61 /** The amount of data to read / write when using {@link #queue} */ 62 @UnsupportedAppUsage 63 private int mLength; 64 65 // for client use 66 private Object mClientData; 67 68 // Prevent the connection from being finalized 69 private UsbDeviceConnection mConnection; 70 71 /** 72 * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or 73 * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. 74 */ 75 private boolean mIsUsingNewQueue; 76 77 /** Temporary buffer than might be used while buffer is enqueued */ 78 private ByteBuffer mTempBuffer; 79 80 private final CloseGuard mCloseGuard = CloseGuard.get(); 81 82 /** 83 * Lock for queue, enqueue and dequeue, so a queue operation can be finished by a dequeue 84 * operation on a different thread. 85 */ 86 private final Object mLock = new Object(); 87 UsbRequest()88 public UsbRequest() { 89 } 90 91 /** 92 * Initializes the request so it can read or write data on the given endpoint. 93 * Whether the request allows reading or writing depends on the direction of the endpoint. 94 * 95 * @param endpoint the endpoint to be used for this request. 96 * @return true if the request was successfully opened. 97 */ initialize(UsbDeviceConnection connection, UsbEndpoint endpoint)98 public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) { 99 mEndpoint = endpoint; 100 mConnection = Objects.requireNonNull(connection, "connection"); 101 102 boolean wasInitialized = native_init(connection, endpoint.getAddress(), 103 endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval()); 104 105 if (wasInitialized) { 106 mCloseGuard.open("UsbRequest.close"); 107 } 108 109 return wasInitialized; 110 } 111 112 /** 113 * Releases all resources related to this request. 114 */ close()115 public void close() { 116 synchronized (mLock) { 117 if (mNativeContext != 0) { 118 mEndpoint = null; 119 mConnection = null; 120 native_close(); 121 mCloseGuard.close(); 122 } 123 } 124 } 125 126 @Override finalize()127 protected void finalize() throws Throwable { 128 try { 129 if (mCloseGuard != null) { 130 mCloseGuard.warnIfOpen(); 131 } 132 133 close(); 134 } finally { 135 super.finalize(); 136 } 137 } 138 139 /** 140 * Returns the endpoint for the request, or null if the request is not opened. 141 * 142 * @return the request's endpoint 143 */ getEndpoint()144 public UsbEndpoint getEndpoint() { 145 return mEndpoint; 146 } 147 148 /** 149 * Returns the client data for the request. 150 * This can be used in conjunction with {@link #setClientData} 151 * to associate another object with this request, which can be useful for 152 * maintaining state between calls to {@link #queue} and 153 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 154 * 155 * @return the client data for the request 156 */ getClientData()157 public Object getClientData() { 158 return mClientData; 159 } 160 161 /** 162 * Sets the client data for the request. 163 * This can be used in conjunction with {@link #getClientData} 164 * to associate another object with this request, which can be useful for 165 * maintaining state between calls to {@link #queue} and 166 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 167 * 168 * @param data the client data for the request 169 */ setClientData(Object data)170 public void setClientData(Object data) { 171 mClientData = data; 172 } 173 174 /** 175 * Queues the request to send or receive data on its endpoint. 176 * <p>For OUT endpoints, the given buffer data will be sent on the endpoint. For IN endpoints, 177 * the endpoint will attempt to read the given number of bytes into the specified buffer. If the 178 * queueing operation is successful, return true. The result will be returned via 179 * {@link UsbDeviceConnection#requestWait}</p> 180 * 181 * @param buffer the buffer containing the bytes to write, or location to store the results of a 182 * read. Position and array offset will be ignored and assumed to be 0. Limit and 183 * capacity will be ignored. Once the request 184 * {@link UsbDeviceConnection#requestWait() is processed} the position will be set 185 * to the number of bytes read/written. 186 * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a 187 * value larger than 16384 bytes would be truncated down to 16384. In API 188 * {@value Build.VERSION_CODES#P} and after, any value of length is valid. 189 * 190 * @return true if the queueing operation succeeded 191 * 192 * @deprecated Use {@link #queue(ByteBuffer)} instead. 193 */ 194 @Deprecated queue(ByteBuffer buffer, int length)195 public boolean queue(ByteBuffer buffer, int length) { 196 UsbDeviceConnection connection = mConnection; 197 if (connection == null) { 198 // The expected exception by CTS Verifier - USB Device test 199 throw new NullPointerException("invalid connection"); 200 } 201 202 // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent 203 // the connection being closed while queueing. 204 return connection.queueRequest(this, buffer, length); 205 } 206 207 /** 208 * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over 209 * there, to prevent the connection being closed while queueing. 210 */ queueIfConnectionOpen(ByteBuffer buffer, int length)211 /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) { 212 UsbDeviceConnection connection = mConnection; 213 if (connection == null || !connection.isOpen()) { 214 // The expected exception by CTS Verifier - USB Device test 215 throw new NullPointerException("invalid connection"); 216 } 217 218 boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 219 boolean result; 220 221 if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P 222 && length > MAX_USBFS_BUFFER_SIZE) { 223 length = MAX_USBFS_BUFFER_SIZE; 224 } 225 226 synchronized (mLock) { 227 // save our buffer for when the request has completed 228 mBuffer = buffer; 229 mLength = length; 230 231 // Note: On a buffer slice we lost the capacity information about the underlying buffer, 232 // hence we cannot check if the access would be a data leak/memory corruption. 233 234 if (buffer.isDirect()) { 235 result = native_queue_direct(buffer, length, out); 236 } else if (buffer.hasArray()) { 237 result = native_queue_array(buffer.array(), length, out); 238 } else { 239 throw new IllegalArgumentException("buffer is not direct and has no array"); 240 } 241 if (!result) { 242 mBuffer = null; 243 mLength = 0; 244 } 245 } 246 247 return result; 248 } 249 250 /** 251 * Queues the request to send or receive data on its endpoint. 252 * 253 * <p>For OUT endpoints, the remaining bytes of the buffer will be sent on the endpoint. For IN 254 * endpoints, the endpoint will attempt to fill the remaining bytes of the buffer. If the 255 * queueing operation is successful, return true. The result will be returned via 256 * {@link UsbDeviceConnection#requestWait}</p> 257 * 258 * @param buffer the buffer containing the bytes to send, or the buffer to fill. The state 259 * of the buffer is undefined until the request is returned by 260 * {@link UsbDeviceConnection#requestWait}. If the request failed the buffer 261 * will be unchanged; if the request succeeded the position of the buffer is 262 * incremented by the number of bytes sent/received. Before 263 * {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes 264 * would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P} 265 * and after, any size buffer is valid. 266 * 267 * @return true if the queueing operation succeeded 268 */ queue(@ullable ByteBuffer buffer)269 public boolean queue(@Nullable ByteBuffer buffer) { 270 UsbDeviceConnection connection = mConnection; 271 if (connection == null) { 272 // The expected exception by CTS Verifier - USB Device test 273 throw new IllegalStateException("invalid connection"); 274 } 275 276 // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent 277 // the connection being closed while queueing. 278 return connection.queueRequest(this, buffer); 279 } 280 281 /** 282 * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over 283 * there, to prevent the connection being closed while queueing. 284 */ queueIfConnectionOpen(@ullable ByteBuffer buffer)285 /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) { 286 UsbDeviceConnection connection = mConnection; 287 if (connection == null || !connection.isOpen()) { 288 // The expected exception by CTS Verifier - USB Device test 289 throw new IllegalStateException("invalid connection"); 290 } 291 292 // Request need to be initialized 293 Preconditions.checkState(mNativeContext != 0, "request is not initialized"); 294 295 // Request can not be currently queued 296 Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); 297 298 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 299 boolean wasQueued; 300 301 synchronized (mLock) { 302 mBuffer = buffer; 303 304 if (buffer == null) { 305 // Null buffers enqueue empty USB requests which is supported 306 mIsUsingNewQueue = true; 307 wasQueued = native_queue(null, 0, 0); 308 } else { 309 if (connection.getContext().getApplicationInfo().targetSdkVersion 310 < Build.VERSION_CODES.P) { 311 // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once 312 Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, 313 "number of remaining bytes"); 314 } 315 316 // Can not receive into read-only buffers. 317 Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be " 318 + "read-only when receiving data"); 319 320 if (!buffer.isDirect()) { 321 mTempBuffer = ByteBuffer.allocateDirect(mBuffer.remaining()); 322 323 if (isSend) { 324 // Copy buffer into temporary buffer 325 mBuffer.mark(); 326 mTempBuffer.put(mBuffer); 327 mTempBuffer.flip(); 328 mBuffer.reset(); 329 } 330 331 // Send/Receive into the temp buffer instead 332 buffer = mTempBuffer; 333 } 334 335 mIsUsingNewQueue = true; 336 wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); 337 } 338 } 339 340 if (!wasQueued) { 341 mIsUsingNewQueue = false; 342 mTempBuffer = null; 343 mBuffer = null; 344 } 345 346 return wasQueued; 347 } 348 dequeue(boolean useBufferOverflowInsteadOfIllegalArg)349 /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) { 350 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 351 int bytesTransferred; 352 353 synchronized (mLock) { 354 if (mIsUsingNewQueue) { 355 bytesTransferred = native_dequeue_direct(); 356 mIsUsingNewQueue = false; 357 358 if (mBuffer == null) { 359 // Nothing to do 360 } else if (mTempBuffer == null) { 361 mBuffer.position(mBuffer.position() + bytesTransferred); 362 } else { 363 mTempBuffer.limit(bytesTransferred); 364 365 // The user might have modified mBuffer which might make put/position fail. 366 // Changing the buffer while a request is in flight is not supported. Still, 367 // make sure to free mTempBuffer correctly. 368 try { 369 if (isSend) { 370 mBuffer.position(mBuffer.position() + bytesTransferred); 371 } else { 372 // Copy temp buffer back into original buffer 373 mBuffer.put(mTempBuffer); 374 } 375 } finally { 376 mTempBuffer = null; 377 } 378 } 379 } else { 380 if (mBuffer.isDirect()) { 381 bytesTransferred = native_dequeue_direct(); 382 } else { 383 bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend); 384 } 385 if (bytesTransferred >= 0) { 386 int bytesToStore = Math.min(bytesTransferred, mLength); 387 try { 388 mBuffer.position(bytesToStore); 389 } catch (IllegalArgumentException e) { 390 if (useBufferOverflowInsteadOfIllegalArg) { 391 Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read " 392 + bytesToStore + " bytes", e); 393 throw new BufferOverflowException(); 394 } else { 395 throw e; 396 } 397 } 398 } 399 } 400 401 mBuffer = null; 402 mLength = 0; 403 } 404 } 405 406 /** 407 * Cancels a pending queue operation. 408 * 409 * @return true if cancelling succeeded 410 */ cancel()411 public boolean cancel() { 412 UsbDeviceConnection connection = mConnection; 413 if (connection == null) { 414 return false; 415 } 416 417 return connection.cancelRequest(this); 418 } 419 420 /** 421 * Cancels a pending queue operation (for use when the UsbDeviceConnection associated 422 * with this request is synchronized). This ensures we don't have a race where the 423 * device is closed and then the request is canceled which would lead to a 424 * use-after-free because the cancel operation uses the device connection 425 * information freed in the when UsbDeviceConnection is closed.<br/> 426 * 427 * This method assumes the connected is not closed while this method is executed. 428 * 429 * @return true if cancelling succeeded. 430 */ cancelIfOpen()431 /* package */ boolean cancelIfOpen() { 432 UsbDeviceConnection connection = mConnection; 433 if (mNativeContext == 0 || (connection != null && !connection.isOpen())) { 434 Log.w(TAG, 435 "Detected attempt to cancel a request on a connection which isn't open"); 436 return false; 437 } 438 return native_cancel(); 439 } 440 native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval)441 private native boolean native_init(UsbDeviceConnection connection, int ep_address, 442 int ep_attributes, int ep_max_packet_size, int ep_interval); native_close()443 private native void native_close(); native_queue(ByteBuffer buffer, int offset, int length)444 private native boolean native_queue(ByteBuffer buffer, int offset, int length); native_queue_array(byte[] buffer, int length, boolean out)445 private native boolean native_queue_array(byte[] buffer, int length, boolean out); native_dequeue_array(byte[] buffer, int length, boolean out)446 private native int native_dequeue_array(byte[] buffer, int length, boolean out); native_queue_direct(ByteBuffer buffer, int length, boolean out)447 private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); native_dequeue_direct()448 private native int native_dequeue_direct(); native_cancel()449 private native boolean native_cancel(); 450 } 451