记录:UsbManager的使用

来源:互联网 发布:龙腾世纪审判捏脸数据 编辑:程序博客网 时间:2024/06/12 01:51

摘要:最近一直使用到身份证刷卡器,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() 获得设备列表,返回的是一个HashMaphasPermission(UsbDevice device) 判断你的应用程序是否有接入此USB设备的权限,如果有则返回真,否则返回falseopenDevice(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 devices        IntentFilter 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;                                else                                    mUsbDev2 = 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-data                android: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数据通信