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