android nfc (二)

在上一篇中的最後寫到了如何過濾和接收一個nfc intent。那麼這裏要寫如何解析已經收到的nfc intent。android

 

一:解析NDEFweb

   一直在說ndef格式,這究竟是個啥格式?據文檔說這是google參考nfc論壇而提出和支持的格式。也就是說nfc的格式和標準確定有不少,google只不過比較支持其中的這一種NDEF。而若是以後apple也搞nfc的話,我懷疑他確定就不會去支持google已經支持的ndef了。我淺薄的如此認爲,之後再看看吧呵呵json

  ndef格式,就是數據被包裹在一個NdefMessage裏,而同時一個NdefMessage裏面又能夠包含多個NdefRecord.固然你也應該還記得,nfc的tag裏面是能夠不包含Ndef數據的,他也能夠包含android.nfc.tech所定義的多種標籤。Google推薦開發人員使用ndef格式的數據來處理android相關的nfc格式數據。瀏覽器

   當一個android設備檢測到nfc標籤包含ndef格式的數據的時候,他就會嘗試的去解析數據中的MIME type或者是URI。爲了作這個事情,他須要解析NdefMessage中的第一個NdefRecord。第一個NdefRecord中的結構通常以下:網絡

3-bit TNF (Type Name Format)app

如何解釋variable length type的數據。並依據不一樣的解析,來肯定發送什麼樣的intent。若是是這裏定義的是TNF_WELL_KNOWN則還要參考variable length type來進一步肯定。若是是網絡地址URL則直接去調用瀏覽器程序了。google

  • Variable length typespa

  • 進一步定義record的類型。具體的請參看android文檔。不過話在這裏多說一句,TNF和TYPE這兩個參數很是的重要,直接決定了你NDEF格式的命中程度。定義的越規範,越清楚,同時在androidmanifest文件中寫的越正確,就越好,不然系統就會被你發TECH_ACTION翻譯

  • 好比:構造的時候寫orm

  • new NdefRecord(
                    NdefRecord.TNF_WELL_KNOWN ,
                    NdefRecord.RTD_TEXT,
                    new byte[0],
                    data.getBytes(Charset.forName("UTF-8")));

  • androidmanifest文件中寫

  •       <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="text/plain"/>     
        </intent-filter>
        這一對就能夠正確的過濾一個text/plain格式的ndef

Variable length ID

    不常用.....是用來惟一標示一個record的。 

 

Variable length payload

    actual這個纔是真正的數據區域。你的讀寫操做通常應該主要是對這個區域的操做。一個NdefMessage能夠有多個NdefRecord因此你不要假設全部的數據都僅僅存儲在第一個NdefRecord中。

 

已經知道了Ndef的格式了,那麼解析帶有Ndef數據的Intent也就簡單不少了。底下這個解析的方法,一般放在onResume或者onNewIntent中。

   
    private boolean readFromTag(Intent in){
        Parcelable[] rawArray = in.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        //we just have noly one NdefMessage,若是你不止一個的話,那麼你要遍歷了。
        NdefMessage mNdefMsg = (NdefMessage)rawArray[0];
        //we just have only one NdefRecord,若是你不止一個record,那麼你也要遍歷出來全部的record
        NdefRecord mNdefRecord = mNdefMsg.getRecords()[0];
        try {
            if(mNdefRecord != null){
                readResult = new String(mNdefRecord.getPayload(),"UTF-8");
                readJson = new JSONObject(readResult);//我舉的例子中用的是json格式,因此這裏要把payload中的數據封裝成json格式的。
                return true;
            }
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        };
        return false;
    }

 

 

 

二:解析其餘tech標籤

   如今你已經會解析ndef標籤了,可是你也應該知道,大多數nfc標籤其實並非ndef格式的。好比你的公交卡,各個城市的公交卡會支持什麼格式,並不必定。至少我理解是這樣的。因此你還要會解析各類tech格式的nfc數據。

  若是你還記得咱們再tech-list中定義了多少種格式,你就應該知道咱們若是要所有解析的話,應該要解析多少種不一樣的nfc格式了。底下這段代碼能夠顯示出你所掃描的nfc卡到底支持哪幾種格式。

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String[] temp = tag.getTechList();
        for(String s :temp){
          Log.i("xxx","s = "+s);
        }

 

   這是一段最經常使用的(我我的理解啊)的nfc解析代碼,主要是解析MifareClassic格式。貌似大多數nfc標籤都會支持這種格式。先普及下關於他的簡單常識:不知道各個格式的數據是怎麼構成的,你壓根就無法解析。

通常來講,給予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的卡來講,每一個區最後一個塊(block)叫Trailer,16個byte,主要來存放讀寫該區的key,能夠有A,B兩個KEY,每一個key長6byte,默認的key通常是FF 或 0,最後一個塊的內存結構以下:

Block 0  Data 16bytes

Block 1  Data 16 bytes

Block 2  Data 16 bytes

Block 3  Trailer 16 bytes

 

Trailer:

Key A: 6 bytes

Access Conditions: 4 bytes

Key B: 6 bytes 

 

因此在寫卡的內存的時候,通常不能寫每一個sector的最後一個block,除非你有要修改KEY和訪問權限的需求。若是KEY A 被你不當心修改掉了,而你不知道修改爲什麼,那與之對應的那個sector你就沒有辦法訪問了。由於在MifareClassic中,若是你要讀取數據,那麼必需要有這個數據地址所在的sector的權限,這個權限就是這個sector的trailer的KEY A或KEY B。

 

這就是解析MifareClassic的代碼了

    private void readTechPayloadMC(Tag tag){
        MifareClassic mc = MifareClassic.get(tag);//經過intent拿到EXTRA_TAG並轉化成MirareClassic格式。

        int bCount = 0 ;
        int bIndex = 0 ;
        try {
            mc.connect();
            int sectorCount = mc.getSectorCount();//得到sector總數
            Log.i("liyufei3","sectorCount = "+sectorCount);
            for(int i =0;i<sectorCount;i++){
                //嘗試去得到每一個sector的認證,只有認證經過才能訪問
                auth = mc.authenticateSectorWithKeyA(i, MifareClassic.KEY_DEFAULT);
                if(auth){
                    //這句其實不是必須的,由於每一個sector中原本就只有4個block
                    bCount = mc.getBlockCountInSector(i);
                   
                    //all blocks are consecutively numbered ,0-64,so we can not use number of cycles to be index.
                    //we just can get the first block in iTH sector

//咱們能夠獲得每個sector中的第一個block的編號
                    bIndex = mc.sectorToBlock(i);

                    for(int j =0;j<bCount;j++){//循環四次拿出一個sector中全部的block
//每次循環bIndex會去++,而後能夠得出每個block的數據。這些數據是字節碼,因此你還有一個翻譯的工做要作。

                        byte[] data = mc.readBlock(bIndex);           
                       String s = readByteArray(data);
                        bIndex++;
                    }
                }else{
                    Log.i("xxx","   auth = false !!!  in sectorCount = "+i);

                   
                }
            }

       } catch (IOException e) {           // TODO Auto-generated catch block           e.printStackTrace();       }   }

相關文章
相關標籤/搜索