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