IC卡 (Integrated Circuit Card,集成电路卡)
射频识别即RFID(Radio Frequency IDentification)
NFC是Near Field Communication缩写,即近距离无线通讯技术。
NFC是一套短距离的无线通信,通常距离是4厘米或更短。NFC工作频率是13.56M Hz,传输速率是106kbit/s 到848kbit/s. NFC总是在一个发起者和一个被动目标之间发生。发起者发出近场无线电波,这个近场可以给被动目标供电。这些被动的目标包括不需要电源的标签,卡,也可以是有电源的设备。
与其他无线通信技术比较, 例如蓝牙和WiFi, NFC提供更低贷款和距离,并且低成本,不需要供电,不需要实现匹配,整个通信过程仅仅是短短的靠近一秒就能完成。
一个带有NFC支持的android设备通常是一个发起者。也可以作为NFC的读写设备。他将检测NFC tags并且打开一个Activity来处理. Android 2.3.3还有支持有限的P2P。
Tags分很多种,其中简单的只提供读写段,有的只能读。复杂的tags可以支持一些运算,加密来控制对tags里数据段的读写。甚至一些tags上有简单的操作系统,允许一些复杂的交互和可以执行一些代码。
本文的代码例子是基于API10的。
要在Android手机中使用NFC,必须在AndroidManifest.xml中如下配置:
<uses-feature android:name="android.hardware.nfc" android:required="true" /> <uses-permission android:name="android.permission.NFC" />
Tag发布系统
当android设备扫描到一个NFC tag,通用的行为是自动找最合适的Activity会处理这个tag Intent而不需要用户来选择哪个Activity来处理。因为设备扫描NFC tags是在很短的范围和时间,如果让用户选择的话,那就有可能需要移动设备,这样将会打断这个扫描过程。你应该开发你只处理需要处理的tags的Activity,以防止让用户选择使用哪个Activity来处理的情况。Android提供两个系统来帮助你正确的识别一个NFC tag是否是你的Activity想要处理的:Intent发布系统和前台Activity发布系统。
Intent发布系统检查所有Activities的intent filters,找出那些定义了可以处理此tag的Activity,如果有多个Activity都配置了处理同一个tag Intent,那么将使用Activity选择器来让用户选择使用哪个Activity。用户选择之后,将使用选择的Activity来处理此Intent.
前台发布系统允许一个Activity覆盖掉Intent发布系统而首先处理此tag Intent,这要求你将要处理Tag Intent的Activity运行在前台,这样当一个NFC tag被扫描到,系统先检测前台的Activity是否支持处理此Intent,如果支持,即将此Intent传给此Activity,如果不支持,则转到Intent发布系统。
以前台前台发布系统为例,需要编写如下代码:
1. 定义变量
private NfcAdapter mAdapter; private String[][] techList; private IntentFilter[] intentFilters; private PendingIntent pendingIntent; private Tag tag;
2. 添加下列代码到Activity的onCreate() 方法里:
//获取nfc适配器 mAdapter = NfcAdapter.getDefaultAdapter(this); //定义程序可以兼容的nfc协议,例子为nfca和nfcv //在Intent filters里声明你想要处理的Intent,一个tag被检测到时先检查前台发布系统, //如果前台Activity符合Intent filter的要求,那么前台的Activity的将处理此Intent。 //如果不符合,前台发布系统将Intent转到Intent发布系统。如果指定了null的Intent filters, //当任意tag被检测到时,你将收到TAG_DISCOVERED intent。因此请注意你应该只处理你想要的Intent。 techList = new String[][] { new String[] { android.nfc.tech.NfcV.class.getName() }, new String[] { android.nfc.tech.NfcA.class.getName() } }; intentFilters = new IntentFilter[] { new IntentFilter( NfcAdapter.ACTION_TECH_DISCOVERED), }; //创建一个 PendingIntent 对象, 这样Android系统就能在一个tag被检测到时定位到这个对象 pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
3. 在onNewIntent方法中:
public void onNewIntent(Intent intent) { tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); return; }
4. 在OnPause方法中:
@Override protected void onPause() { super.onPause(); mAdapter.disableForegroundDispatch(this); }
4. 在OnResume方法中:
@Override protected void onResume() { super.onResume(); //使用前台发布系统 mAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, techList); } }
定义了这些方法以后,运行程序,在不锁屏的情况下,使用NFCV或NFCA的NFC卡靠近的手机的时候OnNewIntent就会被触发。Tag就可以被获取到,可以使用获取到的TAG来查询该卡的一些详细信息和数据。
http://www.cnblogs.com/haoxinyue/archive/2012/05/03/2479599.html
先了解一下MifareClassic协议
在android sdk 的文档中,描述道 “all MifareClassic
I/O operations will be supported, andMIFARE_CLASSIC
NDEF tags will also be supported. In either case,NfcA
will also be enumerated on the tag, because all MIFARE Classic tags are alsoNfcA
.” 所以说NFCA协议是兼容MifareClassic 协议的, 我们可以通过NfcA在android的相关类来处理给予MifareClassic 的RFID卡。
一般来说,给予MifareClassic的射频卡,一般内存大小有3种:
1K: 16个分区(sector),每个分区4个块(block),每个块(block) 16个byte数据
2K: 32个分区,每个分区4个块(block),每个块(block) 16个byte数据
4K:64个分区,每个分区4个块(block),每个块(block) 16个byte数据
对于所有基于MifareClassic的卡来说,每个区最后一个块叫Trailer,16个byte, 主要来存放读写该区的key,可以有A,B两个KEY,每个key长6byte,默认的key一般是FF 或 0,最后一个块的内存结构如下:
//tag 就是在上一篇中onNewIntent中获取的tag
MifareClassic mc = MifareClassic.get(tag); short startAddress = 0; short endAddress = 5; byte[] data = new byte[(endAddress - startAddress + 1 ) * ByteCountPerBlock]; try { mc.connect();for (short i = startAddress; i <= endAddress; i++ ,time++) { boolean auth = false; short sectorAddress = getSectorAddress(i); auth = mc.authenticateSectorWithKeyA(sectorAddress, MifareClassic.KEY_DEFAULT); if (auth){ //the last block of the sector is used for KeyA and KeyB cannot be overwritted short readAddress = (short)(sectorAddress == 0 ? i : i + sectorAddress); byte[] response = mc.readBlock(readAddress); CombineByteArray(data, response, time * ByteCountPerBlock); } else{ throw new NfcException(NfcErrorCode.TemporaryError, "Authorization Error."); } } mc.close(); } catch (NfcException ne) { throw ne; } catch (IOException e) { throw new NfcException(NfcErrorCode.TemporaryError, "Get response, what it is not successfully.", e); } finally { try { mc.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
//tag 就是在上一篇中onNewIntent中获取的tag MifareClassic mc = MifareClassic.get(tag); try { mc.connect(); boolean auth = false; short sectorAddress = 0 auth = mc.authenticateSectorWithKeyA(sectorAddress, MifareClassic.KEY_DEFAULT); if (auth) { //the last block of the sector is used for KeyA and KeyB cannot be overwritted mc.writeBlock(readAddress, dataTemp); mc.close(); } }finally { try { mc.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
完整的代码示例在这里下载