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