android AOA开发

Android从3.1版本可开始引进了对AOA的支持,这是一种允许外部USB硬件与Android设备进行交互的特殊Accessory模式。当一个Android设备处于Accessory模式的时候,已连接的配件则扮演Host的角色(负责给总线供电并且枚举设备),而Android设备则扮演USB device的角色。Android USB配件是一个专门设计的附加到Android 设备上,并实现了一个简单的协议(AOA),使他们能够检测支持Accessory模式的Android设备。配件还必须提供500ma的5V供电电源。许多以前发布的Android设备只能充当USB设备,不能用来与外部USB设备的连接通讯。AOA的支持克服了这个限制。accessory模式最终依赖于硬件设备,并且不是所有的设备都支持Accessory模式。如果设备支持Accessory模式的话,可以在应用的AndroidManifest中使用 uses-feature 元素来标记。

  • AOA的检测与连接流程
  • 代码实现

AOA的检测与连接流程

一个USB配件必须遵循AOA协议,这个协议定义了一个配件如何发现并且与设备建立通讯,通常情况下,一个配件应该按照以下步骤:

  • 等待发现连接的设备
  • 确定设备是否支持Accessory模式
  • 尝试让配件切换到Accessory模式
  • 建立通讯,如果设备支持AOA协议

连接流程如下:
USB开发------android AOA开发_第1张图片

当设备连接时,他们应该在以下三种状态中的一种:
1.已Attached的设备支持Accessory模式,并且已经处于此模式
2.已Attached的设备支持Accessory模式,但不处于此模式
3.设备不支持Accessory模式

当建立连接之后,配件应该检查连接的设备的VID和PID。google固定了AOA设备的VID与PID,如果PID跟VID匹配的话,那么就可以使用USB的块传输的endpoints建立配件与设备之间的通讯。
AOA 设备VID = 0x18D1, PID有多种,其中包括AOA 2.0中新增的,分别对应的关系如下:

PID 模式
0x2D00 accessory
0x2D01 accessory + adb
0x2D02 audio
0x2D03 audio + adb
0x2D04 accessory + audio
0x2D05 accessory + audio + adb

如果VID跟PID不匹配,那么我们就需要请求设备切换为Accessory模式,这时候需要发送51号命令控制请求来确定设备是否支持Android附件协议。如果支持协议,则返回非零数,表示设备支持的协议版本。此请求是端点0上的控制请求,其特征如下:

参数
requestType USB_DIR_IN 或者 USB_TYPE_VENDOR
request 51
value 0
index 0
data 协议版本号

如果设备返回支持的协议版本号,则向设备发送标识字符串信息。该信息允许设备为该附件找出合适的应用程序,并且如果不存在适当的应用程序,则向用户指示下载的URL地址。这些请求是端点0(对于每个字符串ID)的52号命令控制请求,具有以下特征:

参数
requestType USB_DIR_OUT 或者 USB_TYPE_VENDOR
request 52
value 0
index stringID
data UTF-8字符

stringID定义如下(每个字符串最大长度为256,以/0结尾):

参数 描述
manufacturer name 制造商 0
model name 型号 1
description 描述 2
version 版本 3
URI 下载路径 4
serial number 序列号 5

发送string ID后,使用53号控制命令请求设备在accessory模式下启动,使用如下命令:

参数
requestType USB_DIR_OUT 或者 USB_TYPE_VENDOR
request 53
value 0
index 0
data none

在AOA 2.0协议中,增加了对Audio的支持,将设备设置为音频模式(此命令必须在发送53号命令之前发送),控制请求描述如下:

参数
requestType USB_DIR_OUT 或者 USB_TYPE_VENDOR
request 58
value 0 for no audio (default), 1 for 2 channel, 16-bit PCM at 44100 KHz
index 0
data none

代码实现

package com.zdragon.videoio;import android.app.PendingIntent;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.hardware.usb.UsbConstants;import android.hardware.usb.UsbDevice;import android.hardware.usb.UsbDeviceConnection;import android.hardware.usb.UsbManager;import android.os.Handler;import android.os.HandlerThread;import android.os.Looper;import android.os.Message;import android.util.Log;import java.io.UnsupportedEncodingException;/** * Created by GaddiZou on 2018/8/5 0005. */public class UsbTest {    private final static String MANUFACTURER_NAME = "AoA Test MA";    private final static String MODEL_NAME = "AOA Test Model";    private final static String DESCRIPTION = "Description";    private final static String VERSION = "ver1.0";    private final static String URI = "http://www.downloadapk.com";    private final static String SERIAL = "111111111111111111";    private final static String REQUEST_PERMISSION_ACTION = "com.test.usb";    private UsbManager mUsbManager;    private PendingIntent mPendingIntent;    private HandlerThread mHandlerThread = new HandlerThread("usb-handler");    private UsbHandler mUsbHandler;    private class UsbHandler extends Handler {        public final static int MSG_REQUEST_PERMISSION = 0;        public final static int MSG_USB_ATTACHED = 1;        public final static int MSG_USB_DETACHED = 2;        public UsbHandler(Looper looper){            super(looper);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            int what = msg.what;            UsbDevice device = (UsbDevice) msg.obj;            switch(what){                case MSG_REQUEST_PERMISSION:                    if (mUsbManager.hasPermission(device)) {                        switchToAoAMode(device);                    } else {                        //No permission                    }                    break;                case MSG_USB_ATTACHED:                    //TODO                    break;                case MSG_USB_DETACHED:                    //TODO                    break;            }        }    }    private BroadcastReceiver mUsbDeviceEventReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);            Message msg = mUsbHandler.obtainMessage();            msg.obj = device;            if(action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)){                msg.what = UsbHandler.MSG_USB_ATTACHED;            } else if(action.equals(UsbManager.ACTION_USB_ACCESSORY_DETACHED)){                msg.what = UsbHandler.MSG_USB_DETACHED;            } else if(action.equals(REQUEST_PERMISSION_ACTION)){                msg.what = UsbHandler.MSG_REQUEST_PERMISSION;            }            mUsbHandler.sendMessage(msg);        }    };    public UsbTest(Context context){        mHandlerThread.start();        mUsbHandler = new UsbHandler(mHandlerThread.getLooper());        mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);        IntentFilter intentFilter = new IntentFilter();        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);        intentFilter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);        intentFilter.addAction(REQUEST_PERMISSION_ACTION);        context.registerReceiver(mUsbDeviceEventReceiver, intentFilter);        mPendingIntent = PendingIntent.getBroadcast(context,0, new Intent(REQUEST_PERMISSION_ACTION), 0);    }    public void usbRequestPermission(UsbDevice usbDevice){        if(usbDevice == null){            return;        }        mUsbManager.requestPermission(usbDevice, mPendingIntent);    }    public void switchToAoAMode(UsbDevice usbDevice){        if(usbDevice.getVendorId() == 0x18D1 && (usbDevice.getProductId() == 0x2D00 ||                usbDevice.getProductId() == 0x2D01 ||                usbDevice.getProductId() == 0x2D02 ||                usbDevice.getProductId() == 0x2D03 ||                usbDevice.getProductId() == 0x2D04 ||                usbDevice.getProductId() == 0x2D05 )){            //UsbDevice in AOA mode.            sendAccessoryInfo(usbDevice);        }else{            UsbDeviceConnection  udc = mUsbManager.openDevice(usbDevice);            byte [] datas = new byte[2];            int ret = udc.controlTransfer(UsbConstants.USB_DIR_IN, 51, 0,0, datas, 1, 0);            if(ret > 0 && datas[0] == 1){                //device has been switch to support AOA                sendAccessoryInfo(usbDevice);            }else{                //device not support AOA            }        }    }    public void sendAccessoryInfo(UsbDevice usbDevice){        UsbDeviceConnection  udc = mUsbManager.openDevice(usbDevice);        int ret = -1;        try {            byte [] datas =  MANUFACTURER_NAME.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,0, datas, datas.length, 0);            if(ret < 0){                return;            }            datas =  MODEL_NAME.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,1, datas, datas.length, 0);            if(ret < 0){                return;            }            datas =  DESCRIPTION.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,2, datas, datas.length, 0);            if(ret < 0){                return;            }            datas =  VERSION.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,3, datas, datas.length, 0);            if(ret < 0){                return;            }            datas =  URI.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,4, datas, datas.length, 0);            if(ret < 0){                return;            }            datas =  SERIAL.getBytes("utf-8");            ret = udc.controlTransfer(UsbConstants.USB_DIR_OUT, 52, 0,5, datas, datas.length, 0);            if(ret < 0){                return;            }            //After send info ok, we start up accessory.            udc.controlTransfer(UsbConstants.USB_DIR_OUT, 53, 0,0, null, 0, 0);        } catch (UnsupportedEncodingException e) {            Log.e("ERROR", Log.getStackTraceString(e));        }    }}

更多相关文章

  1. Android当中的MVP模式(三)基于分页列表的封装
  2. 移动设备操作系统知识点简摘又名我的期末考试复习第二弹
  3. 手机和平板之外——带你理解跨设备的Android 技术体系
  4. 如何利用android ADK访问外围设备
  5. Android API开发之蓝牙开发之Android蓝牙开发GATT协议

随机推荐

  1. 给购物车加上选择时自动计算功能
  2. 仿京东移动端首页
  3. 第四课 box-sizing、伪类、媒体查询移动
  4. js变量、常量、函数类型、作用域、闭包、
  5. Vue一个通用的组件传递点击事件的两种种
  6. JS实战(自动轮播图+购物车自动计算)
  7. makedown语法测试
  8. js 流程控制 和 DOM操作
  9. 如何开发一款前端工具
  10. img标签到底是行内元素还是块级元素