1 /* 2 * Copyright (C) 2011 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.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.annotation.SystemApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.Context; 25 import android.os.Build; 26 import android.os.ParcelFileDescriptor; 27 28 import com.android.internal.util.Preconditions; 29 30 import dalvik.system.CloseGuard; 31 32 import java.io.FileDescriptor; 33 import java.nio.BufferOverflowException; 34 import java.nio.ByteBuffer; 35 import java.util.concurrent.TimeoutException; 36 37 /** 38 * This class is used for sending and receiving data and control messages to a USB device. 39 * Instances of this class are created by {@link UsbManager#openDevice}. 40 */ 41 public class UsbDeviceConnection { 42 43 private static final String TAG = "UsbDeviceConnection"; 44 45 private final UsbDevice mDevice; 46 47 private Context mContext; 48 49 // used by the JNI code 50 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 51 private long mNativeContext; 52 53 private final CloseGuard mCloseGuard = CloseGuard.get(); 54 55 private final Object mLock = new Object(); 56 57 /** 58 * UsbDevice should only be instantiated by UsbService implementation 59 * @hide 60 */ UsbDeviceConnection(UsbDevice device)61 public UsbDeviceConnection(UsbDevice device) { 62 mDevice = device; 63 } 64 open(String name, ParcelFileDescriptor pfd, @NonNull Context context)65 /* package */ boolean open(String name, ParcelFileDescriptor pfd, @NonNull Context context) { 66 mContext = context.getApplicationContext(); 67 68 synchronized (mLock) { 69 boolean wasOpened = native_open(name, pfd.getFileDescriptor()); 70 71 if (wasOpened) { 72 mCloseGuard.open("UsbDeviceConnection.close"); 73 } 74 75 return wasOpened; 76 } 77 } 78 79 /*** 80 * @return If this connection is currently open and usable. 81 */ isOpen()82 boolean isOpen() { 83 return mNativeContext != 0; 84 } 85 86 /** 87 * @return The application context the connection was created for. 88 * 89 * @hide 90 */ getContext()91 public @Nullable Context getContext() { 92 return mContext; 93 } 94 95 /** 96 * Cancel a request which relates to this connection. 97 * 98 * @return true if the request was successfully cancelled. 99 */ cancelRequest(UsbRequest request)100 /* package */ boolean cancelRequest(UsbRequest request) { 101 synchronized (mLock) { 102 if (!isOpen()) { 103 return false; 104 } 105 106 return request.cancelIfOpen(); 107 } 108 } 109 110 /** 111 * This is meant to be called by UsbRequest's queue() in order to synchronize on 112 * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. 113 */ queueRequest(UsbRequest request, ByteBuffer buffer, int length)114 /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) { 115 synchronized (mLock) { 116 if (!isOpen()) { 117 return false; 118 } 119 120 return request.queueIfConnectionOpen(buffer, length); 121 } 122 } 123 124 /** 125 * This is meant to be called by UsbRequest's queue() in order to synchronize on 126 * UsbDeviceConnection's mLock to prevent the connection being closed while queueing. 127 */ queueRequest(UsbRequest request, @Nullable ByteBuffer buffer)128 /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) { 129 synchronized (mLock) { 130 if (!isOpen()) { 131 return false; 132 } 133 134 return request.queueIfConnectionOpen(buffer); 135 } 136 } 137 138 /** 139 * Releases all system resources related to the device. 140 * Once the object is closed it cannot be used again. 141 * The client must call {@link UsbManager#openDevice} again 142 * to retrieve a new instance to reestablish communication with the device. 143 */ close()144 public void close() { 145 synchronized (mLock) { 146 if (isOpen()) { 147 native_close(); 148 mCloseGuard.close(); 149 } 150 } 151 } 152 153 /** 154 * Returns the native file descriptor for the device, or 155 * -1 if the device is not opened. 156 * This is intended for passing to native code to access the device. 157 * 158 * @return the native file descriptor 159 */ getFileDescriptor()160 public int getFileDescriptor() { 161 return native_get_fd(); 162 } 163 164 /** 165 * Returns the raw USB descriptors for the device. 166 * This can be used to access descriptors not supported directly 167 * via the higher level APIs. 168 * 169 * @return raw USB descriptors 170 */ getRawDescriptors()171 public byte[] getRawDescriptors() { 172 return native_get_desc(); 173 } 174 175 /** 176 * Claims exclusive access to a {@link android.hardware.usb.UsbInterface}. 177 * This must be done before sending or receiving data on any 178 * {@link android.hardware.usb.UsbEndpoint}s belonging to the interface. 179 * 180 * @param intf the interface to claim 181 * @param force true to disconnect kernel driver if necessary 182 * @return true if the interface was successfully claimed 183 */ claimInterface(UsbInterface intf, boolean force)184 public boolean claimInterface(UsbInterface intf, boolean force) { 185 return native_claim_interface(intf.getId(), force); 186 } 187 188 /** 189 * Releases exclusive access to a {@link android.hardware.usb.UsbInterface}. 190 * 191 * @return true if the interface was successfully released 192 */ releaseInterface(UsbInterface intf)193 public boolean releaseInterface(UsbInterface intf) { 194 return native_release_interface(intf.getId()); 195 } 196 197 /** 198 * Sets the current {@link android.hardware.usb.UsbInterface}. 199 * Used to select between two interfaces with the same ID but different alternate setting. 200 * 201 * @return true if the interface was successfully selected 202 */ setInterface(UsbInterface intf)203 public boolean setInterface(UsbInterface intf) { 204 return native_set_interface(intf.getId(), intf.getAlternateSetting()); 205 } 206 207 /** 208 * Sets the device's current {@link android.hardware.usb.UsbConfiguration}. 209 * 210 * @return true if the configuration was successfully set 211 */ setConfiguration(UsbConfiguration configuration)212 public boolean setConfiguration(UsbConfiguration configuration) { 213 return native_set_configuration(configuration.getId()); 214 } 215 216 /** 217 * Performs a control transaction on endpoint zero for this device. 218 * The direction of the transfer is determined by the request type. 219 * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is 220 * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, 221 * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer 222 * is a read. 223 * <p> 224 * This method transfers data starting from index 0 in the buffer. 225 * To specify a different offset, use 226 * {@link #controlTransfer(int, int, int, int, byte[], int, int, int)}. 227 * </p> 228 * 229 * @param requestType request type for this transaction 230 * @param request request ID for this transaction 231 * @param value value field for this transaction 232 * @param index index field for this transaction 233 * @param buffer buffer for data portion of transaction, 234 * or null if no data needs to be sent or received 235 * @param length the length of the data to send or receive 236 * @param timeout in milliseconds 237 * @return length of data transferred (or zero) for success, 238 * or negative value for failure 239 */ controlTransfer(int requestType, int request, int value, int index, @Nullable byte[] buffer, int length, int timeout)240 public int controlTransfer(int requestType, int request, int value, 241 int index, @Nullable byte[] buffer, int length, int timeout) { 242 return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout); 243 } 244 245 /** 246 * Performs a control transaction on endpoint zero for this device. 247 * The direction of the transfer is determined by the request type. 248 * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is 249 * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write, 250 * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer 251 * is a read. 252 * 253 * @param requestType request type for this transaction 254 * @param request request ID for this transaction 255 * @param value value field for this transaction 256 * @param index index field for this transaction 257 * @param buffer buffer for data portion of transaction, 258 * or null if no data needs to be sent or received 259 * @param offset the index of the first byte in the buffer to send or receive 260 * @param length the length of the data to send or receive 261 * @param timeout in milliseconds 262 * @return length of data transferred (or zero) for success, 263 * or negative value for failure 264 */ controlTransfer(int requestType, int request, int value, int index, @Nullable byte[] buffer, int offset, int length, int timeout)265 public int controlTransfer(int requestType, int request, int value, int index, 266 @Nullable byte[] buffer, int offset, int length, int timeout) { 267 checkBounds(buffer, offset, length); 268 return native_control_request(requestType, request, value, index, 269 buffer, offset, length, timeout); 270 } 271 272 /** 273 * Performs a bulk transaction on the given endpoint. 274 * The direction of the transfer is determined by the direction of the endpoint. 275 * <p> 276 * This method transfers data starting from index 0 in the buffer. 277 * To specify a different offset, use 278 * {@link #bulkTransfer(UsbEndpoint, byte[], int, int, int)}. 279 * </p> 280 * 281 * @param endpoint the endpoint for this transaction 282 * @param buffer buffer for data to send or receive; can be {@code null} to wait for next 283 * transaction without reading data 284 * @param length the length of the data to send or receive. Before 285 * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes 286 * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P} 287 * and after, any value of length is valid. 288 * @param timeout in milliseconds, 0 is infinite 289 * @return length of data transferred (or zero) for success, 290 * or negative value for failure 291 */ bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout)292 public int bulkTransfer(UsbEndpoint endpoint, 293 byte[] buffer, int length, int timeout) { 294 return bulkTransfer(endpoint, buffer, 0, length, timeout); 295 } 296 297 /** 298 * Performs a bulk transaction on the given endpoint. 299 * The direction of the transfer is determined by the direction of the endpoint. 300 * 301 * @param endpoint the endpoint for this transaction 302 * @param buffer buffer for data to send or receive 303 * @param offset the index of the first byte in the buffer to send or receive 304 * @param length the length of the data to send or receive. Before 305 * {@value Build.VERSION_CODES#P}, a value larger than 16384 bytes 306 * would be truncated down to 16384. In API {@value Build.VERSION_CODES#P} 307 * and after, any value of length is valid. 308 * @param timeout in milliseconds, 0 is infinite 309 * @return length of data transferred (or zero) for success, 310 * or negative value for failure 311 */ bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout)312 public int bulkTransfer(UsbEndpoint endpoint, 313 byte[] buffer, int offset, int length, int timeout) { 314 checkBounds(buffer, offset, length); 315 if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P 316 && length > UsbRequest.MAX_USBFS_BUFFER_SIZE) { 317 length = UsbRequest.MAX_USBFS_BUFFER_SIZE; 318 } 319 return native_bulk_request(endpoint.getAddress(), buffer, offset, length, timeout); 320 } 321 322 /** 323 * Reset USB port for the connected device. 324 * 325 * @return true if reset succeeds. 326 * 327 * @hide 328 */ 329 @SystemApi 330 @SuppressLint("RequiresPermission") resetDevice()331 public boolean resetDevice() { 332 return native_reset_device(); 333 } 334 335 /** 336 * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation 337 * <p>Note that this may return requests queued on multiple 338 * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use, 339 * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link 340 * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process 341 * the result of this function.</p> 342 * 343 * @return a completed USB request, or null if an error occurred 344 * 345 * @throws IllegalArgumentException Before API {@value Build.VERSION_CODES#O}: if the number of 346 * bytes read or written is more than the limit of the 347 * request's buffer. The number of bytes is determined by the 348 * {@code length} parameter of 349 * {@link UsbRequest#queue(ByteBuffer, int)} 350 * @throws BufferOverflowException In API {@value Build.VERSION_CODES#O} and after: if the 351 * number of bytes read or written is more than the limit of the 352 * request's buffer. The number of bytes is determined by the 353 * {@code length} parameter of 354 * {@link UsbRequest#queue(ByteBuffer, int)} 355 */ requestWait()356 public UsbRequest requestWait() { 357 UsbRequest request = null; 358 try { 359 // -1 is special value indicating infinite wait 360 request = native_request_wait(-1); 361 } catch (TimeoutException e) { 362 // Does not happen, infinite timeout 363 } 364 365 if (request != null) { 366 request.dequeue( 367 mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O); 368 } 369 return request; 370 } 371 372 /** 373 * Waits for the result of a {@link android.hardware.usb.UsbRequest#queue} operation 374 * <p>Note that this may return requests queued on multiple 375 * {@link android.hardware.usb.UsbEndpoint}s. When multiple endpoints are in use, 376 * {@link android.hardware.usb.UsbRequest#getEndpoint} and {@link 377 * android.hardware.usb.UsbRequest#getClientData} can be useful in determining how to process 378 * the result of this function.</p> 379 * <p>Android processes {@link UsbRequest UsbRequests} asynchronously. Hence it is not 380 * guaranteed that {@link #requestWait(long) requestWait(0)} returns a request that has been 381 * queued right before even if the request could have been processed immediately.</p> 382 * 383 * @param timeout timeout in milliseconds. If 0 this method does not wait. 384 * 385 * @return a completed USB request, or {@code null} if an error occurred 386 * 387 * @throws BufferOverflowException if the number of bytes read or written is more than the 388 * limit of the request's buffer. The number of bytes is 389 * determined by the {@code length} parameter of 390 * {@link UsbRequest#queue(ByteBuffer, int)} 391 * @throws TimeoutException if no request was received in {@code timeout} milliseconds. 392 */ requestWait(long timeout)393 public UsbRequest requestWait(long timeout) throws TimeoutException { 394 timeout = Preconditions.checkArgumentNonnegative(timeout, "timeout"); 395 396 UsbRequest request = native_request_wait(timeout); 397 if (request != null) { 398 request.dequeue(true); 399 } 400 return request; 401 } 402 403 /** 404 * Returns the serial number for the device. 405 * This will return null if the device has not been opened. 406 * 407 * @return the device serial number 408 */ getSerial()409 public String getSerial() { 410 return native_get_serial(); 411 } 412 checkBounds(byte[] buffer, int start, int length)413 private static void checkBounds(byte[] buffer, int start, int length) { 414 final int bufferLength = (buffer != null ? buffer.length : 0); 415 if (length < 0 || start < 0 || start + length > bufferLength) { 416 throw new IllegalArgumentException("Buffer start or length out of bounds."); 417 } 418 } 419 420 @Override finalize()421 protected void finalize() throws Throwable { 422 try { 423 if (mCloseGuard != null) { 424 mCloseGuard.warnIfOpen(); 425 } 426 } finally { 427 super.finalize(); 428 } 429 } 430 native_open(String deviceName, FileDescriptor pfd)431 private native boolean native_open(String deviceName, FileDescriptor pfd); native_close()432 private native void native_close(); native_get_fd()433 private native int native_get_fd(); native_get_desc()434 private native byte[] native_get_desc(); native_claim_interface(int interfaceID, boolean force)435 private native boolean native_claim_interface(int interfaceID, boolean force); native_release_interface(int interfaceID)436 private native boolean native_release_interface(int interfaceID); native_set_interface(int interfaceID, int alternateSetting)437 private native boolean native_set_interface(int interfaceID, int alternateSetting); native_set_configuration(int configurationID)438 private native boolean native_set_configuration(int configurationID); native_control_request(int requestType, int request, int value, int index, byte[] buffer, int offset, int length, int timeout)439 private native int native_control_request(int requestType, int request, int value, 440 int index, byte[] buffer, int offset, int length, int timeout); native_bulk_request(int endpoint, byte[] buffer, int offset, int length, int timeout)441 private native int native_bulk_request(int endpoint, byte[] buffer, 442 int offset, int length, int timeout); native_request_wait(long timeout)443 private native UsbRequest native_request_wait(long timeout) throws TimeoutException; native_get_serial()444 private native String native_get_serial(); native_reset_device()445 private native boolean native_reset_device(); 446 } 447