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