一.概念
Android 开放配件 (AOA) 支持功能可让外部 USB 硬件(Android USB 配件)与处于配件模式下的 Android 设备进行交互。当某台 Android 设备处于配件模式时,所连接的配件会充当 USB 主机(为总线供电并列举设备),而 Android 设备则充当 USB 配件。
AOA 有两个支持不同通信类型的版本:
- AOAv1。支持通用配件通信和 adb 调试。适用于 Android 3.1(API 级别 12)及更高版本,在 Android 2.3.4(API 级别10)及更高版本中通过插件库获得支持。
- AOAv2。支持音频流式传输和人机接口设备 (HID) 功能。适用于 Android 4.1(API 级别 16)。
如果使用通用配件协议(而不是使用 adb 或音频协议)与配件通信,则必须提供可以检测 USB 配件连接并建立通信的 Android 应用。
二.android usb通信模式
Android通过两种模式,来支持各种USB外围设备和Android USB附件(硬件实现了Android的附件协议):USB附件模式(accessory)和USB主机模式(host)。在USB附件模式下,外部 USB 硬件充当USB主机。Android设备作为附件的例子,包括机器人控制器、扩展插座(docking stations)、诊断和音乐设备、电子报亭(kiosks)、读卡器等其他设备。这种模式给予不具备主机功能的Android设备,与USB硬件通信的能力。Android USB附件,必须被设计为与装有Android的设备一起工作,并且必须遵循Android附件通讯协议。在USB主机模式下,装有Android的设备扮演着主机的角色。Android设备充当主机的例子,包括数码像机,键盘,鼠标和游戏控制器。那些适应面很广的USB设备,仍可以与Android应用交互,前提是这些Android应用可以正确的与这些设备通讯。
图1展示了两种模式的异同。当Android设备处于主机模式时,它扮演USB主机角色并为总线供电。当Android设备处于附件模式时,被连接的USB硬件(在这种情况下是一个Android USB附件)扮演主机角色并给总线供电。
三.相关API
1.两个相关的类
First Header | Second Header |
---|---|
UsbManager | 允许枚举已连接的USB设备并与其通信 |
UsbAccessory | 是代表USB配件的类,该类提供了方法访问配件的信息 |
通过
UsbManager manager = UsbManager.getInstance(this);
或
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory accessory = UsbManager.getAccessory(intent);
或
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
来获取这两个类的实例
2.Android manifest
声明<uses-feature android:name="android.hardware.usb.accessory" />
为主活动中的 android.hardware.usb.action.USB_ACCESSORY_ATTACHED
意图指定<intent-filter>
和<meta-data>
元素对。<meta-data>
元素指向外部XML资源文件,该文件声明有关要检测的附件的标识信息。
在XML资源文件中,声明要过滤的附件的<usb-accessory>
元素。每个<usb-accessory>
都可以具有以下属性:manufacturer,model,version
,将资源文件保存在res / xml /
目录中。资源文件名(不带.xml扩展名)必须与在<meta-data>
元素中指定的名称相同。
示例:
AndroidManifest.xml
<manifest ...>
<uses-feature android:name="android.hardware.usb.accessory" />
<uses-sdk android:minSdkVersion="<version>" />
...
<application>
<uses-library android:name="com.android.future.usb.accessory" />
<activity ...>
...
<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>
</application>
</manifest>
res/xml/accessory_filter.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>
四.通信过程
1.监听usb设备attach
先注册监听usb设备attach的广播,然后通过
list = usbManager.getAccessoryList();
accessory = list[0];
拿到辅助设备UsbAccessory的对象
2.授权
注册监听授权的广播,可以和监听usb设备attach的receiver合并
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(accessory != null){
//call method to set up accessory communication
}
}
else {
Log.d(TAG, "permission denied for accessory " + accessory);
}
}
}
}
};
在Activity onCreat()注册此receiver
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
在检测到usb设备attach后请求权限
UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);
3.通信
授权成功后可以通过文件描述符进行通信
UsbAccessory mAccessory;
ParcelFileDescriptor mFileDescriptor;
FileInputStream mInputStream;
FileOutputStream mOutputStream;
...
private void openAccessory() {
Log.d(TAG, "openAccessory: " + accessory);
mFileDescriptor = mUsbManager.openAccessory(mAccessory);
if (mFileDescriptor != null) {
FileDescriptor fd = mFileDescriptor.getFileDescriptor();
mInputStream = new FileInputStream(fd);
mOutputStream = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
4.设备退出后关闭fd,清理
BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
// call your method that cleans up and closes communication with the accessory
mFileDescriptor.close()
...
}
}
}
};
五.参考
http://scottmaxiao.github.io/AOA.html
https://source.android.com/devices/accessories/protocol
官方文档https://developer.android.com/guide/topics/connectivity/usb/accessory
https://source.android.com/devices/accessories/custom