USB开发------android AOA开发

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));
        }
    }
}

你可能感兴趣的:(android应用开发)