前言

接触到的扫描设备分为三类:1)-usb或其它方式转串口,常规的打开读取串口即可;2)-usb模拟键盘输入模式,这种模式底层不是串口;3)原生串口模式,最简单,但这种接口普通手机没有。

本文只介绍第二种USB键入模式,通过USB设备查找、匹配、打开、数据读这个流程,可以先于并阻止安卓系统获得内容,就是说如果是后台进程这样读取后,安卓输入框中不会显示扫描到的内容

设备特点

    USB接入方式、即插即用不需要驱动、可以在任意输入框内扫描并“输入”字符串、最后回车。例如,打开一个文本编辑器并处于活动状态,刷卡后可以看到就像键盘输入了一样,得到10位长度的卡号、回车。

    就是说这种设备的接入一般是不需要编程的,但是问题就在于它输出的内容也无法控制,就像是用户在通过一个键盘输入。

拦截这种设备需求的产生

        如果你想在“用户输入”的内容上做一些处理怎么办?要么是响应键盘输入事件,要么是像本文一样直接拦截USB数据流。

先看一下运行效果

(注意:这里有个USB安全提示,一般设备都是无法去除的,在订制机或者拥有系统签名的情况下才能去除)


流程核心代码:

1,获得USB管理器:

usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
        if (usbManager == null) {
            this.mainHandler.obtainMessage(MainHandler.WHAT_SHOW_MESSAGE, "无法获得USB服务").sendToTarget();
            return false;
        }

2,匹配特定的设备:

//获得所有USB设备
        HashMap<String, UsbDevice> usbDeviceMap = usbManager.getDeviceList();
        if (usbDeviceMap.size() == 0) {
            this.mainHandler.obtainMessage(MainHandler.WHAT_SHOW_MESSAGE, "找不到USB设备").sendToTarget();
            return false;
        }
        for (final UsbDevice device : usbDeviceMap.values()) {
            Log.d(tag, "USB:pid=" + device.getProductId() + " vid=" + device.getVendorId());
            if (USB_DEV_PID == device.getProductId() && USB_DEV_VID == device.getVendorId()) {
                this.mainHandler.obtainMessage(MainHandler.WHAT_SHOW_MESSAGE, "已找到USB读卡器").sendToTarget();
                targetUsb = device;
                break;
            }
        }

3,打开USB连接

usbDeviceConnection = usbManager.openDevice(targetUsb);
                if (usbDeviceConnection == null) {
                    mainHandler.obtainMessage(MainHandler.WHAT_USB_ERROR, "打开USB设备失败").sendToTarget();
                    return;
                }
                UsbInterface usbInterface = targetUsb.getInterface(0);
                if (!usbDeviceConnection.claimInterface(usbInterface, true)) {
                    mainHandler.obtainMessage(MainHandler.WHAT_USB_ERROR, "不是已知协议的USB设备,无法使用").sendToTarget();
                    return;
                }

4,开始读取

ArrayList<Byte> listBytes = new ArrayList<>();
                byte[] buffer = new byte[maxPackageSize];
                UsbEndpoint inEndpoint = usbInterface.getEndpoint(0);
                mainHandler.obtainMessage(MainHandler.WHAT_READING, "读取准备就绪,请刷卡").sendToTarget();
                deviceReading = true;
                while (!Thread.interrupted()) {
                    int ret = usbDeviceConnection.bulkTransfer(inEndpoint, buffer, buffer.length, 200);
                    if (ret > 0) {
                        byte[] reab = new byte[ret];
                        System.arraycopy(buffer, 0, reab, 0, ret);
                        //Log.d(tag, "读取到:" + bytesToHexString(reab));
                        //字节流断句
                        for (byte ab : reab) {
                            if (ab == (byte) 0x28) {
                                if (listBytes.size() > 0) {
                                    Byte[] lineBytes = listBytes.toArray(new Byte[0]);
                                    getCarCodeBytes(ArrayUtils.toPrimitive(lineBytes));
                                    listBytes.clear();
                                }
                            } else if (ab != (byte) 0x00) {
                                listBytes.add(ab);
                            }
                        }
                    }
                }

5,解析字节原理

读取到的字节为USB-键值,对照表如下(设备不会输出其它值,故只列出相关部分):

1E Keyboard 1
1F Keyboard 2
20 Keyboard 3
21 Keyboard 4
22 Keyboard 5
23 Keyboard 6
24 Keyboard 7
25 Keyboard 8
26 Keyboard 9
27 Keyboard 0

6,解析方法:

private void decode(byte[] bytes) {
        //转换时,可以直接一一对照,我这里用简单的减法
        StringBuilder intString = new StringBuilder();
        for (byte ab : bytes) {//按字节循环
            //如果0x27,由于它的10进制是39,减去29是10,所以单独处理
            if (ab == (byte) 0x27) intString.append(0);
            //其它都是化为10进制后减去起始值29得到对应值
            else intString.append(ByteTools.byteToInt(ab) - 29);
        }
        String hex = Long.toHexString(Long.parseLong(intString.toString()));
        //TODO 获得字符串后继续其它业务
}

硬件及安卓源码


测试需要设备

1,读卡器可以到TB搜索USB读卡器,或者直接扫描我图片里的二维码,找类似的就行,只要是这种方式的都一样。

2,公头小口转母口大口的USB,TYPE-c或者MicroUsb的就看测试用的手机了。

android usb通讯属于什么类型的usb 安卓usb协议_android

文章写的有点糙,因为是两个版本的原因,改来改去都不满意,最后直接上全部代码,Android Studio项目整体压缩的,直接打开就行了。

注意,使用USB设备时如果没有系统权限,需要申请USB使用权限。