研究android2android aoa通讯时,在网上查询了很多资料,这些资料对Accessory模式的描述,在研究过程中造成了很大的困扰,故此先对基本信息进行介绍。
1.png
VID 固定为Google的官方VID – 0x18D1 PID 在不同的模式下定义如下: ● 0x2D00 - accessory ● 0x2D01 - accessory + adb ● 0x2D02 - audio ● 0x2D03 - audio + adb ● 0x2D04 - accessory + audio ● 0x2D05 - accessory + audio + adb
流程图.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端使用输入输出流,具体代码如下。
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工程
作者:珠穆尼玛_2dfa
链接:https://www.jianshu.com/p/9902179374aa
接口原型 - IT教程,计算机教程,API相关DEMO