研究android2android aoa通讯时,在网上查询了很多资料,这些资料对Accessory模式的描述,在研究过程中造成了很大的困扰,故此先对基本信息进行介绍。
1.1 Host模式与Accessory模式的区别
1.png1.2 Accessory端的PID/VID
VID 固定为Google的官方VID – 0x18D1
PID 在不同的模式下定义如下:
● 0x2D00 - accessory
● 0x2D01 - accessory + adb
● 0x2D02 - audio
● 0x2D03 - audio + adb
● 0x2D04 - accessory + audio
● 0x2D05 - accessory + audio + adb
1.3 Android USB Accessory设备和Android Host设备两者枚举识别工作过程
流程图.PNG枚举过程如图所示
1. 首先USB Accessory设备发起USB控制传输进行正常的USB设备枚举,获取设备描述符和配置描述符等信息。
此时大部分Android设备上报的还只是普通的HID或MTP设备
2. 接下来USB Accessory设备,根据枚举的PID/VID,向对应的USB设备,发起Vendor类型,request值为51(0x33)的控制传输命令(ACCESSORY_GET_PROTOCOL),
看看该Android设备是否支持USB Accessory功能,如果支持的话会返回所支持的AOA协议版本(1或2)。
3. USB Accessory判断到该Android设备支持Accessory功能后,
发起request值为52(0x34)的控制传输命令(ACCESSORY_SEND_STRING),
并把该Accessory设备的相关信息(包括厂家,序列号等)告知Android设备;
4. 最终,USB Accessory设备发起request值为53(0x35)的控制传输命令(ACCESSORY_START),
通知Android设备切换到Accessory功能模式开始工作。
至此,Android Accessory端与Host端的识别工作完成,接下来是通讯:Accessory端使用块传输,Host端使用输入输出流,具体代码如下。
1.4 Android USB Accessory设备和Android设备两者枚举识别工作过程的代码实现
Accessory端
1.设备枚举
public final String myUsbDevices = "1234/1234";
public static final String[]aoaPidVid = {"2D00/18D1","2D01/18D1","2D02/18D1","2D03/18D1","2D04/18D1","2D05/18D1"};
/**
* 枚举usb设备,并修改为Accessory模式</br>
* 本例中只考虑连接一个Accessory设备的情况
* **/
private UsbDevice findDevice(Context context, UsbManager mUsbManager){
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
Log.i(TAG,"initAccessory: deviceList=" + deviceList.size());
if (deviceList == null || deviceList.size() == 0) {
Log.i(TAG,"initAccessory: Not found usb device");
return null;
}
for (UsbDevice dev:deviceList.values()) {
String pid = Integer.toHexString(dev.getProductId());
String vid = Integer.toHexString(dev.getVendorId());
String pidVid = pid+"/"+vid;
Log.i(TAG,"initAccessory: find usb device["+pidVid+"]");
//判断枚举的usb设备是否目标设备
if(myUsbDevices.equals(pidVid)){
while (!mUsbManager.hasPermission(dev)) {
Log.i(TAG,"initAccessory: Do not have permission on device=" + dev.getProductName());
Intent intent = new Intent(IAoaConst.ACTION_USB_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Log.i(TAG,"initAccessory: Trying to get permissions with pendingIntent=" + pendingIntent);
mUsbManager.requestPermission(dev, pendingIntent);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(initAccessory(mUsbManager,dev)){
Log.i(TAG,"initAccessory: Init usb accessory success");
return dev;
}
}else if(isArrayContainStr(aoaPidVid,pidVid)){//判断设备是否为Accessory设备
return dev;
}
}
return null;
}
2.初始化Accessory:
查询Android Host端是否支持AOA协议,向Android Host端发送AndroidAccessory端的厂商/序列号等内容,通知Android Host端切换到Accessory模式
/**
* 配件发送序号52的USB请求报文,通过Index字段携带配件自身信息,包括制造商、型号、版本、设备描述、序 列号URI等。手机根据这些信息启动响应的APP
* 配件发送序号53的USB请求报文,切换USB模式,主要是根据切换的vendorID和productID
* 重新枚举USB设备,准备建立AOA数据通道
*/
private boolean initAccessory(UsbManager mUsbManager,final UsbDevice device) {
Log.i(TAG,"initAccessory: device=[name=" + device.getDeviceName() +
", manufacturerName=" + device.getManufacturerName() +
", productName=" + device.getProductName() +
", deviceId=" + device.getDeviceId() +
", productId=" + device.getProductId() +
", deviceProtocol=" + device.getDeviceProtocol() + "]");
//无拔出usb的动作,直接返回成功,不重新init accessory
if(isDetached){
Log.i(TAG,"initAccessory: have not detach usb,return true");
return true;
}
connection = mUsbManager.openDevice(device);
Log.i(TAG,"initAccessory: conneciton=" + connection);
if (connection == null) {
return false;
}
//Android Host端是否支持AOA协议
int result = getProtocol(connection);
Log.i(TAG,"controlTransfer(51)accessoryVersion = "+result);
if(result==1||result==2){
boolean res = initStringControlTransfer(connection, 0, IAoaConst.USB_AOA_MANUFACTURER); // MANUFACTURER
res = res&&initStringControlTransfer(connection, 1, IAoaConst.USB_AOA_MODEL); // MODEL
res = res&&initStringControlTransfer(connection, 2, IAoaConst.USB_AOA_DESCRIPTION); // DESCRIPTION
res = res&&initStringControlTransfer(connection, 3, IAoaConst.USB_AOA_VERSION); // VERSION
res = res&&initStringControlTransfer(connection, 4, IAoaConst.USB_AOA_URI); // URI
res = res&&initStringControlTransfer(connection, 5, IAoaConst.USB_AOA_SERIAL); // SERIAL
connection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, IAoaConst.INIT_USB_ACCESSORY_TIMEOUT);
connection.close();
return res;
}else{
Log.i(TAG,"Host not support accessory protocol ["+result+"]");
return false;
}
}
3.使用块传输进行通信
Host端
1.枚举accessory设备
public void startHost(Context context,final IUSBCallback usbCallback){
Log.i(TAG,"startHost enter.");
final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
registerUsbReceiver(context,usbCallback);
findUsbAccessory(context,new IFindAccessoryCallback() {
@Override
public void findAccessory(final UsbAccessory accessory) {
if (accessory==null) {
bUsbAttach = false;
Log.w(TAG,"no accessory found");
usbCallback.disconnectd(IAoaErrCode.ERR_NO_ACCESSORY_FIND,"no accessory found");
}else {
if(openAccessory(usbManager,accessory)){
//启动读取数据线程
Log.d(TAG, "mReadThread is start ");
if(mReadThread==null||!mReadThread.isAlive()){
mReadThread = new ReadThread(usbCallback);
mReadThread.start();
}
bUsbAttach = true;
Log.i(TAG,"Open accessory success");
usbCallback.connected();
}else{
bUsbAttach = false;
Log.i(TAG,"Open accessory fail");
usbCallback.disconnectd(IAoaErrCode.ERR_OPEN_ACCESSORY_FAIL,"Open accessory fail");
}
}
}
});
}
private void findUsbAccessory(final Context context,final IFindAccessoryCallback callback){
if(this.findUsbAccessoryThread==null||!this.findUsbAccessoryThread.isAlive()){
this.findUsbAccessoryThread = new Thread(){
@Override
public void run(){
Log.i(TAG,"FindUsbAccessoryThread enter...");
findUsbAccessoryFlag = true;
while(findUsbAccessoryFlag){
try {
sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
// UsbAccessory accessory = (UsbAccessory) getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
final UsbAccessory[] accessoryList = usbManager.getAccessoryList();
if (accessoryList != null && accessoryList.length > 0) {
for(UsbAccessory usbAccessory : accessoryList){
if(isUsbAccessory(usbAccessory)){
while(!usbManager.hasPermission(usbAccessory)){
Intent intent = new Intent(IAoaConst.ACTION_USB_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Log.i(TAG,"initDevice: Trying to get permissions with pendingIntent=" + pendingIntent);
usbManager.requestPermission(usbAccessory, pendingIntent);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Log.i(TAG,"FindUsbAccessoryThread find usbAccessory["+usbAccessory+"]"+usbManager.getDeviceList().size());
callback.findAccessory(usbAccessory);
findUsbAccessoryFlag = false;
}
}
}
}
Log.i(TAG,"FindUsbAccessoryThread exit...");
}
};
this.findUsbAccessoryThread.start();
}
}
private boolean openAccessory(UsbManager usbManager,UsbAccessory accessory) {
try{
if(this.fileDescriptor==null){
this.fileDescriptor = usbManager.openAccessory(accessory);
}
if (this.fileDescriptor != null) {
FileDescriptor fd = fileDescriptor.getFileDescriptor();
this.fileInputStream = new FileInputStream(fd);
this.fileOutStream = new FileOutputStream(fd);
this.bufferedOutputStream = new BufferedOutputStream(fileOutStream);
if(this.fileInputStream==null||this.bufferedOutputStream==null){
return false;
}else return true;
} else {
return false;
}
}catch (Exception e){
Log.w(TAG, "openAccessory exception:"+e.getMessage(),e);
return false;
}
}
2.使用取到的BufferedOutputStream和FileInputStream进行通信
至此Android至Android的Usb aoa通信已完成,以下是完整demo工程
网友评论