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();       }   }

相关文章
相关标签/搜索