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.accessory; 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.assertNotNull; 24 import static org.junit.Assert.assertTrue; 25 26 import android.hardware.usb.UsbAccessory; 27 import android.hardware.usb.UsbManager; 28 import android.os.AsyncTask; 29 import android.os.Bundle; 30 import android.os.ParcelFileDescriptor; 31 import android.os.SystemClock; 32 import android.support.annotation.NonNull; 33 import android.support.annotation.Nullable; 34 import android.util.Log; 35 import android.view.View; 36 import android.widget.ProgressBar; 37 import android.widget.TextView; 38 39 import com.android.compatibility.common.util.ResultType; 40 import com.android.compatibility.common.util.ResultUnit; 41 import com.android.cts.verifier.PassFailButtons; 42 import com.android.cts.verifier.R; 43 44 import java.io.IOException; 45 import java.io.InputStream; 46 import java.io.OutputStream; 47 import java.nio.ByteBuffer; 48 import java.nio.CharBuffer; 49 import java.nio.charset.Charset; 50 import java.util.Arrays; 51 import java.util.Random; 52 53 /** 54 * Guide the user to run test for the USB accessory interface. 55 */ 56 public class UsbAccessoryTestActivity extends PassFailButtons.Activity implements 57 AccessoryAttachmentHandler.AccessoryAttachmentObserver { 58 private static final String LOG_TAG = UsbAccessoryTestActivity.class.getSimpleName(); 59 private static final int MAX_BUFFER_SIZE = 16384; 60 61 private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB 62 63 private TextView mStatus; 64 private ProgressBar mProgress; 65 66 @Override 67 protected void onCreate(@Nullable Bundle savedInstanceState) { 68 super.onCreate(savedInstanceState); 69 70 setContentView(R.layout.usb_main); 71 setInfoResources( 72 R.string.usb_accessory_test, R.string.usb_accessory_test_info, -1); 73 74 mStatus = (TextView) findViewById(R.id.status); 75 mProgress = (ProgressBar) findViewById(R.id.progress_bar); 76 mStatus.setText(R.string.usb_accessory_test_step1); 77 getPassButton().setEnabled(false); 78 79 AccessoryAttachmentHandler.addObserver(this); 80 } 81 82 @Override 83 public void onAttached(UsbAccessory accessory) { 84 mStatus.setText(R.string.usb_accessory_test_step2); 85 mProgress.setVisibility(View.VISIBLE); 86 87 AccessoryAttachmentHandler.removeObserver(this); 88 89 UsbManager usbManager = getSystemService(UsbManager.class); 90 91 (new AsyncTask<Void, Void, Throwable>() { 92 @Override 93 protected Throwable doInBackground(Void... params) { 94 try { 95 assertEquals("Android CTS", accessory.getManufacturer()); 96 assertEquals("Android CTS test companion device", accessory.getModel()); 97 assertEquals("Android device running CTS verifier", accessory.getDescription()); 98 assertEquals("2", accessory.getVersion()); 99 assertEquals("https://source.android.com/compatibility/cts/verifier.html", 100 accessory.getUri()); 101 assertEquals("0", accessory.getSerial()); 102 103 assertTrue(Arrays.asList(usbManager.getAccessoryList()).contains(accessory)); 104 105 runAndAssertException(() -> usbManager.openAccessory(null), 106 NullPointerException.class); 107 108 ParcelFileDescriptor accessoryFd = usbManager.openAccessory(accessory); 109 assertNotNull(accessoryFd); 110 111 try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream( 112 accessoryFd)) { 113 try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream( 114 accessoryFd)) { 115 byte[] origBuffer32 = new byte[32]; 116 (new Random()).nextBytes(origBuffer32); 117 118 byte[] origBufferMax = new byte[MAX_BUFFER_SIZE]; 119 (new Random()).nextBytes(origBufferMax); 120 121 byte[] bufferMax = new byte[MAX_BUFFER_SIZE]; 122 byte[] buffer32 = new byte[32]; 123 byte[] buffer16 = new byte[16]; 124 125 // Echo a transfer 126 nextTest(is, os, "echo 32 bytes"); 127 128 os.write(origBuffer32); 129 130 int numRead = is.read(buffer32); 131 assertEquals(32, numRead); 132 assertArrayEquals(origBuffer32, buffer32); 133 134 // Receive less data than available 135 nextTest(is, os, "echo 32 bytes"); 136 137 os.write(origBuffer32); 138 139 numRead = is.read(buffer16); 140 assertEquals(16, numRead); 141 assertArrayEquals(Arrays.copyOf(origBuffer32, 16), buffer16); 142 143 // If a transfer was only partially read, the rest of the transfer is 144 // lost. We cannot read the second part, hence proceed to the next test. 145 146 // Send two transfers in a row 147 nextTest(is, os, "echo two 16 byte transfers as one"); 148 149 os.write(Arrays.copyOf(origBuffer32, 16)); 150 os.write(Arrays.copyOfRange(origBuffer32, 16, 32)); 151 152 numRead = is.read(buffer32); 153 assertEquals(32, numRead); 154 assertArrayEquals(origBuffer32, buffer32); 155 156 // Receive two transfers in a row into a buffer that is bigger than the 157 // transfer 158 nextTest(is, os, "echo 32 bytes as two 16 byte transfers"); 159 160 os.write(origBuffer32); 161 162 // Even though the buffer would hold 32 bytes the input stream will read 163 // the transfers individually 164 numRead = is.read(buffer32); 165 assertEquals(16, numRead); 166 assertArrayEquals(Arrays.copyOf(origBuffer32, 16), 167 Arrays.copyOf(buffer32, 16)); 168 169 numRead = is.read(buffer32); 170 assertEquals(16, numRead); 171 assertArrayEquals(Arrays.copyOfRange(origBuffer32, 16, 32), 172 Arrays.copyOf(buffer32, 16)); 173 174 // Echo a buffer with the maximum size 175 nextTest(is, os, "echo max bytes"); 176 177 os.write(origBufferMax); 178 179 numRead = is.read(bufferMax); 180 assertEquals(MAX_BUFFER_SIZE, numRead); 181 assertArrayEquals(origBufferMax, bufferMax); 182 183 // Echo a buffer with twice the maximum size 184 nextTest(is, os, "echo max*2 bytes"); 185 186 byte[] oversizeBuffer = new byte[MAX_BUFFER_SIZE * 2]; 187 System.arraycopy(origBufferMax, 0, oversizeBuffer, 0, MAX_BUFFER_SIZE); 188 System.arraycopy(origBufferMax, 0, oversizeBuffer, MAX_BUFFER_SIZE, 189 MAX_BUFFER_SIZE); 190 os.write(oversizeBuffer); 191 192 // The other side can not write more than the maximum size at once, 193 // hence we get two transfers in return 194 numRead = is.read(bufferMax); 195 assertEquals(MAX_BUFFER_SIZE, numRead); 196 assertArrayEquals(origBufferMax, bufferMax); 197 198 numRead = is.read(bufferMax); 199 assertEquals(MAX_BUFFER_SIZE, numRead); 200 assertArrayEquals(origBufferMax, bufferMax); 201 202 nextTest(is, os, "measure out transfer speed"); 203 204 byte[] result = new byte[1]; 205 long bytesSent = 0; 206 long timeStart = SystemClock.elapsedRealtime(); 207 while (bytesSent < TEST_DATA_SIZE_THRESHOLD) { 208 os.write(origBufferMax); 209 bytesSent += MAX_BUFFER_SIZE; 210 } 211 numRead = is.read(result); 212 double speedKBPS = (bytesSent * 8 * 1000. / 1024.) 213 / (SystemClock.elapsedRealtime() - timeStart); 214 assertEquals(1, numRead); 215 assertEquals(1, result[0]); 216 // We don't mandate min speed for now, let's collect data on what it is. 217 getReportLog().setSummary( 218 "Output USB accesory transfer speed", 219 speedKBPS, 220 ResultType.HIGHER_BETTER, 221 ResultUnit.KBPS); 222 Log.i(LOG_TAG, "Write data transfer speed is " + speedKBPS + "KBPS"); 223 224 nextTest(is, os, "measure in transfer speed"); 225 226 long bytesRead = 0; 227 timeStart = SystemClock.elapsedRealtime(); 228 while (bytesRead < TEST_DATA_SIZE_THRESHOLD) { 229 numRead = is.read(bufferMax); 230 bytesRead += numRead; 231 } 232 numRead = is.read(result); 233 speedKBPS = (bytesRead * 8 * 1000. / 1024.) 234 / (SystemClock.elapsedRealtime() - timeStart); 235 assertEquals(1, numRead); 236 assertEquals(1, result[0]); 237 // We don't mandate min speed for now, let's collect data on what it is. 238 getReportLog().setSummary( 239 "Input USB accesory transfer speed", 240 speedKBPS, 241 ResultType.HIGHER_BETTER, 242 ResultUnit.KBPS); 243 Log.i(LOG_TAG, "Read data transfer speed is " + speedKBPS + "KBPS"); 244 245 nextTest(is, os, "done"); 246 } 247 } 248 249 accessoryFd.close(); 250 251 return null; 252 } catch (Throwable t) { 253 return t; 254 } 255 } 256 257 @Override 258 protected void onPostExecute(Throwable t) { 259 if (t == null) { 260 setTestResultAndFinish(true); 261 } else { 262 fail(null, t); 263 } 264 } 265 }).execute(); 266 } 267 268 /** 269 * Signal to the companion device that we want to switch to the next test. 270 * 271 * @param is The input stream from the companion device 272 * @param os The output stream from the companion device 273 * @param testName The name of the new test 274 */ 275 private boolean nextTest(@NonNull InputStream is, @NonNull OutputStream os, 276 @NonNull String testName) throws IOException { 277 Log.i(LOG_TAG, "Init new test " + testName); 278 279 ByteBuffer nameBuffer = Charset.forName("UTF-8").encode(CharBuffer.wrap(testName)); 280 byte[] sizeBuffer = {(byte) nameBuffer.limit()}; 281 282 os.write(sizeBuffer); 283 os.write(Arrays.copyOf(nameBuffer.array(), nameBuffer.limit())); 284 285 int ret = is.read(); 286 if (ret <= 0) { 287 Log.i(LOG_TAG, "Last test failed " + ret); 288 return false; 289 } 290 291 os.write(0); 292 293 Log.i(LOG_TAG, "Running " + testName); 294 295 return true; 296 } 297 298 @Override 299 protected void onDestroy() { 300 AccessoryAttachmentHandler.removeObserver(this); 301 302 super.onDestroy(); 303 } 304 305 /** 306 * Indicate that the test failed. 307 */ 308 private void fail(@Nullable String s, @Nullable Throwable e) { 309 Log.e(LOG_TAG, s, e); 310 setTestResultAndFinish(false); 311 } 312 } 313