Home | History | Annotate | Download | only in device
      1 /*
      2  * Copyright (C) 2016 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 com.android.cts.verifier.usb.device;
     18 
     19 import static com.android.cts.verifier.usb.Util.runAndAssertException;
     20 
     21 import static org.junit.Assert.assertArrayEquals;
     22 import static org.junit.Assert.assertEquals;
     23 import static org.junit.Assert.assertFalse;
     24 import static org.junit.Assert.assertNotNull;
     25 import static org.junit.Assert.assertNull;
     26 import static org.junit.Assert.assertSame;
     27 import static org.junit.Assert.assertTrue;
     28 
     29 import android.app.PendingIntent;
     30 import android.content.BroadcastReceiver;
     31 import android.content.Context;
     32 import android.content.Intent;
     33 import android.content.IntentFilter;
     34 import android.hardware.usb.UsbConfiguration;
     35 import android.hardware.usb.UsbConstants;
     36 import android.hardware.usb.UsbDevice;
     37 import android.hardware.usb.UsbDeviceConnection;
     38 import android.hardware.usb.UsbEndpoint;
     39 import android.hardware.usb.UsbInterface;
     40 import android.hardware.usb.UsbManager;
     41 import android.hardware.usb.UsbRequest;
     42 import android.os.Bundle;
     43 import android.util.ArraySet;
     44 import android.util.Log;
     45 import android.util.Pair;
     46 import android.view.View;
     47 import android.widget.ProgressBar;
     48 import android.widget.TextView;
     49 
     50 import androidx.annotation.NonNull;
     51 import androidx.annotation.Nullable;
     52 
     53 import com.android.cts.verifier.PassFailButtons;
     54 import com.android.cts.verifier.R;
     55 
     56 import java.nio.BufferOverflowException;
     57 import java.nio.ByteBuffer;
     58 import java.nio.CharBuffer;
     59 import java.nio.charset.Charset;
     60 import java.util.ArrayList;
     61 import java.util.HashMap;
     62 import java.util.LinkedList;
     63 import java.util.Map;
     64 import java.util.NoSuchElementException;
     65 import java.util.Random;
     66 import java.util.Set;
     67 import java.util.concurrent.TimeoutException;
     68 import java.util.concurrent.atomic.AtomicInteger;
     69 
     70 public class UsbDeviceTestActivity extends PassFailButtons.Activity {
     71     private static final String ACTION_USB_PERMISSION =
     72             "com.android.cts.verifier.usb.device.USB_PERMISSION";
     73     private static final String LOG_TAG = UsbDeviceTestActivity.class.getSimpleName();
     74     private static final int TIMEOUT_MILLIS = 5000;
     75     private static final int LARGE_BUFFER_SIZE = 124619;
     76 
     77     private UsbManager mUsbManager;
     78     private BroadcastReceiver mUsbDeviceConnectionReceiver;
     79     private Thread mTestThread;
     80     private TextView mStatus;
     81     private ProgressBar mProgress;
     82 
     83     /**
     84      * Some N and older accessories do not send a zero sized package after a request that is a
     85      * multiple of the maximum package size.
     86      */
     87     private boolean mDoesCompanionZeroTerminate;
     88 
     89     private static long now() {
     90         return System.nanoTime() / 1000000;
     91     }
     92 
     93     /**
     94      * Check if we should expect a zero sized transfer after a certain sized transfer
     95      *
     96      * @param transferSize The size of the previous transfer
     97      *
     98      * @return {@code true} if a zero sized transfer is expected
     99      */
    100     private boolean isZeroTransferExpected(int transferSize, @NonNull UsbEndpoint ep) {
    101         return mDoesCompanionZeroTerminate && transferSize % ep.getMaxPacketSize() == 0;
    102     }
    103 
    104     @Override
    105     protected void onCreate(Bundle savedInstanceState) {
    106         super.onCreate(savedInstanceState);
    107 
    108         setContentView(R.layout.usb_main);
    109         setInfoResources(R.string.usb_device_test, R.string.usb_device_test_info, -1);
    110 
    111         mStatus = (TextView) findViewById(R.id.status);
    112         mProgress = (ProgressBar) findViewById(R.id.progress_bar);
    113 
    114         mUsbManager = getSystemService(UsbManager.class);
    115 
    116         getPassButton().setEnabled(false);
    117 
    118         IntentFilter filter = new IntentFilter();
    119         filter.addAction(ACTION_USB_PERMISSION);
    120         filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
    121 
    122         mStatus.setText(R.string.usb_device_test_step1);
    123 
    124         mUsbDeviceConnectionReceiver = new BroadcastReceiver() {
    125             @Override
    126             public void onReceive(Context context, Intent intent) {
    127                 synchronized (UsbDeviceTestActivity.this) {
    128                     UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
    129 
    130                     switch (intent.getAction()) {
    131                         case UsbManager.ACTION_USB_DEVICE_ATTACHED:
    132                             if (!AoapInterface.isDeviceInAoapMode(device)) {
    133                                 mStatus.setText(R.string.usb_device_test_step2);
    134                             }
    135 
    136                             mUsbManager.requestPermission(device,
    137                                     PendingIntent.getBroadcast(UsbDeviceTestActivity.this, 0,
    138                                             new Intent(ACTION_USB_PERMISSION), 0));
    139                             break;
    140                         case ACTION_USB_PERMISSION:
    141                             boolean granted = intent.getBooleanExtra(
    142                                     UsbManager.EXTRA_PERMISSION_GRANTED, false);
    143 
    144                             if (granted) {
    145                                 if (!AoapInterface.isDeviceInAoapMode(device)) {
    146                                     mStatus.setText(R.string.usb_device_test_step3);
    147 
    148                                     UsbDeviceConnection connection = mUsbManager.openDevice(device);
    149                                     try {
    150                                         makeThisDeviceAnAccessory(connection);
    151                                     } finally {
    152                                         connection.close();
    153                                     }
    154                                 } else {
    155                                     mStatus.setText(R.string.usb_device_test_step4);
    156                                     mProgress.setIndeterminate(true);
    157                                     mProgress.setVisibility(View.VISIBLE);
    158 
    159                                     unregisterReceiver(mUsbDeviceConnectionReceiver);
    160                                     mUsbDeviceConnectionReceiver = null;
    161 
    162                                     // Do not run test on main thread
    163                                     mTestThread = new Thread() {
    164                                         @Override
    165                                         public void run() {
    166                                             runTests(device);
    167                                         }
    168                                     };
    169 
    170                                     mTestThread.start();
    171                                 }
    172                             } else {
    173                                 fail("Permission to connect to " + device.getProductName()
    174                                         + " not granted", null);
    175                             }
    176                             break;
    177                     }
    178                 }
    179             }
    180         };
    181 
    182         registerReceiver(mUsbDeviceConnectionReceiver, filter);
    183     }
    184 
    185     /**
    186      * Indicate that the test failed.
    187      */
    188     private void fail(@Nullable String s, @Nullable Throwable e) {
    189         Log.e(LOG_TAG, s, e);
    190         setTestResultAndFinish(false);
    191     }
    192 
    193     /**
    194      * Converts the device under test into an Android accessory. Accessories are USB hosts that are
    195      * detected on the device side via {@link UsbManager#getAccessoryList()}.
    196      *
    197      * @param connection The connection to the USB device
    198      */
    199     private void makeThisDeviceAnAccessory(@NonNull UsbDeviceConnection connection) {
    200         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER,
    201                 "Android CTS");
    202         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL,
    203                 "Android device under CTS test");
    204         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION,
    205                 "Android device running CTS verifier");
    206         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION, "2");
    207         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI,
    208                 "https://source.android.com/compatibility/cts/verifier.html");
    209         AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, "0");
    210         AoapInterface.sendAoapStart(connection);
    211     }
    212 
    213     /**
    214      * Switch to next test.
    215      *
    216      * @param connection   Connection to the USB device
    217      * @param in           The in endpoint
    218      * @param out          The out endpoint
    219      * @param nextTestName The name of the new test
    220      */
    221     private void nextTest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
    222             @NonNull UsbEndpoint out, @NonNull CharSequence nextTestName) {
    223         Log.v(LOG_TAG, "Finishing previous test");
    224 
    225         // Make sure name length is not a multiple of 8 to avoid zero-termination issues
    226         StringBuilder safeNextTestName = new StringBuilder(nextTestName);
    227         if (nextTestName.length() % 8 == 0) {
    228             safeNextTestName.append(' ');
    229         }
    230 
    231         // Send name of next test
    232         assertTrue(safeNextTestName.length() <= Byte.MAX_VALUE);
    233         ByteBuffer nextTestNameBuffer = Charset.forName("UTF-8")
    234                 .encode(CharBuffer.wrap(safeNextTestName));
    235         byte[] sizeBuffer = { (byte) nextTestNameBuffer.limit() };
    236         int numSent = connection.bulkTransfer(out, sizeBuffer, 1, 0);
    237         assertEquals(1, numSent);
    238 
    239         numSent = connection.bulkTransfer(out, nextTestNameBuffer.array(),
    240                 nextTestNameBuffer.limit(), 0);
    241         assertEquals(nextTestNameBuffer.limit(), numSent);
    242 
    243         // Receive result of last test
    244         byte[] lastTestResultBytes = new byte[1];
    245         int numReceived = connection.bulkTransfer(in, lastTestResultBytes,
    246                 lastTestResultBytes.length, TIMEOUT_MILLIS);
    247         assertEquals(1, numReceived);
    248         assertEquals(1, lastTestResultBytes[0]);
    249 
    250         // Send ready signal
    251         sizeBuffer[0] = 42;
    252         numSent = connection.bulkTransfer(out, sizeBuffer, 1, 0);
    253         assertEquals(1, numSent);
    254 
    255         Log.i(LOG_TAG, "Running test \"" + safeNextTestName + "\"");
    256     }
    257 
    258     /**
    259      * Receive a transfer that has size zero using bulk-transfer.
    260      *
    261      * @param connection Connection to the USB device
    262      * @param in         The in endpoint
    263      */
    264     private void receiveZeroSizedTransfer(@NonNull UsbDeviceConnection connection,
    265             @NonNull UsbEndpoint in) {
    266         byte[] buffer = new byte[1];
    267         int numReceived = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS);
    268         assertEquals(0, numReceived);
    269     }
    270 
    271     /**
    272      * Send some data and expect it to be echoed back.
    273      *
    274      * @param connection Connection to the USB device
    275      * @param in         The in endpoint
    276      * @param out        The out endpoint
    277      * @param size       The number of bytes to send
    278      */
    279     private void echoBulkTransfer(@NonNull UsbDeviceConnection connection,
    280             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int size) {
    281         byte[] sentBuffer = new byte[size];
    282         Random r = new Random();
    283         r.nextBytes(sentBuffer);
    284 
    285         int numSent = connection.bulkTransfer(out, sentBuffer, sentBuffer.length, 0);
    286         assertEquals(size, numSent);
    287 
    288         byte[] receivedBuffer = new byte[size];
    289         int numReceived = connection.bulkTransfer(in, receivedBuffer, receivedBuffer.length,
    290                 TIMEOUT_MILLIS);
    291         assertEquals(size, numReceived);
    292 
    293         assertArrayEquals(sentBuffer, receivedBuffer);
    294 
    295         if (isZeroTransferExpected(size, in)) {
    296             receiveZeroSizedTransfer(connection, in);
    297         }
    298     }
    299 
    300     /**
    301      * Send some data and expect it to be echoed back (but have an offset in the send buffer).
    302      *
    303      * @param connection Connection to the USB device
    304      * @param in         The in endpoint
    305      * @param out        The out endpoint
    306      * @param size       The number of bytes to send
    307      */
    308     private void echoBulkTransferOffset(@NonNull UsbDeviceConnection connection,
    309             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int offset, int size) {
    310         byte[] sentBuffer = new byte[offset + size];
    311         Random r = new Random();
    312         r.nextBytes(sentBuffer);
    313 
    314         int numSent = connection.bulkTransfer(out, sentBuffer, offset, size, 0);
    315         assertEquals(size, numSent);
    316 
    317         byte[] receivedBuffer = new byte[offset + size];
    318         int numReceived = connection.bulkTransfer(in, receivedBuffer, offset, size, TIMEOUT_MILLIS);
    319         assertEquals(size, numReceived);
    320 
    321         for (int i = 0; i < offset + size; i++) {
    322             if (i < offset) {
    323                 assertEquals(0, receivedBuffer[i]);
    324             } else {
    325                 assertEquals(sentBuffer[i], receivedBuffer[i]);
    326             }
    327         }
    328 
    329         if (isZeroTransferExpected(size, in)) {
    330             receiveZeroSizedTransfer(connection, in);
    331         }
    332     }
    333 
    334     /**
    335      * Send a transfer that is large.
    336      *
    337      * @param connection Connection to the USB device
    338      * @param in         The in endpoint
    339      * @param out        The out endpoint
    340      */
    341     private void echoLargeBulkTransfer(@NonNull UsbDeviceConnection connection,
    342             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out) {
    343         int totalSize = LARGE_BUFFER_SIZE;
    344         byte[] sentBuffer = new byte[totalSize];
    345         Random r = new Random();
    346         r.nextBytes(sentBuffer);
    347 
    348         int numSent = connection.bulkTransfer(out, sentBuffer, sentBuffer.length, 0);
    349 
    350         // Buffer will be completely transferred
    351         assertEquals(LARGE_BUFFER_SIZE, numSent);
    352 
    353         byte[] receivedBuffer = new byte[totalSize];
    354         int numReceived = connection.bulkTransfer(in, receivedBuffer, receivedBuffer.length,
    355                 TIMEOUT_MILLIS);
    356 
    357         // All of the buffer will be echoed back
    358         assertEquals(LARGE_BUFFER_SIZE, numReceived);
    359 
    360         for (int i = 0; i < totalSize; i++) {
    361             assertEquals(sentBuffer[i], receivedBuffer[i]);
    362         }
    363 
    364         if (isZeroTransferExpected(LARGE_BUFFER_SIZE, in)) {
    365             receiveZeroSizedTransfer(connection, in);
    366         }
    367     }
    368 
    369     /**
    370      * Receive data but supply an empty buffer. This causes the thread to block until any data is
    371      * sent. The zero-sized receive-transfer just returns without data and the next transfer can
    372      * actually read the data.
    373      *
    374      * @param connection Connection to the USB device
    375      * @param in         The in endpoint
    376      * @param buffer     The buffer to use
    377      * @param offset     The offset into the buffer
    378      * @param length     The lenght of data to receive
    379      */
    380     private void receiveWithEmptyBuffer(@NonNull UsbDeviceConnection connection,
    381             @NonNull UsbEndpoint in, @Nullable byte[] buffer, int offset, int length) {
    382         long startTime = now();
    383         int numReceived;
    384         if (offset == 0) {
    385             numReceived = connection.bulkTransfer(in, buffer, length, 0);
    386         } else {
    387             numReceived = connection.bulkTransfer(in, buffer, offset, length, 0);
    388         }
    389         long endTime = now();
    390         assertEquals(-1, numReceived);
    391 
    392         // The transfer should block
    393         assertTrue(endTime - startTime > 100);
    394 
    395         numReceived = connection.bulkTransfer(in, new byte[1], 1, 0);
    396         assertEquals(1, numReceived);
    397     }
    398 
    399     /**
    400      * Tests {@link UsbDeviceConnection#controlTransfer}.
    401      *
    402      * <p>Note: We cannot send ctrl data to the device as it thinks it talks to an accessory, hence
    403      * the testing is currently limited.</p>
    404      *
    405      * @param connection The connection to use for testing
    406      *
    407      * @throws Throwable
    408      */
    409     private void ctrlTransferTests(@NonNull UsbDeviceConnection connection) throws Throwable {
    410         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, null, 1, 0),
    411                 IllegalArgumentException.class);
    412 
    413         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, new byte[1], -1, 0),
    414                 IllegalArgumentException.class);
    415 
    416         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, new byte[1], 2, 0),
    417                 IllegalArgumentException.class);
    418 
    419         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, null, 0, 1, 0),
    420                 IllegalArgumentException.class);
    421 
    422         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, new byte[1], 0, -1, 0),
    423                 IllegalArgumentException.class);
    424 
    425         runAndAssertException(() -> connection.controlTransfer(0, 0, 0, 0, new byte[1], 1, 1, 0),
    426                 IllegalArgumentException.class);
    427     }
    428 
    429     /**
    430      * Search an {@link UsbInterface} for an {@link UsbEndpoint endpoint} of a certain direction.
    431      *
    432      * @param iface     The interface to search
    433      * @param direction The direction the endpoint is for.
    434      *
    435      * @return The first endpoint found or {@link null}.
    436      */
    437     private @NonNull UsbEndpoint getEndpoint(@NonNull UsbInterface iface, int direction) {
    438         for (int i = 0; i < iface.getEndpointCount(); i++) {
    439             UsbEndpoint ep = iface.getEndpoint(i);
    440             if (ep.getDirection() == direction) {
    441                 return ep;
    442             }
    443         }
    444 
    445         throw new IllegalStateException("Could not find " + direction + " endpoint in "
    446                 + iface.getName());
    447     }
    448 
    449     /**
    450      * Receive a transfer that has size zero using deprecated usb-request methods.
    451      *
    452      * @param connection Connection to the USB device
    453      * @param in         The in endpoint
    454      */
    455     private void receiveZeroSizeRequestLegacy(@NonNull UsbDeviceConnection connection,
    456             @NonNull UsbEndpoint in) {
    457         UsbRequest receiveZero = new UsbRequest();
    458         boolean isInited = receiveZero.initialize(connection, in);
    459         assertTrue(isInited);
    460         ByteBuffer zeroBuffer = ByteBuffer.allocate(1);
    461         receiveZero.queue(zeroBuffer, 1);
    462 
    463         UsbRequest finished = connection.requestWait();
    464         assertEquals(receiveZero, finished);
    465         assertEquals(0, zeroBuffer.position());
    466     }
    467 
    468     /**
    469      * Send a USB request using the {@link UsbRequest#queue legacy path} and receive it back.
    470      *
    471      * @param connection      The connection to use
    472      * @param in              The endpoint to receive requests from
    473      * @param out             The endpoint to send requests to
    474      * @param size            The size of the request to send
    475      * @param originalSize    The size of the original buffer
    476      * @param sliceStart      The start of the final buffer in the original buffer
    477      * @param sliceEnd        The end of the final buffer in the original buffer
    478      * @param positionInSlice The position parameter in the final buffer
    479      * @param limitInSlice    The limited parameter in the final buffer
    480      * @param useDirectBuffer If the buffer to be used should be a direct buffer
    481      */
    482     private void echoUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
    483             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int size, int originalSize,
    484             int sliceStart, int sliceEnd, int positionInSlice, int limitInSlice,
    485             boolean useDirectBuffer) {
    486         Random random = new Random();
    487 
    488         UsbRequest sent = new UsbRequest();
    489         boolean isInited = sent.initialize(connection, out);
    490         assertTrue(isInited);
    491         Object sentClientData = new Object();
    492         sent.setClientData(sentClientData);
    493 
    494         UsbRequest receive = new UsbRequest();
    495         isInited = receive.initialize(connection, in);
    496         assertTrue(isInited);
    497         Object receiveClientData = new Object();
    498         receive.setClientData(receiveClientData);
    499 
    500         ByteBuffer bufferSent;
    501         if (useDirectBuffer) {
    502             bufferSent = ByteBuffer.allocateDirect(originalSize);
    503         } else {
    504             bufferSent = ByteBuffer.allocate(originalSize);
    505         }
    506         for (int i = 0; i < originalSize; i++) {
    507             bufferSent.put((byte) random.nextInt());
    508         }
    509         bufferSent.position(sliceStart);
    510         bufferSent.limit(sliceEnd);
    511         ByteBuffer bufferSentSliced = bufferSent.slice();
    512         bufferSentSliced.position(positionInSlice);
    513         bufferSentSliced.limit(limitInSlice);
    514 
    515         bufferSent.position(0);
    516         bufferSent.limit(originalSize);
    517 
    518         ByteBuffer bufferReceived;
    519         if (useDirectBuffer) {
    520             bufferReceived = ByteBuffer.allocateDirect(originalSize);
    521         } else {
    522             bufferReceived = ByteBuffer.allocate(originalSize);
    523         }
    524         bufferReceived.position(sliceStart);
    525         bufferReceived.limit(sliceEnd);
    526         ByteBuffer bufferReceivedSliced = bufferReceived.slice();
    527         bufferReceivedSliced.position(positionInSlice);
    528         bufferReceivedSliced.limit(limitInSlice);
    529 
    530         bufferReceived.position(0);
    531         bufferReceived.limit(originalSize);
    532 
    533         boolean wasQueued = receive.queue(bufferReceivedSliced, size);
    534         assertTrue(wasQueued);
    535         wasQueued = sent.queue(bufferSentSliced, size);
    536         assertTrue(wasQueued);
    537 
    538         for (int reqRun = 0; reqRun < 2; reqRun++) {
    539             UsbRequest finished;
    540 
    541             try {
    542                 finished = connection.requestWait();
    543             } catch (BufferOverflowException e) {
    544                 if (size > bufferSentSliced.limit() || size > bufferReceivedSliced.limit()) {
    545                     Log.e(LOG_TAG, "Expected failure", e);
    546                     continue;
    547                 } else {
    548                     throw e;
    549                 }
    550             }
    551 
    552             // Should we have gotten a failure?
    553             if (finished == receive) {
    554                 // We should have gotten an exception if size > limit
    555                 assertTrue(bufferReceivedSliced.limit() >= size);
    556 
    557                 assertEquals(size, bufferReceivedSliced.position());
    558 
    559                 for (int i = 0; i < size; i++) {
    560                     if (i < size) {
    561                         assertEquals(bufferSent.get(i), bufferReceived.get(i));
    562                     } else {
    563                         assertEquals(0, bufferReceived.get(i));
    564                     }
    565                 }
    566 
    567                 assertSame(receiveClientData, finished.getClientData());
    568                 assertSame(in, finished.getEndpoint());
    569             } else {
    570                 assertEquals(size, bufferSentSliced.position());
    571 
    572                 // We should have gotten an exception if size > limit
    573                 assertTrue(bufferSentSliced.limit() >= size);
    574                 assertSame(sent, finished);
    575                 assertSame(sentClientData, finished.getClientData());
    576                 assertSame(out, finished.getEndpoint());
    577             }
    578             finished.close();
    579         }
    580 
    581         if (isZeroTransferExpected(size, in)) {
    582             receiveZeroSizeRequestLegacy(connection, in);
    583         }
    584     }
    585 
    586     /**
    587      * Receive a transfer that has size zero using current usb-request methods.
    588      *
    589      * @param connection Connection to the USB device
    590      * @param in         The in endpoint
    591      */
    592     private void receiveZeroSizeRequest(@NonNull UsbDeviceConnection connection,
    593             @NonNull UsbEndpoint in) {
    594         UsbRequest receiveZero = new UsbRequest();
    595         boolean isInited = receiveZero.initialize(connection, in);
    596         assertTrue(isInited);
    597         ByteBuffer zeroBuffer = ByteBuffer.allocate(1);
    598         receiveZero.queue(zeroBuffer);
    599 
    600         UsbRequest finished = connection.requestWait();
    601         assertEquals(receiveZero, finished);
    602         assertEquals(0, zeroBuffer.position());
    603     }
    604 
    605     /**
    606      * Send a USB request and receive it back.
    607      *
    608      * @param connection      The connection to use
    609      * @param in              The endpoint to receive requests from
    610      * @param out             The endpoint to send requests to
    611      * @param originalSize    The size of the original buffer
    612      * @param sliceStart      The start of the final buffer in the original buffer
    613      * @param sliceEnd        The end of the final buffer in the original buffer
    614      * @param positionInSlice The position parameter in the final buffer
    615      * @param limitInSlice    The limited parameter in the final buffer
    616      * @param useDirectBuffer If the buffer to be used should be a direct buffer
    617      */
    618     private void echoUsbRequest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
    619             @NonNull UsbEndpoint out, int originalSize, int sliceStart, int sliceEnd,
    620             int positionInSlice, int limitInSlice, boolean useDirectBuffer,
    621             boolean makeSendBufferReadOnly) {
    622         Random random = new Random();
    623 
    624         UsbRequest sent = new UsbRequest();
    625         boolean isInited = sent.initialize(connection, out);
    626         assertTrue(isInited);
    627         Object sentClientData = new Object();
    628         sent.setClientData(sentClientData);
    629 
    630         UsbRequest receive = new UsbRequest();
    631         isInited = receive.initialize(connection, in);
    632         assertTrue(isInited);
    633         Object receiveClientData = new Object();
    634         receive.setClientData(receiveClientData);
    635 
    636         ByteBuffer bufferSent;
    637         if (useDirectBuffer) {
    638             bufferSent = ByteBuffer.allocateDirect(originalSize);
    639         } else {
    640             bufferSent = ByteBuffer.allocate(originalSize);
    641         }
    642         for (int i = 0; i < originalSize; i++) {
    643             bufferSent.put((byte) random.nextInt());
    644         }
    645         if (makeSendBufferReadOnly) {
    646             bufferSent = bufferSent.asReadOnlyBuffer();
    647         }
    648         bufferSent.position(sliceStart);
    649         bufferSent.limit(sliceEnd);
    650         ByteBuffer bufferSentSliced = bufferSent.slice();
    651         bufferSentSliced.position(positionInSlice);
    652         bufferSentSliced.limit(limitInSlice);
    653 
    654         bufferSent.position(0);
    655         bufferSent.limit(originalSize);
    656 
    657         ByteBuffer bufferReceived;
    658         if (useDirectBuffer) {
    659             bufferReceived = ByteBuffer.allocateDirect(originalSize);
    660         } else {
    661             bufferReceived = ByteBuffer.allocate(originalSize);
    662         }
    663         bufferReceived.position(sliceStart);
    664         bufferReceived.limit(sliceEnd);
    665         ByteBuffer bufferReceivedSliced = bufferReceived.slice();
    666         bufferReceivedSliced.position(positionInSlice);
    667         bufferReceivedSliced.limit(limitInSlice);
    668 
    669         bufferReceived.position(0);
    670         bufferReceived.limit(originalSize);
    671 
    672         boolean wasQueued = receive.queue(bufferReceivedSliced);
    673         assertTrue(wasQueued);
    674         wasQueued = sent.queue(bufferSentSliced);
    675         assertTrue(wasQueued);
    676 
    677         for (int reqRun = 0; reqRun < 2; reqRun++) {
    678             UsbRequest finished = connection.requestWait();
    679 
    680             if (finished == receive) {
    681                 assertEquals(limitInSlice, bufferReceivedSliced.limit());
    682                 assertEquals(limitInSlice, bufferReceivedSliced.position());
    683 
    684                 for (int i = 0; i < originalSize; i++) {
    685                     if (i >= sliceStart + positionInSlice && i < sliceStart + limitInSlice) {
    686                         assertEquals(bufferSent.get(i), bufferReceived.get(i));
    687                     } else {
    688                         assertEquals(0, bufferReceived.get(i));
    689                     }
    690                 }
    691 
    692                 assertSame(receiveClientData, finished.getClientData());
    693                 assertSame(in, finished.getEndpoint());
    694             } else {
    695                 assertEquals(limitInSlice, bufferSentSliced.limit());
    696                 assertEquals(limitInSlice, bufferSentSliced.position());
    697 
    698                 assertSame(sent, finished);
    699                 assertSame(sentClientData, finished.getClientData());
    700                 assertSame(out, finished.getEndpoint());
    701             }
    702             finished.close();
    703         }
    704 
    705         if (isZeroTransferExpected(sliceStart + limitInSlice - (sliceStart + positionInSlice), in)) {
    706             receiveZeroSizeRequest(connection, in);
    707         }
    708     }
    709 
    710     /**
    711      * Send a USB request using the {@link UsbRequest#queue legacy path} and receive it back.
    712      *
    713      * @param connection      The connection to use
    714      * @param in              The endpoint to receive requests from
    715      * @param out             The endpoint to send requests to
    716      * @param size            The size of the request to send
    717      * @param useDirectBuffer If the buffer to be used should be a direct buffer
    718      */
    719     private void echoUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
    720             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, int size, boolean useDirectBuffer) {
    721         echoUsbRequestLegacy(connection, in, out, size, size, 0, size, 0, size, useDirectBuffer);
    722     }
    723 
    724     /**
    725      * Send a USB request and receive it back.
    726      *
    727      * @param connection      The connection to use
    728      * @param in              The endpoint to receive requests from
    729      * @param out             The endpoint to send requests to
    730      * @param size            The size of the request to send
    731      * @param useDirectBuffer If the buffer to be used should be a direct buffer
    732      */
    733     private void echoUsbRequest(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint in,
    734             @NonNull UsbEndpoint out, int size, boolean useDirectBuffer) {
    735         echoUsbRequest(connection, in, out, size, 0, size, 0, size, useDirectBuffer, false);
    736     }
    737 
    738     /**
    739      * Send a USB request which more than the allowed size and receive it back.
    740      *
    741      * @param connection      The connection to use
    742      * @param in              The endpoint to receive requests from
    743      * @param out             The endpoint to send requests to
    744      */
    745     private void echoLargeUsbRequestLegacy(@NonNull UsbDeviceConnection connection,
    746             @NonNull UsbEndpoint in, @NonNull UsbEndpoint out) {
    747         Random random = new Random();
    748         int totalSize = LARGE_BUFFER_SIZE;
    749 
    750         UsbRequest sent = new UsbRequest();
    751         boolean isInited = sent.initialize(connection, out);
    752         assertTrue(isInited);
    753 
    754         UsbRequest receive = new UsbRequest();
    755         isInited = receive.initialize(connection, in);
    756         assertTrue(isInited);
    757 
    758         byte[] sentBytes = new byte[totalSize];
    759         random.nextBytes(sentBytes);
    760         ByteBuffer bufferSent = ByteBuffer.wrap(sentBytes);
    761 
    762         byte[] receivedBytes = new byte[totalSize];
    763         ByteBuffer bufferReceived = ByteBuffer.wrap(receivedBytes);
    764 
    765         boolean wasQueued = receive.queue(bufferReceived, totalSize);
    766         assertTrue(wasQueued);
    767         wasQueued = sent.queue(bufferSent, totalSize);
    768         assertTrue(wasQueued);
    769 
    770         for (int requestNum = 0; requestNum < 2; requestNum++) {
    771             UsbRequest finished = connection.requestWait();
    772             if (finished == receive) {
    773                 // Entire buffer is received
    774                 assertEquals(bufferReceived.position(), totalSize);
    775                 for (int i = 0; i < totalSize; i++) {
    776                     assertEquals(sentBytes[i], receivedBytes[i]);
    777                 }
    778             } else {
    779                 assertSame(sent, finished);
    780             }
    781             finished.close();
    782         }
    783 
    784         if (isZeroTransferExpected(LARGE_BUFFER_SIZE, in)) {
    785             receiveZeroSizedTransfer(connection, in);
    786         }
    787     }
    788 
    789     /**
    790      * Time out while waiting for USB requests.
    791      *
    792      * @param connection The connection to use
    793      */
    794     private void timeoutWhileWaitingForUsbRequest(@NonNull UsbDeviceConnection connection)
    795             throws Throwable {
    796         runAndAssertException(() -> connection.requestWait(-1), IllegalArgumentException.class);
    797 
    798         long startTime = now();
    799         runAndAssertException(() -> connection.requestWait(100), TimeoutException.class);
    800         assertTrue(now() - startTime >= 100);
    801         assertTrue(now() - startTime < 400);
    802 
    803         startTime = now();
    804         runAndAssertException(() -> connection.requestWait(0), TimeoutException.class);
    805         assertTrue(now() - startTime < 400);
    806     }
    807 
    808     /**
    809      * Receive a USB request before a timeout triggers
    810      *
    811      * @param connection The connection to use
    812      * @param in         The endpoint to receive requests from
    813      */
    814     private void receiveAfterTimeout(@NonNull UsbDeviceConnection connection,
    815             @NonNull UsbEndpoint in, long timeout) throws InterruptedException, TimeoutException {
    816         UsbRequest reqQueued = new UsbRequest();
    817         ByteBuffer buffer = ByteBuffer.allocate(1);
    818 
    819         reqQueued.initialize(connection, in);
    820         reqQueued.queue(buffer);
    821 
    822         // Let the kernel receive and process the request
    823         Thread.sleep(50);
    824 
    825         long startTime = now();
    826         UsbRequest reqFinished = connection.requestWait(timeout);
    827         assertTrue(now() - startTime < timeout + 50);
    828         assertSame(reqQueued, reqFinished);
    829         reqFinished.close();
    830     }
    831 
    832     /**
    833      * Send a USB request with size 0 using the {@link UsbRequest#queue legacy path}.
    834      *
    835      * @param connection      The connection to use
    836      * @param out             The endpoint to send requests to
    837      * @param useDirectBuffer Send data from a direct buffer
    838      */
    839     private void sendZeroLengthRequestLegacy(@NonNull UsbDeviceConnection connection,
    840             @NonNull UsbEndpoint out, boolean useDirectBuffer) {
    841         UsbRequest sent = new UsbRequest();
    842         boolean isInited = sent.initialize(connection, out);
    843         assertTrue(isInited);
    844 
    845         ByteBuffer buffer;
    846         if (useDirectBuffer) {
    847             buffer = ByteBuffer.allocateDirect(0);
    848         } else {
    849             buffer = ByteBuffer.allocate(0);
    850         }
    851 
    852         boolean isQueued = sent.queue(buffer, 0);
    853         assertTrue(isQueued);
    854         UsbRequest finished = connection.requestWait();
    855         assertSame(finished, sent);
    856         finished.close();
    857     }
    858 
    859     /**
    860      * Send a USB request with size 0.
    861      *
    862      * @param connection      The connection to use
    863      * @param out             The endpoint to send requests to
    864      * @param useDirectBuffer Send data from a direct buffer
    865      */
    866     private void sendZeroLengthRequest(@NonNull UsbDeviceConnection connection,
    867             @NonNull UsbEndpoint out, boolean useDirectBuffer) {
    868         UsbRequest sent = new UsbRequest();
    869         boolean isInited = sent.initialize(connection, out);
    870         assertTrue(isInited);
    871 
    872         ByteBuffer buffer;
    873         if (useDirectBuffer) {
    874             buffer = ByteBuffer.allocateDirect(0);
    875         } else {
    876             buffer = ByteBuffer.allocate(0);
    877         }
    878 
    879         boolean isQueued = sent.queue(buffer);
    880         assertTrue(isQueued);
    881         UsbRequest finished = connection.requestWait();
    882         assertSame(finished, sent);
    883         finished.close();
    884     }
    885 
    886     /**
    887      * Send a USB request with a null buffer.
    888      *
    889      * @param connection      The connection to use
    890      * @param out             The endpoint to send requests to
    891      */
    892     private void sendNullRequest(@NonNull UsbDeviceConnection connection,
    893             @NonNull UsbEndpoint out) {
    894         UsbRequest sent = new UsbRequest();
    895         boolean isInited = sent.initialize(connection, out);
    896         assertTrue(isInited);
    897 
    898         boolean isQueued = sent.queue(null);
    899         assertTrue(isQueued);
    900         UsbRequest finished = connection.requestWait();
    901         assertSame(finished, sent);
    902         finished.close();
    903     }
    904 
    905     /**
    906      * Receive a USB request with size 0.
    907      *
    908      * @param connection      The connection to use
    909      * @param in             The endpoint to recevie requests from
    910      */
    911     private void receiveZeroLengthRequestLegacy(@NonNull UsbDeviceConnection connection,
    912             @NonNull UsbEndpoint in, boolean useDirectBuffer) {
    913         UsbRequest zeroReceived = new UsbRequest();
    914         boolean isInited = zeroReceived.initialize(connection, in);
    915         assertTrue(isInited);
    916 
    917         UsbRequest oneReceived = new UsbRequest();
    918         isInited = oneReceived.initialize(connection, in);
    919         assertTrue(isInited);
    920 
    921         ByteBuffer buffer;
    922         if (useDirectBuffer) {
    923             buffer = ByteBuffer.allocateDirect(0);
    924         } else {
    925             buffer = ByteBuffer.allocate(0);
    926         }
    927 
    928         ByteBuffer buffer1;
    929         if (useDirectBuffer) {
    930             buffer1 = ByteBuffer.allocateDirect(1);
    931         } else {
    932             buffer1 = ByteBuffer.allocate(1);
    933         }
    934 
    935         boolean isQueued = zeroReceived.queue(buffer);
    936         assertTrue(isQueued);
    937         isQueued = oneReceived.queue(buffer1);
    938         assertTrue(isQueued);
    939 
    940         // We expect both to be returned after some time
    941         ArrayList<UsbRequest> finished = new ArrayList<>(2);
    942 
    943         // We expect both request to come back after the delay, but then quickly
    944         long startTime = now();
    945         finished.add(connection.requestWait());
    946         long firstReturned = now();
    947         finished.add(connection.requestWait());
    948         long secondReturned = now();
    949 
    950         assertTrue(firstReturned - startTime > 100);
    951         assertTrue(secondReturned - firstReturned < 100);
    952 
    953         assertTrue(finished.contains(zeroReceived));
    954         assertTrue(finished.contains(oneReceived));
    955     }
    956 
    957     /**
    958      * Tests the {@link UsbRequest#queue legacy implementaion} of {@link UsbRequest} and
    959      * {@link UsbDeviceConnection#requestWait()}.
    960      *
    961      * @param connection The connection to use for testing
    962      * @param iface      The interface of the android accessory interface of the device
    963      * @throws Throwable
    964      */
    965     private void usbRequestLegacyTests(@NonNull UsbDeviceConnection connection,
    966             @NonNull UsbInterface iface) throws Throwable {
    967         // Find bulk in and out endpoints
    968         assertTrue(iface.getEndpointCount() == 2);
    969         final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
    970         final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
    971         assertNotNull(in);
    972         assertNotNull(out);
    973 
    974         // Single threaded send and receive
    975         nextTest(connection, in, out, "Echo 1 byte");
    976         echoUsbRequestLegacy(connection, in, out, 1, true);
    977 
    978         nextTest(connection, in, out, "Echo 1 byte");
    979         echoUsbRequestLegacy(connection, in, out, 1, false);
    980 
    981         nextTest(connection, in, out, "Echo 16384 bytes");
    982         echoUsbRequestLegacy(connection, in, out, 16384, true);
    983 
    984         nextTest(connection, in, out, "Echo 16384 bytes");
    985         echoUsbRequestLegacy(connection, in, out, 16384, false);
    986 
    987         nextTest(connection, in, out, "Echo large buffer");
    988         echoLargeUsbRequestLegacy(connection, in, out);
    989 
    990         // Send empty requests
    991         sendZeroLengthRequestLegacy(connection, out, true);
    992         sendZeroLengthRequestLegacy(connection, out, false);
    993 
    994         // waitRequest with timeout
    995         timeoutWhileWaitingForUsbRequest(connection);
    996 
    997         nextTest(connection, in, out, "Receive byte after some time");
    998         receiveAfterTimeout(connection, in, 400);
    999 
   1000         nextTest(connection, in, out, "Receive byte immediately");
   1001         // Make sure the data is received before we queue the request for it
   1002         Thread.sleep(50);
   1003         receiveAfterTimeout(connection, in, 0);
   1004 
   1005         /* TODO: Unreliable
   1006 
   1007         // Zero length means waiting for the next data and then return
   1008         nextTest(connection, in, out, "Receive byte after some time");
   1009         receiveZeroLengthRequestLegacy(connection, in, true);
   1010 
   1011         nextTest(connection, in, out, "Receive byte after some time");
   1012         receiveZeroLengthRequestLegacy(connection, in, true);
   1013 
   1014         */
   1015 
   1016         // UsbRequest.queue ignores position, limit, arrayOffset, and capacity
   1017         nextTest(connection, in, out, "Echo 42 bytes");
   1018         echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 42, 5, 42, false);
   1019 
   1020         nextTest(connection, in, out, "Echo 42 bytes");
   1021         echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 42, 0, 36, false);
   1022 
   1023         nextTest(connection, in, out, "Echo 42 bytes");
   1024         echoUsbRequestLegacy(connection, in, out, 42, 42, 5, 42, 0, 36, false);
   1025 
   1026         nextTest(connection, in, out, "Echo 42 bytes");
   1027         echoUsbRequestLegacy(connection, in, out, 42, 42, 0, 36, 0, 31, false);
   1028 
   1029         nextTest(connection, in, out, "Echo 42 bytes");
   1030         echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 47, 0, 47, false);
   1031 
   1032         nextTest(connection, in, out, "Echo 42 bytes");
   1033         echoUsbRequestLegacy(connection, in, out, 42, 47, 5, 47, 0, 42, false);
   1034 
   1035         nextTest(connection, in, out, "Echo 42 bytes");
   1036         echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 42, 0, 42, false);
   1037 
   1038         nextTest(connection, in, out, "Echo 42 bytes");
   1039         echoUsbRequestLegacy(connection, in, out, 42, 47, 0, 47, 5, 47, false);
   1040 
   1041         nextTest(connection, in, out, "Echo 42 bytes");
   1042         echoUsbRequestLegacy(connection, in, out, 42, 47, 5, 47, 5, 36, false);
   1043 
   1044         // Illegal arguments
   1045         final UsbRequest req1 = new UsbRequest();
   1046         runAndAssertException(() -> req1.initialize(null, in), NullPointerException.class);
   1047         runAndAssertException(() -> req1.initialize(connection, null), NullPointerException.class);
   1048         boolean isInited = req1.initialize(connection, in);
   1049         assertTrue(isInited);
   1050         runAndAssertException(() -> req1.queue(null, 0), NullPointerException.class);
   1051         runAndAssertException(() -> req1.queue(ByteBuffer.allocate(1).asReadOnlyBuffer(), 1),
   1052                 IllegalArgumentException.class);
   1053         req1.close();
   1054 
   1055         // Cannot queue closed request
   1056         runAndAssertException(() -> req1.queue(ByteBuffer.allocate(1), 1),
   1057                 NullPointerException.class);
   1058         runAndAssertException(() -> req1.queue(ByteBuffer.allocateDirect(1), 1),
   1059                 NullPointerException.class);
   1060     }
   1061 
   1062     /**
   1063      * Repeat c n times
   1064      *
   1065      * @param c The character to repeat
   1066      * @param n The number of times to repeat
   1067      *
   1068      * @return c repeated n times
   1069      */
   1070     public static String repeat(char c, int n) {
   1071         final StringBuilder result = new StringBuilder();
   1072         for (int i = 0; i < n; i++) {
   1073             if (c != ' ' && i % 10 == 0) {
   1074                 result.append(i / 10);
   1075             } else {
   1076                 result.append(c);
   1077             }
   1078         }
   1079         return result.toString();
   1080     }
   1081 
   1082     /**
   1083      * Tests {@link UsbRequest} and {@link UsbDeviceConnection#requestWait()}.
   1084      *
   1085      * @param connection The connection to use for testing
   1086      * @param iface      The interface of the android accessory interface of the device
   1087      * @throws Throwable
   1088      */
   1089     private void usbRequestTests(@NonNull UsbDeviceConnection connection,
   1090             @NonNull UsbInterface iface) throws Throwable {
   1091         // Find bulk in and out endpoints
   1092         assertTrue(iface.getEndpointCount() == 2);
   1093         final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
   1094         final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
   1095         assertNotNull(in);
   1096         assertNotNull(out);
   1097 
   1098         // Single threaded send and receive
   1099         nextTest(connection, in, out, "Echo 1 byte");
   1100         echoUsbRequest(connection, in, out, 1, true);
   1101 
   1102         nextTest(connection, in, out, "Echo 1 byte");
   1103         echoUsbRequest(connection, in, out, 1, false);
   1104 
   1105         nextTest(connection, in, out, "Echo 16384 bytes");
   1106         echoUsbRequest(connection, in, out, 16384, true);
   1107 
   1108         nextTest(connection, in, out, "Echo 16384 bytes");
   1109         echoUsbRequest(connection, in, out, 16384, false);
   1110 
   1111         // Send empty requests
   1112         sendZeroLengthRequest(connection, out, true);
   1113         sendZeroLengthRequest(connection, out, false);
   1114         sendNullRequest(connection, out);
   1115 
   1116         /* TODO: Unreliable
   1117 
   1118         // Zero length means waiting for the next data and then return
   1119         nextTest(connection, in, out, "Receive byte after some time");
   1120         receiveZeroLengthRequest(connection, in, true);
   1121 
   1122         nextTest(connection, in, out, "Receive byte after some time");
   1123         receiveZeroLengthRequest(connection, in, true);
   1124 
   1125         */
   1126 
   1127         for (int startOfSlice : new int[]{0, 1}) {
   1128             for (int endOffsetOfSlice : new int[]{0, 2}) {
   1129                 for (int positionInSlice : new int[]{0, 5}) {
   1130                     for (int limitOffsetInSlice : new int[]{0, 11}) {
   1131                         for (boolean useDirectBuffer : new boolean[]{true, false}) {
   1132                             for (boolean makeSendBufferReadOnly : new boolean[]{true, false}) {
   1133                                 int sliceSize = 42 + positionInSlice + limitOffsetInSlice;
   1134                                 int originalSize = sliceSize + startOfSlice + endOffsetOfSlice;
   1135 
   1136                                 nextTest(connection, in, out, "Echo 42 bytes");
   1137 
   1138                                 // Log buffer, slice, and data offsets
   1139                                 Log.i(LOG_TAG,
   1140                                         "buffer" + (makeSendBufferReadOnly ? "(ro): [" : ":     [")
   1141                                                 + repeat('.', originalSize) + "]");
   1142                                 Log.i(LOG_TAG,
   1143                                         "slice:     " + repeat(' ', startOfSlice) + " [" + repeat(
   1144                                                 '.', sliceSize) + "]");
   1145                                 Log.i(LOG_TAG,
   1146                                         "data:      " + repeat(' ', startOfSlice + positionInSlice)
   1147                                                 + " [" + repeat('.', 42) + "]");
   1148 
   1149                                 echoUsbRequest(connection, in, out, originalSize, startOfSlice,
   1150                                         originalSize - endOffsetOfSlice, positionInSlice,
   1151                                         sliceSize - limitOffsetInSlice, useDirectBuffer,
   1152                                         makeSendBufferReadOnly);
   1153                             }
   1154                         }
   1155                     }
   1156                 }
   1157             }
   1158         }
   1159 
   1160         // Illegal arguments
   1161         final UsbRequest req1 = new UsbRequest();
   1162         runAndAssertException(() -> req1.initialize(null, in), NullPointerException.class);
   1163         runAndAssertException(() -> req1.initialize(connection, null), NullPointerException.class);
   1164         boolean isInited = req1.initialize(connection, in);
   1165         assertTrue(isInited);
   1166         runAndAssertException(() -> req1.queue(ByteBuffer.allocate(16384 + 1).asReadOnlyBuffer()),
   1167                 IllegalArgumentException.class);
   1168         runAndAssertException(() -> req1.queue(ByteBuffer.allocate(1).asReadOnlyBuffer()),
   1169                 IllegalArgumentException.class);
   1170         req1.close();
   1171 
   1172         // Cannot queue closed request
   1173         runAndAssertException(() -> req1.queue(ByteBuffer.allocate(1)),
   1174                 IllegalStateException.class);
   1175         runAndAssertException(() -> req1.queue(ByteBuffer.allocateDirect(1)),
   1176                 IllegalStateException.class);
   1177 
   1178         // Initialize
   1179         UsbRequest req2 = new UsbRequest();
   1180         isInited = req2.initialize(connection, in);
   1181         assertTrue(isInited);
   1182         isInited = req2.initialize(connection, out);
   1183         assertTrue(isInited);
   1184         req2.close();
   1185 
   1186         // Close
   1187         req2 = new UsbRequest();
   1188         req2.close();
   1189 
   1190         req2.initialize(connection, in);
   1191         req2.close();
   1192         req2.close();
   1193     }
   1194 
   1195     /** State of a {@link UsbRequest} in flight */
   1196     private static class RequestState {
   1197         final ByteBuffer buffer;
   1198         final Object clientData;
   1199 
   1200         private RequestState(ByteBuffer buffer, Object clientData) {
   1201             this.buffer = buffer;
   1202             this.clientData = clientData;
   1203         }
   1204     }
   1205 
   1206     /** Recycles elements that might be expensive to create */
   1207     private abstract class Recycler<T> {
   1208         private final Random mRandom;
   1209         private final LinkedList<T> mData;
   1210 
   1211         protected Recycler() {
   1212             mData = new LinkedList<>();
   1213             mRandom = new Random();
   1214         }
   1215 
   1216         /**
   1217          * Add a new element to be recycled.
   1218          *
   1219          * @param newElement The element that is not used anymore and can be used by someone else.
   1220          */
   1221         private void recycle(@NonNull T newElement) {
   1222             synchronized (mData) {
   1223                 if (mRandom.nextBoolean()) {
   1224                     mData.addLast(newElement);
   1225                 } else {
   1226                     mData.addFirst(newElement);
   1227                 }
   1228             }
   1229         }
   1230 
   1231         /**
   1232          * Get a recycled element or create a new one if needed.
   1233          *
   1234          * @return An element that can be used (maybe recycled)
   1235          */
   1236         private @NonNull T get() {
   1237             T recycledElement;
   1238 
   1239             try {
   1240                 synchronized (mData) {
   1241                     recycledElement = mData.pop();
   1242                 }
   1243             } catch (NoSuchElementException ignored) {
   1244                 recycledElement = create();
   1245             }
   1246 
   1247             reset(recycledElement);
   1248 
   1249             return recycledElement;
   1250         }
   1251 
   1252         /** Reset internal state of {@code recycledElement} */
   1253         protected abstract void reset(@NonNull T recycledElement);
   1254 
   1255         /** Create a new element */
   1256         protected abstract @NonNull T create();
   1257 
   1258         /** Get all elements that are currently recycled and waiting to be used again */
   1259         public @NonNull LinkedList<T> getAll() {
   1260             return mData;
   1261         }
   1262     }
   1263 
   1264     /**
   1265      * Common code between {@link QueuerThread} and {@link ReceiverThread}.
   1266      */
   1267     private class TestThread extends Thread {
   1268         /** State copied from the main thread (see runTest()) */
   1269         protected final UsbDeviceConnection mConnection;
   1270         protected final Recycler<UsbRequest> mInRequestRecycler;
   1271         protected final Recycler<UsbRequest> mOutRequestRecycler;
   1272         protected final Recycler<ByteBuffer> mBufferRecycler;
   1273         protected final HashMap<UsbRequest, RequestState> mRequestsInFlight;
   1274         protected final HashMap<Integer, Integer> mData;
   1275         protected final ArrayList<Throwable> mErrors;
   1276 
   1277         protected volatile boolean mShouldStop;
   1278 
   1279         TestThread(@NonNull UsbDeviceConnection connection,
   1280                 @NonNull Recycler<UsbRequest> inRequestRecycler,
   1281                 @NonNull Recycler<UsbRequest> outRequestRecycler,
   1282                 @NonNull Recycler<ByteBuffer> bufferRecycler,
   1283                 @NonNull HashMap<UsbRequest, RequestState> requestsInFlight,
   1284                 @NonNull HashMap<Integer, Integer> data,
   1285                 @NonNull ArrayList<Throwable> errors) {
   1286             super();
   1287 
   1288             mShouldStop = false;
   1289             mConnection = connection;
   1290             mBufferRecycler = bufferRecycler;
   1291             mInRequestRecycler = inRequestRecycler;
   1292             mOutRequestRecycler = outRequestRecycler;
   1293             mRequestsInFlight = requestsInFlight;
   1294             mData = data;
   1295             mErrors = errors;
   1296         }
   1297 
   1298         /**
   1299          * Stop thread
   1300          */
   1301         void abort() {
   1302             mShouldStop = true;
   1303             interrupt();
   1304         }
   1305     }
   1306 
   1307     /**
   1308      * A thread that queues matching write and read {@link UsbRequest requests}. We expect the
   1309      * writes to be echoed back and return in unchanged in the read requests.
   1310      * <p> This thread just issues the requests and does not care about them anymore after the
   1311      * system took them. The {@link ReceiverThread} handles the result of both write and read
   1312      * requests.</p>
   1313      */
   1314     private class QueuerThread extends TestThread {
   1315         private static final int MAX_IN_FLIGHT = 64;
   1316         private static final long RUN_TIME = 10 * 1000;
   1317 
   1318         private final AtomicInteger mCounter;
   1319 
   1320         /**
   1321          * Create a new thread that queues matching write and read UsbRequests.
   1322          *
   1323          * @param connection Connection to communicate with
   1324          * @param inRequestRecycler Pool of in-requests that can be reused
   1325          * @param outRequestRecycler Pool of out-requests that can be reused
   1326          * @param bufferRecycler Pool of byte buffers that can be reused
   1327          * @param requestsInFlight State of the requests currently in flight
   1328          * @param data Mapping counter -> data
   1329          * @param counter An atomic counter
   1330          * @param errors Pool of throwables created by threads like this
   1331          */
   1332         QueuerThread(@NonNull UsbDeviceConnection connection,
   1333                 @NonNull Recycler<UsbRequest> inRequestRecycler,
   1334                 @NonNull Recycler<UsbRequest> outRequestRecycler,
   1335                 @NonNull Recycler<ByteBuffer> bufferRecycler,
   1336                 @NonNull HashMap<UsbRequest, RequestState> requestsInFlight,
   1337                 @NonNull HashMap<Integer, Integer> data,
   1338                 @NonNull AtomicInteger counter,
   1339                 @NonNull ArrayList<Throwable> errors) {
   1340             super(connection, inRequestRecycler, outRequestRecycler, bufferRecycler,
   1341                     requestsInFlight, data, errors);
   1342 
   1343             mCounter = counter;
   1344         }
   1345 
   1346         @Override
   1347         public void run() {
   1348             Random random = new Random();
   1349 
   1350             long endTime = now() + RUN_TIME;
   1351 
   1352             while (now() < endTime && !mShouldStop) {
   1353                 try {
   1354                     int counter = mCounter.getAndIncrement();
   1355 
   1356                     if (counter % 1024 == 0) {
   1357                         Log.i(LOG_TAG, "Counter is " + counter);
   1358                     }
   1359 
   1360                     // Write [1:counter:data]
   1361                     UsbRequest writeRequest = mOutRequestRecycler.get();
   1362                     ByteBuffer writeBuffer = mBufferRecycler.get();
   1363                     int data = random.nextInt();
   1364                     writeBuffer.put((byte)1).putInt(counter).putInt(data);
   1365                     writeBuffer.flip();
   1366 
   1367                     // Send read that will receive the data back from the write as the other side
   1368                     // will echo all requests.
   1369                     UsbRequest readRequest = mInRequestRecycler.get();
   1370                     ByteBuffer readBuffer = mBufferRecycler.get();
   1371 
   1372                     // Register requests
   1373                     synchronized (mRequestsInFlight) {
   1374                         // Wait until previous requests were processed
   1375                         while (mRequestsInFlight.size() > MAX_IN_FLIGHT) {
   1376                             try {
   1377                                 mRequestsInFlight.wait();
   1378                             } catch (InterruptedException e) {
   1379                                 break;
   1380                             }
   1381                         }
   1382 
   1383                         if (mShouldStop) {
   1384                             break;
   1385                         } else {
   1386                             mRequestsInFlight.put(writeRequest, new RequestState(writeBuffer,
   1387                                     writeRequest.getClientData()));
   1388                             mRequestsInFlight.put(readRequest, new RequestState(readBuffer,
   1389                                     readRequest.getClientData()));
   1390                             mRequestsInFlight.notifyAll();
   1391                         }
   1392                     }
   1393 
   1394                     // Store which data was written for the counter
   1395                     synchronized (mData) {
   1396                         mData.put(counter, data);
   1397                     }
   1398 
   1399                     // Send both requests to the system. Once they finish the ReceiverThread will
   1400                     // be notified
   1401                     boolean isQueued = writeRequest.queue(writeBuffer);
   1402                     assertTrue(isQueued);
   1403 
   1404                     isQueued = readRequest.queue(readBuffer, 9);
   1405                     assertTrue(isQueued);
   1406                 } catch (Throwable t) {
   1407                     synchronized (mErrors) {
   1408                         mErrors.add(t);
   1409                         mErrors.notify();
   1410                     }
   1411                     break;
   1412                 }
   1413             }
   1414         }
   1415     }
   1416 
   1417     /**
   1418      * A thread that receives processed UsbRequests and compares the expected result. The requests
   1419      * can be both read and write requests. The requests were created and given to the system by
   1420      * the {@link QueuerThread}.
   1421      */
   1422     private class ReceiverThread extends TestThread {
   1423         private final UsbEndpoint mOut;
   1424 
   1425         /**
   1426          * Create a thread that receives processed UsbRequests and compares the expected result.
   1427          *
   1428          * @param connection Connection to communicate with
   1429          * @param out Endpoint to queue write requests on
   1430          * @param inRequestRecycler Pool of in-requests that can be reused
   1431          * @param outRequestRecycler Pool of out-requests that can be reused
   1432          * @param bufferRecycler Pool of byte buffers that can be reused
   1433          * @param requestsInFlight State of the requests currently in flight
   1434          * @param data Mapping counter -> data
   1435          * @param errors Pool of throwables created by threads like this
   1436          */
   1437         ReceiverThread(@NonNull UsbDeviceConnection connection, @NonNull UsbEndpoint out,
   1438                 @NonNull Recycler<UsbRequest> inRequestRecycler,
   1439                 @NonNull Recycler<UsbRequest> outRequestRecycler,
   1440                 @NonNull Recycler<ByteBuffer> bufferRecycler,
   1441                 @NonNull HashMap<UsbRequest, RequestState> requestsInFlight,
   1442                 @NonNull HashMap<Integer, Integer> data, @NonNull ArrayList<Throwable> errors) {
   1443             super(connection, inRequestRecycler, outRequestRecycler, bufferRecycler,
   1444                     requestsInFlight, data, errors);
   1445 
   1446             mOut = out;
   1447         }
   1448 
   1449         @Override
   1450         public void run() {
   1451             while (!mShouldStop) {
   1452                 try {
   1453                     // Wait until a request is queued as mConnection.requestWait() cannot be
   1454                     // interrupted.
   1455                     synchronized (mRequestsInFlight) {
   1456                         while (mRequestsInFlight.isEmpty()) {
   1457                             try {
   1458                                 mRequestsInFlight.wait();
   1459                             } catch (InterruptedException e) {
   1460                                 break;
   1461                             }
   1462                         }
   1463 
   1464                         if (mShouldStop) {
   1465                             break;
   1466                         }
   1467                     }
   1468 
   1469                     // Receive request
   1470                     UsbRequest request = mConnection.requestWait();
   1471                     assertNotNull(request);
   1472 
   1473                     // Find the state the request should have
   1474                     RequestState state;
   1475                     synchronized (mRequestsInFlight) {
   1476                         state = mRequestsInFlight.remove(request);
   1477                         mRequestsInFlight.notifyAll();
   1478                     }
   1479 
   1480                     // Compare client data
   1481                     assertSame(state.clientData, request.getClientData());
   1482 
   1483                     // There is nothing more to check about write requests, but for read requests
   1484                     // (the ones going to an out endpoint) we know that it just an echoed back write
   1485                     // request.
   1486                     if (!request.getEndpoint().equals(mOut)) {
   1487                         state.buffer.flip();
   1488 
   1489                         // Read request buffer, check that data is correct
   1490                         byte alive = state.buffer.get();
   1491                         int counter = state.buffer.getInt();
   1492                         int receivedData = state.buffer.getInt();
   1493 
   1494                         // We stored which data-combinations were written
   1495                         int expectedData;
   1496                         synchronized(mData) {
   1497                             expectedData = mData.remove(counter);
   1498                         }
   1499 
   1500                         // Make sure read request matches a write request we sent before
   1501                         assertEquals(1, alive);
   1502                         assertEquals(expectedData, receivedData);
   1503                     }
   1504 
   1505                     // Recycle buffers and requests so they can be reused later.
   1506                     mBufferRecycler.recycle(state.buffer);
   1507 
   1508                     if (request.getEndpoint().equals(mOut)) {
   1509                         mOutRequestRecycler.recycle(request);
   1510                     } else {
   1511                         mInRequestRecycler.recycle(request);
   1512                     }
   1513                 } catch (Throwable t) {
   1514                     synchronized (mErrors) {
   1515                         mErrors.add(t);
   1516                         mErrors.notify();
   1517                     }
   1518                     break;
   1519                 }
   1520             }
   1521         }
   1522     }
   1523 
   1524     /**
   1525      * Tests parallel issuance and receiving of {@link UsbRequest usb requests}.
   1526      *
   1527      * @param connection The connection to use for testing
   1528      * @param iface      The interface of the android accessory interface of the device
   1529      */
   1530     private void parallelUsbRequestsTests(@NonNull UsbDeviceConnection connection,
   1531             @NonNull UsbInterface iface) {
   1532         // Find bulk in and out endpoints
   1533         assertTrue(iface.getEndpointCount() == 2);
   1534         final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
   1535         final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
   1536         assertNotNull(in);
   1537         assertNotNull(out);
   1538 
   1539         // Recycler for requests for the in-endpoint
   1540         Recycler<UsbRequest> inRequestRecycler = new Recycler<UsbRequest>() {
   1541             @Override
   1542             protected void reset(@NonNull UsbRequest recycledElement) {
   1543                 recycledElement.setClientData(new Object());
   1544             }
   1545 
   1546             @Override
   1547             protected @NonNull UsbRequest create() {
   1548                 UsbRequest request = new UsbRequest();
   1549                 request.initialize(connection, in);
   1550 
   1551                 return request;
   1552             }
   1553         };
   1554 
   1555         // Recycler for requests for the in-endpoint
   1556         Recycler<UsbRequest> outRequestRecycler = new Recycler<UsbRequest>() {
   1557             @Override
   1558             protected void reset(@NonNull UsbRequest recycledElement) {
   1559                 recycledElement.setClientData(new Object());
   1560             }
   1561 
   1562             @Override
   1563             protected @NonNull UsbRequest create() {
   1564                 UsbRequest request = new UsbRequest();
   1565                 request.initialize(connection, out);
   1566 
   1567                 return request;
   1568             }
   1569         };
   1570 
   1571         // Recycler for requests for read and write buffers
   1572         Recycler<ByteBuffer> bufferRecycler = new Recycler<ByteBuffer>() {
   1573             @Override
   1574             protected void reset(@NonNull ByteBuffer recycledElement) {
   1575                 recycledElement.rewind();
   1576             }
   1577 
   1578             @Override
   1579             protected @NonNull ByteBuffer create() {
   1580                 return ByteBuffer.allocateDirect(9);
   1581             }
   1582         };
   1583 
   1584         HashMap<UsbRequest, RequestState> requestsInFlight = new HashMap<>();
   1585 
   1586         // Data in the requests
   1587         HashMap<Integer, Integer> data = new HashMap<>();
   1588         AtomicInteger counter = new AtomicInteger(0);
   1589 
   1590         // Errors created in the threads
   1591         ArrayList<Throwable> errors = new ArrayList<>();
   1592 
   1593         // Create two threads that queue read and write requests
   1594         QueuerThread queuer1 = new QueuerThread(connection, inRequestRecycler,
   1595                 outRequestRecycler, bufferRecycler, requestsInFlight, data, counter, errors);
   1596         QueuerThread queuer2 = new QueuerThread(connection, inRequestRecycler,
   1597                 outRequestRecycler, bufferRecycler, requestsInFlight, data, counter, errors);
   1598 
   1599         // Create a thread that receives the requests after they are processed.
   1600         ReceiverThread receiver = new ReceiverThread(connection, out, inRequestRecycler,
   1601                 outRequestRecycler, bufferRecycler, requestsInFlight, data, errors);
   1602 
   1603         nextTest(connection, in, out, "Echo until stop signal");
   1604 
   1605         queuer1.start();
   1606         queuer2.start();
   1607         receiver.start();
   1608 
   1609         Log.i(LOG_TAG, "Waiting for queuers to stop");
   1610 
   1611         try {
   1612             queuer1.join();
   1613             queuer2.join();
   1614         } catch (InterruptedException e) {
   1615             synchronized(errors) {
   1616                 errors.add(e);
   1617             }
   1618         }
   1619 
   1620         if (errors.isEmpty()) {
   1621             Log.i(LOG_TAG, "Wait for all requests to finish");
   1622             synchronized (requestsInFlight) {
   1623                 while (!requestsInFlight.isEmpty()) {
   1624                     try {
   1625                         requestsInFlight.wait();
   1626                     } catch (InterruptedException e) {
   1627                         synchronized(errors) {
   1628                             errors.add(e);
   1629                         }
   1630                         break;
   1631                     }
   1632                 }
   1633             }
   1634 
   1635             receiver.abort();
   1636 
   1637             try {
   1638                 receiver.join();
   1639             } catch (InterruptedException e) {
   1640                 synchronized(errors) {
   1641                     errors.add(e);
   1642                 }
   1643             }
   1644 
   1645             // Close all requests that are currently recycled
   1646             inRequestRecycler.getAll().forEach(UsbRequest::close);
   1647             outRequestRecycler.getAll().forEach(UsbRequest::close);
   1648         } else {
   1649             receiver.abort();
   1650         }
   1651 
   1652         for (Throwable t : errors) {
   1653             Log.e(LOG_TAG, "Error during test", t);
   1654         }
   1655 
   1656         byte[] stopBytes = new byte[9];
   1657         connection.bulkTransfer(out, stopBytes, 9, 0);
   1658 
   1659         // If we had any error make the test fail
   1660         assertEquals(0, errors.size());
   1661     }
   1662 
   1663     /**
   1664      * Tests {@link UsbDeviceConnection#bulkTransfer}.
   1665      *
   1666      * @param connection The connection to use for testing
   1667      * @param iface      The interface of the android accessory interface of the device
   1668      * @throws Throwable
   1669      */
   1670     private void bulkTransferTests(@NonNull UsbDeviceConnection connection,
   1671             @NonNull UsbInterface iface) throws Throwable {
   1672         // Find bulk in and out endpoints
   1673         assertTrue(iface.getEndpointCount() == 2);
   1674         final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
   1675         final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
   1676         assertNotNull(in);
   1677         assertNotNull(out);
   1678 
   1679         // Transmission tests
   1680         nextTest(connection, in, out, "Echo 1 byte");
   1681         echoBulkTransfer(connection, in, out, 1);
   1682 
   1683         nextTest(connection, in, out, "Echo 42 bytes");
   1684         echoBulkTransferOffset(connection, in, out, 23, 42);
   1685 
   1686         nextTest(connection, in, out, "Echo 16384 bytes");
   1687         echoBulkTransfer(connection, in, out, 16384);
   1688 
   1689         nextTest(connection, in, out, "Echo large buffer");
   1690         echoLargeBulkTransfer(connection, in, out);
   1691 
   1692         // Illegal arguments
   1693         runAndAssertException(() -> connection.bulkTransfer(out, new byte[1], 2, 0),
   1694                 IllegalArgumentException.class);
   1695         runAndAssertException(() -> connection.bulkTransfer(in, new byte[1], 2, 0),
   1696                 IllegalArgumentException.class);
   1697         runAndAssertException(() -> connection.bulkTransfer(out, new byte[2], 1, 2, 0),
   1698                 IllegalArgumentException.class);
   1699         runAndAssertException(() -> connection.bulkTransfer(in, new byte[2], 1, 2, 0),
   1700                 IllegalArgumentException.class);
   1701         runAndAssertException(() -> connection.bulkTransfer(out, new byte[1], -1, 0),
   1702                 IllegalArgumentException.class);
   1703         runAndAssertException(() -> connection.bulkTransfer(in, new byte[1], -1, 0),
   1704                 IllegalArgumentException.class);
   1705         runAndAssertException(() -> connection.bulkTransfer(out, new byte[1], 1, -1, 0),
   1706                 IllegalArgumentException.class);
   1707         runAndAssertException(() -> connection.bulkTransfer(in, new byte[1], 1, -1, 0),
   1708                 IllegalArgumentException.class);
   1709         runAndAssertException(() -> connection.bulkTransfer(out, new byte[1], -1, -1, 0),
   1710                 IllegalArgumentException.class);
   1711         runAndAssertException(() -> connection.bulkTransfer(in, new byte[1], -1, -1, 0),
   1712                 IllegalArgumentException.class);
   1713         runAndAssertException(() -> connection.bulkTransfer(null, new byte[1], 1, 0),
   1714                 NullPointerException.class);
   1715 
   1716         // Transmissions that do nothing
   1717         int numSent = connection.bulkTransfer(out, null, 0, 0);
   1718         assertEquals(0, numSent);
   1719 
   1720         numSent = connection.bulkTransfer(out, null, 0, 0, 0);
   1721         assertEquals(0, numSent);
   1722 
   1723         numSent = connection.bulkTransfer(out, new byte[0], 0, 0);
   1724         assertEquals(0, numSent);
   1725 
   1726         numSent = connection.bulkTransfer(out, new byte[0], 0, 0, 0);
   1727         assertEquals(0, numSent);
   1728 
   1729         numSent = connection.bulkTransfer(out, new byte[2], 2, 0, 0);
   1730         assertEquals(0, numSent);
   1731 
   1732         /* TODO: These tests are flaky as they appear to be affected by previous tests
   1733 
   1734         // Transmissions that do not transfer data:
   1735         // - first transfer blocks until data is received, but does not return the data.
   1736         // - The data is read in the second transfer
   1737         nextTest(connection, in, out, "Receive byte after some time");
   1738         receiveWithEmptyBuffer(connection, in, null, 0, 0);
   1739 
   1740         nextTest(connection, in, out, "Receive byte after some time");
   1741         receiveWithEmptyBuffer(connection, in, new byte[0], 0, 0);
   1742 
   1743         nextTest(connection, in, out, "Receive byte after some time");
   1744         receiveWithEmptyBuffer(connection, in, new byte[2], 2, 0);
   1745 
   1746         */
   1747 
   1748         // Timeouts
   1749         int numReceived = connection.bulkTransfer(in, new byte[1], 1, 100);
   1750         assertEquals(-1, numReceived);
   1751 
   1752         nextTest(connection, in, out, "Receive byte after some time");
   1753         numReceived = connection.bulkTransfer(in, new byte[1], 1, 10000);
   1754         assertEquals(1, numReceived);
   1755 
   1756         nextTest(connection, in, out, "Receive byte after some time");
   1757         numReceived = connection.bulkTransfer(in, new byte[1], 1, 0);
   1758         assertEquals(1, numReceived);
   1759 
   1760         nextTest(connection, in, out, "Receive byte after some time");
   1761         numReceived = connection.bulkTransfer(in, new byte[1], 1, -1);
   1762         assertEquals(1, numReceived);
   1763 
   1764         numReceived = connection.bulkTransfer(in, new byte[2], 1, 1, 100);
   1765         assertEquals(-1, numReceived);
   1766 
   1767         nextTest(connection, in, out, "Receive byte after some time");
   1768         numReceived = connection.bulkTransfer(in, new byte[2], 1, 1, 0);
   1769         assertEquals(1, numReceived);
   1770 
   1771         nextTest(connection, in, out, "Receive byte after some time");
   1772         numReceived = connection.bulkTransfer(in, new byte[2], 1, 1, -1);
   1773         assertEquals(1, numReceived);
   1774     }
   1775 
   1776     /**
   1777      * Test if the companion device zero-terminates their requests that are multiples of the
   1778      * maximum package size. Then sets {@link #mDoesCompanionZeroTerminate} if the companion
   1779      * zero terminates
   1780      *
   1781      * @param connection Connection to the USB device
   1782      * @param iface      The interface to use
   1783      */
   1784     private void testIfCompanionZeroTerminates(@NonNull UsbDeviceConnection connection,
   1785             @NonNull UsbInterface iface) {
   1786         assertTrue(iface.getEndpointCount() == 2);
   1787         final UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
   1788         final UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
   1789         assertNotNull(in);
   1790         assertNotNull(out);
   1791 
   1792         nextTest(connection, in, out, "does companion zero terminate");
   1793 
   1794         // The other size sends:
   1795         // - 1024 bytes
   1796         // - maybe a zero sized package
   1797         // - 1 byte
   1798 
   1799         byte[] buffer = new byte[1024];
   1800         int numTransferred = connection.bulkTransfer(in, buffer, 1024, 0);
   1801         assertEquals(1024, numTransferred);
   1802 
   1803         numTransferred = connection.bulkTransfer(in, buffer, 1, 0);
   1804         if (numTransferred == 0) {
   1805             assertEquals(0, numTransferred);
   1806 
   1807             numTransferred = connection.bulkTransfer(in, buffer, 1, 0);
   1808             assertEquals(1, numTransferred);
   1809 
   1810             mDoesCompanionZeroTerminate = true;
   1811             Log.i(LOG_TAG, "Companion zero terminates");
   1812         } else {
   1813             assertEquals(1, numTransferred);
   1814             Log.i(LOG_TAG, "Companion does not zero terminate - an older device");
   1815         }
   1816     }
   1817 
   1818     /**
   1819      * Send signal to the remove device that testing is finished.
   1820      *
   1821      * @param connection The connection to use for testing
   1822      * @param iface      The interface of the android accessory interface of the device
   1823      */
   1824     private void endTesting(@NonNull UsbDeviceConnection connection, @NonNull UsbInterface iface) {
   1825         // "done" signals that testing is over
   1826         nextTest(connection, getEndpoint(iface, UsbConstants.USB_DIR_IN),
   1827                 getEndpoint(iface, UsbConstants.USB_DIR_OUT), "done");
   1828     }
   1829 
   1830     /**
   1831      * Test the behavior of {@link UsbDeviceConnection#claimInterface} and
   1832      * {@link UsbDeviceConnection#releaseInterface}.
   1833      *
   1834      * <p>Note: The interface under test is <u>not</u> claimed by a kernel driver, hence there is
   1835      * no difference in behavior between force and non-force versions of
   1836      * {@link UsbDeviceConnection#claimInterface}</p>
   1837      *
   1838      * @param connection The connection to use
   1839      * @param iface The interface to claim and release
   1840      *
   1841      * @throws Throwable
   1842      */
   1843     private void claimInterfaceTests(@NonNull UsbDeviceConnection connection,
   1844             @NonNull UsbInterface iface) throws Throwable {
   1845         // The interface is not claimed by the kernel driver, so not forcing it should work
   1846         boolean claimed = connection.claimInterface(iface, false);
   1847         assertTrue(claimed);
   1848         boolean released = connection.releaseInterface(iface);
   1849         assertTrue(released);
   1850 
   1851         // Forcing if it is not necessary does no harm
   1852         claimed = connection.claimInterface(iface, true);
   1853         assertTrue(claimed);
   1854 
   1855         // Re-claiming does nothing
   1856         claimed = connection.claimInterface(iface, true);
   1857         assertTrue(claimed);
   1858 
   1859         released = connection.releaseInterface(iface);
   1860         assertTrue(released);
   1861 
   1862         // Re-releasing is not allowed
   1863         released = connection.releaseInterface(iface);
   1864         assertFalse(released);
   1865 
   1866         // Using an unclaimed interface claims it automatically
   1867         int numSent = connection.bulkTransfer(getEndpoint(iface, UsbConstants.USB_DIR_OUT), null, 0,
   1868                 0);
   1869         assertEquals(0, numSent);
   1870 
   1871         released = connection.releaseInterface(iface);
   1872         assertTrue(released);
   1873 
   1874         runAndAssertException(() -> connection.claimInterface(null, true),
   1875                 NullPointerException.class);
   1876         runAndAssertException(() -> connection.claimInterface(null, false),
   1877                 NullPointerException.class);
   1878         runAndAssertException(() -> connection.releaseInterface(null), NullPointerException.class);
   1879     }
   1880 
   1881     /**
   1882      * Test all input parameters to {@link UsbDeviceConnection#setConfiguration} .
   1883      *
   1884      * <p>Note:
   1885      * <ul>
   1886      *     <li>The device under test only supports one configuration, hence changing configuration
   1887      * is not tested.</li>
   1888      *     <li>This test sets the current configuration again. This resets the device.</li>
   1889      * </ul></p>
   1890      *
   1891      * @param device the device under test
   1892      * @param connection The connection to use
   1893      * @param iface An interface of the device
   1894      *
   1895      * @throws Throwable
   1896      */
   1897     private void setConfigurationTests(@NonNull UsbDevice device,
   1898             @NonNull UsbDeviceConnection connection, @NonNull UsbInterface iface) throws Throwable {
   1899         assertTrue(device.getConfigurationCount() == 1);
   1900         boolean wasSet = connection.setConfiguration(device.getConfiguration(0));
   1901         assertTrue(wasSet);
   1902 
   1903         // Cannot set configuration for a device with a claimed interface
   1904         boolean claimed = connection.claimInterface(iface, false);
   1905         assertTrue(claimed);
   1906         wasSet = connection.setConfiguration(device.getConfiguration(0));
   1907         assertFalse(wasSet);
   1908         boolean released = connection.releaseInterface(iface);
   1909         assertTrue(released);
   1910 
   1911         runAndAssertException(() -> connection.setConfiguration(null), NullPointerException.class);
   1912     }
   1913 
   1914     /**
   1915      * Test all input parameters to {@link UsbDeviceConnection#setConfiguration} .
   1916      *
   1917      * <p>Note: The interface under test only supports one settings, hence changing the setting can
   1918      * not be tested.</p>
   1919      *
   1920      * @param connection The connection to use
   1921      * @param iface The interface to test
   1922      *
   1923      * @throws Throwable
   1924      */
   1925     private void setInterfaceTests(@NonNull UsbDeviceConnection connection,
   1926             @NonNull UsbInterface iface) throws Throwable {
   1927         boolean claimed = connection.claimInterface(iface, false);
   1928         assertTrue(claimed);
   1929         boolean wasSet = connection.setInterface(iface);
   1930         assertTrue(wasSet);
   1931         boolean released = connection.releaseInterface(iface);
   1932         assertTrue(released);
   1933 
   1934         // Setting the interface for an unclaimed interface automatically claims it
   1935         wasSet = connection.setInterface(iface);
   1936         assertTrue(wasSet);
   1937         released = connection.releaseInterface(iface);
   1938         assertTrue(released);
   1939 
   1940         runAndAssertException(() -> connection.setInterface(null), NullPointerException.class);
   1941     }
   1942 
   1943     /**
   1944      * Enumerate all known devices and check basic relationship between the properties.
   1945      */
   1946     private void enumerateDevices() throws Exception {
   1947         Set<Integer> knownDeviceIds = new ArraySet<>();
   1948 
   1949         for (Map.Entry<String, UsbDevice> entry : mUsbManager.getDeviceList().entrySet()) {
   1950             UsbDevice device = entry.getValue();
   1951 
   1952             assertEquals(entry.getKey(), device.getDeviceName());
   1953             assertNotNull(device.getDeviceName());
   1954 
   1955             // Device ID should be unique
   1956             assertFalse(knownDeviceIds.contains(device.getDeviceId()));
   1957             knownDeviceIds.add(device.getDeviceId());
   1958 
   1959             assertEquals(device.getDeviceName(), UsbDevice.getDeviceName(device.getDeviceId()));
   1960 
   1961             // Properties without constraints
   1962             device.getManufacturerName();
   1963             device.getProductName();
   1964             device.getVersion();
   1965             device.getSerialNumber();
   1966             device.getVendorId();
   1967             device.getProductId();
   1968             device.getDeviceClass();
   1969             device.getDeviceSubclass();
   1970             device.getDeviceProtocol();
   1971 
   1972             Set<UsbInterface> interfacesFromAllConfigs = new ArraySet<>();
   1973             Set<Integer> knownConfigurationIds = new ArraySet<>();
   1974             int numConfigurations = device.getConfigurationCount();
   1975             for (int configNum = 0; configNum < numConfigurations; configNum++) {
   1976                 UsbConfiguration config = device.getConfiguration(configNum);
   1977                 Set<Pair<Integer, Integer>> knownInterfaceIds = new ArraySet<>();
   1978 
   1979                 // Configuration ID should be unique
   1980                 assertFalse(knownConfigurationIds.contains(config.getId()));
   1981                 knownConfigurationIds.add(config.getId());
   1982 
   1983                 assertTrue(config.getMaxPower() >= 0);
   1984 
   1985                 // Properties without constraints
   1986                 config.getName();
   1987                 config.isSelfPowered();
   1988                 config.isRemoteWakeup();
   1989 
   1990                 int numInterfaces = config.getInterfaceCount();
   1991                 for (int interfaceNum = 0; interfaceNum < numInterfaces; interfaceNum++) {
   1992                     UsbInterface iface = config.getInterface(interfaceNum);
   1993                     interfacesFromAllConfigs.add(iface);
   1994 
   1995                     Pair<Integer, Integer> ifaceId = new Pair<>(iface.getId(),
   1996                             iface.getAlternateSetting());
   1997                     assertFalse(knownInterfaceIds.contains(ifaceId));
   1998                     knownInterfaceIds.add(ifaceId);
   1999 
   2000                     // Properties without constraints
   2001                     iface.getName();
   2002                     iface.getInterfaceClass();
   2003                     iface.getInterfaceSubclass();
   2004                     iface.getInterfaceProtocol();
   2005 
   2006                     int numEndpoints = iface.getEndpointCount();
   2007                     for (int endpointNum = 0; endpointNum < numEndpoints; endpointNum++) {
   2008                         UsbEndpoint endpoint = iface.getEndpoint(endpointNum);
   2009 
   2010                         assertEquals(endpoint.getAddress(),
   2011                                 endpoint.getEndpointNumber() | endpoint.getDirection());
   2012 
   2013                         assertTrue(endpoint.getDirection() == UsbConstants.USB_DIR_OUT ||
   2014                                 endpoint.getDirection() == UsbConstants.USB_DIR_IN);
   2015 
   2016                         assertTrue(endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_CONTROL ||
   2017                                 endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_ISOC ||
   2018                                 endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK ||
   2019                                 endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT);
   2020 
   2021                         assertTrue(endpoint.getMaxPacketSize() >= 0);
   2022                         assertTrue(endpoint.getInterval() >= 0);
   2023 
   2024                         // Properties without constraints
   2025                         endpoint.getAttributes();
   2026                     }
   2027                 }
   2028             }
   2029 
   2030             int numInterfaces = device.getInterfaceCount();
   2031             for (int interfaceNum = 0; interfaceNum < numInterfaces; interfaceNum++) {
   2032                 assertTrue(interfacesFromAllConfigs.contains(device.getInterface(interfaceNum)));
   2033             }
   2034         }
   2035     }
   2036 
   2037     /**
   2038      * Run tests.
   2039      *
   2040      * @param device The device to run the test against. This device is running
   2041      *               com.android.cts.verifierusbcompanion.DeviceTestCompanion
   2042      */
   2043     private void runTests(@NonNull UsbDevice device) {
   2044         try {
   2045             // Find the AOAP interface
   2046             ArrayList<String> allInterfaces = new ArrayList<>();
   2047             UsbInterface iface = null;
   2048             for (int i = 0; i < device.getConfigurationCount(); i++) {
   2049                 allInterfaces.add(device.getInterface(i).toString());
   2050 
   2051                 if (device.getInterface(i).getName().equals("Android Accessory Interface")) {
   2052                     iface = device.getInterface(i);
   2053                     break;
   2054                 }
   2055             }
   2056             assertNotNull("No \"Android Accessory Interface\" interface found in " + allInterfaces,
   2057                     iface);
   2058 
   2059             enumerateDevices();
   2060 
   2061             UsbDeviceConnection connection = mUsbManager.openDevice(device);
   2062             assertNotNull(connection);
   2063 
   2064             claimInterfaceTests(connection, iface);
   2065 
   2066             boolean claimed = connection.claimInterface(iface, false);
   2067             assertTrue(claimed);
   2068 
   2069             testIfCompanionZeroTerminates(connection, iface);
   2070 
   2071             usbRequestLegacyTests(connection, iface);
   2072             usbRequestTests(connection, iface);
   2073             parallelUsbRequestsTests(connection, iface);
   2074             ctrlTransferTests(connection);
   2075             bulkTransferTests(connection, iface);
   2076 
   2077             // Signal to the DeviceTestCompanion that there are no more transfer test
   2078             endTesting(connection, iface);
   2079             boolean released = connection.releaseInterface(iface);
   2080             assertTrue(released);
   2081 
   2082             setInterfaceTests(connection, iface);
   2083             setConfigurationTests(device, connection, iface);
   2084 
   2085             assertFalse(connection.getFileDescriptor() == -1);
   2086             assertNotNull(connection.getRawDescriptors());
   2087             assertFalse(connection.getRawDescriptors().length == 0);
   2088             assertEquals(device.getSerialNumber(), connection.getSerial());
   2089 
   2090             connection.close();
   2091 
   2092             // We should not be able to communicate with the device anymore
   2093             assertFalse(connection.claimInterface(iface, true));
   2094             assertFalse(connection.releaseInterface(iface));
   2095             assertFalse(connection.setConfiguration(device.getConfiguration(0)));
   2096             assertFalse(connection.setInterface(iface));
   2097             assertTrue(connection.getFileDescriptor() == -1);
   2098             assertNull(connection.getRawDescriptors());
   2099             assertNull(connection.getSerial());
   2100             assertEquals(-1, connection.bulkTransfer(getEndpoint(iface, UsbConstants.USB_DIR_OUT),
   2101                     new byte[1], 1, 0));
   2102             assertEquals(-1, connection.bulkTransfer(getEndpoint(iface, UsbConstants.USB_DIR_OUT),
   2103                     null, 0, 0));
   2104             assertEquals(-1, connection.bulkTransfer(getEndpoint(iface, UsbConstants.USB_DIR_IN),
   2105                     null, 0, 0));
   2106             assertFalse((new UsbRequest()).initialize(connection, getEndpoint(iface,
   2107                     UsbConstants.USB_DIR_IN)));
   2108 
   2109             // Double close should do no harm
   2110             connection.close();
   2111 
   2112             setTestResultAndFinish(true);
   2113         } catch (Throwable e) {
   2114             fail(null, e);
   2115         }
   2116     }
   2117 
   2118     @Override
   2119     protected void onDestroy() {
   2120         if (mUsbDeviceConnectionReceiver != null) {
   2121             unregisterReceiver(mUsbDeviceConnectionReceiver);
   2122         }
   2123 
   2124         super.onDestroy();
   2125     }
   2126 }
   2127