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 31 /** 32 * A class representing USB request packet. 33 * This can be used for both reading and writing data to or from a 34 * {@link android.hardware.usb.UsbDeviceConnection}. 35 * UsbRequests can be used to transfer data on bulk and interrupt endpoints. 36 * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer} 37 * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}. 38 * Requests on interrupt endpoints are only send and received asynchronously. 39 * 40 * <p>Requests on endpoint zero are not supported by this class; 41 * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead. 42 */ 43 public class UsbRequest { 44 45 private static final String TAG = "UsbRequest"; 46 47 // From drivers/usb/core/devio.c 48 static final int MAX_USBFS_BUFFER_SIZE = 16384; 49 50 // used by the JNI code 51 @UnsupportedAppUsage 52 private long mNativeContext; 53 54 private UsbEndpoint mEndpoint; 55 56 /** The buffer that is currently being read / written */ 57 @UnsupportedAppUsage 58 private ByteBuffer mBuffer; 59 60 /** The amount of data to read / write when using {@link #queue} */ 61 @UnsupportedAppUsage 62 private int mLength; 63 64 // for client use 65 private Object mClientData; 66 67 // Prevent the connection from being finalized 68 private UsbDeviceConnection mConnection; 69 70 /** 71 * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or 72 * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}. 73 */ 74 private boolean mIsUsingNewQueue; 75 76 /** Temporary buffer than might be used while buffer is enqueued */ 77 private ByteBuffer mTempBuffer; 78 79 private final CloseGuard mCloseGuard = CloseGuard.get(); 80 81 /** 82 * Lock for queue, enqueue and dequeue, so a queue operation can be finished by a dequeue 83 * operation on a different thread. 84 */ 85 private final Object mLock = new Object(); 86 UsbRequest()87 public UsbRequest() { 88 } 89 90 /** 91 * Initializes the request so it can read or write data on the given endpoint. 92 * Whether the request allows reading or writing depends on the direction of the endpoint. 93 * 94 * @param endpoint the endpoint to be used for this request. 95 * @return true if the request was successfully opened. 96 */ initialize(UsbDeviceConnection connection, UsbEndpoint endpoint)97 public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) { 98 mEndpoint = endpoint; 99 mConnection = Preconditions.checkNotNull(connection, "connection"); 100 101 boolean wasInitialized = native_init(connection, endpoint.getAddress(), 102 endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval()); 103 104 if (wasInitialized) { 105 mCloseGuard.open("close"); 106 } 107 108 return wasInitialized; 109 } 110 111 /** 112 * Releases all resources related to this request. 113 */ close()114 public void close() { 115 if (mNativeContext != 0) { 116 mEndpoint = null; 117 mConnection = null; 118 native_close(); 119 mCloseGuard.close(); 120 } 121 } 122 123 @Override finalize()124 protected void finalize() throws Throwable { 125 try { 126 if (mCloseGuard != null) { 127 mCloseGuard.warnIfOpen(); 128 } 129 130 close(); 131 } finally { 132 super.finalize(); 133 } 134 } 135 136 /** 137 * Returns the endpoint for the request, or null if the request is not opened. 138 * 139 * @return the request's endpoint 140 */ getEndpoint()141 public UsbEndpoint getEndpoint() { 142 return mEndpoint; 143 } 144 145 /** 146 * Returns the client data for the request. 147 * This can be used in conjunction with {@link #setClientData} 148 * to associate another object with this request, which can be useful for 149 * maintaining state between calls to {@link #queue} and 150 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 151 * 152 * @return the client data for the request 153 */ getClientData()154 public Object getClientData() { 155 return mClientData; 156 } 157 158 /** 159 * Sets the client data for the request. 160 * This can be used in conjunction with {@link #getClientData} 161 * to associate another object with this request, which can be useful for 162 * maintaining state between calls to {@link #queue} and 163 * {@link android.hardware.usb.UsbDeviceConnection#requestWait} 164 * 165 * @param data the client data for the request 166 */ setClientData(Object data)167 public void setClientData(Object data) { 168 mClientData = data; 169 } 170 171 /** 172 * Queues the request to send or receive data on its endpoint. 173 * <p>For OUT endpoints, the given buffer data will be sent on the endpoint. For IN endpoints, 174 * the endpoint will attempt to read the given number of bytes into the specified buffer. If the 175 * queueing operation is successful, return true. The result will be returned via 176 * {@link UsbDeviceConnection#requestWait}</p> 177 * 178 * @param buffer the buffer containing the bytes to write, or location to store the results of a 179 * read. Position and array offset will be ignored and assumed to be 0. Limit and 180 * capacity will be ignored. Once the request 181 * {@link UsbDeviceConnection#requestWait() is processed} the position will be set 182 * to the number of bytes read/written. 183 * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a 184 * value larger than 16384 bytes would be truncated down to 16384. In API 185 * {@value Build.VERSION_CODES#P} and after, any value of length is valid. 186 * 187 * @return true if the queueing operation succeeded 188 * 189 * @deprecated Use {@link #queue(ByteBuffer)} instead. 190 */ 191 @Deprecated queue(ByteBuffer buffer, int length)192 public boolean queue(ByteBuffer buffer, int length) { 193 boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 194 boolean result; 195 196 if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P 197 && length > MAX_USBFS_BUFFER_SIZE) { 198 length = MAX_USBFS_BUFFER_SIZE; 199 } 200 201 synchronized (mLock) { 202 // save our buffer for when the request has completed 203 mBuffer = buffer; 204 mLength = length; 205 206 // Note: On a buffer slice we lost the capacity information about the underlying buffer, 207 // hence we cannot check if the access would be a data leak/memory corruption. 208 209 if (buffer.isDirect()) { 210 result = native_queue_direct(buffer, length, out); 211 } else if (buffer.hasArray()) { 212 result = native_queue_array(buffer.array(), length, out); 213 } else { 214 throw new IllegalArgumentException("buffer is not direct and has no array"); 215 } 216 if (!result) { 217 mBuffer = null; 218 mLength = 0; 219 } 220 } 221 222 return result; 223 } 224 225 /** 226 * Queues the request to send or receive data on its endpoint. 227 * 228 * <p>For OUT endpoints, the remaining bytes of the buffer will be sent on the endpoint. For IN 229 * endpoints, the endpoint will attempt to fill the remaining bytes of the buffer. If the 230 * queueing operation is successful, return true. The result will be returned via 231 * {@link UsbDeviceConnection#requestWait}</p> 232 * 233 * @param buffer the buffer containing the bytes to send, or the buffer to fill. The state 234 * of the buffer is undefined until the request is returned by 235 * {@link UsbDeviceConnection#requestWait}. If the request failed the buffer 236 * will be unchanged; if the request succeeded the position of the buffer is 237 * incremented by the number of bytes sent/received. Before 238 * {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes 239 * would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P} 240 * and after, any size buffer is valid. 241 * 242 * @return true if the queueing operation succeeded 243 */ queue(@ullable ByteBuffer buffer)244 public boolean queue(@Nullable ByteBuffer buffer) { 245 // Request need to be initialized 246 Preconditions.checkState(mNativeContext != 0, "request is not initialized"); 247 248 // Request can not be currently queued 249 Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued"); 250 251 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 252 boolean wasQueued; 253 254 synchronized (mLock) { 255 mBuffer = buffer; 256 257 if (buffer == null) { 258 // Null buffers enqueue empty USB requests which is supported 259 mIsUsingNewQueue = true; 260 wasQueued = native_queue(null, 0, 0); 261 } else { 262 if (mConnection.getContext().getApplicationInfo().targetSdkVersion 263 < Build.VERSION_CODES.P) { 264 // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once 265 Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE, 266 "number of remaining bytes"); 267 } 268 269 // Can not receive into read-only buffers. 270 Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be " 271 + "read-only when receiving data"); 272 273 if (!buffer.isDirect()) { 274 mTempBuffer = ByteBuffer.allocateDirect(mBuffer.remaining()); 275 276 if (isSend) { 277 // Copy buffer into temporary buffer 278 mBuffer.mark(); 279 mTempBuffer.put(mBuffer); 280 mTempBuffer.flip(); 281 mBuffer.reset(); 282 } 283 284 // Send/Receive into the temp buffer instead 285 buffer = mTempBuffer; 286 } 287 288 mIsUsingNewQueue = true; 289 wasQueued = native_queue(buffer, buffer.position(), buffer.remaining()); 290 } 291 } 292 293 if (!wasQueued) { 294 mIsUsingNewQueue = false; 295 mTempBuffer = null; 296 mBuffer = null; 297 } 298 299 return wasQueued; 300 } 301 dequeue(boolean useBufferOverflowInsteadOfIllegalArg)302 /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) { 303 boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT); 304 int bytesTransferred; 305 306 synchronized (mLock) { 307 if (mIsUsingNewQueue) { 308 bytesTransferred = native_dequeue_direct(); 309 mIsUsingNewQueue = false; 310 311 if (mBuffer == null) { 312 // Nothing to do 313 } else if (mTempBuffer == null) { 314 mBuffer.position(mBuffer.position() + bytesTransferred); 315 } else { 316 mTempBuffer.limit(bytesTransferred); 317 318 // The user might have modified mBuffer which might make put/position fail. 319 // Changing the buffer while a request is in flight is not supported. Still, 320 // make sure to free mTempBuffer correctly. 321 try { 322 if (isSend) { 323 mBuffer.position(mBuffer.position() + bytesTransferred); 324 } else { 325 // Copy temp buffer back into original buffer 326 mBuffer.put(mTempBuffer); 327 } 328 } finally { 329 mTempBuffer = null; 330 } 331 } 332 } else { 333 if (mBuffer.isDirect()) { 334 bytesTransferred = native_dequeue_direct(); 335 } else { 336 bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend); 337 } 338 if (bytesTransferred >= 0) { 339 int bytesToStore = Math.min(bytesTransferred, mLength); 340 try { 341 mBuffer.position(bytesToStore); 342 } catch (IllegalArgumentException e) { 343 if (useBufferOverflowInsteadOfIllegalArg) { 344 Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read " 345 + bytesToStore + " bytes", e); 346 throw new BufferOverflowException(); 347 } else { 348 throw e; 349 } 350 } 351 } 352 } 353 354 mBuffer = null; 355 mLength = 0; 356 } 357 } 358 359 /** 360 * Cancels a pending queue operation. 361 * 362 * @return true if cancelling succeeded 363 */ cancel()364 public boolean cancel() { 365 return native_cancel(); 366 } 367 native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval)368 private native boolean native_init(UsbDeviceConnection connection, int ep_address, 369 int ep_attributes, int ep_max_packet_size, int ep_interval); native_close()370 private native void native_close(); native_queue(ByteBuffer buffer, int offset, int length)371 private native boolean native_queue(ByteBuffer buffer, int offset, int length); native_queue_array(byte[] buffer, int length, boolean out)372 private native boolean native_queue_array(byte[] buffer, int length, boolean out); native_dequeue_array(byte[] buffer, int length, boolean out)373 private native int native_dequeue_array(byte[] buffer, int length, boolean out); native_queue_direct(ByteBuffer buffer, int length, boolean out)374 private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out); native_dequeue_direct()375 private native int native_dequeue_direct(); native_cancel()376 private native boolean native_cancel(); 377 } 378