学习开源项目NFCard,最新版源码以及几年前代码相比较,发现之前的版本可以支持读羊城通,而现在不再支持读羊城通卡信息,那定一个小目标。通过NFC读取羊城通卡片信息之余额和交易记录。

实现的效果如图:

目录
1.建立工程,编写NFC相关代码;
2.根据开源项目中的指令,读取余额;
3.根据开源项目中的指令,读取交易记录;
4.根据卡片原始信息解析数据;

一、编写NFC相关代码

    import android.app.PendingIntent;import android.content.Intent;import android.nfc.NfcAdapter;import android.nfc.Tag;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity {private NfcAdapter mNfcAdapter;private PendingIntent mPendingIntent;private Intent mIntent;private final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK;private NfcAdapter.ReaderCallback mReaderCallback = new NfcAdapter.ReaderCallback() {@Overridepublic void onTagDiscovered(Tag tag) {System.out.println(tag.toString());}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overrideprotected void onStart() {super.onStart();initNfc();}@Overrideprotected void onResume() {super.onResume();registerNfc();}@Overrideprotected void onPause() {super.onPause();unRegisterNfc();}private void initNfc() {mNfcAdapter = NfcAdapter.getDefaultAdapter(this);mIntent = new Intent(NfcAdapter.ACTION_TECH_DISCOVERED);mIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);mPendingIntent = PendingIntent.getActivity(this, 0, mIntent, 0);}private void registerNfc() {Bundle bundle = new Bundle();mNfcAdapter.enableReaderMode(this, mReaderCallback, READER_FLAGS, bundle);}private void unRegisterNfc() {mNfcAdapter.disableReaderMode(this);}}

运行上述代码的效果:(羊城通紧贴在手机NFC感应处)

I/System.out: TAG: Tech [android.nfc.tech.IsoDep, android.nfc.tech.NfcA]

看源码解释一下:
注册NFC调用了NfcAdapter的enableReaderMode方法,先看看源码:

    /*** Limit the NFC controller to reader mode while this Activity is in the foreground.** <p>In this mode the NFC controller will only act as an NFC tag reader/writer,* thus disabling any peer-to-peer (Android Beam) and card-emulation modes of* the NFC adapter on this device.** <p>Use {@link #FLAG_READER_SKIP_NDEF_CHECK} to prevent the platform from* performing any NDEF checks in reader mode. Note that this will prevent the* {@link Ndef} tag technology from being enumerated on the tag, and that* NDEF-based tag dispatch will not be functional.** <p>For interacting with tags that are emulated on another Android device* using Android's host-based card-emulation, the recommended flags are* {@link #FLAG_READER_NFC_A} and {@link #FLAG_READER_SKIP_NDEF_CHECK}.** @param activity the Activity that requests the adapter to be in reader mode* @param callback the callback to be called when a tag is discovered* @param flags Flags indicating poll technologies and other optional parameters* @param extras Additional extras for configuring reader mode.*/public void enableReaderMode(Activity activity, ReaderCallback callback, int flags,Bundle extras) {mNfcActivityManager.enableReaderMode(activity, callback, flags, extras);}

三行代码三大段注释,nice。enableReaderMode()抠脚翻译如下:

限制NFC的模式为读卡器模式

在这种模式中,就仅仅是可以用NFC读写带有NFC芯片的标签(卡片、贴纸等),不允许点对点模式和卡模拟模式。

可以通过FLAG_READER_SKIP_NDEF_CHECK这个标志过滤NDEF标签,这个就是标志就是第三个参数啦,NDEF(NFC Data Exchange Format,NFC数据交换格式)是Android SDK API主要支持NFC论坛标准。

如果是准备与另一台Android卡模拟设备交互,那么建议设置的标志就是FLAG_READER_NFC_A和FLAG_READER_SKIP_NDEF_CHECK

参数callback:发现符合的标签就回调到callback
参数extras : 对读卡器模式进行一些配置(先晾它一会,目前只是传了一个空bundle进去)
参数flags:标志

上述参数中有一个flags,我们顺便也看看有哪些flag以及flag的作用是什么,看源码然后抠脚解释下:

    /*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag enables polling for Nfc-A technology.*/public static final int FLAG_READER_NFC_A = 0x1;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag enables polling for Nfc-B technology.*/public static final int FLAG_READER_NFC_B = 0x2;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag enables polling for Nfc-F technology.*/public static final int FLAG_READER_NFC_F = 0x4;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag enables polling for Nfc-V (ISO15693) technology.*/public static final int FLAG_READER_NFC_V = 0x8;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag enables polling for NfcBarcode technology.*/public static final int FLAG_READER_NFC_BARCODE = 0x10;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag allows the caller to prevent the* platform from performing an NDEF check on the tags it* finds.*/public static final int FLAG_READER_SKIP_NDEF_CHECK = 0x80;/*** Flag for use with {@link #enableReaderMode(Activity, ReaderCallback, int, Bundle)}.* <p>* Setting this flag allows the caller to prevent the* platform from playing sounds when it discovers a tag.*/public static final int FLAG_READER_NO_PLATFORM_SOUNDS = 0x100;
flag value meaning
FLAG_READER_NFC_A 0x1 支持NFCA数据格式
FLAG_READER_NFC_B 0x2 支持NFCB数据格式
FLAG_READER_NFC_F 0x4 支持NFCF数据格式
FLAG_READER_NFC_V 0x8 支持NFCV数据格式
FLAG_READER_NFC_BARCODE 0x10 支持NFCBARCODE数据格式
FLAG_READER_SKIP_NDEF_CHECK 0x80 过滤NDEF数据格式
FLAG_READER_NO_PLATFORM_SOUNDS 0x100 关闭发现TAG时的声音

看完上述,估计有点蒙,好像也有点跑偏,赶紧回到注册NFC的这个方法中,我们在onResume中调用了enableReaderMode,此方法在卡片(此处指羊城通)贴到手机NFC感应处时会回调到ReaderCallback中,所以我们在onTagDiscovered这个回调中即可与卡片进行交互

二、根据指令读取羊城通余额

首先我们可以去交通信息中心下载一份《城市公共交通IC卡技术规范》卡片的部分,认真去阅读(一头扎进去估计难看懂),我们知道选择目录的指令为:00A40400+lc+文件名+00;读取余额的指令为:805C000204(指令为7816报文格式)
其次我们可以去阅读NFCard这个开源项目,从源码中知道,选择的文件名为:”PAY.TICL”,lc为:08
到此,我们整理下所需指令:

command meaning
00A40400085041592E5449434C00 选择PAY.TICL目录(P的十六进制ASCII码为50)
805C000204 读取余额

上述拼接指令过程中,需要把字符换成对应的十六进制ASCII码,好在Google的Sample给我们提供了这些转换方法,恩,又可以抄一波(具体Sample路径:”sdk根目录”\samples\android-“version”\connectivity\CardReader..\LoyaltyCardReader.java):

    /*** Utility class to convert a byte array to a hexadecimal string.** @param bytes Bytes to convert* @return String, containing hexadecimal representation.*/public static String ByteArrayToHexString(byte[] bytes) {final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};char[] hexChars = new char[bytes.length * 2];int v;for ( int j = 0; j < bytes.length; j++ ) {v = bytes[j] & 0xFF;hexChars[j * 2] = hexArray[v >>> 4];hexChars[j * 2 + 1] = hexArray[v & 0x0F];}return new String(hexChars);}/*** Utility class to convert a hexadecimal string to a byte string.** <p>Behavior with input strings containing non-hexadecimal characters is undefined.** @param s String containing hexadecimal characters to convert* @return Byte array generated from input*/public static byte[] HexStringToByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)+ Character.digit(s.charAt(i+1), 16));}return data;}

有了卡片,有了卡片指令,我们就开始通过NFC进行交互
回到我们NFC的回调方法中,我们可以从回调方法onTagDiscovered(Tag tag)拿到一个TAG,这个TAG从输出的日志看,有IsoDep,通过IsoDep类的transceive方法即可发送指令数据到卡片并且返回响应数据:

        @Overridepublic void onTagDiscovered(Tag tag) {System.out.println(tag.toString());if (tag.toString().contains(IsoDep.class.getName())) {IsoDep isoDep = IsoDep.get(tag);if (isoDep != null) {try {isoDep.connect();//连接//选择目录System.out.print("指令报文:" + "00A40400085041592E5449434C00");byte[] resp_dir = isoDep.transceive(Commands.HexStringToByteArray("00A40400085041592E5449434C00"));System.out.println("  响应报文:" + Commands.ByteArrayToHexString(resp_dir));//读取余额System.out.print("指令报文:" + "805C000204");byte[] resp_balance = isoDep.transceive(Commands.HexStringToByteArray("805C000204"));System.out.println("  响应报文:" + Commands.ByteArrayToHexString(resp_balance));} catch (IOException e) {e.printStackTrace();}}}}

具体数据为:

I/System.out: 指令报文:00A40400085041592E5449434C00 响应报文:6F3484085041592E5449434CA5289F0801029F0C21FFFFFFFFFFFFFFFF000000000000000000000000000000002016122400000186A09000
I/System.out: 指令报文:805C000204 响应报文:00000E479000

根据7816报文格式,响应报文格式为DATA+SW1+SW2,SW1和SW2为状态字,分别占一个字节,由此DATA=00000E47,SW1=90,SW2=00。00000E47转十进制则是3655,这个时候我们用QQ来读一下羊城通对比一下余额是否正确,QQ读出余额如下图:

我们使用的transceive方法,将原始数据发送至卡片标签,并且得到响应,如果中途移开卡片,则会抛出TagLostException(也是继承IOException),如果中途读写失败或者取消,则抛出IOException。

    /*** Send raw ISO-DEP data to the tag and receive the response.** <p>Applications must only send the INF payload, and not the start of frame and* end of frame indicators. Applications do not need to fragment the payload, it* will be automatically fragmented and defragmented by {@link #transceive} if* it exceeds FSD/FSC limits.** <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes* that can be sent with {@link #transceive}.** <p>This is an I/O operation and will block until complete. It must* not be called from the main application thread. A blocked call will be canceled with* {@link IOException} if {@link #close} is called from another thread.** <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.** @param data command bytes to send, must not be null* @return response bytes received, will not be null* @throws TagLostException if the tag leaves the field* @throws IOException if there is an I/O failure, or this operation is canceled*/public byte[] transceive(byte[] data) throws IOException {
        return transceive(data, true);}

小结:
我们通过开源项目NFCard、Google的Sample之CardReader、交通信息中心的《城市公共交通IC卡技术规范》文档,成功读出了羊城通余额。

这里需要提醒的是,最新版的NFCard源码读不出我手中的羊城通,显示为未知卡片,反而找到2013年的版本才读出来,本人手中的卡有效期也是2013年-2018年。这样估计是各版本的卡有所不同。

接下来就是读取卡片的卡号、有效期、交易记录这些信息,并且解析数据,显示在界面上。

[NFC] 读羊城通卡片信息相关推荐

  1. Android NFC读MifareClassic卡获取卡片ID 类型 扇区 存储空间

    1.首先要在AndroidManifest.xml中声明如下配置信息: 为了能够使用Android手机的NFC功能,需要在Manifest文件中添加相应的权限: 详细配置请参考-->Androi ...

  2. Android 实现 NFC 读取卡片信息

    效果图: 因为朋友需要个读取NFC卡片数据的功能,所以最近看了一下Android 系统下NFC 读取卡片信息的操作. NFC(近距离无线通信 ) 是一组近距离无线技术,通常只有在距离不超过 4 厘米时 ...

  3. Android NFC开发详解 总结和NFC读卡实例解析

    文章目录 前言 一.什么是NFC? 二.基础知识 1.什么是NDEF? 2.NFC技术的操作模式 3.标签的技术类型 4.实现方式的分类 5.流程 三.获取标签内容 1.检查环境 2.获取NFC标签 ...

  4. nfc读卡java开发,分享一段飞天R502读卡器的JAVA读卡代码

    飞天R502 收到客户一个NFC读写卡器,是飞天R502,具体介绍请看官网资料 代码 package com.test; import javax.smartcardio.*; import java ...

  5. Android Studio NFC读取CPU卡信息

    今天接到一个NFC读CPU卡的需求,做个总结. 总结之前,吐槽一下,一定要查一下测试机支不支持NFC功能,我拿乐视S3开发了一上午,以为哪里出了BUG或者权限没加,查了无数资料,下了十几个demo,死 ...

  6. android上用NFC读卡

    NFC on android NFC on android 2019/5/9 目录 文章目录 代码下载 目的 代码 申请NFC权限 为Activity 添加 singleTask 接收卡片信息 Act ...

  7. 基于Android NFC传感器读取身份证信息demo

    摘要:通过导入开发包sdk开发基几Android NFC设备读取身份证信息 1. 开发条件: 1)鱼住往来科技的身份验证sdk :下载地址:https://www.yzfuture.cn/views/ ...

  8. android nfc读取公交卡信息_手机NFC可以复制小区用的门禁卡吗?

    很多带有NFC功能的手机和手环,可以用来代替小区门禁卡.公司门禁,以及各类一卡通.(NFC是一种非接触式通讯技术,工作的理论距离是0~10cm)以前出门必备手机.钱包.钥匙三件套,自移动支付出现.使用 ...

  9. Android nfc读卡模式流程

    最近一个项目需要将Android的nfc接口底层通过串口对接到外围一个单片机上,而nfc设备是接在单片机上的,这就需要对Android的nfc框架进行梳理,确定与单片机对接的接口通讯:上网查找发现资料 ...

最新文章

  1. python表白-python3实现表白神器
  2. DFS Tempter of the Bone
  3. Gentoo 安装日记 08 (安装stage3: 设置时区 和 Hostname)
  4. 关于Rabbitmq的routingkey的作用
  5. cbow word2vec 损失_Skip-gram和CBOW知识点
  6. lsof/netstat命令的一个重要作用: 根据进程查端口, 根据端口查进程
  7. mysql查询结果每条记录两个字段求和_MYSQL实现将两个结果集合并,并且按照时间字段分组,其他字段的值求和...
  8. c语言写程序轮询是什么意思,单片机轮询按键程序
  9. python中不论类的名字是什么歌_Python自动猜歌名,还愁排名上不去嘛?
  10. 再谈几种语言的运行速度比较:看第三方比较结论!
  11. 微软应用商店有哪些值得推荐的应用
  12. 调用企业微信接口注意事项
  13. 矩形已知三个点的坐标,求第四个点的坐标
  14. python中r 4.2f%r,006 Python中的 字符串String
  15. 【Python】一篇文章学习Pandas包 Pandas Series、DataFrame 对比学习
  16. 微信公众号基础功能搭建
  17. 如何在 Unity 中制作一个道具系统
  18. win10+vs2017配置MPI和OpenMP
  19. SSM+人才交流平台 毕业设计-附源码221022
  20. java骑车与走路_骑行/步行路线规划

热门文章

  1. easyUI datebox 日期只显示年月及年月日切换的实现 js样式重新渲染加载
  2. Ps编程脚本开发零基础学脚本(一)
  3. java 兔子问题_Java算法之“兔子问题”
  4. Unix Domain Sockets
  5. 【云原生|中间件】我们为什么要使用DCM?
  6. 主视图和左视图算体积最大最小值
  7. 计算机无法检测电池损耗怎么办,笔记本电脑如何检测电池损耗程度-鲁大师检测电池损耗的方法 - 河东软件园...
  8. JEECG3.8 全套实战视频全部开放,免费下载!
  9. 相忘于江湖:《监控》
  10. [note]First draft of a report on the EDVAC (1~2)