blob: 0389584b809fb7be731e14bb1eb68f1ad9457d17 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.cts.verifier.usb.accessory;
import static com.android.cts.verifier.usb.Util.runAndAssertException;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;
/**
* Guide the user to run test for the USB accessory interface.
*/
public class UsbAccessoryTestActivity extends PassFailButtons.Activity implements
AccessoryAttachmentHandler.AccessoryAttachmentObserver {
private static final String LOG_TAG = UsbAccessoryTestActivity.class.getSimpleName();
private static final int MAX_BUFFER_SIZE = 16384;
private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB
private TextView mStatus;
private ProgressBar mProgress;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.usb_main);
setInfoResources(
R.string.usb_accessory_test, R.string.usb_accessory_test_info, -1);
mStatus = (TextView) findViewById(R.id.status);
mProgress = (ProgressBar) findViewById(R.id.progress_bar);
mStatus.setText(R.string.usb_accessory_test_step1);
getPassButton().setEnabled(false);
AccessoryAttachmentHandler.addObserver(this);
}
@Override
public void onAttached(UsbAccessory accessory) {
mStatus.setText(R.string.usb_accessory_test_step2);
mProgress.setVisibility(View.VISIBLE);
AccessoryAttachmentHandler.removeObserver(this);
UsbManager usbManager = getSystemService(UsbManager.class);
(new AsyncTask<Void, Void, Throwable>() {
@Override
protected Throwable doInBackground(Void... params) {
try {
assertEquals("Android device running CTS verifier", accessory.getDescription());
assertEquals("Android", accessory.getManufacturer());
assertEquals("Android device", accessory.getModel());
assertEquals("0", accessory.getSerial());
assertEquals("https://source.android.com/compatibility/cts/verifier.html",
accessory.getUri());
assertEquals("1", accessory.getVersion());
assertTrue(Arrays.asList(usbManager.getAccessoryList()).contains(accessory));
runAndAssertException(() -> usbManager.openAccessory(null),
NullPointerException.class);
ParcelFileDescriptor accessoryFd = usbManager.openAccessory(accessory);
assertNotNull(accessoryFd);
try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
accessoryFd)) {
try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(
accessoryFd)) {
byte[] origBuffer32 = new byte[32];
(new Random()).nextBytes(origBuffer32);
byte[] origBufferMax = new byte[MAX_BUFFER_SIZE];
(new Random()).nextBytes(origBufferMax);
byte[] bufferMax = new byte[MAX_BUFFER_SIZE];
byte[] buffer32 = new byte[32];
byte[] buffer16 = new byte[16];
// Echo a transfer
nextTest(is, os, "echo 32 bytes");
os.write(origBuffer32);
int numRead = is.read(buffer32);
assertEquals(32, numRead);
assertArrayEquals(origBuffer32, buffer32);
// Receive less data than available
nextTest(is, os, "echo 32 bytes");
os.write(origBuffer32);
numRead = is.read(buffer16);
assertEquals(16, numRead);
assertArrayEquals(Arrays.copyOf(origBuffer32, 16), buffer16);
// If a transfer was only partially read, the rest of the transfer is
// lost. We cannot read the second part, hence proceed to the next test.
// Send two transfers in a row
nextTest(is, os, "echo two 16 byte transfers as one");
os.write(Arrays.copyOf(origBuffer32, 16));
os.write(Arrays.copyOfRange(origBuffer32, 16, 32));
numRead = is.read(buffer32);
assertEquals(32, numRead);
assertArrayEquals(origBuffer32, buffer32);
// Receive two transfers in a row into a buffer that is bigger than the
// transfer
nextTest(is, os, "echo 32 bytes as two 16 byte transfers");
os.write(origBuffer32);
// Even though the buffer would hold 32 bytes the input stream will read
// the transfers individually
numRead = is.read(buffer32);
assertEquals(16, numRead);
assertArrayEquals(Arrays.copyOf(origBuffer32, 16),
Arrays.copyOf(buffer32, 16));
numRead = is.read(buffer32);
assertEquals(16, numRead);
assertArrayEquals(Arrays.copyOfRange(origBuffer32, 16, 32),
Arrays.copyOf(buffer32, 16));
// Echo a buffer with the maximum size
nextTest(is, os, "echo max bytes");
os.write(origBufferMax);
numRead = is.read(bufferMax);
assertEquals(MAX_BUFFER_SIZE, numRead);
assertArrayEquals(origBufferMax, bufferMax);
// Echo a buffer with twice the maximum size
nextTest(is, os, "echo max*2 bytes");
byte[] oversizeBuffer = new byte[MAX_BUFFER_SIZE * 2];
System.arraycopy(origBufferMax, 0, oversizeBuffer, 0, MAX_BUFFER_SIZE);
System.arraycopy(origBufferMax, 0, oversizeBuffer, MAX_BUFFER_SIZE,
MAX_BUFFER_SIZE);
os.write(oversizeBuffer);
// The other side can not write more than the maximum size at once,
// hence we get two transfers in return
numRead = is.read(bufferMax);
assertEquals(MAX_BUFFER_SIZE, numRead);
assertArrayEquals(origBufferMax, bufferMax);
numRead = is.read(bufferMax);
assertEquals(MAX_BUFFER_SIZE, numRead);
assertArrayEquals(origBufferMax, bufferMax);
nextTest(is, os, "measure out transfer speed");
byte[] result = new byte[1];
long bytesSent = 0;
long timeStart = SystemClock.elapsedRealtime();
while (bytesSent < TEST_DATA_SIZE_THRESHOLD) {
os.write(origBufferMax);
bytesSent += MAX_BUFFER_SIZE;
}
numRead = is.read(result);
double speedKBPS = (bytesSent * 8 * 1000. / 1024.)
/ (SystemClock.elapsedRealtime() - timeStart);
assertEquals(1, numRead);
assertEquals(1, result[0]);
// We don't mandate min speed for now, let's collect data on what it is.
getReportLog().setSummary(
"Output USB accesory transfer speed",
speedKBPS,
ResultType.HIGHER_BETTER,
ResultUnit.KBPS);
Log.i(LOG_TAG, "Write data transfer speed is " + speedKBPS + "KBPS");
nextTest(is, os, "measure in transfer speed");
long bytesRead = 0;
timeStart = SystemClock.elapsedRealtime();
while (bytesRead < TEST_DATA_SIZE_THRESHOLD) {
numRead = is.read(bufferMax);
bytesRead += numRead;
}
numRead = is.read(result);
speedKBPS = (bytesRead * 8 * 1000. / 1024.)
/ (SystemClock.elapsedRealtime() - timeStart);
assertEquals(1, numRead);
assertEquals(1, result[0]);
// We don't mandate min speed for now, let's collect data on what it is.
getReportLog().setSummary(
"Input USB accesory transfer speed",
speedKBPS,
ResultType.HIGHER_BETTER,
ResultUnit.KBPS);
Log.i(LOG_TAG, "Read data transfer speed is " + speedKBPS + "KBPS");
nextTest(is, os, "done");
}
}
accessoryFd.close();
return null;
} catch (Throwable t) {
return t;
}
}
@Override
protected void onPostExecute(Throwable t) {
if (t == null) {
setTestResultAndFinish(true);
} else {
fail(null, t);
}
}
}).execute();
}
/**
* Signal to the companion device that we want to switch to the next test.
*
* @param is The input stream from the companion device
* @param os The output stream from the companion device
* @param testName The name of the new test
*/
private boolean nextTest(@NonNull InputStream is, @NonNull OutputStream os,
@NonNull String testName) throws IOException {
Log.i(LOG_TAG, "Init new test " + testName);
ByteBuffer nameBuffer = Charset.forName("UTF-8").encode(CharBuffer.wrap(testName));
byte[] sizeBuffer = {(byte) nameBuffer.limit()};
os.write(sizeBuffer);
os.write(Arrays.copyOf(nameBuffer.array(), nameBuffer.limit()));
int ret = is.read();
if (ret <= 0) {
Log.i(LOG_TAG, "Last test failed " + ret);
return false;
}
os.write(0);
Log.i(LOG_TAG, "Running " + testName);
return true;
}
@Override
protected void onDestroy() {
AccessoryAttachmentHandler.removeObserver(this);
super.onDestroy();
}
/**
* Indicate that the test failed.
*/
private void fail(@Nullable String s, @Nullable Throwable e) {
Log.e(LOG_TAG, s, e);
setTestResultAndFinish(false);
}
}