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.verifierusbcompanion; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.app.PendingIntent; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.hardware.usb.UsbConstants; 27 import android.hardware.usb.UsbDevice; 28 import android.hardware.usb.UsbDeviceConnection; 29 import android.hardware.usb.UsbEndpoint; 30 import android.hardware.usb.UsbInterface; 31 import android.hardware.usb.UsbManager; 32 import android.support.annotation.NonNull; 33 34 import java.nio.ByteBuffer; 35 import java.nio.charset.Charset; 36 37 /** 38 * Companion code for com.android.cts.verifier.usb.device.UsbAccessoryTestActivity 39 */ 40 class AccessoryTestCompanion extends TestCompanion { 41 private static final int TIMEOUT_MILLIS = 500; 42 private static final int MAX_BUFFER_SIZE = 16384; 43 private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB 44 45 private static final String ACTION_USB_PERMISSION = 46 "com.android.cts.verifierusbcompanion.USB_PERMISSION"; 47 48 private UsbManager mUsbManager; 49 private BroadcastReceiver mUsbDeviceConnectionReceiver; 50 private UsbDevice mDevice; 51 52 AccessoryTestCompanion(@NonNull Context context, @NonNull TestObserver observer) { 53 super(context, observer); 54 } 55 56 /** 57 * @throws Throwable 58 */ 59 @Override 60 protected void runTest() throws Throwable { 61 updateStatus("Waiting for device under test to connect"); 62 63 mUsbManager = getContext().getSystemService(UsbManager.class); 64 65 mUsbDeviceConnectionReceiver = new BroadcastReceiver() { 66 @Override 67 public void onReceive(Context context, Intent intent) { 68 synchronized (AccessoryTestCompanion.this) { 69 UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); 70 71 switch (intent.getAction()) { 72 case UsbManager.ACTION_USB_DEVICE_ATTACHED: 73 if (mUsbManager.hasPermission(device)) { 74 onDeviceAccessPermitted(device); 75 } else { 76 mUsbManager.requestPermission(device, 77 PendingIntent.getBroadcast(getContext(), 0, 78 new Intent(ACTION_USB_PERMISSION), 0)); 79 } 80 break; 81 case ACTION_USB_PERMISSION: 82 boolean granted = intent.getBooleanExtra( 83 UsbManager.EXTRA_PERMISSION_GRANTED, false); 84 85 if (granted) { 86 onDeviceAccessPermitted(device); 87 } else { 88 fail("Permission to connect to " + device.getProductName() 89 + " not granted"); 90 } 91 break; 92 } 93 } 94 } 95 }; 96 97 IntentFilter filter = new IntentFilter(); 98 filter.addAction(ACTION_USB_PERMISSION); 99 filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); 100 101 getContext().registerReceiver(mUsbDeviceConnectionReceiver, filter); 102 103 synchronized (this) { 104 while (mDevice == null) { 105 wait(); 106 } 107 } 108 109 UsbInterface iface = null; 110 for (int i = 0; i < mDevice.getConfigurationCount(); i++) { 111 if (mDevice.getInterface(i).getName().equals("Android Accessory Interface")) { 112 iface = mDevice.getInterface(i); 113 break; 114 } 115 } 116 117 UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN); 118 UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT); 119 120 UsbDeviceConnection connection = mUsbManager.openDevice(mDevice); 121 122 try { 123 String testName; 124 do { 125 testName = nextTest(connection, in, out, true); 126 127 updateStatus("Running test \"" + testName + "\""); 128 129 switch (testName) { 130 case "echo 32 bytes": { 131 byte[] buffer = new byte[32]; 132 133 int numTransferred = connection.bulkTransfer(in, buffer, 32, 0); 134 assertEquals(32, numTransferred); 135 136 numTransferred = connection.bulkTransfer(out, buffer, 32, 0); 137 assertEquals(32, numTransferred); 138 } 139 break; 140 141 case "echo two 16 byte transfers as one": { 142 byte[] buffer = new byte[48]; 143 144 // We receive the individual transfers even if we wait for more data 145 int numTransferred = connection.bulkTransfer(in, buffer, 32, 0); 146 assertEquals(16, numTransferred); 147 numTransferred = connection.bulkTransfer(in, buffer, 16, 32, 0); 148 assertEquals(16, numTransferred); 149 150 numTransferred = connection.bulkTransfer(out, buffer, 32, 0); 151 assertEquals(32, numTransferred); 152 } 153 break; 154 155 case "echo 32 bytes as two 16 byte transfers": { 156 byte[] buffer = new byte[32]; 157 158 int numTransferred = connection.bulkTransfer(in, buffer, 32, 0); 159 assertEquals(32, numTransferred); 160 161 numTransferred = connection.bulkTransfer(out, buffer, 16, 0); 162 assertEquals(16, numTransferred); 163 numTransferred = connection.bulkTransfer(out, buffer, 16, 16, 0); 164 assertEquals(16, numTransferred); 165 } 166 break; 167 168 case "measure out transfer speed": { 169 byte[] buffer = new byte[MAX_BUFFER_SIZE]; 170 171 long bytesRead = 0; 172 while (bytesRead < TEST_DATA_SIZE_THRESHOLD) { 173 int numTransferred = connection.bulkTransfer( 174 in, buffer, MAX_BUFFER_SIZE, 0); 175 bytesRead += numTransferred; 176 } 177 178 // MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero 179 // sized package after. Some older devices do not send these packages, but 180 // this is not compliant anymore. 181 int numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS); 182 assertEquals(0, numTransferred); 183 184 byte[] confirm = new byte[] {1}; 185 numTransferred = connection.bulkTransfer(out, confirm, 1, 0); 186 assertEquals(1, numTransferred); 187 } 188 break; 189 190 case "measure in transfer speed": { 191 byte[] buffer = new byte[MAX_BUFFER_SIZE]; 192 193 long bytesWritten = 0; 194 int numTransferred = 0; 195 while (bytesWritten < TEST_DATA_SIZE_THRESHOLD) { 196 numTransferred = 197 connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0); 198 assertEquals(MAX_BUFFER_SIZE, numTransferred); 199 bytesWritten += numTransferred; 200 } 201 202 byte[] confirm = new byte[] {1}; 203 numTransferred = connection.bulkTransfer(out, confirm, 1, 0); 204 assertEquals(1, numTransferred); 205 } 206 break; 207 208 case "echo max bytes": { 209 byte[] buffer = new byte[MAX_BUFFER_SIZE]; 210 211 int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE, 212 0); 213 assertEquals(MAX_BUFFER_SIZE, numTransferred); 214 215 // MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero 216 // sized package after. Some older devices do not send these packages, but 217 // this is not compliant anymore. 218 numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS); 219 assertEquals(0, numTransferred); 220 221 numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0); 222 assertEquals(MAX_BUFFER_SIZE, numTransferred); 223 } 224 break; 225 226 case "echo max*2 bytes": { 227 byte[] buffer = new byte[MAX_BUFFER_SIZE * 2]; 228 229 int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE, 230 0); 231 assertEquals(MAX_BUFFER_SIZE, numTransferred); 232 233 // Oversized transfers get split into two 234 numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE, 235 MAX_BUFFER_SIZE, 0); 236 assertEquals(MAX_BUFFER_SIZE, numTransferred); 237 238 // MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero 239 // sized package after. Some older devices do not send these packages, but 240 // this is not compliant anymore. 241 numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS); 242 assertEquals(0, numTransferred); 243 244 numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 100); 245 assertEquals(MAX_BUFFER_SIZE, numTransferred); 246 247 numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 248 MAX_BUFFER_SIZE, 0); 249 assertEquals(MAX_BUFFER_SIZE, numTransferred); 250 } 251 break; 252 253 default: 254 break; 255 } 256 } while (!testName.equals("done")); 257 } finally { 258 connection.close(); 259 } 260 } 261 262 /** 263 * If access to a device was permitted either make the device an accessory if it already is, 264 * start the test. 265 * 266 * @param device The device access was permitted to 267 */ 268 private void onDeviceAccessPermitted(@NonNull UsbDevice device) { 269 if (!AoapInterface.isDeviceInAoapMode(device)) { 270 UsbDeviceConnection connection = mUsbManager.openDevice(device); 271 try { 272 makeThisDeviceAnAccessory(connection); 273 } finally { 274 connection.close(); 275 } 276 } else { 277 getContext().unregisterReceiver(mUsbDeviceConnectionReceiver); 278 mUsbDeviceConnectionReceiver = null; 279 280 synchronized (AccessoryTestCompanion.this) { 281 mDevice = device; 282 283 AccessoryTestCompanion.this.notifyAll(); 284 } 285 } 286 } 287 288 @NonNull private String nextTest(@NonNull UsbDeviceConnection connection, 289 @NonNull UsbEndpoint in, @NonNull UsbEndpoint out, boolean isSuccess) { 290 byte[] sizeBuffer = new byte[1]; 291 292 updateStatus("Waiting for next test"); 293 294 int numTransferred = connection.bulkTransfer(in, sizeBuffer, 1, 0); 295 assertEquals(1, numTransferred); 296 297 int nameSize = sizeBuffer[0]; 298 299 byte[] nameBuffer = new byte[nameSize]; 300 numTransferred = connection.bulkTransfer(in, nameBuffer, nameSize, 0); 301 assertEquals(nameSize, numTransferred); 302 303 numTransferred = connection.bulkTransfer(out, new byte[]{(byte) (isSuccess ? 1 : 0)}, 1, 0); 304 assertEquals(1, numTransferred); 305 306 numTransferred = connection.bulkTransfer(in, new byte[1], 1, 0); 307 assertEquals(1, numTransferred); 308 309 String name = Charset.forName("UTF-8").decode(ByteBuffer.wrap(nameBuffer)).toString(); 310 311 updateStatus("Next test is " + name); 312 313 return name; 314 } 315 316 /** 317 * Search an {@link UsbInterface} for an {@link UsbEndpoint endpoint} of a certain direction. 318 * 319 * @param iface The interface to search 320 * @param direction The direction the endpoint is for. 321 * 322 * @return The first endpoint found or {@link null}. 323 */ 324 @NonNull private UsbEndpoint getEndpoint(@NonNull UsbInterface iface, int direction) { 325 for (int i = 0; i < iface.getEndpointCount(); i++) { 326 UsbEndpoint ep = iface.getEndpoint(i); 327 if (ep.getDirection() == direction) { 328 return ep; 329 } 330 } 331 332 throw new IllegalStateException("Could not find " + direction + " endpoint in " 333 + iface.getName()); 334 } 335 336 /** 337 * Converts the device under test into an Android accessory. Accessories are USB hosts that are 338 * detected on the device side via {@link UsbManager#getAccessoryList()}. 339 * 340 * @param connection The connection to the USB device 341 */ 342 private void makeThisDeviceAnAccessory(@NonNull UsbDeviceConnection connection) { 343 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER, 344 "Android"); 345 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL, 346 "Android device"); 347 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION, 348 "Android device running CTS verifier"); 349 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION, "1"); 350 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI, 351 "https://source.android.com/compatibility/cts/verifier.html"); 352 AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, "0"); 353 AoapInterface.sendAoapStart(connection); 354 } 355 } 356