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 IUsbNative mUsb; 34 private final byte[] mDescriptor = new byte[18]; 35 private Pointer mHandle; 36 UsbDevice(@onnull IUsbNative usb, @Nonnull Pointer devicePointer)37 UsbDevice(@Nonnull IUsbNative usb, @Nonnull Pointer devicePointer) { 38 mUsb = usb; 39 40 // retrieve device descriptor 41 mUsb.libusb_get_device_descriptor(devicePointer, mDescriptor); 42 43 // obtain device handle 44 PointerByReference handle = new PointerByReference(); 45 mUsb.libusb_open(devicePointer, handle); 46 mHandle = handle.getValue(); 47 } 48 49 /** 50 * Performs a synchronous control transaction with unlimited timeout. 51 * 52 * @return number of bytes transferred, or an error code 53 */ controlTransfer(byte requestType, byte request, int value, int index, byte[] data)54 public int controlTransfer(byte requestType, byte request, int value, int index, byte[] data) { 55 return mUsb.libusb_control_transfer( 56 checkNotNull(mHandle), 57 requestType, 58 request, 59 (short) value, 60 (short) index, 61 data, 62 (short) data.length, 63 0); 64 } 65 66 /** 67 * Performs a USB port reset. A LIBUSB_ERROR_NOT_FOUND error may indicate that the connection 68 * was reset, but that this {@link UsbDevice} is no longer valid and needs to be recreated. 69 * 70 * @return 0 on success or error code 71 */ reset()72 public int reset() { 73 return mUsb.libusb_reset_device(checkNotNull(mHandle)); 74 } 75 76 /** @return true if device handle is non-null, but does not check if resetting is necessary */ isValid()77 public boolean isValid() { 78 return mHandle != null; 79 } 80 81 /** @return device's serial number or {@code null} if serial could not be determined */ 82 @Nullable getSerialNumber()83 public String getSerialNumber() { 84 if (!isValid() || mDescriptor[16] <= 0) { 85 // no device handle or string index is invalid 86 return null; 87 } 88 89 byte[] data = new byte[64]; 90 int length = mUsb.libusb_get_string_descriptor_ascii(mHandle, mDescriptor[16], data, 64); 91 return length > 0 ? new String(data, 0, length) : null; 92 } 93 94 /** @return device's vendor ID */ getVendorId()95 public int getVendorId() { 96 return Shorts.fromBytes(mDescriptor[9], mDescriptor[8]); 97 } 98 99 /** @return device's product ID */ getProductId()100 public int getProductId() { 101 return Shorts.fromBytes(mDescriptor[11], mDescriptor[10]); 102 } 103 104 /** @return true if device is AOAv2-compatible */ isAoaCompatible()105 public boolean isAoaCompatible() { 106 return isValid() && controlTransfer(INPUT, ACCESSORY_GET_PROTOCOL, 0, 0, new byte[2]) >= 2; 107 } 108 109 /** Close the connection if necessary. */ 110 @Override close()111 public void close() { 112 if (isValid()) { 113 mUsb.libusb_close(mHandle); 114 mHandle = null; 115 } 116 } 117 } 118