DesfireCard Read项目总结

--------------------坑-------------------------

DesfireCard Read项目总结

由于之前读卡器模块读卡速度过慢,换了读卡器模块。所以需要开发android端的程序。下面记录一下工作中出现的问题。

一、命名的问题。

函数名的大小写、格式(需要使用统一规则),以及英文的语法(注意例如时态、主谓宾的顺序)问题。

二、SDK的封装。

什么是需要封装进SDK中的以及需要传出的数据,这里是需要考虑的问题。

例如,所定义的函数select_applications的返回值。

这里不能返回数据长度一般为不太复杂的数据,需要判断的东西(即判断length的大小),要放在SDK中,不能写在外面,应该定义length >= 91的时候返回false。

注意数据的传出问题。

三、组包以及USB的通讯。

①首先,需要定义命令。

android端与读卡器模块的通讯需多次按一定的规律发送命令,因此需要组包。具体命令是通过PC端的软件解出来的,我需要发送的包,就是解出来的数据,并且保证在发送包之后能得到同样的数据。

组包的思路是首先定义一个String,例如private String cmd_add_0 = "11 10 00 4d 00 20 00";,然后定义一个函数public byte[] create_packet(byte cmd,String add_data),在函数内对String数据进行处理转换成byte[],然后拼接数据或改变某个位置的数据,create_packet的返回值byte[]并不是实际发送的数据(注意这里还有一个序列号需要修改,因为USB通讯序列号是会发生改变的,因此还需要定义一个函数private boolean send_cmd(byte cmd, String add_cmd_data, int ms, byte[] data),data用来传出返回的数据,即接收到的数据。在函数内改变序列号的位置,真正发送的数据的是通过send函数,发送的byte是packet这个byte[],global_seq是一个表示序列号的全局变量,在send_cmd函数内定义一个自加1操作,以控制序列号增长,同时,在发送命令之后,需要验证序列号是否相等,然后才会对data进行赋值)。

byte[] packet  = create_packet(cmd, add_cmd_data);

packet[6] = global_seq;//组包完成

byte[] recv = send(packet, ms);//发送命令

.......................................................................

global_seq += 1;

这里注意要考虑函数返回值的问题以及recv为null的情况。当recv为null时(即发送命令之后收到的数据异常的情况,我们需要做一个判断),需要返回false,否则可能会出现bug。

②USB的通讯。

1.枚举USB设备。

USB设备的识别是通过PID和VID来识别的。因此,首先要知道读卡器设备的这两个数据并定义在res目录下的xml文件中,然后在manifest中的对应activity中注册。

android:resource="@xml/device_filter" />

同时,需要加上这一句feature。

<uses-featureandroid:name="android.hardware.usb.host"android:required="false"/>

USB主要相关类有:UsbManager、UsbDevice、UsbDeviceConnection、UsbEndpoint。

UsbManager :通过getSystemService实例化。

mUsbManager= (UsbManager) context.getSystemService(Context.USB_SERVICE);

UsbDevice:表示的是设备。通过调用UsbManager.getDeviceList,获取了deviceList,然后遍历deviceList,通过调用getVendorId可以获取连接到android的USB设备的VID,然后通过判断当设备的VID来找到设备即device,然后就可以对设备进行操作了(device.getVendorId() == 0x273a)。

UsbDeviceConnection:调用mUsbManager.openDevice(device)返回该类,用来表示打开device的状态。

UsbEndpoint:一个USB有3种EP。Input、Output、Interrupt。USB通讯是通过对EP进行操作来进行通讯的。实例化mDevice之后,通过调用getInterface获取UsbInterface,然后对UsbInterface进行操作即可获取EP。

在获取了EP之后,判断app是否具有USB通讯的权限(具体申请权限写在下面),有权限才能进行openDevice操作,否则会报错,openDevice返回UsbDeviceConnection。

mUsbManager.hasPermission(mDevice)

mUsbManager.openDevice(device)

//0、1、2分别对应了不同的EP。Input和Output均是指对android设备而言。

mEndpointIn= GetEndPoint( device,2);

mEndpointOut= GetEndPoint( device,1);

mInterruptEndpoint= GetEndPoint( device,0);

//定义函数

publicUsbEndpointGetEndPoint( UsbDevice device,intnEndpoint){

...........

UsbInterface intf = device.getInterface(0);

ep1 = intf.getEndpoint( nEndpoint );//intput和output的EP

if(ep1.getType() ==USB_ENDPOINT_XFER_INT){

ep1 = intf.getEndpoint( nEndpoint );//虽然这个判断感觉没有用。

...........

}

}

读到卡返回的命令为80 03(这里是十进制,对应十六进制是50) ,Interrupt返回的命令为80 02。(注意interrupt也算In的一种)。

IN     50 03

IN     50 03

2.申请通讯权限。android中USB通讯是需要申请通讯权限的。具体思路是动态定义一个broadcast,然后再注册。要确保申请权限之后才执行usb相关操作,否则容易出错,并且要考虑无device情况(即没有插入读卡器模块的情况),不然在未插入设备的情况下打开app会直接挂掉。

以上2步联系紧密。

..................................................

3.现在需要检测卡的状态(是否有卡),当检测到读到卡的时候,才能进行下一步操作。那么如何中读卡器中获取信息呢?主要是UsbRequest类。

........................................

//这个虽然之前实例化了,但是在这里还是要加上,不然会出现错误,具体原因我还没搞清//楚,待查证。

ByteBuffer buffer = ByteBuffer.allocate(16);

try{

mConnection= GetConnection(mDevice);

}catch(java.lang.SecurityException e) {

e.printStackTrace();

}

//这里需要用这个mInterruptEndpoint

UsbRequest request =newUsbRequest();

request.initialize(mConnection,mInterruptEndpoint);

request.queue(buffer,16);

//这里大致就是UsbRequest将收到的数据,存进buffer中。

//queue  boolean queue (ByteBuffer buffer)

//Queues the request to send or receive data on its endpoint.

//requestWait:Waits for the result of aqueue(ByteBuffer)operation

//UsbRequestrequestWait ()

//由此可知调用wait是为了让queue(buffer)做完,即将数据存进buffer中。

if(mConnection.requestWait() == request) {

Log.d(TAG,"after requestWait");

//然后判断

bytestatus0 = buffer.get(0);

bytestatus1 = buffer.get(1);

Log.d(TAG,"status get:"+ status0 +","+ status1);

if(status0 ==80&& status1 ==3) {

mOnCardListener.OnCardState(true);

reader_power_init();//这里检测到卡之后,需要进行初始化操作,这是Read Card之前的准备工作。

return1;

}

if(status0 ==80&& status1 ==2) {

mOnCardListener.OnCardState(false);

return0;

}

if(status0 ==0&& status1 ==0) {

return-1;

}

}

return-1;

这里我读到了三种状态:

读到卡80, 03 。

没有卡80,02 。

No change 0,0(这个待考证,我只是获取到了这个status的数据,然后进行了判断而已)。

80, 03这个数据是读卡器模块在检测到卡之后自动发送给android设备的,无需任何操作,我这里只是将EP收到的数据读出来进行判断,当收到80,03即表示有卡,那么我就可以进行下一步的操作。

这里需要单独开了一个线程去做detect_card的操作(因为需要一直检测状态)。

//根据detect_card函数的返回值来判断卡的状态。并且加上一个500ms的延时(每500ms调用一次detect_card函数,赋值给detect_card_ret),detect_card_ret应该是一个有三种状态的返回值,而不能直接通过detect_card的返回值赋值。

public voidrun() {

while(true) {

..............................

intret =mUSBReaderSDK.detect_card();

if(ret==1)// card inserted.detect_card_ret=1;

else if(ret==0)//card removed.detect_card_ret=0;

//else : -1: no changed, so ignored.try{

Thread.sleep(500);

}catch(InterruptedException e){

e.printStackTrace();

}

................

Time      ret    detect_card_ret         status

0          1               1                        insert card

500        -1          ignore                 insertcard(no changes)

1000       0               0                   remove

1500       -1          ignore               remove


//因为这里只需要对insert和remove状态进行监测,changes状态无需返回值。

detect_card_ret是个全局变量,下面需要通过对此变量进行判断,来决定是否开启ReadThread线程。这里的思路是定义一个ReadButton,每点击一次就new ReadThread,然后start。但是,开启线程之前,需要先做条件判断。首先判断是否有卡(即detect_card_ret是否为1),然后就是需要一个线程标志位(readthread_is_running,默认为false)。

当这两个条件满足后,才能真正开启线程。线程标志位需要在开启线程(即start thread)之前置true,执行完线程之后置false(即run函数的末尾处,以便下次开启)。

if(detect_card_ret==1&& !readthread_is_running){

readthread_is_running=true;

starThread();

}

private classReadThreadextendsThread{

@Override

public voidrun(){

....................

readthread_is_running=false;

}

}

ReadThread中,定义了如何Read Card。读卡的本质就是一系列数据的收发以及解析过程。

Desfire卡的读卡流程是

power_off

power_on

get_applications

select_applications

list_file

read_file

power_off

四、Util类的积累。

十六进制的byte与String之间的转换,以及小端的转换。

五、整个读卡的流程总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容