Android到Android的USB AOA協(xié)議通訊的實(shí)現(xiàn)

研究android2android aoa通訊時企垦,在網(wǎng)上查詢了很多資料帖渠,這些資料對Accessory模式的描述桑涎,在研究過程中造成了很大的困擾即硼,故此先對基本信息進(jìn)行介紹逃片。

1.1 Host模式與Accessory模式的區(qū)別

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設(shè)備和Android Host設(shè)備兩者枚舉識別工作過程

流程圖.PNG

枚舉過程如圖所示

1. 首先USB Accessory設(shè)備發(fā)起USB控制傳輸進(jìn)行正常的USB設(shè)備枚舉,獲取設(shè)備描述符和配置描述符等信息只酥。

此時大部分Android設(shè)備上報(bào)的還只是普通的HID或MTP設(shè)備

2. 接下來USB Accessory設(shè)備褥实,根據(jù)枚舉的PID/VID呀狼,向?qū)?yīng)的USB設(shè)備,發(fā)起Vendor類型损离,request值為51(0x33)的控制傳輸命令(ACCESSORY_GET_PROTOCOL)哥艇,

看看該Android設(shè)備是否支持USB Accessory功能,如果支持的話會返回所支持的AOA協(xié)議版本(1或2)僻澎。

3. USB Accessory判斷到該Android設(shè)備支持Accessory功能后她奥,

發(fā)起request值為52(0x34)的控制傳輸命令(ACCESSORY_SEND_STRING),

并把該Accessory設(shè)備的相關(guān)信息(包括廠家怎棱,序列號等)告知Android設(shè)備;

4. 最終绷跑,USB Accessory設(shè)備發(fā)起request值為53(0x35)的控制傳輸命令(ACCESSORY_START)拳恋,

通知Android設(shè)備切換到Accessory功能模式開始工作。

至此砸捏,Android Accessory端與Host端的識別工作完成谬运,接下來是通訊:Accessory端使用塊傳輸,Host端使用輸入輸出流垦藏,具體代碼如下梆暖。

1.4 Android USB Accessory設(shè)備和Android設(shè)備兩者枚舉識別工作過程的代碼實(shí)現(xiàn)

Accessory端
1.設(shè)備枚舉

    public final String myUsbDevices = "1234/1234";
    public static final String[]aoaPidVid = {"2D00/18D1","2D01/18D1","2D02/18D1","2D03/18D1","2D04/18D1","2D05/18D1"};
    /**
     * 枚舉usb設(shè)備,并修改為Accessory模式</br>
     * 本例中只考慮連接一個Accessory設(shè)備的情況
     * **/
    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設(shè)備是否目標(biāo)設(shè)備
            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)){//判斷設(shè)備是否為Accessory設(shè)備
                return dev;
            }

        }
        return null;
    }

2.初始化Accessory:
查詢Android Host端是否支持AOA協(xié)議掂骏,向Android Host端發(fā)送AndroidAccessory端的廠商/序列號等內(nèi)容轰驳,通知Android Host端切換到Accessory模式

/**
         * 配件發(fā)送序號52的USB請求報(bào)文,通過Index字段攜帶配件自身信息弟灼,包括制造商级解、型號、版本田绑、設(shè)備描述勤哗、序 列號URI等。手機(jī)根據(jù)這些信息啟動響應(yīng)的APP
         * 配件發(fā)送序號53的USB請求報(bào)文掩驱,切換USB模式芒划,主要是根據(jù)切換的vendorID和productID
         * 重新枚舉USB設(shè)備,準(zhǔn)備建立AOA數(shù)據(jù)通道
         */
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協(xié)議
        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.使用塊傳輸進(jìn)行通信

Host端
1.枚舉accessory設(shè)備

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)){
                        //啟動讀取數(shù)據(jù)線程
                        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進(jìn)行通信

至此Android至Android的Usb aoa通信已完成,以下是完整demo工程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苔可,一起剝皮案震驚了整個濱河市缴挖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌焚辅,老刑警劉巖映屋,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苟鸯,死亡現(xiàn)場離奇詭異,居然都是意外死亡棚点,警方通過查閱死者的電腦和手機(jī)早处,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘫析,“玉大人砌梆,你說我怎么就攤上這事”嵫” “怎么了咸包?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杖虾。 經(jīng)常有香客問我烂瘫,道長,這世上最難降的妖魔是什么奇适? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任坟比,我火速辦了婚禮,結(jié)果婚禮上嚷往,老公的妹妹穿的比我還像新娘葛账。我一直安慰自己,他們只是感情好皮仁,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布籍琳。 她就那樣靜靜地躺著,像睡著了一般贷祈。 火紅的嫁衣襯著肌膚如雪巩割。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天付燥,我揣著相機(jī)與錄音宣谈,去河邊找鬼。 笑死键科,一個胖子當(dāng)著我的面吹牛闻丑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勋颖,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嗦嗡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饭玲?” 一聲冷哼從身側(cè)響起侥祭,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后矮冬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谈宛,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年胎署,在試婚紗的時候發(fā)現(xiàn)自己被綠了吆录。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡琼牧,死狀恐怖恢筝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情巨坊,我是刑警寧澤撬槽,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站趾撵,受9級特大地震影響恢氯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鼓寺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望勋磕。 院中可真熱鬧妈候,春花似錦、人聲如沸挂滓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赶站。三九已至幔虏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贝椿,已是汗流浹背想括。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留烙博,地道東北人瑟蜈。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像渣窜,于是被迫代替她去往敵國和親铺根。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359