一.概念
Android 開放配件 (AOA) 支持功能可讓外部 USB 硬件(Android USB 配件)與處于配件模式下的 Android 設(shè)備進行交互。當某臺 Android 設(shè)備處于配件模式時课蔬,所連接的配件會充當 USB 主機(為總線供電并列舉設(shè)備)悲伶,而 Android 設(shè)備則充當 USB 配件勒虾。
AOA 有兩個支持不同通信類型的版本:
- AOAv1婚苹。支持通用配件通信和 adb 調(diào)試澎粟。適用于 Android 3.1(API 級別 12)及更高版本扎谎,在 Android 2.3.4(API 級別10)及更高版本中通過插件庫獲得支持晃琳。
- AOAv2帽哑。支持音頻流式傳輸和人機接口設(shè)備 (HID) 功能供置。適用于 Android 4.1(API 級別 16)柳爽。
如果使用通用配件協(xié)議(而不是使用 adb 或音頻協(xié)議)與配件通信进鸠,則必須提供可以檢測 USB 配件連接并建立通信的 Android 應(yīng)用稠曼。
二.android usb通信模式
Android通過兩種模式,來支持各種USB外圍設(shè)備和Android USB附件(硬件實現(xiàn)了Android的附件協(xié)議):USB附件模式(accessory)和USB主機模式(host)客年。在USB附件模式下霞幅,外部 USB 硬件充當USB主機。Android設(shè)備作為附件的例子量瓜,包括機器人控制器司恳、擴展插座(docking stations)、診斷和音樂設(shè)備绍傲、電子報亭(kiosks)扔傅、讀卡器等其他設(shè)備。這種模式給予不具備主機功能的Android設(shè)備烫饼,與USB硬件通信的能力猎塞。Android USB附件,必須被設(shè)計為與裝有Android的設(shè)備一起工作枫弟,并且必須遵循Android附件通訊協(xié)議邢享。在USB主機模式下,裝有Android的設(shè)備扮演著主機的角色淡诗。Android設(shè)備充當主機的例子骇塘,包括數(shù)碼像機伊履,鍵盤,鼠標和游戲控制器款违。那些適應(yīng)面很廣的USB設(shè)備唐瀑,仍可以與Android應(yīng)用交互,前提是這些Android應(yīng)用可以正確的與這些設(shè)備通訊插爹。
圖1展示了兩種模式的異同哄辣。當Android設(shè)備處于主機模式時,它扮演USB主機角色并為總線供電赠尾。當Android設(shè)備處于附件模式時力穗,被連接的USB硬件(在這種情況下是一個Android USB附件)扮演主機角色并給總線供電。
三.相關(guān)API
1.兩個相關(guān)的類
First Header | Second Header |
---|---|
UsbManager | 允許枚舉已連接的USB設(shè)備并與其通信 |
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資源文件,該文件聲明有關(guān)要檢測的附件的標識信息寸宵。
在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.監(jiān)聽usb設(shè)備attach
先注冊監(jiān)聽usb設(shè)備attach的廣播,然后通過
list = usbManager.getAccessoryList();
accessory = list[0];
拿到輔助設(shè)備UsbAccessory的對象
2.授權(quán)
注冊監(jiān)聽授權(quán)的廣播感猛,可以和監(jiān)聽usb設(shè)備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設(shè)備attach后請求權(quán)限
UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);
3.通信
授權(quán)成功后可以通過文件描述符進行通信
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.設(shè)備退出后關(guān)閉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