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