1 /*
2  * Copyright (C) 2010 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.Nullable;
20 import android.compat.annotation.UnsupportedAppUsage;
21 import android.os.Build;
22 import android.util.Log;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import dalvik.system.CloseGuard;
27 
28 import java.nio.BufferOverflowException;
29 import java.nio.ByteBuffer;
30 
31 /**
32  * A class representing USB request packet.
33  * This can be used for both reading and writing data to or from a
34  * {@link android.hardware.usb.UsbDeviceConnection}.
35  * UsbRequests can be used to transfer data on bulk and interrupt endpoints.
36  * Requests on bulk endpoints can be sent synchronously via {@link UsbDeviceConnection#bulkTransfer}
37  * or asynchronously via {@link #queue} and {@link UsbDeviceConnection#requestWait}.
38  * Requests on interrupt endpoints are only send and received asynchronously.
39  *
40  * <p>Requests on endpoint zero are not supported by this class;
41  * use {@link UsbDeviceConnection#controlTransfer} for endpoint zero requests instead.
42  */
43 public class UsbRequest {
44 
45     private static final String TAG = "UsbRequest";
46 
47     // From drivers/usb/core/devio.c
48     static final int MAX_USBFS_BUFFER_SIZE = 16384;
49 
50     // used by the JNI code
51     @UnsupportedAppUsage
52     private long mNativeContext;
53 
54     private UsbEndpoint mEndpoint;
55 
56     /** The buffer that is currently being read / written */
57     @UnsupportedAppUsage
58     private ByteBuffer mBuffer;
59 
60     /** The amount of data to read / write when using {@link #queue} */
61     @UnsupportedAppUsage
62     private int mLength;
63 
64     // for client use
65     private Object mClientData;
66 
67     // Prevent the connection from being finalized
68     private UsbDeviceConnection mConnection;
69 
70     /**
71      * Whether this buffer was {@link #queue(ByteBuffer) queued using the new behavior} or
72      * {@link #queue(ByteBuffer, int) queued using the deprecated behavior}.
73      */
74     private boolean mIsUsingNewQueue;
75 
76     /** Temporary buffer than might be used while buffer is enqueued */
77     private ByteBuffer mTempBuffer;
78 
79     private final CloseGuard mCloseGuard = CloseGuard.get();
80 
81     /**
82      * Lock for queue, enqueue and dequeue, so a queue operation can be finished by a dequeue
83      * operation on a different thread.
84      */
85     private final Object mLock = new Object();
86 
UsbRequest()87     public UsbRequest() {
88     }
89 
90     /**
91      * Initializes the request so it can read or write data on the given endpoint.
92      * Whether the request allows reading or writing depends on the direction of the endpoint.
93      *
94      * @param endpoint the endpoint to be used for this request.
95      * @return true if the request was successfully opened.
96      */
initialize(UsbDeviceConnection connection, UsbEndpoint endpoint)97     public boolean initialize(UsbDeviceConnection connection, UsbEndpoint endpoint) {
98         mEndpoint = endpoint;
99         mConnection = Preconditions.checkNotNull(connection, "connection");
100 
101         boolean wasInitialized = native_init(connection, endpoint.getAddress(),
102                 endpoint.getAttributes(), endpoint.getMaxPacketSize(), endpoint.getInterval());
103 
104         if (wasInitialized) {
105             mCloseGuard.open("close");
106         }
107 
108         return wasInitialized;
109     }
110 
111     /**
112      * Releases all resources related to this request.
113      */
close()114     public void close() {
115         if (mNativeContext != 0) {
116             mEndpoint = null;
117             mConnection = null;
118             native_close();
119             mCloseGuard.close();
120         }
121     }
122 
123     @Override
finalize()124     protected void finalize() throws Throwable {
125         try {
126             if (mCloseGuard != null) {
127                 mCloseGuard.warnIfOpen();
128             }
129 
130             close();
131         } finally {
132             super.finalize();
133         }
134     }
135 
136     /**
137      * Returns the endpoint for the request, or null if the request is not opened.
138      *
139      * @return the request's endpoint
140      */
getEndpoint()141     public UsbEndpoint getEndpoint() {
142         return mEndpoint;
143     }
144 
145     /**
146      * Returns the client data for the request.
147      * This can be used in conjunction with {@link #setClientData}
148      * to associate another object with this request, which can be useful for
149      * maintaining state between calls to {@link #queue} and
150      * {@link android.hardware.usb.UsbDeviceConnection#requestWait}
151      *
152      * @return the client data for the request
153      */
getClientData()154     public Object getClientData() {
155         return mClientData;
156     }
157 
158     /**
159      * Sets the client data for the request.
160      * This can be used in conjunction with {@link #getClientData}
161      * to associate another object with this request, which can be useful for
162      * maintaining state between calls to {@link #queue} and
163      * {@link android.hardware.usb.UsbDeviceConnection#requestWait}
164      *
165      * @param data the client data for the request
166      */
setClientData(Object data)167     public void setClientData(Object data) {
168         mClientData = data;
169     }
170 
171     /**
172      * Queues the request to send or receive data on its endpoint.
173      * <p>For OUT endpoints, the given buffer data will be sent on the endpoint. For IN endpoints,
174      * the endpoint will attempt to read the given number of bytes into the specified buffer. If the
175      * queueing operation is successful, return true. The result will be returned via
176      * {@link UsbDeviceConnection#requestWait}</p>
177      *
178      * @param buffer the buffer containing the bytes to write, or location to store the results of a
179      *               read. Position and array offset will be ignored and assumed to be 0. Limit and
180      *               capacity will be ignored. Once the request
181      *               {@link UsbDeviceConnection#requestWait() is processed} the position will be set
182      *               to the number of bytes read/written.
183      * @param length number of bytes to read or write. Before {@value Build.VERSION_CODES#P}, a
184      *               value larger than 16384 bytes would be truncated down to 16384. In API
185      *               {@value Build.VERSION_CODES#P} and after, any value of length is valid.
186      *
187      * @return true if the queueing operation succeeded
188      *
189      * @deprecated Use {@link #queue(ByteBuffer)} instead.
190      */
191     @Deprecated
queue(ByteBuffer buffer, int length)192     public boolean queue(ByteBuffer buffer, int length) {
193         boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
194         boolean result;
195 
196         if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
197                 && length > MAX_USBFS_BUFFER_SIZE) {
198             length = MAX_USBFS_BUFFER_SIZE;
199         }
200 
201         synchronized (mLock) {
202             // save our buffer for when the request has completed
203             mBuffer = buffer;
204             mLength = length;
205 
206             // Note: On a buffer slice we lost the capacity information about the underlying buffer,
207             // hence we cannot check if the access would be a data leak/memory corruption.
208 
209             if (buffer.isDirect()) {
210                 result = native_queue_direct(buffer, length, out);
211             } else if (buffer.hasArray()) {
212                 result = native_queue_array(buffer.array(), length, out);
213             } else {
214                 throw new IllegalArgumentException("buffer is not direct and has no array");
215             }
216             if (!result) {
217                 mBuffer = null;
218                 mLength = 0;
219             }
220         }
221 
222         return result;
223     }
224 
225     /**
226      * Queues the request to send or receive data on its endpoint.
227      *
228      * <p>For OUT endpoints, the remaining bytes of the buffer will be sent on the endpoint. For IN
229      * endpoints, the endpoint will attempt to fill the remaining bytes of the buffer. If the
230      * queueing operation is successful, return true. The result will be returned via
231      * {@link UsbDeviceConnection#requestWait}</p>
232      *
233      * @param buffer the buffer containing the bytes to send, or the buffer to fill. The state
234      *               of the buffer is undefined until the request is returned by
235      *               {@link UsbDeviceConnection#requestWait}. If the request failed the buffer
236      *               will be unchanged; if the request succeeded the position of the buffer is
237      *               incremented by the number of bytes sent/received. Before
238      *               {@value Build.VERSION_CODES#P}, a buffer of length larger than 16384 bytes
239      *               would throw IllegalArgumentException. In API {@value Build.VERSION_CODES#P}
240      *               and after, any size buffer is valid.
241      *
242      * @return true if the queueing operation succeeded
243      */
queue(@ullable ByteBuffer buffer)244     public boolean queue(@Nullable ByteBuffer buffer) {
245         // Request need to be initialized
246         Preconditions.checkState(mNativeContext != 0, "request is not initialized");
247 
248         // Request can not be currently queued
249         Preconditions.checkState(!mIsUsingNewQueue, "this request is currently queued");
250 
251         boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
252         boolean wasQueued;
253 
254         synchronized (mLock) {
255             mBuffer = buffer;
256 
257             if (buffer == null) {
258                 // Null buffers enqueue empty USB requests which is supported
259                 mIsUsingNewQueue = true;
260                 wasQueued = native_queue(null, 0, 0);
261             } else {
262                 if (mConnection.getContext().getApplicationInfo().targetSdkVersion
263                         < Build.VERSION_CODES.P) {
264                     // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
265                     Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
266                             "number of remaining bytes");
267                 }
268 
269                 // Can not receive into read-only buffers.
270                 Preconditions.checkArgument(!(buffer.isReadOnly() && !isSend), "buffer can not be "
271                         + "read-only when receiving data");
272 
273                 if (!buffer.isDirect()) {
274                     mTempBuffer = ByteBuffer.allocateDirect(mBuffer.remaining());
275 
276                     if (isSend) {
277                         // Copy buffer into temporary buffer
278                         mBuffer.mark();
279                         mTempBuffer.put(mBuffer);
280                         mTempBuffer.flip();
281                         mBuffer.reset();
282                     }
283 
284                     // Send/Receive into the temp buffer instead
285                     buffer = mTempBuffer;
286                 }
287 
288                 mIsUsingNewQueue = true;
289                 wasQueued = native_queue(buffer, buffer.position(), buffer.remaining());
290             }
291         }
292 
293         if (!wasQueued) {
294             mIsUsingNewQueue = false;
295             mTempBuffer = null;
296             mBuffer = null;
297         }
298 
299         return wasQueued;
300     }
301 
dequeue(boolean useBufferOverflowInsteadOfIllegalArg)302     /* package */ void dequeue(boolean useBufferOverflowInsteadOfIllegalArg) {
303         boolean isSend = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
304         int bytesTransferred;
305 
306         synchronized (mLock) {
307             if (mIsUsingNewQueue) {
308                 bytesTransferred = native_dequeue_direct();
309                 mIsUsingNewQueue = false;
310 
311                 if (mBuffer == null) {
312                     // Nothing to do
313                 } else if (mTempBuffer == null) {
314                     mBuffer.position(mBuffer.position() + bytesTransferred);
315                 } else {
316                     mTempBuffer.limit(bytesTransferred);
317 
318                     // The user might have modified mBuffer which might make put/position fail.
319                     // Changing the buffer while a request is in flight is not supported. Still,
320                     // make sure to free mTempBuffer correctly.
321                     try {
322                         if (isSend) {
323                             mBuffer.position(mBuffer.position() + bytesTransferred);
324                         } else {
325                             // Copy temp buffer back into original buffer
326                             mBuffer.put(mTempBuffer);
327                         }
328                     } finally {
329                         mTempBuffer = null;
330                     }
331                 }
332             } else {
333                 if (mBuffer.isDirect()) {
334                     bytesTransferred = native_dequeue_direct();
335                 } else {
336                     bytesTransferred = native_dequeue_array(mBuffer.array(), mLength, isSend);
337                 }
338                 if (bytesTransferred >= 0) {
339                     int bytesToStore = Math.min(bytesTransferred, mLength);
340                     try {
341                         mBuffer.position(bytesToStore);
342                     } catch (IllegalArgumentException e) {
343                         if (useBufferOverflowInsteadOfIllegalArg) {
344                             Log.e(TAG, "Buffer " + mBuffer + " does not have enough space to read "
345                                     + bytesToStore + " bytes", e);
346                             throw new BufferOverflowException();
347                         } else {
348                             throw e;
349                         }
350                     }
351                 }
352             }
353 
354             mBuffer = null;
355             mLength = 0;
356         }
357     }
358 
359     /**
360      * Cancels a pending queue operation.
361      *
362      * @return true if cancelling succeeded
363      */
cancel()364     public boolean cancel() {
365         return native_cancel();
366     }
367 
native_init(UsbDeviceConnection connection, int ep_address, int ep_attributes, int ep_max_packet_size, int ep_interval)368     private native boolean native_init(UsbDeviceConnection connection, int ep_address,
369             int ep_attributes, int ep_max_packet_size, int ep_interval);
native_close()370     private native void native_close();
native_queue(ByteBuffer buffer, int offset, int length)371     private native boolean native_queue(ByteBuffer buffer, int offset, int length);
native_queue_array(byte[] buffer, int length, boolean out)372     private native boolean native_queue_array(byte[] buffer, int length, boolean out);
native_dequeue_array(byte[] buffer, int length, boolean out)373     private native int native_dequeue_array(byte[] buffer, int length, boolean out);
native_queue_direct(ByteBuffer buffer, int length, boolean out)374     private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out);
native_dequeue_direct()375     private native int native_dequeue_direct();
native_cancel()376     private native boolean native_cancel();
377 }
378