1 /* 2 * Copyright (C) 2018 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 package com.android.helper.aoa; 17 18 import static com.android.helper.aoa.AoaDevice.ACCESSORY_GET_PROTOCOL; 19 import static com.android.helper.aoa.AoaDevice.INPUT; 20 21 import static com.google.common.base.Preconditions.checkNotNull; 22 23 import com.google.common.primitives.Shorts; 24 import com.sun.jna.Pointer; 25 import com.sun.jna.ptr.PointerByReference; 26 27 import javax.annotation.Nonnull; 28 import javax.annotation.Nullable; 29 30 /** Connected USB device. */ 31 public class UsbDevice implements AutoCloseable { 32 33 private final UsbHelper mHelper; 34 private final IUsbNative mUsb; 35 private final byte[] mDescriptor = new byte[18]; 36 private Pointer mHandle; 37 UsbDevice(@onnull UsbHelper helper, @Nonnull Pointer devicePointer)38 UsbDevice(@Nonnull UsbHelper helper, @Nonnull Pointer devicePointer) { 39 mHelper = helper; 40 mUsb = helper.getUsb(); 41 42 // retrieve device descriptor 43 mUsb.libusb_get_device_descriptor(devicePointer, mDescriptor); 44 45 // obtain device handle 46 PointerByReference handle = new PointerByReference(); 47 mUsb.libusb_open(devicePointer, handle); 48 mHandle = handle.getValue(); 49 } 50 51 /** 52 * Performs a synchronous control transaction with the default timeout. 53 * 54 * @return number of bytes transferred, or an error code 55 */ controlTransfer(byte requestType, byte request, int value, int index, byte[] data)56 public int controlTransfer(byte requestType, byte request, int value, int index, byte[] data) { 57 int timeout = (int) mHelper.getTransferTimeout().toMillis(); 58 return controlTransfer(requestType, request, value, index, data, timeout); 59 } 60 61 /** 62 * Performs a synchronous control transaction. 63 * 64 * @return number of bytes transferred, or an error code 65 */ controlTransfer( byte requestType, byte request, int value, int index, byte[] data, int timeout)66 public int controlTransfer( 67 byte requestType, byte request, int value, int index, byte[] data, int timeout) { 68 return mUsb.libusb_control_transfer( 69 checkNotNull(mHandle), 70 requestType, 71 request, 72 (short) value, 73 (short) index, 74 data, 75 (short) data.length, 76 timeout); 77 } 78 79 /** 80 * Performs a USB port reset. A LIBUSB_ERROR_NOT_FOUND error may indicate that the connection 81 * was reset, but that this {@link UsbDevice} is no longer valid and needs to be recreated. 82 * 83 * @return 0 on success or error code 84 */ reset()85 public int reset() { 86 return mUsb.libusb_reset_device(checkNotNull(mHandle)); 87 } 88 89 /** @return true if device handle is non-null, but does not check if resetting is necessary */ isValid()90 public boolean isValid() { 91 return mHandle != null; 92 } 93 94 /** @return device's serial number or {@code null} if serial could not be determined */ 95 @Nullable getSerialNumber()96 public String getSerialNumber() { 97 if (!isValid() || mDescriptor[16] <= 0) { 98 // no device handle or string index is invalid 99 return null; 100 } 101 102 byte[] data = new byte[64]; 103 int length = mUsb.libusb_get_string_descriptor_ascii(mHandle, mDescriptor[16], data, 64); 104 return length > 0 ? new String(data, 0, length) : null; 105 } 106 107 /** @return device's vendor ID */ getVendorId()108 public int getVendorId() { 109 return Shorts.fromBytes(mDescriptor[9], mDescriptor[8]); 110 } 111 112 /** @return device's product ID */ getProductId()113 public int getProductId() { 114 return Shorts.fromBytes(mDescriptor[11], mDescriptor[10]); 115 } 116 117 /** @return true if device is AOAv2-compatible */ isAoaCompatible()118 public boolean isAoaCompatible() { 119 return isValid() && controlTransfer(INPUT, ACCESSORY_GET_PROTOCOL, 0, 0, new byte[2]) >= 2; 120 } 121 122 /** Close the connection if necessary. */ 123 @Override close()124 public void close() { 125 if (isValid()) { 126 mUsb.libusb_close(mHandle); 127 mHandle = null; 128 } 129 } 130 } 131