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