blob: 0bf8ca22906f944fb609204e1b1f149a61573960 [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.verifierusbcompanion;
import static org.junit.Assert.assertEquals;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.support.annotation.NonNull;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
/**
* Companion code for com.android.cts.verifier.usb.device.UsbAccessoryTestActivity
*/
class AccessoryTestCompanion extends TestCompanion {
private static final int TIMEOUT_MILLIS = 500;
private static final int MAX_BUFFER_SIZE = 16384;
private static final int TEST_DATA_SIZE_THRESHOLD = 100 * 1024 * 1024; // 100MB
private static final String ACTION_USB_PERMISSION =
"com.android.cts.verifierusbcompanion.USB_PERMISSION";
private UsbManager mUsbManager;
private BroadcastReceiver mUsbDeviceConnectionReceiver;
private UsbDevice mDevice;
AccessoryTestCompanion(@NonNull Context context, @NonNull TestObserver observer) {
super(context, observer);
}
/**
* @throws Throwable
*/
@Override
protected void runTest() throws Throwable {
updateStatus("Waiting for device under test to connect");
mUsbManager = getContext().getSystemService(UsbManager.class);
mUsbDeviceConnectionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
synchronized (AccessoryTestCompanion.this) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
switch (intent.getAction()) {
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
if (mUsbManager.hasPermission(device)) {
onDeviceAccessPermitted(device);
} else {
mUsbManager.requestPermission(device,
PendingIntent.getBroadcast(getContext(), 0,
new Intent(ACTION_USB_PERMISSION), 0));
}
break;
case ACTION_USB_PERMISSION:
boolean granted = intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false);
if (granted) {
onDeviceAccessPermitted(device);
} else {
fail("Permission to connect to " + device.getProductName()
+ " not granted");
}
break;
}
}
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
getContext().registerReceiver(mUsbDeviceConnectionReceiver, filter);
synchronized (this) {
while (mDevice == null) {
wait();
}
}
UsbInterface iface = null;
for (int i = 0; i < mDevice.getConfigurationCount(); i++) {
if (mDevice.getInterface(i).getName().equals("Android Accessory Interface")) {
iface = mDevice.getInterface(i);
break;
}
}
UsbEndpoint in = getEndpoint(iface, UsbConstants.USB_DIR_IN);
UsbEndpoint out = getEndpoint(iface, UsbConstants.USB_DIR_OUT);
UsbDeviceConnection connection = mUsbManager.openDevice(mDevice);
try {
String testName;
do {
testName = nextTest(connection, in, out, true);
updateStatus("Running test \"" + testName + "\"");
switch (testName) {
case "echo 32 bytes": {
byte[] buffer = new byte[32];
int numTransferred = connection.bulkTransfer(in, buffer, 32, 0);
assertEquals(32, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, 32, 0);
assertEquals(32, numTransferred);
}
break;
case "echo two 16 byte transfers as one": {
byte[] buffer = new byte[48];
// We receive the individual transfers even if we wait for more data
int numTransferred = connection.bulkTransfer(in, buffer, 32, 0);
assertEquals(16, numTransferred);
numTransferred = connection.bulkTransfer(in, buffer, 16, 32, 0);
assertEquals(16, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, 32, 0);
assertEquals(32, numTransferred);
}
break;
case "echo 32 bytes as two 16 byte transfers": {
byte[] buffer = new byte[32];
int numTransferred = connection.bulkTransfer(in, buffer, 32, 0);
assertEquals(32, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, 16, 0);
assertEquals(16, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, 16, 16, 0);
assertEquals(16, numTransferred);
}
break;
case "measure out transfer speed": {
byte[] buffer = new byte[MAX_BUFFER_SIZE];
long bytesRead = 0;
while (bytesRead < TEST_DATA_SIZE_THRESHOLD) {
int numTransferred = connection.bulkTransfer(
in, buffer, MAX_BUFFER_SIZE, 0);
bytesRead += numTransferred;
}
// MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero
// sized package after. Some older devices do not send these packages, but
// this is not compliant anymore.
int numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS);
assertEquals(0, numTransferred);
byte[] confirm = new byte[] {1};
numTransferred = connection.bulkTransfer(out, confirm, 1, 0);
assertEquals(1, numTransferred);
}
break;
case "measure in transfer speed": {
byte[] buffer = new byte[MAX_BUFFER_SIZE];
long bytesWritten = 0;
int numTransferred = 0;
while (bytesWritten < TEST_DATA_SIZE_THRESHOLD) {
numTransferred =
connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
bytesWritten += numTransferred;
}
byte[] confirm = new byte[] {1};
numTransferred = connection.bulkTransfer(out, confirm, 1, 0);
assertEquals(1, numTransferred);
}
break;
case "echo max bytes": {
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE,
0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
// MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero
// sized package after. Some older devices do not send these packages, but
// this is not compliant anymore.
numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS);
assertEquals(0, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
}
break;
case "echo max*2 bytes": {
byte[] buffer = new byte[MAX_BUFFER_SIZE * 2];
int numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE,
0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
// Oversized transfers get split into two
numTransferred = connection.bulkTransfer(in, buffer, MAX_BUFFER_SIZE,
MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
// MAX_BUFFER_SIZE is a multiple of the package size, hence we get a zero
// sized package after. Some older devices do not send these packages, but
// this is not compliant anymore.
numTransferred = connection.bulkTransfer(in, buffer, 1, TIMEOUT_MILLIS);
assertEquals(0, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE, 100);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
numTransferred = connection.bulkTransfer(out, buffer, MAX_BUFFER_SIZE,
MAX_BUFFER_SIZE, 0);
assertEquals(MAX_BUFFER_SIZE, numTransferred);
}
break;
default:
break;
}
} while (!testName.equals("done"));
} finally {
connection.close();
}
}
/**
* If access to a device was permitted either make the device an accessory if it already is,
* start the test.
*
* @param device The device access was permitted to
*/
private void onDeviceAccessPermitted(@NonNull UsbDevice device) {
if (!AoapInterface.isDeviceInAoapMode(device)) {
UsbDeviceConnection connection = mUsbManager.openDevice(device);
try {
makeThisDeviceAnAccessory(connection);
} finally {
connection.close();
}
} else {
getContext().unregisterReceiver(mUsbDeviceConnectionReceiver);
mUsbDeviceConnectionReceiver = null;
synchronized (AccessoryTestCompanion.this) {
mDevice = device;
AccessoryTestCompanion.this.notifyAll();
}
}
}
@NonNull private String nextTest(@NonNull UsbDeviceConnection connection,
@NonNull UsbEndpoint in, @NonNull UsbEndpoint out, boolean isSuccess) {
byte[] sizeBuffer = new byte[1];
updateStatus("Waiting for next test");
int numTransferred = connection.bulkTransfer(in, sizeBuffer, 1, 0);
assertEquals(1, numTransferred);
int nameSize = sizeBuffer[0];
byte[] nameBuffer = new byte[nameSize];
numTransferred = connection.bulkTransfer(in, nameBuffer, nameSize, 0);
assertEquals(nameSize, numTransferred);
numTransferred = connection.bulkTransfer(out, new byte[]{(byte) (isSuccess ? 1 : 0)}, 1, 0);
assertEquals(1, numTransferred);
numTransferred = connection.bulkTransfer(in, new byte[1], 1, 0);
assertEquals(1, numTransferred);
String name = Charset.forName("UTF-8").decode(ByteBuffer.wrap(nameBuffer)).toString();
updateStatus("Next test is " + name);
return name;
}
/**
* Search an {@link UsbInterface} for an {@link UsbEndpoint endpoint} of a certain direction.
*
* @param iface The interface to search
* @param direction The direction the endpoint is for.
*
* @return The first endpoint found or {@link null}.
*/
@NonNull private UsbEndpoint getEndpoint(@NonNull UsbInterface iface, int direction) {
for (int i = 0; i < iface.getEndpointCount(); i++) {
UsbEndpoint ep = iface.getEndpoint(i);
if (ep.getDirection() == direction) {
return ep;
}
}
throw new IllegalStateException("Could not find " + direction + " endpoint in "
+ iface.getName());
}
/**
* Converts the device under test into an Android accessory. Accessories are USB hosts that are
* detected on the device side via {@link UsbManager#getAccessoryList()}.
*
* @param connection The connection to the USB device
*/
private void makeThisDeviceAnAccessory(@NonNull UsbDeviceConnection connection) {
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MANUFACTURER,
"Android");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_MODEL,
"Android device");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_DESCRIPTION,
"Android device running CTS verifier");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_VERSION, "1");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_URI,
"https://source.android.com/compatibility/cts/verifier.html");
AoapInterface.sendString(connection, AoapInterface.ACCESSORY_STRING_SERIAL, "0");
AoapInterface.sendAoapStart(connection);
}
}