android USB通信

USB模式

支持USB accessory模式和USB host模式。通过这两种模式,android支持各种各样的USB 外围设备和USB 配件(硬件需要实现android配件协议)。

USB accessory模式中,外接的USB硬件设备扮演USB 主机,这种方式使不支持USB host模式的android设备有了与USB硬件交互的能力,不过android USB 配件必须遵循android accessory communication protocol。

在USB host模式下,android设备扮演主机角色。

下图展示的是两种模式的区别。在USB host模式下,android设备担任host并且驱动总线,在USB accessory模式下,连接的USB 硬件充当host并且驱动总线。

image

USB host模式使用

相关API

介绍
UsbManager 获取连接的USB设备并与之通信
UsbDevice 代表一个连接的USB 设备,包含一系列方法获取自身信息,包括interfaces,endpoints
UsbInterface 代表USB 设备上定义的一系列功能接口,一个usb设备可以有一个或多个接口
UsbEndpoint 代表一个interface通信频道,一个interface可以有一个或多个endpoints,一般含有输入输出两个端点来支持双工通信
UsbDeviceConnection 代表设备连接的一个链路,将数据传输到端点上,这个类允许你同步或异步的来回发送数据
UsbRequest 代表一个异步请求,通过UsbDeviceConnection来跟设备通信
UsbConstants 定义了linux内核文件linux/usb/ch9.h中的常量

在绝大部分情况下,当你需要跟USB通信时,你需要用到这些类(UsbRequest类仅在异步通信时用到)。一般的,获取UsbManager找到目标UsbDevice,接着找到恰当的UsbInterface和这个interface的UsbEndpoint,得到了正确的UsbEndpoint后,打开设备获得UsbDeviceConnection来跟USB 设备通信。

代码步骤

android manifest配置

配置usb feature 是因为并不是所有的android设备都保证支持USB host模式

<uses-feature android:name="android.hardware.usb.host"/>

获取UsbManager

UsbManager usbManager=(UsbManager)context.getSystemService(Context.USB_SERVICE)

获取目标UsbDevice

可以根据需求选择设备vid组合pid或设备deviceName寻找目标UsbDevice

String targetDeviceName="...";
UsbDevice targetDevice=null;
HashMap<String,UsbDevice> deviceMap=usbManager.getDeviceList();
Iterator<UsbDevice> iterator=deviceMap.values().iterator();
while(iterator.hasNext()){
      UsbDevice device=iterator.next();
      if(targetDeviceName.equals(device.getDeviceName())){
            targetDevice=device;
            break;
      }
}

申请USB使用权限

获取到UsbDevice后需要检查该UsbDevice是否已经获取到使用权限。如果没有权限则要申请USB使用权限,

if (!usbManager.hasPermission(usbDevice)) {
    IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
            mContext.registerReceiver(mUsbPermissionReceiver, filter);
            PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
usbManager.requestPermission(usbDevice, pi);
}

private BroadcastReceiver mUsbPermissionReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_DEVICE_PERMISSION.equals(action)) {
            UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
            boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
            if (granted) {
                //获得了usb使用权限
                usbDeviceInit(device);
            }
        }
    }
};

获取UsbInterface以及对应得UsbEndpoints,UsbDeviceConnection

private void usbDeviceInit(UsbDevice device) {
    int interfaceCount = device.getInterfaceCount();
    UsbInterface usbInterface = null;
    for (int i = 0; i < interfaceCount; i++) {
        usbInterface = device.getInterface(i);
        if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER) {
            break;
        }
    }
    if (usbInterface != null) {
       //获取UsbDeviceConnection
        UsbDeviceConnection connection = mUsbManager.openDevice(device);
        if (connection != null) {
              if (connection.claimInterface(usbInterface, true)) {
                for (int j = 0; j < usbInterface.getEndpointCount(); j++) {
                    UsbEndpoint endpoint = usbInterface.getEndpoint(j);
                    //类型为大块传输
                    if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
                        if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
                            mUsbEndpointOutMap.put(device.getDeviceName(), endpoint);
                        } else {
                            mUsbEndpointInMap.put(device.getDeviceName(), endpoint);
                        }
                    }
                }
            }
        }


    }
}

和USB设备通信

调用UsbDeviceConnection的bulkTransfer方法与USB设备通信,向USB设备发送数据用endpointOut,接受USB设备的数据用endpointIn。

private int bulk(String deviceName, ArrayList<Byte> command) {
    if (command!=null && !command.isEmpty()) {
        UsbEndpoint endpointOut = mUsbEndpointOutMap.get(deviceName);
        UsbDeviceConnection connection = mUsbDeviceConnectionMap.get(deviceName);
        if (endpointOut == null || connection == null) {
            return ErrorCode.CODE_ERROR;
        }
        byte[] data = new byte[command.size()];
        for (int i = 0; i < command.size(); i++) {
            data[i] = command.get(i).byteValue();
        }
        int ret = connection.bulkTransfer(endpointOut, data, data.length, TIME_OUT);//超时时间可以长一点,不然数据量大的时候容易超时错误
        if (ret >= 0) {
            return ErrorCode.CODE_SUCCESS;
        }

        return ErrorCode.CODE_ERROR;
    }else {
        return ErrorCode.CODE_SUCCESS;
    }

}

USB设备插入,拔出通知

当USB设备插入或拔出时系统会发出相应的广播,因此注册这些广播就可以得到这些通知,然后进行相应的处理。

IntentFilter filter = new IntentFilter();   
//注册USB设备插入,拔出动作
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager. ACTION_USB_DEVICE_DETACHED);
registerReceiver(receiver, filter);


BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
            //TODO 检查一个该device是否是我们的目标设备
            if(!isTargetDevice(device)){
                  return
            }
            String action=intent.getAction();
            if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
                    
                usbDeviceInit(device);
            }else if(action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)){
                /*TODO 释放UsbInterface 关闭UsbDeviceConnection

                  mUsbDeviceConnection.releaseInterface(interface);
                  mUsbDeviceConnection.close();
*
            }
        }
    };

推荐阅读更多精彩内容