Commit 12c8691d authored by Brian Muramatsu's avatar Brian Muramatsu
Browse files

CTS Verifier USB Accessory Test

Bug 5001945

This test is an adaptation of the AccessoryChat and
accessorychat USB accessory testing programs found in
frameworks/base/libs/usb/tests. It requires starting
up the CTS Verifier, connecting a device, and then
running the cts-usb-accessory program.

The accessorychat is now called cts-usb-accessory.
I removed the condition that limited accessorychat
to connect to only a certain vendor's device. Furthermore,
you do not type messages to send a message. The program
merely responds with an acknowledgement message that the
Android device receives and logs.

AccessoryChat was melded into the CTS Verifier, the manual
tester. It also does not allow a user to enter custom
messages, but it sends a message to start a dialog with
the cts-usb-accessory. After 10 or messages are collected,
the test activates the pass button.

Finally, there is an additional variable in CtsTestCaseList
to add cts-usb-accessory to the CTS tools directory. There
will need to be another change i...
parent 9ceaf260
......@@ -80,3 +80,7 @@ CTS_TEST_CASE_LIST := \
$(CTS_APPS_LIST) \
$(CTS_COVERAGE_TEST_CASE_LIST) \
$(CTS_SECURITY_APPS_LIST)
# The following files will be placed in the tools directory of the CTS distribution
CTS_TOOLS_LIST := \
$(HOST_OUT)/bin/cts-usb-accessory
......@@ -39,7 +39,20 @@
</intent-filter>
</activity>
<activity android:name=".TestListActivity" android:label="@string/test_list_title" />
<activity android:name=".TestListActivity" android:label="@string/app_name">
<!--
TestListActivity will have the USB accessory Test in its test list, but it
does not have any code to handle the USB accessory. The test has to be started
from TestListActivity or the pass/fail status won't be properly recorded. Also
this is to prevent the dialog saying there is no application able to support the
accessory from being shown.
-->
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>
<provider android:name=".TestResultsProvider"
android:authorities="com.android.cts.verifier.testresultsprovider" />
......@@ -186,6 +199,16 @@
<service android:name=".audioquality.ExperimentService" />
<activity android:name=".usb.UsbAccessoryTestActivity"
android:label="@string/usb_accessory_test"
android:configChanges="keyboardHidden|orientation">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_hardware" />
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
>
<LinearLayout android:orientation="vertical"
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_weight="1"
>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usb_sent_messages"
style="?android:attr/listSeparatorTextViewStyle"
/>
<FrameLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
>
<ListView android:id="@+id/usb_sent_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView android:id="@+id/usb_empty_sent_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/usb_no_messages"
android:visibility="gone"
/>
</FrameLayout>
</LinearLayout>
<include layout="@layout/vertical_divider" />
<LinearLayout android:orientation="vertical"
android:layout_width="1px"
android:layout_height="match_parent"
android:layout_weight="1"
>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usb_received_messages"
style="?android:attr/listSeparatorTextViewStyle"
/>
<FrameLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
>
<ListView android:id="@+id/usb_received_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView android:id="@+id/usb_empty_received_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/usb_no_messages"
android:visibility="gone"
/>
</FrameLayout>
</LinearLayout>
</LinearLayout>
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
......@@ -16,6 +16,5 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="5dp"
style="@style/MessageRow"
/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usb_sent_messages"
style="?android:attr/listSeparatorTextViewStyle"
/>
<FrameLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
>
<ListView android:id="@+id/usb_sent_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView android:id="@+id/usb_empty_sent_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/usb_no_messages"
android:visibility="gone"
/>
</FrameLayout>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/usb_received_messages"
style="?android:attr/listSeparatorTextViewStyle"
/>
<FrameLayout android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
>
<ListView android:id="@+id/usb_received_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<TextView android:id="@+id/usb_empty_received_messages"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/usb_no_messages"
android:visibility="gone"
/>
</FrameLayout>
<include layout="@layout/pass_fail_buttons" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/MessageRow"
/>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#333"
android:layout_width="1px"
android:layout_height="match_parent"
/>
......@@ -30,6 +30,7 @@
<string name="test_list_title">Manual Test List</string>
<string name="test_category_audio">Audio</string>
<string name="test_category_device_admin">Device Administration</string>
<string name="test_category_hardware">Hardware</string>
<string name="test_category_networking">Networking</string>
<string name="test_category_sensors">Sensors</string>
<string name="test_category_security">Security</string>
......@@ -245,4 +246,27 @@
<string name="aq_recording_error">Error reading data from AudioRecord instance</string>
<string name="aq_exception_error">Exception thrown during test: %1$s</string>
<!-- Strings for USB accessory test activity -->
<string name="usb_accessory_test">USB Accessory Test</string>
<string name="usb_accessory_test_info">
1. Connect your Android device to a computer and run the \'cts-usb-accessory\' program from
the CTS tools directory.
\n\n2. If you have not started the CTS Verifier, press \'OK\' when asked to open the CTS
Verifier when the accessory is connected. \n\nIf you are already in this test,
then you can press \'Cancel\' but press \'OK\' in the next dialog asking whether to allow
CTS Verifier to access the accessory.
\n\n3. You should see the accessory and the CTS Verifier display a series of messages
which indicates that the accessory support is working properly.
</string>
<string name="usb_not_available_title">USB accessory feature is not available?</string>
<string name="usb_not_available_message">If your device is supposed to support USB accessories, your API implementation is not behaving correctly!</string>
<string name="usb_received_messages">Received Messages</string>
<string name="usb_sent_messages">Sent Messages</string>
<string name="usb_no_messages">No messages</string>
<string name="usb_message_thread_started">Starting message processing...</string>
<string name="usb_message_thread_exception">Exception occurred while processing a message...</string>
<string name="usb_message_thread_ended">Stopping message processing...</string>
<string name="usb_test_passed">Received all expected messages. Pass button enabled!</string>
<string name="usb_file_descriptor_error">Could not open file descriptor for USB accessory... try reconnecting and restarting the accessory?</string>
</resources>
......@@ -6,4 +6,9 @@
<style name="VersionFont" parent="@android:style/TextAppearance.Large">
<item name="android:textColor">#ffffff</item>
</style>
</resources>
\ No newline at end of file
<style name="MessageRow">
<item name="android:gravity">center_vertical</item>
<item name="android:textSize">18sp</item>
<item name="android:padding">5dp</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<resources>
<usb-accessory manufacturer="Android CTS" model="CTS USB Accessory" version="1.0" />
</resources>
\ No newline at end of file
/*
* Copyright (C) 2011 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;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Test for USB accessories. The test activity interacts with a cts-usb-accessory program that
* acts as an accessory by exchanging a series of messages.
*/
public class UsbAccessoryTestActivity extends PassFailButtons.Activity {
private static final String TAG = UsbAccessoryTestActivity.class.getSimpleName();
private static final int FILE_DESCRIPTOR_PROBLEM_DIALOG_ID = 1;
private static final String ACTION_USB_PERMISSION =
"com.android.cts.verifier.usb.USB_PERMISSION";
private ArrayAdapter<String> mReceivedMessagesAdapter;
private ArrayAdapter<String> mSentMessagesAdapter;
private MessageHandler mHandler;
private UsbManager mUsbManager;
private PendingIntent mPermissionIntent;
private boolean mPermissionRequestPending;
private UsbReceiver mUsbReceiver;
private ParcelFileDescriptor mFileDescriptor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.usb_main);
setInfoResources(R.string.usb_accessory_test, R.string.usb_accessory_test_info, -1);
setPassFailButtonClickListeners();
// Don't allow a test pass until the accessory and the Android device exchange messages...
getPassButton().setEnabled(false);
if (!hasUsbAccessorySupport()) {
showNoUsbAccessoryDialog();
}
mReceivedMessagesAdapter = new ArrayAdapter<String>(this, R.layout.usb_message_row);
mSentMessagesAdapter = new ArrayAdapter<String>(this, R.layout.usb_message_row);
mHandler = new MessageHandler();
mUsbManager = (UsbManager) getSystemService(USB_SERVICE);
mPermissionIntent = PendingIntent.getBroadcast(this, 0,
new Intent(ACTION_USB_PERMISSION), 0);
mUsbReceiver = new UsbReceiver();
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
registerReceiver(mUsbReceiver, filter);
setupListViews();
}
private boolean hasUsbAccessorySupport() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
}
private void showNoUsbAccessoryDialog() {
new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.usb_not_available_title)
.setMessage(R.string.usb_not_available_message)
.setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.show();
}
private void setupListViews() {
ListView sentMessages = (ListView) findViewById(R.id.usb_sent_messages);
ListView receivedMessages = (ListView) findViewById(R.id.usb_received_messages);
View emptySentView = findViewById(R.id.usb_empty_sent_messages);
View emptyReceivedView = findViewById(R.id.usb_empty_received_messages);
sentMessages.setEmptyView(emptySentView);
receivedMessages.setEmptyView(emptyReceivedView);
receivedMessages.setAdapter(mReceivedMessagesAdapter);
sentMessages.setAdapter(mSentMessagesAdapter);
}
class UsbReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_USB_PERMISSION.equals(intent.getAction())
|| UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(intent.getAction())) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
} else {
Log.i(TAG, "Permission denied...");
}
mPermissionRequestPending = false;
}
}
}
private void openAccessory(UsbAccessory accessory) {
mFileDescriptor = mUsbManager.openAccessory(accessory);
if (mFileDescriptor != null) {
FileDescriptor fileDescriptor = mFileDescriptor.getFileDescriptor();
FileInputStream inputStream = new FileInputStream(fileDescriptor);
FileOutputStream outputStream = new FileOutputStream(fileDescriptor);
new MessageThread(inputStream, outputStream, mHandler).start();
} else {
showDialog(FILE_DESCRIPTOR_PROBLEM_DIALOG_ID);
}
}
static class MessageThread extends Thread {
private final InputStream mInputStream;
private final OutputStream mOutputStream;
private final MessageHandler mHandler;
private int mNextMessageNumber = 0;
MessageThread(InputStream inputStream, OutputStream outputStream, MessageHandler handler) {
this.mInputStream = inputStream;
this.mOutputStream = outputStream;
this.mHandler = handler;
}
@Override
public void run() {
mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_STARTING);
try {
// Wait a bit or else the messages can appear to quick and be confusing...
Thread.sleep(2000);
sendMessage();
// Wait for response and send message acks...
int numRead = 0;
byte[] buffer = new byte[16384];
while (numRead >= 0) {
numRead = mInputStream.read(buffer);
if (numRead > 0) {
handleReceivedMessage(buffer, numRead);
}
}
} catch (IOException e) {
Log.e(TAG, "Exception while reading from input stream", e);
mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_EXCEPTION);
} catch (InterruptedException e) {
Log.e(TAG, "Exception while reading from input stream", e);
mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_EXCEPTION);
}
mHandler.sendEmptyMessage(MessageHandler.MESSAGE_THREAD_ENDING);
}
private void handleReceivedMessage(byte[] buffer, int numRead) throws IOException {
// TODO: Check the contents of the message?
String text = new String(buffer, 0, numRead).trim();
mHandler.sendReceivedMessage(text);
// Send back a response..
if (mNextMessageNumber <= 10) {
sendMessage();
} else {
mHandler.sendEmptyMessage(MessageHandler.TEST_PASSED);
}
}
private void sendMessage() throws IOException {
String text = "Message from Android device #" + mNextMessageNumber++;
mOutputStream.write(text.getBytes());
mHandler.sendSentMessage(text);
}
}
class MessageHandler extends Handler {
static final int RECEIVED_MESSAGE = 1;
static final int SENT_MESSAGE = 2;
static final int MESSAGE_THREAD_STARTING = 3;
static final int MESSAGE_THREAD_EXCEPTION = 4;
static final int MESSAGE_THREAD_ENDING = 5;
static final int TEST_PASSED = 6;
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case RECEIVED_MESSAGE:
mReceivedMessagesAdapter.add((String) msg.obj);
break;
case SENT_MESSAGE:
mSentMessagesAdapter.add((String) msg.obj);
break;
case MESSAGE_THREAD_STARTING:
showToast(R.string.usb_message_thread_started);
break;
case MESSAGE_THREAD_EXCEPTION:
showToast(R.string.usb_message_thread_exception);
break;
case MESSAGE_THREAD_ENDING:
showToast(R.string.usb_message_thread_ended);
break;
case TEST_PASSED:
showToast(R.string.usb_test_passed);
getPassButton().setEnabled(true);
break;
default:
throw new IllegalArgumentException("Bad message type: " + msg.what);
}
}
private void showToast(int messageId) {
Toast.makeText(UsbAccessoryTestActivity.this, messageId, Toast.LENGTH_SHORT).show();
}
void sendReceivedMessage(String text) {
Message message = Message.obtain(this, RECEIVED_MESSAGE);
message.obj = text;
sendMessage(message);
}
void sendSentMessage(String text) {
Message message = Message.obtain(this, SENT_MESSAGE);
message.obj = text;
sendMessage(message);
}
}
@Override
protected void onResume() {
super.onResume();
UsbAccessory[] accessories = mUsbManager.getAccessoryList();
UsbAccessory accessory = accessories != null && accessories.length > 0
? accessories[0]
: null;
if (accessory != null) {
if (mUsbManager.hasPermission(accessory)) {
openAccessory(accessory);
} else {
if (!mPermissionRequestPending) {
mUsbManager.requestPermission(accessory, mPermissionIntent);
mPermissionRequestPending = true;
}
}
}
}
@Override
protected void onPause() {
super.onPause();
if (mFileDescriptor != null) {
try {
mFileDescriptor.close();
} catch (IOException e) {
Log.e(TAG, "Exception while closing file descriptor", e);
} finally {
mFileDescriptor = null;
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.usb_main);
setupListViews();
}
@Override
public Dialog onCreateDialog(int id, Bundle args) {
switch (id) {
case FILE_DESCRIPTOR_PROBLEM_DIALOG_ID:
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.usb_accessory_test)
.setMessage(R.string.usb_file_descriptor_error)
.create();
default:
return super.onCreateDialog(id, args);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mUsbReceiver);
}
}
#
# Copyright (C) 2011 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.
#
# Build for Linux (desktop) host
ifeq ($(HOST_OS),linux)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := cts-usb-accessory.c
LOCAL_MODULE := cts-usb-accessory
LOCAL_C_INCLUDES += bionic/libc/kernel/common
LOCAL_STATIC_LIBRARIES := libusbhost libcutils
LOCAL_LDLIBS += -lpthread
LOCAL_CFLAGS := -g -O0
include $(BUILD_HOST_EXECUTABLE)
endif
/*
* Copyright (C) 2011 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.
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>
#include <usbhost/usbhost.h>
#include <linux/usb/f_accessory.h>
struct usb_device *sDevice = NULL;
static void* message_thread(void* arg) {
int *endpoints = (int *)arg;
int ret = 0;
int num = 0;
char message[50];
while (sDevice && ret >= 0) {
char buffer[16384];
ret = usb_device_bulk_transfer(sDevice, endpoints[0], buffer, sizeof(buffer), 1000);
if (ret < 0 && errno == ETIMEDOUT) {
ret = 0;
}
if (ret > 0) {
printf("[RECV] ");
fwrite(buffer, 1, ret, stdout);
printf("\n");
// Respond by sending a message back
sprintf(message, "Message from Android accessory #%d", num++);
printf("[SENT] %s\n", message);
fflush(stdout);
usb_device_bulk_transfer(sDevice, endpoints[1], message, strlen(message), 1000);
}
}
return NULL;
}
static void milli_sleep(int millis) {
struct timespec tm;
tm.tv_sec = 0;
tm.tv_nsec = millis * 1000000;
nanosleep(&tm, NULL);
}
static void send_string(struct usb_device *device, int index, const char* string) {
int ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
ACCESSORY_SEND_STRING, 0, index, (void *)string, strlen(string) + 1, 0);
// some devices can't handle back-to-back requests, so delay a bit
milli_sleep(10);
}
static int usb_device_added(const char *devname, void* client_data) {
struct usb_descriptor_header* desc;
struct usb_descriptor_iter iter;
uint16_t vendorId, productId;
int ret;
pthread_t th;
struct usb_device *device = usb_device_open(devname);
if (!device) {
fprintf(stderr, "usb_device_open failed\n");
return 0;
}
vendorId = usb_device_get_vendor_id(device);
productId = usb_device_get_product_id(device);
if (!sDevice && (vendorId == 0x18D1 && (productId == 0x2D00 || productId == 0x2D01))) {
struct usb_descriptor_header* desc;
struct usb_descriptor_iter iter;
struct usb_interface_descriptor *intf = NULL;
struct usb_endpoint_descriptor *ep1 = NULL;
struct usb_endpoint_descriptor *ep2 = NULL;
printf("Found Android device in accessory mode (%x:%x)...\n",
vendorId, productId);
sDevice = device;
usb_descriptor_iter_init(device, &iter);
while ((desc = usb_descriptor_iter_next(&iter)) != NULL && (!intf || !ep1 || !ep2)) {
if (desc->bDescriptorType == USB_DT_INTERFACE) {
intf = (struct usb_interface_descriptor *)desc;
} else if (desc->bDescriptorType == USB_DT_ENDPOINT) {
if (ep1)
ep2 = (struct usb_endpoint_descriptor *)desc;
else
ep1 = (struct usb_endpoint_descriptor *)desc;
}
}
if (!intf) {
fprintf(stderr, "Interface not found\n");
exit(1);
}
if (!ep1 || !ep2) {
fprintf(stderr, "Endpoints not found\n");
exit(1);
}
if (usb_device_claim_interface(device, intf->bInterfaceNumber)) {
fprintf(stderr, "usb_device_claim_interface failed errno: %d\n", errno);
exit(1);
}
int endpoints[2];
if ((ep1->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) {
endpoints[0] = ep1->bEndpointAddress;
endpoints[1] = ep2->bEndpointAddress;
} else {
endpoints[0] = ep2->bEndpointAddress;
endpoints[1] = ep1->bEndpointAddress;
}
pthread_create(&th, NULL, message_thread, (void *)endpoints);
} else {
printf("Found possible Android device (%x:%x) "
"- attempting to switch to accessory mode...\n", vendorId, productId);
uint16_t protocol = 0;
ret = usb_device_control_transfer(device, USB_DIR_IN | USB_TYPE_VENDOR,
ACCESSORY_GET_PROTOCOL, 0, 0, &protocol, sizeof(protocol), 0);
if (ret == 2)
printf("Device supports protocol version %d\n", protocol);
else
fprintf(stderr, "Failed to read protocol version\n");
send_string(device, ACCESSORY_STRING_MANUFACTURER, "Android CTS");
send_string(device, ACCESSORY_STRING_MODEL, "CTS USB Accessory");
send_string(device, ACCESSORY_STRING_DESCRIPTION, "CTS USB Accessory");
send_string(device, ACCESSORY_STRING_VERSION, "1.0");
send_string(device, ACCESSORY_STRING_URI,
"http://source.android.com/compatibility/cts-intro.html");
send_string(device, ACCESSORY_STRING_SERIAL, "1234567890");
ret = usb_device_control_transfer(device, USB_DIR_OUT | USB_TYPE_VENDOR,
ACCESSORY_START, 0, 0, 0, 0, 0);
return 0;
}
if (device != sDevice)
usb_device_close(device);
return 0;
}
static int usb_device_removed(const char *devname, void* client_data) {
if (sDevice && !strcmp(usb_device_get_name(sDevice), devname)) {
usb_device_close(sDevice);
sDevice = NULL;
// exit when we are disconnected
return 1;
}
return 0;
}
int main(int argc, char* argv[]) {
printf("CTS USB Accessory Tester\n");
struct usb_host_context* context = usb_host_init();
if (!context) {
fprintf(stderr, "usb_host_init failed");
return 1;
}
// this will never return so it is safe to pass thiz directly
usb_host_run(context, usb_device_added, usb_device_removed, NULL, NULL);
return 0;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment