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