Home | History | Annotate | Download | only in usb
      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 
     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      */
     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      */
    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
    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      */
    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      */
    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      */
    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
    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      */
    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 
    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      */
    360     public boolean cancel() {
    361         return native_cancel();
    362     }
    363 
    364     private native boolean native_init(UsbDeviceConnection connection, int ep_address,
    365             int ep_attributes, int ep_max_packet_size, int ep_interval);
    366     private native void native_close();
    367     private native boolean native_queue(ByteBuffer buffer, int offset, int length);
    368     private native boolean native_queue_array(byte[] buffer, int length, boolean out);
    369     private native int native_dequeue_array(byte[] buffer, int length, boolean out);
    370     private native boolean native_queue_direct(ByteBuffer buffer, int length, boolean out);
    371     private native int native_dequeue_direct();
    372     private native boolean native_cancel();
    373 }
    374