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