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协议

连接流程如下:
这里写图片描述

当设备连接时,他们应该在以下三种状态中的一种:
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模式
0x2D00accessory
0x2D01accessory + adb
0x2D02audio
0x2D03audio + adb
0x2D04accessory + audio
0x2D05accessory + audio + adb

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

参数
requestTypeUSB_DIR_IN 或者 USB_TYPE_VENDOR
request51
value0
index0
data协议版本号

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

参数
requestTypeUSB_DIR_OUT 或者 USB_TYPE_VENDOR
request52
value0
indexstringID
dataUTF-8字符

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

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

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

参数
requestTypeUSB_DIR_OUT 或者 USB_TYPE_VENDOR
request53
value0
index0
datanone

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

参数
requestTypeUSB_DIR_OUT 或者 USB_TYPE_VENDOR
request58
value0 for no audio (default), 1 for 2 channel, 16-bit PCM at 44100 KHz
index0
datanone

代码实现

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));
        }
    }
}
  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值