| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.helper.aoa; |
| |
| import static com.android.helper.aoa.AoaDevice.ACCESSORY_GET_PROTOCOL; |
| import static com.android.helper.aoa.AoaDevice.INPUT; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import com.google.common.primitives.Shorts; |
| import com.sun.jna.Pointer; |
| import com.sun.jna.ptr.PointerByReference; |
| |
| import javax.annotation.Nonnull; |
| import javax.annotation.Nullable; |
| |
| /** Connected USB device. */ |
| public class UsbDevice implements AutoCloseable { |
| |
| private final UsbHelper mHelper; |
| private final IUsbNative mUsb; |
| private final byte[] mDescriptor = new byte[18]; |
| private Pointer mHandle; |
| |
| UsbDevice(@Nonnull UsbHelper helper, @Nonnull Pointer devicePointer) { |
| mHelper = helper; |
| mUsb = helper.getUsb(); |
| |
| // retrieve device descriptor |
| mUsb.libusb_get_device_descriptor(devicePointer, mDescriptor); |
| |
| // obtain device handle |
| PointerByReference handle = new PointerByReference(); |
| mUsb.libusb_open(devicePointer, handle); |
| mHandle = handle.getValue(); |
| } |
| |
| /** |
| * Performs a synchronous control transaction with the default timeout. |
| * |
| * @return number of bytes transferred, or an error code |
| */ |
| public int controlTransfer(byte requestType, byte request, int value, int index, byte[] data) { |
| int timeout = (int) mHelper.getTransferTimeout().toMillis(); |
| return controlTransfer(requestType, request, value, index, data, timeout); |
| } |
| |
| /** |
| * Performs a synchronous control transaction. |
| * |
| * @return number of bytes transferred, or an error code |
| */ |
| public int controlTransfer( |
| byte requestType, byte request, int value, int index, byte[] data, int timeout) { |
| return mUsb.libusb_control_transfer( |
| checkNotNull(mHandle), |
| requestType, |
| request, |
| (short) value, |
| (short) index, |
| data, |
| (short) data.length, |
| timeout); |
| } |
| |
| /** |
| * Performs a USB port reset. A LIBUSB_ERROR_NOT_FOUND error may indicate that the connection |
| * was reset, but that this {@link UsbDevice} is no longer valid and needs to be recreated. |
| * |
| * @return 0 on success or error code |
| */ |
| public int reset() { |
| return mUsb.libusb_reset_device(checkNotNull(mHandle)); |
| } |
| |
| /** @return true if device handle is non-null, but does not check if resetting is necessary */ |
| public boolean isValid() { |
| return mHandle != null; |
| } |
| |
| /** @return device's serial number or {@code null} if serial could not be determined */ |
| @Nullable |
| public String getSerialNumber() { |
| if (!isValid() || mDescriptor[16] <= 0) { |
| // no device handle or string index is invalid |
| return null; |
| } |
| |
| byte[] data = new byte[64]; |
| int length = mUsb.libusb_get_string_descriptor_ascii(mHandle, mDescriptor[16], data, 64); |
| return length > 0 ? new String(data, 0, length) : null; |
| } |
| |
| /** @return device's vendor ID */ |
| public int getVendorId() { |
| return Shorts.fromBytes(mDescriptor[9], mDescriptor[8]); |
| } |
| |
| /** @return device's product ID */ |
| public int getProductId() { |
| return Shorts.fromBytes(mDescriptor[11], mDescriptor[10]); |
| } |
| |
| /** @return true if device is AOAv2-compatible */ |
| public boolean isAoaCompatible() { |
| return isValid() && controlTransfer(INPUT, ACCESSORY_GET_PROTOCOL, 0, 0, new byte[2]) >= 2; |
| } |
| |
| /** Close the connection if necessary. */ |
| @Override |
| public void close() { |
| if (isValid()) { |
| mUsb.libusb_close(mHandle); |
| mHandle = null; |
| } |
| } |
| } |