Home | History | Annotate | Download | only in verifierusbcompanion
      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