摘要:最近一直使用到身份证刷卡器,M1刷卡器。都有相关的demo。很快的使用上了,不过在使用M1的时候,没有循环(类似公交卡可以一直刷卡)读卡的demo,自己随便的开一个线程去无限读。事与愿违,不没有成功,不能抽时间查相关的UsbManager相关的资料。在这里把其简单的记录一下,^_^, 毕竟口号就抄,抄,抄. .(侵删)

  USB背景知识

  USB是一种数据通信方式,也是一种数据总线,而且是最复杂的总线之一

  硬件上,它是用插头连接。一边是公头(plug),一边是母头(receptacle)。例如:PC上的插头就是母头,USB设备使用公头与PC相连

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

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

  Android中的USB

  Android中对USB支持是3.1开始,显然是加强Android平板的对外扩展能力。而对 USB使用更多的是Android在工业中的使用。Android功能板子一般都会提供多个USB和串口,它们是连接外设手段的桥梁。比如:现在市面上的一体机,Android的板子上面会有很多的USB和串口,但是其中Android板子除了一个系统什么都没有,没有任何的传感器,摄像头等等。。全部是外接,就等同于一台式电脑。

  我们在使用这些外接设备的时候,我们需要来了解一下Android为我们提供的API。其具体在android.hardware.usb包中。我们需要了解一下UsbManagerUsbDeviceUsbDeviceConnection , UsbEndpoint , UsbInterface UsbRequest , UsbConstants , 这几个类,只要使用到USB都要用到它们。

  • UsbManager:获得USB的状态,与连接的USB设备通信
  • UsbDevice:USB设备的抽象,它包含了一个或多个的UsbInterface,而每个UsbInterface包含多个UsbEndpoint。Host与其通信,先打开UsbDeviceConnection,使用UsbRequest在一个端点(UsbEndpoint)发送和接受数据。
  • UsbDeviceConnection:host与device建立的连接,并在endpoint传输数据。
  • UsbEndpoint:endpoint是interface的通信信道。
  • UsbInterface : 定理设备的功能集,一个UsbDevice包含多个UsbInterface,每个UsbInterface都是独立的。
  • UsbRequest:usb请求包。可以在UsbDeviceConnection上异步传输数据。注意是只在异步通信时才会使用到它。
  • UsbConstants:usb常量定义,对应 linux/usb/ch9.h
UsbManager常用方法
getDeviceList() 获得设备列表,返回的是一个HashMap
hasPermission(UsbDevice device) 判断你的应用程序是否有接入此USB设备的权限,如果有则返回真,否则返回false
openDevice(UsbDevice device) 打开USB设备,以便向此USB设备发送和接受数据,返回一个关于此USB设备的连接
requestPermission(UsbDevice device, PendingIntent pi) 向USB设备请求临时的接入权限
UsbDevice常用方法
getDeviceClass() 返回此USB设备的类别,用一个整型来表示
getDeviceId() 返回唯一标识此设备的ID号,也用一个整型来表示
getDeviceName() 返回此设备的名称,用一个字符串来表示
getDeviceProtocol() 返回此设备的协议类别,用一个整型来表示
getDeviceSubclass() 返回此设备的子类别,用一个整型来表示
getVendorId() 返回生产商ID
getProductId() 返回产品ID
getInterfaceCount() 返回此设备的接口数量
getInterface(int index) 得到此设备的一个接口,返回一个UsbInterface
UsbDeviceConnection常用方法
bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) 通过给定的endpoint来进行大量的数据传输,传输的方向取决于该节点的方向,buffer是要发送或接收的字节数组,length是该字节数组的长度。传输成功则返回所传输的字节数组的长度,失败则返回负数
controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout 该方法通过0节点向此设备传输数据,传输的方向取决于请求的类别,如果requestType为USB_DIR_OUT则为写数据,USB_DIR_IN, 则为读数据
UsbEndpoint常用方法
getAddress() 获得此节点的地址
getAttributes() 获得此节点的属性
getDirection() 获得此节点的数据传输方向
UsbInterface常用方法
getId() 得到给接口的id号。
getInterfaceClass() 得到该接口的类别
getInterfaceSubclass() 得到该接口的子类
getInterfaceProtocol() 得到该接口的协议类别。
getEndpointCount() 获得关于此接口的节点数量
getEndpoint(int index) 对于指定的index获得此接口的一个节点,返回一个UsbEndpoint

  USB的广播

  可以通过广播接接收器接收USB的插拔信息:当插入USB插头到一个USB端口或从一个USB端口移除一USB插头。都可以获取到

PendingIntent permissionIntent1 = PendingIntent.getBroadcast(this, 0,new Intent(ACTION_USB_PERMISSION), 0);// Broadcast listen for new devicesIntentFilter filter = new IntentFilter();filter.addAction(ACTION_USB_PERMISSION);filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);this.registerReceiver(mUsbReceiver, filter);/**  BroadcastReceiver when insert/remove the device USB plug into/from a USB port*  创建一个广播接收器接收USB插拔信息:当插入USB插头插到一个USB端口,或从一个USB端口,移除装置的USB插头*/BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {public void onReceive(Context context, Intent intent) {String action = intent.getAction();if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {//插入事件} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {//拔出事件} else if (ACTION_USB_PERMISSION.equals(action)) {//权限请求事件synchronized (this){UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)){if ((device.getProductId() == 8211 && device.getVendorId() == 1305)|| (device.getProductId() == 8213 && device.getVendorId() == 1305)){if(mUsbDriver.openUsbDevice(device)){if(device.getProductId()==8211)mUsbDev1 = device;elsemUsbDev2 = device;}}}else {Toast.makeText(MainActivity.this,"permission denied for device",Toast.LENGTH_SHORT).show();//Log.d(TAG, "permission denied for device " + device);}}}}};

  USB的启动程序和 pid的注册

  时常在有多个视屏播放器,浏览器没有设置为默认的情况下都会弹出一个选择框。usb也一样,需要我们在AndroidManifest.xml进行注册。可以注册在activity标签里面,也可以注册在 application标签里面。不管在那里面,都需要在对应的activity标签里面注册

<intent-filter><action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /></intent-filter>

  和添加权限

 <uses-feature
        android:name="android.hardware.usb.host"android:required="true"/><uses-permission android:name="android.permission.MANAGE_USB"/><uses-permission android:name="android.permission.HARDWARE_TEST"/>

  进行筛选我们自己的 USB的厂家id和产品id。可以在res下新建一个xml。

<?xml version="1.0" encoding="utf-8"?>
<resources><!-- 以下内容的 vendor-id、product-id就是USB的vid和pid了--><usb-device vendor-id="4292" product-id="33896"/><usb-device vendor-id="1024" product-id="50010"/><usb-device vendor-id="1155" product-id="19799"/><usb-device vendor-id="4292" product-id="33485"/>
</resources>

  再在AndroidManifest.xml的activity或者application标签注册

            <meta-dataandroid:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"android:resource="@xml/device_filter"/>

  在这样之后,在USB插入的时候,就会自行启动我们的activity。

  USB的通信

   把USB连接成功,就需要进行与它通信了。

bulkTransfer(this.h[this.g], var5, var4, 3000)

  在做好准备之后,就这一个话。哈哈哈哈哈,啊嗝

  这里摘抄两个前辈写的
  前辈一:

private void initCommunication(UsbDevice device) {tvInfo.append("initCommunication in\n");if(1234 == device.getVendorId() && 5678 == device.getProductId()) {tvInfo.append("initCommunication in right device\n");int interfaceCount = device.getInterfaceCount();for (int interfaceIndex = 0; interfaceIndex < interfaceCount; interfaceIndex++) {UsbInterface usbInterface = device.getInterface(interfaceIndex);if ((UsbConstants.USB_CLASS_CDC_DATA != usbInterface.getInterfaceClass())&& (UsbConstants.USB_CLASS_COMM != usbInterface.getInterfaceClass())) {continue;}for (int i = 0; i < usbInterface.getEndpointCount(); i++) {UsbEndpoint ep = usbInterface.getEndpoint(i);if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {mUsbEndpointIn = ep;} else {mUsbEndpointOut = ep;}}}if ((null == mUsbEndpointIn) || (null == mUsbEndpointOut)) {tvInfo.append("endpoint is null\n");mUsbEndpointIn = null;mUsbEndpointOut = null;mUsbInterface = null;} else {tvInfo.append("\nendpoint out: " + mUsbEndpointOut + ",endpoint in: " +mUsbEndpointIn.getAddress()+"\n");mUsbInterface = usbInterface;mUsbDeviceConnection = mUsbManager.openDevice(device);break;}}}}

  前辈二 : (一个jar,这里只截取其获取getEndpoint)

        private UsbInterface[] e = new UsbInterface[2];private UsbDeviceConnection[] f = new UsbDeviceConnection[2];private int g = -1;private UsbEndpoint[] h = new UsbEndpoint[2];private UsbEndpoint[] i = new UsbEndpoint[2];int var2 = this.d[this.g].getInterfaceCount();Log.i("UsbDriver", " m_Device[m_UsbDevIdx].getInterfaceCount():" + var2);if(var2 == 0) {return false;} else {if(var2 > 0) {this.e[this.g] = this.d[this.g].getInterface(0);}if(this.e[this.g].getEndpoint(1) != null) {this.i[this.g] = this.e[this.g].getEndpoint(1);}if(this.e[this.g].getEndpoint(0) != null) {this.h[this.g] = this.e[this.g].getEndpoint(0);}

  这个指示一部分,可以看出里面获取的手法就简化了很多

  上面的可以愉快的玩耍了,就讲一下把其转化成一直读卡遇到的问题。代码如下:

    public boolean init(Context context) {UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);HashMap<String, UsbDevice> devices = manager.getDeviceList();LoggerUtil.i(TAG, "device.size = " + devices.size());if (devices.size() <= 0) {return false;}UsbDevice myUsbDevice = null;Iterator<UsbDevice> iterator = devices.values().iterator();while (iterator.hasNext()) {UsbDevice device = iterator.next();LoggerUtil.i(TAG, "device vid = " + device.getVendorId() + " , pid = " + device.getProductId());if (ReaderAndroidUsb.isSupported(device)) {myUsbDevice = device;break;}}if (myUsbDevice == null) {return false;}// 判断是否拥有该设备的连接权限if (!manager.hasPermission(myUsbDevice)) {PendingIntent pd = PendingIntent.getBroadcast(context, 0,new Intent(Device_USB), PendingIntent.FLAG_UPDATE_CURRENT);/** 展示征求用户同意连接这个设备的权限的对话框。 当用户回应这个对话框时,* 广播接收器就会收到一个包含用一个boolean值来表示结果的EXTRA_PERMISSION_GRANTED字段的意图。* 在连接设备之前检查这个字段的值是否为true和设备之间的“交流”*/manager.requestPermission(myUsbDevice, pd);}if (!manager.hasPermission(myUsbDevice)) {return false;}// 如果已经拥有该设备的连接权限,直接对该设备操作ReaderAndroidUsb readerAndroidUsb = new ReaderAndroidUsb(manager);readerAndroidUsb.closeReader();try {/** 函数说明: 直接打开 USB 接口读写器*   返回值:*   >=0 表示打开读写器成功*   <0 表示打开读写器失败。*/int st = readerAndroidUsb.openReader(myUsbDevice);if (st >= 0) {reader = readerAndroidUsb;return true;} else {return false;}} catch (Exception e) {return false;}}

  ReaderAndroidUsb 为我使用读卡器中的一个打开卡的方法。最初的时候我没有在 init( )方法里面传递 context,其中的 USBmanager也只在类的初始化的时候,进行初始化一次。后面的循环读卡中,刷卡一段时间就再也刷不起了。

  后面的而修改办法就是,同上。初始化传入context,重新获取 UsbManager,一切重新开始。在刷卡成功后,关闭当前的usb的连接。等同于每一次都是单次刷卡,只是把单次刷卡需要调用的东西通过代码实时完成。

参考链接

  1. USB基础知识概论
  2. usb中的endpoint(端点)和传输模式
  3. Android实战技巧之四十九:Usb通信之USB Host
  4. Android下的USB Host介绍和开发
  5. Android开发之USB数据通信

记录:UsbManager的使用相关推荐

  1. Android Framework 记录之一

    描述 class BadXmlException extends SAXParseException xml解析错误 Children 子集 Element 一个元素 interface Elemen ...

  2. Android Framework 记录

    记录 1.下载源码,目录如下: 2.Android系统的层次如下: 3.项目目录简单分析如下: 4.telphony目录 文件 描述 CellIdentityCdma //描述电信通信标识 CellI ...

  3. mysql建立联合索引,mysql建立唯一键,mysql如何解决重复记录联合索引

    在项目中,常常要用到联合唯一   在一些配置表中,一些列的组合成为一条记录.   比如,在游戏中,游戏的分区和用户id会形成一条记录.(比如,一个qq用户可以在艾欧尼亚.德玛西亚创建两个账号) 添加联 ...

  4. 实现 连续15签到记录_MySQL和Redis实现用户签到,你喜欢怎么实现?

    现在的网站和app开发中,签到是一个很常见的功能 如微博签到送积分,签到排行榜 微博签到 如移动app ,签到送流量等活动, 移动app签到 用户签到是提高用户粘性的有效手段,用的好能事半功倍! 下面 ...

  5. 记录一次http请求失败的问题分析

    问题背景 当前我有一个基于Flask编写的Restful服务,由于业务的需求,我需要将该服务打包成docker 镜像进行离线部署,原始服务的端口是在6661端口进行开启,为了区分,在docker中启动 ...

  6. Pytorch学习记录-torchtext和Pytorch的实例( 使用神经网络训练Seq2Seq代码)

    Pytorch学习记录-torchtext和Pytorch的实例1 0. PyTorch Seq2Seq项目介绍 1. 使用神经网络训练Seq2Seq 1.1 简介,对论文中公式的解读 1.2 数据预 ...

  7. LeetCode简单题之学生出勤记录 I

    题目 给你一个字符串 s 表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤.迟到.到场).记录中只含下面三种字符: 'A':Absent,缺勤 'L':Late,迟到 'P':Pre ...

  8. 关于TVM的点滴记录

    关于TVM的点滴记录

  9. MySql数据库Update批量更新与批量更新多条记录的不同值实现方法

    批量更新 mysql更新语句很简单,更新一条数据的某个字段,一般这样写: UPDATE mytable SET myfield = 'value' WHERE other_field = 'other ...

最新文章

  1. mysql sys库 oom_MySQL 5.6因为OOM导致数据库重启
  2. C++调用Python
  3. c语言编程:输入一个数看它是不是素数
  4. Nginx配置文件nginx.conf中文详解
  5. python 爬虫 ~ 查看收发包的情况
  6. java 正则 小数_详解Java判断是否是整数,小数或实数的正则表达式
  7. NA-NP-IE系列实验5:配置文件的备份和IOS 的备份
  8. 安卓案例:安卓对话框演示
  9. 4KB/2MB/1GB 4级/5级分页模式下的线性地址翻译以及CR3
  10. 刘强东卸任京东 CEO,“二号位”徐雷接棒:三大电商巨头“二把手”正式集齐
  11. ChaLearn Gesture Challenge_3:Approximated gradients源码简单分析
  12. 试用了GIMP的Smart remove selection,结果有些失望啊,哈哈
  13. fatal error: absl/synchronization/mutex.h: No such file or directory
  14. 断点下载神器-RandomAccessFile
  15. C# CAD二次开发之字体替换 文字样式 处理DBText
  16. 前端——动态生成表格
  17. 这篇文章记录平时学习、项目中遇到的问题(未解决的居多)
  18. 关于找工作---职业规划[转]
  19. vue 点击谁 谁变色
  20. FPGA 十进制 转化为二进制

热门文章

  1. 电商平台-商品价格的设计与架构
  2. python2.x 默认编码问题
  3. 苹果手机换鸿蒙系统,鸿蒙横空出世了,换掉苹果机你愿意吗?
  4. 网上酒店客房预定系统数据库设计
  5. 探索多维数据极端值处理方法
  6. ubuntu16.04安装anaconda3并配置tensorflow(CPU)+sypder(亲测有效)
  7. FGui导出图集提示 vips2png
  8. el-table设置封装
  9. 图片多样化切换_您想让您的工作室更多样化吗?
  10. 缓解疲劳,闻咖啡比喝咖啡更好