背景知识

USB是一种数据通信方式,也是一种数据总线,而且是最复杂的总线之一。
硬件上,它是用插头连接。一边是公头(plug),一边是母头(receptacle)。例如,PC上的插座就是母头,USB设备使用公头与PC连接。
目前USB硬件接口分三种,普通PC上使用的叫Type;原来诺基亚功能机时代的接口为Mini USB;目前Android手机使用的Micro USB/TYPE-C。

Host

USB是由Host端控制整个总线的数据传输的。单个USB总线上,只能有一个Host。

OTG

On The Go,这是在USB2.0引入的一种mode,提出了一个新的概念叫主机协商协议(Host Negotiation Protocol),允许两个设备间商量谁去当Host。

想了解更多USB知识,请参考USB官网以及下面这篇文章:
http://www.crifan.com/files/doc/docbook/usb_basic/release/html/usb_basic.html

USB HOST/DEVICE/OTG概念:

1.USB 设备分为 HOST (主设备)和SLAVE (从设备),只有当一台 HOST 与一台 SLAVE 连接时才能实现数据的传输。
(1) USBHOST 是指主机。
(2) USBOTG 设备既能做主机,又能做设备。 OTG 技术就是实现在没有 Host 的情况下,实现从设备间的数据传送。
当 OTG 插到电脑上时. OTG 的角色就是连接电脑的 device (读卡器),也就是 SLAVE (从设备);当USB/SD device插到 OTG 上, OTG 的角色就是 HOST 主机。(有些手机也经常用到 OTG的功能)

Android使用(Host连接)

首先应该介绍,USBManager这个类,这个类用于访问USB状态并与USB设备通信。目前只开放HOST模式。
初始化USBManager mUsbManager = ((UsbManager) context.getSystemService(USB_SERVICE));
里面有几个重要的方法:

1.getDeviceList()——获取当前USB连接从设备的列表,返回一个存储UsbDevice的HashMap<String,UsbDevice>
2.getAccessoryList()——这个就是获取Host的列表(用不到)
3.openDevice(UsbDevice)—— 这个方法是USB连接方法,连接成功后会返回一个UsbDeviceConnection对象。
4.requestPermission(UsbDevice,PendingIntent)——这个方法用于请求USB权限。

监听USB插入/拔出

通过广播意图android.hardware.usb.action.USB_STATE,实现插入拔出监听

  class USBStateReceive extends BroadcastReceiver {public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";public static final String USB_CONNECTED = "connected";private IUsbStateChangeListener mUsbStateChangeListener;public void onReceive(Context paramContext, Intent intent) {String action = intent.getAction();Log.d(TAG, "usb action + " + action);switch (action) {case UsbManager.ACTION_USB_DEVICE_ATTACHED:UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);Log.d(TAG, "device_attached," + device.toString());if (mUsbStateChangeListener != null) {mUsbStateChangeListener.onAttach(device);}break;case UsbManager.ACTION_USB_DEVICE_DETACHED:device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);Log.d(TAG, "device_detached," + device.toString());if (mUsbStateChangeListener != null) {mUsbStateChangeListener.onDetach(device);}break;case ACTION_USB_STATE:boolean connected = intent.getBooleanExtra(USB_CONNECTED, false);break;}}public void setUsbStateChangeListener(IUsbStateChangeListener usbStateChangeListener) {this.mUsbStateChangeListener = usbStateChangeListener;}}

这个广播也可以获取到当前插入/拔出的设备信息。可以通过这个广播来监听新插入/拔出的USB设备。

当前连接的USB设备

可以通过getDeviceList获取当前连接的USB设备
为此我们编写一个USBFinder 用来查找USB设备。


package cn.xiaolongonly.usbhelper.usbmodule;import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;import java.util.HashMap;
import java.util.Iterator;/*** <USB设备查询器>** @author xiaolong 719243738@qq.com* @version v1.0* @since 2019/4/10 15:36*/
public class USBFinder {private UsbManager mUsbManager;private static USBFinder usbFinder;private USBFinder(UsbManager usbManager) {this.mUsbManager = usbManager;}/*** Application Context;**/public static void init(UsbManager usbManager) {usbFinder = new USBFinder(usbManager);}public static USBFinder getInstance() {if (usbFinder == null) {throw new RuntimeException("需要在Application中執行USBFinder.init()");}return usbFinder;}public HashMap<String, UsbDevice> findUsbMap() {HashMap<String, UsbDevice> deviceMap = mUsbManager.getDeviceList();return deviceMap;}public UsbDevice findUsbDevice(int vendorId, int productId) {HashMap<String, UsbDevice> deviceMap = mUsbManager.getDeviceList();Iterator iterator = deviceMap.values().iterator();UsbDevice localUsbDevice;while (iterator.hasNext()) {localUsbDevice = (UsbDevice) iterator.next();if (localUsbDevice.getVendorId() == vendorId || localUsbDevice.getProductId() == productId) {return localUsbDevice;}}return null;}
}
  1. findUsbMap 用于找到当前已连接的USB设备列表,这个用户选择连接非常有用,比如有多个USB设备,选择一个连接。(跟上一个通过广播监听的区别是,有时候一些USB设备默认是插着的,通过上面的广播并不能知道)
  2. findUsbDevice 遍历设备列表,通过vendorId和productId找到合适的USB设备,通过这个可以省掉让用户选择的过程,如果连接的设备只有一个的话可以直接用这个。

USB连接,如何确认USB连接及USB授权?

Android 在USBManager 提供了一个 requestPermission(UsbDevice device, PendingIntent pi),这个模式是手机作为HOST对设备做授权。

    public void getPermission(Context context, UsbManager usbManager, UsbDevice usbDevice, final UsbPermissionReceiverListener callBack) {if (usbManager.hasPermission(usbDevice)) {callBack.onSuccessful();return;}mContext = context;IntentFilter intentFilter = new IntentFilter();intentFilter.addAction("com.android.example.USB_PERMISSION");mContext.registerReceiver(mUsbReceiver, intentFilter);//注册广播接收者mUsbReceiver.setUsbPermissionReceiverListener(new UsbPermissionReceiverListener() {@Overridepublic void onSuccessful() {mContext.unregisterReceiver(mUsbReceiver);callBack.onSuccessful();}@Overridepublic void onFail(String msg) {mContext.unregisterReceiver(mUsbReceiver);callBack.onFail(msg);}});mUsbReceiver.setUsbDeviceName(usbDevice.getDeviceName());//为广播接受者设置DeviceName名称用于与接收的名称进行对比PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);usbManager.requestPermission(usbDevice, mPermissionIntent);}

授权广播

 class PermissionReceiver extends BroadcastReceiver {protected UsbPermissionReceiverListener mUsbPeramissionRecevierListener = null;protected String mUsbDeviceName = "";public void onReceive(Context paramContext, Intent intent) {String action = intent.getAction();Log.d(TAG, "usb action + " + action);if (intent == null) {mUsbPeramissionRecevierListener.onFail("授权失败:未知授权申请!");} else {if (ACTION_USB_PERMISSION.equals(intent.getAction())) {UsbDevice localUsbDevice = intent.getParcelableExtra("device");if (localUsbDevice != null) {if (!(intent.getBooleanExtra("permission", false))) {//用户取消授权if (this.mUsbPeramissionRecevierListener != null) {mUsbPeramissionRecevierListener.onFail("用户取消授权,授权失败!");}} else {String deviceName = localUsbDevice.getDeviceName();if (deviceName.equals(this.mUsbDeviceName)) {//若获得的deviceName与_usbDeviceName一致if (this.mUsbPeramissionRecevierListener != null) {mUsbPeramissionRecevierListener.onSuccessful();}} else {if (this.mUsbPeramissionRecevierListener != null) {mUsbPeramissionRecevierListener.onFail("授權失敗:設備名稱不一致!");}}}} else {return;}} else {mUsbPeramissionRecevierListener.onFail("授權失敗:未知授权申请!");return;}}}public void setUsbPermissionReceiverListener(UsbPermissionReceiverListener paramAsyncTaskListener) {this.mUsbPeramissionRecevierListener = paramAsyncTaskListener;}public void setUsbDeviceName(String usbDeviceName) {this.mUsbDeviceName = usbDeviceName;}}

授权广播类,监听的是com.android.example.USB_PERMISSION

通过以上几个方法我们可以获取到当前连接的USB设备了,接下来就是建立传输通道

数据传输

这边先要获取一个UsbDeviceConnection 对象, 也就是通过USBManager.openDevice(UsbDevice)获取连接的对象。

    /*** 选择USB设备和interface,打开。** @return*/public int open(UsbDevice usbDevice, int interfaceIndex) {mDevice = usbDevice;if (!setInterface(this.mDevice, interfaceIndex)) {Log.d(TAG, "interfaceIndex OutOfBound!");return -1;}this.mInterface = mDevice.getInterface(interfaceIndex);if (!(setEndpoint(this.mInterface))) {return -1;}this.mDeviceConnection = this.mManager.openDevice(usbDevice);if (this.mDeviceConnection == null) {return -1;}return 0;}

讲一下为什么这个类需要传interfaceIndex,一般来讲一个UsbDevice 会有多个UsbInterface, 不同的interface是分开的,有不同的功能。每个UsbInterface又会有多个UsbEndpoint,用于处理输入和输出,也就是数据传输。
传入interfaceIndex是为了能够找到符合要求的index.

检验interface和endpoint 是否符合

  protected boolean setInterface(UsbDevice mDevice, int interfaceIndex) {if (mDevice != null && mDevice.getInterfaceCount() > interfaceIndex) {return true;} else {return false;}}/*** 设置Endpoint,根据Device的Interface** @param paramUsbInterface* @return*/protected boolean setEndpoint(UsbInterface paramUsbInterface) {if (paramUsbInterface.getEndpointCount() != 2) {Log.d(TAG, "Endpoint count less than 2..");return false;}for (int m = 0; m < paramUsbInterface.getEndpointCount(); m++) {UsbEndpoint ep = paramUsbInterface.getEndpoint(m);if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {this.mEndpointOut = ep;} else {this.mEndpointIn = ep;}} else {return false;}}return true;}

这两个方法就是用来看是不是能找到对应的UsbInterface和UsbEndpoint,因为我们的需求是要这个既有输入又有输出的UsbInterface。所以做了相应的校验。

接下来是数据传输

    public void sendData(byte[] data, IDataSendCallBack iDataSendCallBack, int timeOut) {if (this.mDeviceConnection == null) {if (iDataSendCallBack != null) {iDataSendCallBack.onSendResult(CODE_DEVICE_NO_OPEN, null, "未开启设备,请先执行open()!");}}this.mDeviceConnection.claimInterface(this.mInterface, true);//调用了本地bulkTransfer(this.mDeviceConnection, this.mEndpointOut, this.mEndpointIn, data, data.length, timeOut, iDataSendCallBack);this.mDeviceConnection.releaseInterface(this.mInterface);//释放接口}protected void bulkTransfer(final UsbDeviceConnection usbDeviceConnection, UsbEndpoint outPoint, final UsbEndpoint inPoint, byte[] datas, int dataLength, final int timeLimit, final IDataSendCallBack iDataSendCallBack) {int bulkTransferLength = usbDeviceConnection.bulkTransfer(outPoint,datas, dataLength, timeLimit);if (bulkTransferLength < 0) {if (iDataSendCallBack != null) {iDataSendCallBack.onSendResult(CODE_SEND_FAIL, datas, "发送失败!");}}if (iDataSendCallBack != null) {iDataSendCallBack.onSendResult(CODE_SEND_SUCCESS, datas, "發送成功!");}if (mEndpointIn == null) {if (iDataSendCallBack != null) {iDataSendCallBack.onSendResult(CODE_NO_INPOINT, null,"該interface底下沒有接口端口,无法接收数据!");}}new Thread(new Runnable() {@Overridepublic void run() {while (true) {byte[] inByte = new byte[1024];int inLength = usbDeviceConnection.bulkTransfer(inPoint,inByte, inByte.length, timeLimit);if (inLength > 0) {inByte = Arrays.copyOf(inByte, inLength);if (iDataSendCallBack != null) {iDataSendCallBack.onSendResult(CODE_DATA_RECEIVE,inByte, "收到数据!");}} else {break;}}}}).start();}

DeviceConnection.claimInterface()占用了这个interface,在发送结束后releaseInterface 相当于释放interface.
项目要求是一次输入,一次输出。所以每次都会开开合合,如果不是这种需求的话应该可以长期开着等全部结束后关闭,类似于Socket。

Talk is cheap, here the code.

项目地址:https://github.com/guoxiaolongonly/USBHelper

参考自:https://blog.csdn.net/cui130/article/details/81329634

AndroidUSB—OTG通信相关推荐

  1. android OTG (USB读写,U盘读写)最全使用相关总结

    androidOTG (USB读写,U盘读写) 最全使用相关总结 简介 第一种读取方法:android推荐使用的通过endpoint的形式进行通信 第二种读取方法:像读你sdcard的形式来读你的U盘 ...

  2. Android USB OTG U盘读写相关使用最全总结

    Android USB OTG U盘读写相关使用最全总结 https://blog.csdn.net/qq_29924041/article/details/80141514 androidOTG ( ...

  3. 高通平台 USB OTG功能

    1.什么是OTG? USB OTG是USBOn-The-Go的缩写,即OTG技术就是实现在没有Host的情况下,实现设备间的数据传送. 2.硬件接口 5根线 USB_HS_ID USB_HS_D_P ...

  4. Android 获得 usb 权限的两种方式

    0. 前言 在做 USB OTG 通信时,第一步就是要能够获取到 usb 的使用权限,因此特地在此处介绍一下两种我用过的获取 usb 权限方式. 1. 直接在 AndroidManifest.xml ...

  5. 学习Zynq笔记(1):7020开发平台简介

    文章目录 一.结构示意图 二.结构尺寸 三.zynq7000 PS系统的主要参数 PL系统的主要参数 四.PS端的外设 1.QSPI FLASH 2.DDR3 DRAM 3.以太网接口 4.SD卡槽 ...

  6. androidUSB通信——打印机

    androidUSB通信打印机 一usb基础 二创建基本usb通信 1qwm_usb_xmlxml说明 2AndroidManifestxml清单文件配置 3UsbDemoActivityjava u ...

  7. Android-USB通信

    Android-USB通信 本文记录下,Android平台上如何与USB设备进行通信.我这里使用的USB设备是一个USB加密设备(简称Ukey),通过与Ukey通信,对数据进行加密,提供一些加密算法. ...

  8. 安卓OTG 安卓转串口 安卓手机与单片机通信 USB转串口

    随着物联网发展,很多的安卓网关出现,如果安卓跟单片机结合那就非常完美了. 目前安卓手机跟单片机通信都是通过蓝牙或者wifi的方式无线通信,好处是可以分开长距离通信,但是往往使用的时候安卓板子跟单片机都 ...

  9. 安卓Android OTG USB串口通信FT232R

    [实例截图] 了解嵌入式的读者应该知道在单片机编程中串口(uart)通讯接口最常用的就是TTL和USB接口,将单片机TTL转USB就可以接入电脑查看串口数据实现电脑与单片机通讯,在Android AS ...

最新文章

  1. Python:正则表达式re模块
  2. asp.net的定义
  3. VM虚拟机链接克隆及linux eth0网卡的快速设置方法
  4. Oracle学习总结(9)—— Oracle 常用的基本操作
  5. 特斯拉市值站上万亿元大关,BBA如何在“自动驾驶”上做空它?
  6. 指针(Pointer)
  7. python 如何运行程序
  8. 坚果pro2刷机分享
  9. 使用存储过程备份SqlServer数据库
  10. excel小写转大写公式_excel数字小写转大写公式的教程
  11. 图解通信原理与案例分析-10:楼宇有线对讲电话机案例--模拟基带点对点通信详解
  12. u盘无法格式化不在计算机中,u盘被写保护无法格式化怎么办 u盘无法格式化原因及解决...
  13. 获取b站某个up的视频aid、cid
  14. 汽车电子行业英语、缩写、中英对照
  15. 循序渐进学SAP系列(一):--SAP该如何入门
  16. 微信小程序登录注册demo+java服务器(一)
  17. 峰值检测电路和精密整流电路
  18. 机房网络服务器维修图片,机房机柜尾纤布线图片 机房布线维护整理
  19. 深度学习(17)—— 度量学习
  20. python 时间序列突变检测_Python 百度指数突变点检测

热门文章

  1. 学生信息管理系统之用户登录:用户登录流程
  2. win10 chrome被毒霸2345劫持主页处理过程与结果
  3. 马蜂窝裁php换java,php又又又凉凉了吗
  4. 蓝牙基础(一):版本特点与分类
  5. WIN7 ftp网线直连传东西怎么设置
  6. Java 超.简易RPG游戏
  7. matlab实验-拉格朗日插值的龙格(Runge)现象
  8. 按位取反‘~’是啥?
  9. 不懂这些高并发分布式架构、分布式系统的数据一致性解决方案,你如何能找到高新互联网工作呢?强势解析eBay BASE模式、去哪儿及蘑菇街分布式架构...
  10. h3c服务器r4900清空配置信息,H3C R4900 G3服务器HDM初始化配置