美文网首页Android开发Android技术知识Android开发
Android到Android的USB AOA协议通讯的实现

Android到Android的USB AOA协议通讯的实现

作者: 珠穆尼玛_2dfa | 来源:发表于2020-05-21 18:29 被阅读0次

研究android2android aoa通讯时,在网上查询了很多资料,这些资料对Accessory模式的描述,在研究过程中造成了很大的困扰,故此先对基本信息进行介绍。

1.1 Host模式与Accessory模式的区别

1.png

1.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工程

相关文章

网友评论

    本文标题:Android到Android的USB AOA协议通讯的实现

    本文链接:https://www.haomeiwen.com/subject/dnptahtx.html