Android Nfc读写标签 您所在的位置:网站首页 卡片类型classic是什么卡 Android Nfc读写标签

Android Nfc读写标签

2024-06-30 15:15| 来源: 网络整理| 查看: 265

  文章介绍NDEF和MifareClassic两种格式数据读写标签的方法。 在进行Nfc的读写之前,了解卡片的存储结构是非常重要的(非常重要,尤其对理解MifareClassic数据格式)。 卡片常用的数据格式: MifareClassic数据格式就是NfcA。 IsoDep:各种交通卡。如:北京市政交通卡。 NfcB:二代身份证。 NfcF:Felica用的是。 NfcV:德州仪器的VicinityCard卡用的是。 Ndef:安卓主推的传输数据格式。

一、Nfc卡片存储结构

M1卡:容量1K。16个扇区(sector),每个扇区4个块(block),每个块(block) 16个byte数据 每个扇区的前三个块,可用于存贮数据。 第0扇区的第一个块除外,它用于存放厂商数据,已经固化,不可更改。 每个扇区的第四个块为控制块,用于控制该扇形区的访问权限。它的前6个字节为KeyA,后6个字节为KeyB,中间的4个字节为存取控制。KeyA和KeyB记录了两种加密方式,存取控制里记录了该扇区的读写权限支持KeyA验证还是KeyB验证。

扇形区 块 块中的数据 扇区 0(sectorIndex 0) 块 0(blockIndex 0) 厂家数据不可修改 扇区 0(sectorIndex 0) 块 1(blockIndex 1) 0........................15 (byte) 扇区 0(sectorIndex 0) 块 2(blockIndex 2) 0........................15 (byte) 扇区 0(sectorIndex 0) 块 3(blockIndex 3) 密码A 存取控制 密码B : : : : : : 扇区 15(sectorIndex 15) 块 60(blockIndex 60) 0........................15 (byte) 扇区 15(sectorIndex 15) 块 61(blockIndex 61) 0........................15 (byte) 扇区 15(sectorIndex 15) 块 62(blockIndex 62) 0........................15 (byte) 扇区 15(sectorIndex 15) 块 63(blockIndex 63) 密码A 存取控制 密码B 数据块.png 二、NFC数据过滤器

NFC有三种过滤器:ACTION_NDEF_DISCOVERED,ACTION_TECH_DISCOVERED,ACTION_TAG_DISCOVERED。

1、过滤器介绍

ACTION_NDEF_DISCOVERED 当卡片中包含NDEF格式的数据,将使用该模式启动Activity。 ACTION_TECH_DISCOVERED 当卡片中包含非NDEF格式的数据(如:MifareClassic)将使用该模式启动Activity。 ACTION_TAG_DISCOVERED 过滤规则是最不严格的,只要符合NFC规范中的任一种则都会响应。

优先级顺序,上面三种规则依次递减

如果卡片是Ndef格式的数据 如果activity注册了ACTION_NDEF_DISCOVERED,会优先响应ACTION_NDEF_DISCOVERED。 如果activity没有注册ACTION_NDEF_DISCOVERED,会优先响应ACTION_TECH_DISCOVERED。 如果activity没有注册ACTION_NDEF_DISCOVERED和ACTION_TECH_DISCOVERED,会响应ACTION_TAG_DISCOVERED。 如果卡片是非Ndef格式的数据 即使activity注册了ACTION_NDEF_DISCOVERED,也不会响应ACTION_NDEF_DISCOVERED。 如果activity注册了ACTION_TECH_DISCOVERED,会优先响应ACTION_TECH_DISCOVERED。 如果activity没有注册ACTION_TECH_DISCOVERED,会响应ACTION_TAG_DISCOVERED。

ACTION_TAG_DISCOVERED的优先级是最低,activity无法获得更详细的一些信息,所以不会一开始就选择TAG_DISCOVERED来响应。

2、过滤器注册

静态注册 在AndroidMainfest.xml中声明。在需要处理NFC读卡的Activity标签里声明过滤器。当在桌面刷卡的时候,手机系统会弹窗出现可以处理该Intent的APP。

筛选NFC卡片的类型。

在res>xml目录下添加nfc_tech_filter.xml

android.nfc.tech.NfcA android.nfc.tech.NfcB android.nfc.tech.NfcF android.nfc.tech.NfcV android.nfc.tech.Ndef android.nfc.tech.NdefFormatable android.nfc.tech.IsoDep android.nfc.tech.MifareClassic android.nfc.tech.MifareUltralight

动态注册 在代码里声明过滤器,在桌面刷卡的时候,不会弹出可响应APP的弹窗。

Intent intent = new Intent(activity, activity.getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); mPendingIntent = PendingIntent.getActivity(activity, 0, intent, 0); //intentFilter过滤----ndef IntentFilter ndefFilter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try { //文本类型 ndefFilter.addDataType("text/plain"); } catch (IntentFilter.MalformedMimeTypeException e) { e.printStackTrace(); } //intentFilter过滤----非ndef IntentFilter techFilter = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); //intentFilter过滤器列表 mIntentFilter = new IntentFilter[]{ndefFilter, techFilter}; //匹配的数据格式列表 mTechList = new String[][]{ {MifareClassic.class.getName()}, {NfcA.class.getName()}, {Ndef.class.getName()}, {NdefFormatable.class.getName()}}; 三、NFC的工具类 1、初始化 mNfcAdapter = NfcAdapter.getDefaultAdapter(activity); if (mNfcAdapter == null) { ToastUtils.showShort("设备不支持NFC功能!"); } else { if (!mNfcAdapter.isEnabled()) { showSettingDailog(); } else { Logger.e(TAG, "NFC功能已打开!"); init(); } } 2、启动 /** * Nfc监听intent */ public void enableForegroundDispatch() { if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { mNfcAdapter.enableForegroundDispatch(activity, mPendingIntent, mIntentFilter, mTechList); } } 2、关闭 /** * 取消监听Nfc */ public void disableForegroundDispatch() { if (mNfcAdapter != null && mNfcAdapter.isEnabled()) { mNfcAdapter.disableForegroundDispatch(activity); } } 3、读取NDEF格式 /** * 读取Ndef的数据 * * @return */ private String readNdef(Intent intent) { String info = ""; Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); NdefMessage msgs[] = null; if (rawMsgs != null) { msgs = new NdefMessage[rawMsgs.length]; for (int i = 0; i < rawMsgs.length; i++) { msgs[i] = (NdefMessage) rawMsgs[i]; } NdefRecord record = msgs[0].getRecords()[0]; if (record != null) { byte[] payload = record.getPayload(); //下面代码分析payload:状态字节+ISO语言编码(ASCLL)+文本数据(UTF_8/UTF_16) //其中payload[0]放置状态字节:如果bit7为0,文本数据以UTF_8格式编码,如果为1则以UTF_16编码 //bit6是保留位,默认为0 /* * payload[0] contains the "Status Byte Encodings" field, per the * NFC Forum "Text Record Type Definition" section 3.2.1. * * bit7 is the Text Encoding Field. * * if (Bit_7 == 0): The text is encoded in UTF-8 if (Bit_7 == 1): * The text is encoded in UTF16 * * Bit_6 is reserved for future use and must be set to zero. * * Bits 5 to 0 are the length of the IANA language code. */ String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16"; //处理bit5-0。bit5-0表示语言编码长度(字节数) int languageCodeLength = payload[0] & 0x3f; //获取语言编码(从payload的第2个字节读取languageCodeLength个字节作为语言编码) try { String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } //解析出实际的文本数据 try { info = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } return info; } 5、写NDEF格式 /** * 往nfc写入数据 */ public void writeNFCToTag(String text, Intent intent) throws IOException, FormatException { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //生成语言编码的字节数组,中文编码 byte[] langBytes = Locale.CHINA.getLanguage().getBytes( Charset.forName("US-ASCII")); //将要写入的文本以UTF_8格式进行编码 Charset utfEncoding = Charset.forName("UTF-8"); //由于已经确定文本的格式编码为UTF_8,所以直接将payload的第1个字节的第7位设为0 byte[] textBytes = text.getBytes(utfEncoding); int utfBit = 0; //定义和初始化状态字节 char status = (char) (utfBit + langBytes.length); //创建存储payload的字节数组 byte[] data = new byte[1 + langBytes.length + textBytes.length]; //设置状态字节 data[0] = (byte) status; //设置语言编码 System.arraycopy(langBytes, 0, data, 1, langBytes.length); //设置实际要写入的文本 System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{record}); //转换成字节获得大小 int size = ndefMessage.toByteArray().length; //2.判断NFC标签的数据类型(通过Ndef.get方法) Ndef ndef = Ndef.get(tag); //判断是否为NDEF标签 if (ndef != null) { ndef.connect(); //判断是否支持可写 if (!ndef.isWritable()) { return; } //判断标签的容量是否够用 if (ndef.getMaxSize() < size) { return; } //3.写入数据 ndef.writeNdefMessage(ndefMessage); Logger.e(TAG, "写入NDEF成功"); } else { //当我们买回来的NFC标签是没有格式化的,或者没有分区的执行此步 //Ndef格式类 NdefFormatable format = NdefFormatable.get(tag); //判断是否获得了NdefFormatable对象,有一些标签是只读的或者不允许格式化的 if (format != null) { //连接 format.connect(); //格式化并将信息写入标签 format.format(ndefMessage); Logger.e(TAG, "格式化并写入NDEF成功"); } else { Logger.e(TAG, "格式化并写入NDEF失败"); } } } 6、读取MifareClassic格式 private String readMifareClassic(Intent intent) { String info = ""; List list = new ArrayList(); boolean auth = false; //读取TAG MifareClassic mfc = MifareClassic.get(getNFCTag(intent)); try { mfc.connect(); if (mfc.isConnected()) { int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数 for (int j = 0; j < sectorCount; j++) { auth = mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_NFC_FORUM); int bCount; int bIndex; if (auth) { // 读取扇区中的块 bCount = mfc.getBlockCountInSector(j); bIndex = mfc.sectorToBlock(j); for (int i = 0; i < bCount; i++) { byte[] data = mfc.readBlock(bIndex); if (i < bCount - 1) { if (!bytesToHexString(data).equals("0x00000000000000000000000000000000")) { list.add(data); } } bIndex++; } } } if (list.size() > 0) { byte[] aa = new byte[list.size() * list.get(0).length]; for (int i = 0; i < list.size(); i++) { byte[] bytes = list.get(i); System.arraycopy(bytes, 0, aa, bytes.length * i, bytes.length); } info = new String(trim(aa), Charset.forName("utf-8")); } } } catch (Exception e) { Logger.e(TAG, e); } finally { try { mfc.close(); } catch (IOException e) { Logger.e(TAG, e); } } return info; } 7、写MifareClassic格式 public void writeMifareClassic(String data, Intent intent) throws IOException { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); MifareClassic mfc = MifareClassic.get(tag); try { mfc.connect(); int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数 for (int j = 0; j < sectorCount; j++) { if (mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_NFC_FORUM)) { // 读取扇区中的块 int bCount = mfc.getBlockCountInSector(j); int bIndex = mfc.sectorToBlock(j); for (int i = 0; i < bCount - 1; i++) { if (!bytesToHexString(mfc.readBlock(bIndex + i)).equals("0x00000000000000000000000000000000")) { mfc.writeBlock(bIndex + i, new byte[16]); } } } } byte[] bytes = new byte[16]; System.arraycopy(data.getBytes(), 0, bytes, 0, data.getBytes().length); if (mfc.authenticateSectorWithKeyA(1, MifareClassic.KEY_NFC_FORUM)) { mfc.writeBlock(4, bytes); } Logger.e(TAG, "写入MifareClassic成功"); } finally { try { mfc.close(); } catch (IOException e) { e.printStackTrace(); } } } 8、清空数据 public boolean clear(Intent intent) throws IOException { boolean result = false; Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); MifareClassic mfc = MifareClassic.get(tag); try { mfc.connect(); int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数 for (int j = 0; j < sectorCount; j++) { if (mfc.authenticateSectorWithKeyA(j, MifareClassic.KEY_NFC_FORUM)) { // 读取扇区中的块 int bCount = mfc.getBlockCountInSector(j); int bIndex = mfc.sectorToBlock(j); for (int i = 0; i < bCount - 1; i++) { if (!bytesToHexString(mfc.readBlock(bIndex + i)).equals("0x00000000000000000000000000000000")) { mfc.writeBlock(bIndex + i, new byte[16]); } } } } result = true; Logger.e(TAG, "清空成功"); } finally { try { mfc.close(); } catch (IOException e) { e.printStackTrace(); } } return result; } 四、NFC调用 1、重写onNewIntent()方法 @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); String id = nfcUtils.readNFCId(nfcUtils.getNFCTag(intent)); Logger.e(TAG, "nfcID:" + id); String message = nfcUtils.readMessage(intent); Logger.e(TAG, "nfcMessage:" + message); } 2、启动和关闭 @Override protected void onResume() { super.onResume(); Logger.e(TAG, "onResume"); nfcUtils.enableForegroundDispatch(); } @Override protected void onPause() { super.onPause(); Logger.e(TAG, "onPause"); nfcUtils.disableForegroundDispatch(); } 代码太多了,懒的粘了 第一次写文章欢迎大家指教 附上GitHub地址: https://github.com/yaogoodgoodde/SimpleNfc.git


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有