android nfc中MifareClassic格式的讀寫


Android支持的數據格式

 

數據格式的Intent filter

AndroidManifest.xml文件中,要像向下列示例那樣,在<activity>元素內的<meta-data>元素中指定你創建的資源文件:

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <activity>  
  2. ...  
  3.     <intent-filter>  
  4.         <action android:name="android.nfc.action.TECH_DISCOVERED" />  
  5.     </intent-filter>  
  6.     <meta-data  
  7.         android:name="android.nfc.action.TECH_DISCOVERED"  
  8.         android:resource="@xml/nfc_tech_filter" />  
  9.  ...  
  10. </activity>  

 

nfc_tech_filter.xml文件(一個Tag標簽只有全部匹配tech-list元素中的tech元素指定的nfc芯片時才認為被匹配):

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
  2.     <tech-list>  
  3.         <tech>android.nfc.tech.IsoDep</tech>  
  4.         <tech>android.nfc.tech.NfcA</tech>  
  5.         <tech>android.nfc.tech.NfcB</tech>  
  6.         <tech>android.nfc.tech.NfcF</tech>  
  7.         <tech>android.nfc.tech.NfcV</tech>  
  8.         <tech>android.nfc.tech.Ndef</tech>  
  9.         <tech>android.nfc.tech.NdefFormatable</tech>  
  10.         <tech>android.nfc.tech.MifareClassic</tech>  
  11.         <tech>android.nfc.tech.MifareUltralight</tech>  
  12.     </tech-list>  
  13. </resources>  

 

也可創建多個資源文件(多個資源文件是OR關系,每個資源文件中的芯片是AND關系):

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
  2.     <tech-list>  
  3.     <tech>android.nfc.tech.NfcA</tech>  
  4.     <tech>android.nfc.tech.Ndef</tech>  
  5.     <tech>android.nfc.tech.NdefFormatable</tech>  
  6.     </tech-list>  
  7. </resources>  

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
  2.     <tech-list>  
  3.     <tech>android.nfc.tech.Ndef</tech>  
  4.     <tech>android.nfc.tech.NdefFormatable</tech>  
  5.     </tech-list>  
  6. </resources>  

或者在同一個資源文件中創建多個<tech-list>元素(多個<tech-list>元素之間是OR關系,<tech-list>元素中的<tech>是AND關系):

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">  
  2.     <tech-list>  
  3.         <tech>android.nfc.tech.NfcA</tech>  
  4.     </tech-list>  
  5.     <tech-list>  
  6.         <tech>android.nfc.tech.NfcB</tech>  
  7.     </tech-list>  
  8.     <tech-list>  
  9.         <tech>android.nfc.tech.MifareClassic</tech>  
  10.     </tech-list>  
  11. </resources>  

 

 


查看標簽支持數據格式的方法:

通過Tag.getTechlist()方法,獲得標簽所支持的數據格式

通過Tag.getId()方法,獲得標簽的唯一ID標識

NfcAdapter == null:表示設備不支持NFC硬件

NfcAdapter.isEnable()方法:判斷NFC是否開啟

 

綜上所述:

一個Tag通過Tag.getTechlist()方法獲取它所支持的所有標簽類型,如果清單文件中所引用的<tech-list>資源文件中所有的<tech>中的芯片是Tag標簽所有支持標簽的子集則被匹配的,可以寫多個<tech-list>,每個<tech-list>時獨立的,只要有其中一個<tech-list>中的所有的<tech>中的芯片類型全部匹配Tag所支持的芯片則認為是匹配的。多個<tech-list>是OR關系,<tech-list>中的<tech>是AND關系。

 

MifareClassic標簽的外形結構


MifareClassic標簽的數據結構

注意事項(假設1k空間):

第一扇區的第一塊一般用於制造商占用塊

0-15個扇區:一個扇區對應4個塊,所以總共有64個塊,序號分別為0-63,第一個扇區對應:0-3塊,第二個扇區對應:4-7塊...

每個扇區的最后一個塊用來存放密碼或控制位,其余為數據塊,一個塊占用16個字節,keyA占用6字節,控制位占用4字節,keyB占用6字節

 

MifareClassic類的常用方法

get():根據Tag對象來獲得MifareClassic對象;

Connect():允許對MifareClassic標簽進行IO操作;

getType():獲得MifareClassic標簽的具體類型:TYPE_CLASSIC,TYPE_PLUA,TYPE_PRO,TYPE_UNKNOWN;

getSectorCount():獲得標簽總共有的扇區數量;

getBlockCount():獲得標簽總共有的的塊數量;

getSize():獲得標簽的容量:SIZE_1K,SIZE_2K,SIZE_4K,SIZE_MINI

authenticateSectorWithKeyA(int SectorIndex,byte[] Key):驗證當前扇區的KeyA密碼,返回值為ture或false。

常用KeyA:默認出廠密碼:KEY_DEFAULT,

各種用途的供貨商必須配合該技術的MAD:KEY_MIFARE_APPLICATION_DIRECTORY

被格式化成NDEF格式的密碼:KEY_NFC_FORUM

getBlockCountInSector(int):獲得當前扇區的所包含塊的數量;

sectorToBlock(int):當前扇區的第1塊的塊號;

writeBlock(int,data):將數據data寫入當前塊;注意:data必須剛好是16Byte,末尾不能用0填充,應該用空格

readBlock(int):讀取當前塊的數據。

close():禁止對標簽的IO操作,釋放資源。

 


MifareClassic標簽的讀寫流程

獲得Adapter對象

獲得Tag對象

獲得MifareClassic對象

讀取數據塊的數據

Connect(),readBlock(),close()

獲得Adapter對象

獲得Tag對象

獲得MifareClassic對象

將數據塊寫入標簽

Connect(),writeBlock(),close()

 

官方文檔:

 

Working with tag technologies and the ACTION_TECH_DISCOVERED intent

 

When a device scans a tag that has NDEF data on it, but could not be mapped to a MIME or URI, the tag dispatch system tries to start an activity with the ACTION_TECH_DISCOVERED intent. The ACTION_TECH_DISCOVERED is also used when a tag with non-NDEF data is scanned. Having this fallback allows you to work with the data on the tag directly if the tag dispatch system could not parse it for you. The basic steps when working with tag technologies are as follows:

  1. Filter for an ACTION_TECH_DISCOVERED intent specifying the tag technologies that you want to handle. SeeFiltering for NFC intents for more information. In general, the tag dispatch system tries to start aACTION_TECH_DISCOVERED intent when an NDEF message cannot be mapped to a MIME type or URI, or if the tag scanned did not contain NDEF data. For more information on how this is determined, see The Tag Dispatch System.
  2. When your application receives the intent, obtain the Tag object from the intent:
    Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  3. Obtain an instance of a TagTechnology, by calling one of the get factory methods of the classes in theandroid.nfc.tech package. You can enumerate the supported technologies of the tag by callinggetTechList() before calling a get factory method. For example, to obtain an instance of MifareUltralightfrom a Tag, do the following:
    MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));

Reading and writing to tags

Reading and writing to an NFC tag involves obtaining the tag from the intent and opening communication with the tag. You must define your own protocol stack to read and write data to the tag. Keep in mind, however, that you can still read and write NDEF data when working directly with a tag. It is up to you how you want to structure things. The following example shows how to work with a MIFARE Ultralight tag.

package com.example.android.nfc; import android.nfc.Tag; import android.nfc.tech.MifareUltralight; import android.util.Log; import java.io.IOException; import java.nio.charset.Charset; public class MifareUltralightTagTester {     private static final String TAG = MifareUltralightTagTester.class.getSimpleName();     public void writeTag(Tag tag, String tagText) {         MifareUltralight ultralight = MifareUltralight.get(tag);         try {             ultralight.connect();             ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII")));             ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII")));             ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII")));             ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII")));         } catch (IOException e) {             Log.e(TAG, "IOException while closing MifareUltralight...", e);         } finally {             try {                 ultralight.close();             } catch (IOException e) {                 Log.e(TAG, "IOException while closing MifareUltralight...", e);             }         }     }     public String readTag(Tag tag) {         MifareUltralight mifare = MifareUltralight.get(tag);         try {             mifare.connect();             byte[] payload = mifare.readPages(4);             return new String(payload, Charset.forName("US-ASCII"));         } catch (IOException e) {             Log.e(TAG, "IOException while writing MifareUltralight             message...", e);         } finally {             if (mifare != null) {                try {                    mifare.close();                }                catch (IOException e) {                    Log.e(TAG, "Error closing tag...", e);                }             }         }         return null;     } }

 

 

例子程序:

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <CheckBox  
  8.         android:id="@+id/checkbox_write"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:text="是否向NFC標簽寫入數據" />  
  12.   
  13.     <TextView  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:layout_marginBottom="5dp"  
  17.         android:text="請將NFC標簽或貼紙靠近手機背面"  
  18.         android:textSize="16sp" />  
  19.   
  20.     <ImageView  
  21.         android:layout_width="match_parent"  
  22.         android:layout_height="match_parent"  
  23.         android:layout_margin="10dp"  
  24.         android:src="@drawable/read_nfc_tag" />  
  25.   
  26. </LinearLayout>  


MainActivity:

 

 

[java]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. package mobile.android.mifareultralight;  
  2.   
  3. import java.io.IOException;  
  4. import java.nio.charset.Charset;  
  5.   
  6. import android.app.Activity;  
  7. import android.app.PendingIntent;  
  8. import android.content.Intent;  
  9. import android.nfc.NfcAdapter;  
  10. import android.nfc.Tag;  
  11. import android.nfc.tech.MifareClassic;  
  12. import android.os.Bundle;  
  13. import android.util.Log;  
  14. import android.widget.CheckBox;  
  15. import android.widget.Toast;  
  16.   
  17. public class MifareultralightMainActivity extends Activity {  
  18.   
  19.     private CheckBox      mWriteData;  
  20.   
  21.     private NfcAdapter    mNfcAdapter;  
  22.   
  23.     private PendingIntent mPendingIntent;  
  24.   
  25.     @Override  
  26.     public void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.   
  29.         setContentView(R.layout.activity_mifareultralight);  
  30.         mWriteData = (CheckBox) findViewById(R.id.checkbox_write);  
  31.         mNfcAdapter = mNfcAdapter.getDefaultAdapter(this);  
  32.         if (mNfcAdapter == null) {  
  33.             Toast.makeText(this, "設備不支持NFC!", Toast.LENGTH_LONG).show();  
  34.             finish();  
  35.             return;  
  36.         }  
  37.         if (!mNfcAdapter.isEnabled()) {  
  38.             Toast.makeText(this, "請在系統設置中先啟用NFC功能!", Toast.LENGTH_LONG).show();  
  39.             finish();  
  40.             return;  
  41.         }  
  42.         mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,  
  43.                 getClass()), 0);  
  44.   
  45.     }  
  46.   
  47.     @Override  
  48.     public void onResume() {  
  49.         super.onResume();  
  50.         if (mNfcAdapter != null)  
  51.             mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null,  
  52.                     null);  
  53.     }  
  54.   
  55.     @Override  
  56.     public void onNewIntent(Intent intent) {  
  57.   
  58.         Tag tag = intent.getParcelableExtra(mNfcAdapter.EXTRA_TAG);  
  59.         String[] techList = tag.getTechList();  
  60.         boolean haveMifareUltralight = false;  
  61.         for (String tech : techList) {  
  62.             if (tech.indexOf("MifareClassic") >= 0) {  
  63.                 haveMifareUltralight = true;  
  64.                 break;  
  65.             }  
  66.         }  
  67.         if (!haveMifareUltralight) {  
  68.             Toast.makeText(this, "不支持MifareClassic", Toast.LENGTH_LONG).show();  
  69.             return;  
  70.         }  
  71.         if (mWriteData.isChecked()) {  
  72.             writeTag(tag);  
  73.         } else {  
  74.             String data = readTag(tag);  
  75.             if (data != null) {  
  76.                 Log.i(data, "ouput");  
  77.                 Toast.makeText(this, data, Toast.LENGTH_LONG).show();  
  78.             }  
  79.         }  
  80.   
  81.     }  
  82.   
  83.     @Override  
  84.     public void onPause() {  
  85.         super.onPause();  
  86.         if (mNfcAdapter != null)  
  87.             mNfcAdapter.disableForegroundDispatch(this);  
  88.   
  89.     }  
  90.   
  91.     public void writeTag(Tag tag) {  
  92.   
  93.         MifareClassic mfc = MifareClassic.get(tag);  
  94.   
  95.         try {  
  96.             mfc.connect();  
  97.             boolean auth = false;  
  98.             short sectorAddress = 1;  
  99.             auth = mfc.authenticateSectorWithKeyA(sectorAddress,  
  100.                     MifareClassic.KEY_NFC_FORUM);  
  101.             if (auth) {  
  102.                 // the last block of the sector is used for KeyA and KeyB cannot be overwritted  
  103.                 mfc.writeBlock(4, "1313838438000000".getBytes());  
  104.                 mfc.writeBlock(5, "1322676888000000".getBytes());  
  105.                 mfc.close();  
  106.                 Toast.makeText(this, "寫入成功", Toast.LENGTH_SHORT).show();  
  107.             }  
  108.         } catch (IOException e) {  
  109.             // TODO Auto-generated catch block  
  110.             e.printStackTrace();  
  111.         } finally {  
  112.             try {  
  113.                 mfc.close();  
  114.             } catch (IOException e) {  
  115.                 // TODO Auto-generated catch block  
  116.                 e.printStackTrace();  
  117.             }  
  118.         }  
  119.     }  
  120.   
  121.     //字符序列轉換為16進制字符串  
  122.     private String bytesToHexString(byte[] src) {  
  123.         StringBuilder stringBuilder = new StringBuilder("0x");  
  124.         if (src == null || src.length <= 0) {  
  125.             return null;  
  126.         }  
  127.         char[] buffer = new char[2];  
  128.         for (int i = 0; i < src.length; i++) {  
  129.             buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);  
  130.             buffer[1] = Character.forDigit(src[i] & 0x0F, 16);  
  131.             System.out.println(buffer);  
  132.             stringBuilder.append(buffer);  
  133.         }  
  134.         return stringBuilder.toString();  
  135.     }  
  136.   
  137.     public String readTag(Tag tag) {  
  138.         MifareClassic mfc = MifareClassic.get(tag);  
  139.         for (String tech : tag.getTechList()) {  
  140.             System.out.println(tech);  
  141.         }  
  142.         boolean auth = false;  
  143.         //讀取TAG  
  144.   
  145.         try {  
  146.             String metaInfo = "";  
  147.             //Enable I/O operations to the tag from this TagTechnology object.  
  148.             mfc.connect();  
  149.             int type = mfc.getType();//獲取TAG的類型  
  150.             int sectorCount = mfc.getSectorCount();//獲取TAG中包含的扇區數  
  151.             String typeS = "";  
  152.             switch (type) {  
  153.                 case MifareClassic.TYPE_CLASSIC:  
  154.                     typeS = "TYPE_CLASSIC";  
  155.                     break;  
  156.                 case MifareClassic.TYPE_PLUS:  
  157.                     typeS = "TYPE_PLUS";  
  158.                     break;  
  159.                 case MifareClassic.TYPE_PRO:  
  160.                     typeS = "TYPE_PRO";  
  161.                     break;  
  162.                 case MifareClassic.TYPE_UNKNOWN:  
  163.                     typeS = "TYPE_UNKNOWN";  
  164.                     break;  
  165.             }  
  166.             metaInfo += "卡片類型:" + typeS + "\n共" + sectorCount + "個扇區\n共"  
  167.                     + mfc.getBlockCount() + "個塊\n存儲空間: " + mfc.getSize()  
  168.                     + "B\n";  
  169.             for (int j = 0; j < sectorCount; j++) {  
  170.                 //Authenticate a sector with key A.  
  171.                 auth = mfc.authenticateSectorWithKeyA(j,  
  172.                         MifareClassic.KEY_NFC_FORUM);  
  173.                 int bCount;  
  174.                 int bIndex;  
  175.                 if (auth) {  
  176.                     metaInfo += "Sector " + j + ":驗證成功\n";  
  177.                     // 讀取扇區中的塊  
  178.                     bCount = mfc.getBlockCountInSector(j);  
  179.                     bIndex = mfc.sectorToBlock(j);  
  180.                     for (int i = 0; i < bCount; i++) {  
  181.                         byte[] data = mfc.readBlock(bIndex);  
  182.                         metaInfo += "Block " + bIndex + " : "  
  183.                                 + bytesToHexString(data) + "\n";  
  184.                         bIndex++;  
  185.                     }  
  186.                 } else {  
  187.                     metaInfo += "Sector " + j + ":驗證失敗\n";  
  188.                 }  
  189.             }  
  190.             return metaInfo;  
  191.         } catch (Exception e) {  
  192.             Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();  
  193.             e.printStackTrace();  
  194.         } finally {  
  195.             if (mfc != null) {  
  196.                 try {  
  197.                     mfc.close();  
  198.                 } catch (IOException e) {  
  199.                     Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG)  
  200.                             .show();  
  201.                 }  
  202.             }  
  203.         }  
  204.         return null;  
  205.   
  206.     }  
  207. }  


清單文件:

 

 

[html]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
    1. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    2.     package="mobile.android.mifareultralight"  
    3.     android:versionCode="1"  
    4.     android:versionName="1.0" >  
    5.   
    6.     <uses-sdk  
    7.         android:minSdkVersion="15"  
    8.         android:targetSdkVersion="15" />  
    9.   
    10.     <uses-permission android:name="android.permission.NFC" />  
    11.   
    12.     <application  
    13.         android:icon="@drawable/ic_launcher"  
    14.         android:label="@string/app_name"  
    15.         android:theme="@style/AppTheme" >  
    16.         <activity  
    17.             android:name=".MifareultralightMainActivity"  
    18.             android:label="Mifareultralight"  
    19.             android:launchMode="singleTop"  
    20.             android:screenOrientation="portrait" >  
    21.             <intent-filter>  
    22.                 <action android:name="android.intent.action.MAIN" />  
    23.                 <category android:name="android.intent.category.LAUNCHER" />  
    24.             </intent-filter>  
    25.         </activity>  
    26.     </application>  
    27.   
    28. </manifest>  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM