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 import static org.junit.Assert.assertNotNull; 21 22 import android.content.Context; 23 import android.hardware.usb.UsbAccessory; 24 import android.hardware.usb.UsbManager; 25 import android.os.ParcelFileDescriptor; 26 import android.support.annotation.NonNull; 27 28 import java.io.IOException; 29 import java.io.InputStream; 30 import java.io.OutputStream; 31 import java.nio.ByteBuffer; 32 import java.nio.charset.Charset; 33 34 /** 35 * Companion code for com.android.cts.verifier.usb.device.UsbDeviceTestActivity 36 */ 37 class DeviceTestCompanion extends TestCompanion { 38 private static final int MAX_BUFFER_SIZE = 16384; 39 private static final int OVERSIZED_BUFFER_SIZE = MAX_BUFFER_SIZE + 100; 40 41 DeviceTestCompanion(@NonNull Context context, @NonNull TestObserver observer) { 42 super(context, observer); 43 } 44 45 /** 46 * Switches to next test: 47 * <ol> 48 * <li>Send result of last test to device under test</li> 49 * <li>Receive name of next test</li> 50 * </ol> 51 * 52 * @param is The stream to read from 53 * @param os The stream to write to 54 * @param lastTestSucceeded If the last test succeeded 55 * 56 * @return Name of next test or empty string if there is no next test 57 * 58 * @throws IOException if the communication with the device under test is disturbed 59 */ 60 private String nextTest(@NonNull InputStream is, @NonNull OutputStream os, 61 boolean lastTestSucceeded) throws IOException { 62 // Read next test name 63 byte[] sizeBuffer = new byte[1]; 64 int numRead = is.read(sizeBuffer); 65 assertEquals(1, numRead); 66 67 byte[] nextTestNameBytes = new byte[sizeBuffer[0]]; 68 numRead = is.read(nextTestNameBytes); 69 assertEquals(sizeBuffer[0], numRead); 70 71 // Write test result 72 os.write(lastTestSucceeded ? (byte) 1 : (byte) 0); 73 74 // Wait for ready signal 75 numRead = is.read(sizeBuffer); 76 assertEquals(42, sizeBuffer[0]); 77 assertEquals(1, numRead); 78 79 return Charset.forName("UTF-8").decode( 80 ByteBuffer.wrap(nextTestNameBytes)).toString().trim(); 81 } 82 83 /** 84 * Read some bytes and send them back to the sender. 85 * 86 * @param is Stream to read from 87 * @param os Stream to write to 88 * @param size The number of bytes to read 89 * 90 * @return {@code true} iff the bytes could be read and written 91 * 92 * @throws IOException 93 */ 94 private boolean echoBytes(@NonNull InputStream is, @NonNull OutputStream os, int size) 95 throws IOException { 96 byte[] buffer = new byte[size]; 97 98 int numRead = is.read(buffer); 99 if (numRead != size) { 100 return false; 101 } 102 103 os.write(buffer); 104 return true; 105 } 106 107 /** 108 * Read packages of (size:data) send the data back to the sender. Do this until the package size 109 * is declared as 0. 110 * 111 * @param is Stream to read from 112 * @param os Stream to write to 113 * 114 * @return {@code true} iff the bytes could be read and written 115 * 116 * @throws IOException 117 */ 118 private boolean echoUntilStopSignal(@NonNull InputStream is, @NonNull OutputStream os) { 119 try { 120 while (!shouldAbort()) { 121 byte[] dataBytes = new byte[9]; 122 int numRead = is.read(dataBytes); 123 assertEquals(9, numRead); 124 125 if (shouldAbort() || dataBytes[0] == 0) { 126 break; 127 } 128 129 os.write(dataBytes); 130 } 131 132 return true; 133 } catch (IOException e) { 134 return false; 135 } 136 } 137 138 /** 139 * Some N and older accessories do not send a zero sized package after a request that is a 140 * multiple of the maximum package size. Hence send such a package to let the device under test 141 * figure out how the accessory driver is implemented on this side. 142 * 143 * @param os The stream to send to 144 */ 145 private void helpToFigureOutIfCompanionZeroTerminates(@NonNull OutputStream os) 146 throws IOException { 147 // 1024 is a multiple of all package sizes that are currently known 148 os.write(new byte[1024]); 149 os.write(new byte[1]); 150 } 151 152 @Override 153 protected void runTest() throws Throwable { 154 UsbAccessory accessory; 155 156 final UsbAccessory[] accessoryReceiver = new UsbAccessory[1]; 157 AccessoryAttachmentHandler.AccessoryAttachmentObserver accessoryAttachmentObserver = 158 a -> { 159 synchronized (DeviceTestCompanion.this) { 160 accessoryReceiver[0] = a; 161 notifyAll(); 162 } 163 }; 164 AccessoryAttachmentHandler.addObserver(accessoryAttachmentObserver); 165 166 updateStatus("Waiting for device under test to connect"); 167 synchronized (this) { 168 do { 169 wait(); 170 } while (accessoryReceiver[0] == null); 171 172 accessory = accessoryReceiver[0]; 173 } 174 175 updateStatus("Connecting to " + accessory.getDescription()); 176 UsbManager usbManager = getContext().getSystemService(UsbManager.class); 177 ParcelFileDescriptor fd = usbManager.openAccessory(accessory); 178 assertNotNull(fd); 179 180 try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(fd)) { 181 try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(fd)) { 182 String testName; 183 boolean isSuccess = true; 184 185 do { 186 testName = nextTest(is, os, isSuccess); 187 188 updateStatus("Running test \"" + testName + "\""); 189 190 switch (testName) { 191 case "does companion zero terminate": 192 helpToFigureOutIfCompanionZeroTerminates(os); 193 isSuccess = true; 194 break; 195 case "Echo 1 byte": 196 isSuccess = echoBytes(is, os, 1); 197 break; 198 case "Echo 42 bytes": 199 isSuccess = echoBytes(is, os, 42); 200 break; 201 case "Echo max bytes": 202 isSuccess = echoBytes(is, os, MAX_BUFFER_SIZE); 203 break; 204 case "Echo oversized buffer": 205 // The bytes beyond MAX_BUFFER_SIZE got ignored when sending 206 isSuccess = echoBytes(is, os, MAX_BUFFER_SIZE); 207 break; 208 case "Receive oversized buffer": { 209 byte[] buffer = new byte[OVERSIZED_BUFFER_SIZE]; 210 buffer[0] = 1; 211 buffer[MAX_BUFFER_SIZE - 1] = 2; 212 buffer[MAX_BUFFER_SIZE] = 3; 213 buffer[OVERSIZED_BUFFER_SIZE - 1] = 4; 214 215 os.write(buffer); 216 217 isSuccess = true; 218 } 219 break; 220 case "Receive byte after some time": 221 Thread.sleep(200); 222 os.write(new byte[1]); 223 isSuccess = true; 224 break; 225 case "Receive byte immediately": 226 os.write(new byte[1]); 227 isSuccess = true; 228 break; 229 case "Echo until stop signal": 230 isSuccess = echoUntilStopSignal(is, os); 231 break; 232 case "done": 233 isSuccess = true; 234 break; 235 default: 236 throw new IllegalStateException("unknown test"); 237 } 238 } while (!"done".equals(testName)); 239 } 240 } 241 242 AccessoryAttachmentHandler.removeObserver(accessoryAttachmentObserver); 243 } 244 } 245