android nfc中MifareClassic格式的读写


Android支持的数据格式


数据格式的Intent filter 

AndroidManifest.xml文件中,要像向下列示例那样,在<activity>元素内的<meta-data>元素中指定你创建的资源文件:

<activity>
...
  <intent-filter>
    <action android:name="android.nfc.action.TECH_DISCOVERED" />
  </intent-filter>
  <meta-data
    android:name="android.nfc.action.TECH_DISCOVERED"
    android:resource="@xml/nfc_tech_filter" />
 ...
</activity>
nfc_tech_filter.xml文件(一个Tag标签只有全部匹配tech-list元素中的tech元素指定的nfc芯片时才认为被匹配):
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <tech-list>
    <tech>android.nfc.tech.IsoDep</tech>
    <tech>android.nfc.tech.NfcA</tech>
    <tech>android.nfc.tech.NfcB</tech>
    <tech>android.nfc.tech.NfcF</tech>
    <tech>android.nfc.tech.NfcV</tech>
    <tech>android.nfc.tech.Ndef</tech>
    <tech>android.nfc.tech.NdefFormatable</tech>
    <tech>android.nfc.tech.MifareClassic</tech>
    <tech>android.nfc.tech.MifareUltralight</tech>
  </tech-list>
</resources>

也可创建多个资源文件(多个资源文件是OR关系,每个资源文件中的芯片是AND关系):

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

<tech-list>
  <tech>android.nfc.tech.NfcA</tech>
  <tech>android.nfc.tech.Ndef</tech>
  <tech>android.nfc.tech.NdefFormatable</tech>
  </tech-list>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <tech-list>
  <tech>android.nfc.tech.Ndef</tech>
  <tech>android.nfc.tech.NdefFormatable</tech>
  </tech-list>
</resources>
或者在同一个资源文件中创建多个<tech-list>元素(多个<tech-list>元素之间是OR关系,<tech-list>元素中的<tech>是AND关系):
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  <tech-list>
    <tech>android.nfc.tech.NfcA</tech>
  </tech-list>
  <tech-list>
    <tech>android.nfc.tech.NfcB</tech>
  </tech-list>
  <tech-list>
    <tech>android.nfc.tech.MifareClassic</tech>
  </tech-list>
</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标签的数据结构

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写入当前块;

readBlock(int):读取当前块的数据。

close():禁止对标签的IO操作,释放资源。


MifareClassic标签的读写流程

获得Adapter对象

获得Tag对象

获得MifareClassic对象

读取数据块的数据

Connect(),readBlock(),close()  

获得Adapter对象

获得Tag对象

获得MifareClassic对象

将数据块写入标签

Connect(),writeBlock(),close() 

例子程序:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical" >

  <CheckBox
    android:id="@+id/checkbox_write"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="是否向NFC标签写入数据" />

  <TextView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="5dp"
    android:text="请将NFC标签或贴纸靠近手机背面"
    android:textSize="16sp" />

  <ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:src="@drawable/read_nfc_tag" />

</LinearLayout>
MainActivity: 
package mobile.android.mifareultralight;

import java.io.IOException;
import java.nio.charset.Charset;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.MifareClassic;
import android.os.Bundle;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.Toast;

public class MifareultralightMainActivity extends Activity {

  private CheckBox	  mWriteData;

  private NfcAdapter	mNfcAdapter;

  private PendingIntent mPendingIntent;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_mifareultralight);
    mWriteData = (CheckBox) findViewById(R.id.checkbox_write);
    mNfcAdapter = mNfcAdapter.getDefaultAdapter(this);
    if (mNfcAdapter == null) {
      Toast.makeText(this, "设备不支持NFC!", Toast.LENGTH_LONG).show();
      finish();
      return;
    }
    if (!mNfcAdapter.isEnabled()) {
      Toast.makeText(this, "请在系统设置中先启用NFC功能!", Toast.LENGTH_LONG).show();
      finish();
      return;
    }
    mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
        getClass()), 0);

  }

  @Override
  public void onResume() {
    super.onResume();
    if (mNfcAdapter != null)
      mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null,
          null);
  }

  @Override
  public void onNewIntent(Intent intent) {

    Tag tag = intent.getParcelableExtra(mNfcAdapter.EXTRA_TAG);
    String[] techList = tag.getTechList();
    boolean haveMifareUltralight = false;
    for (String tech : techList) {
      if (tech.indexOf("MifareClassic") >= 0) {
        haveMifareUltralight = true;
        break;
      }
    }
    if (!haveMifareUltralight) {
      Toast.makeText(this, "不支持MifareClassic", Toast.LENGTH_LONG).show();
      return;
    }
    if (mWriteData.isChecked()) {
      writeTag(tag);
    } else {
      String data = readTag(tag);
      if (data != null) {
        Log.i(data, "ouput");
        Toast.makeText(this, data, Toast.LENGTH_LONG).show();
      }
    }

  }

  @Override
  public void onPause() {
    super.onPause();
    if (mNfcAdapter != null)
      mNfcAdapter.disableForegroundDispatch(this);

  }

  public void writeTag(Tag tag) {

    MifareClassic mfc = MifareClassic.get(tag);

    try {
      mfc.connect();
      boolean auth = false;
      short sectorAddress = 1;
      auth = mfc.authenticateSectorWithKeyA(sectorAddress,
          MifareClassic.KEY_NFC_FORUM);
      if (auth) {
        // the last block of the sector is used for KeyA and KeyB cannot be overwritted
        mfc.writeBlock(4, "1313838438000000".getBytes());
        mfc.writeBlock(5, "1322676888000000".getBytes());
        mfc.close();
        Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show();
      }
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } finally {
      try {
        mfc.close();
      } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
      }
    }
  }

  //字符序列转换为16进制字符串
  private String bytesToHexString(byte[] src) {
    StringBuilder stringBuilder = new StringBuilder("0x");
    if (src == null || src.length <= 0) {
      return null;
    }
    char[] buffer = new char[2];
    for (int i = 0; i < src.length; i++) {
      buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);
      buffer[1] = Character.forDigit(src[i] & 0x0F, 16);
      System.out.println(buffer);
      stringBuilder.append(buffer);
    }
    return stringBuilder.toString();
  }

  public String readTag(Tag tag) {
    MifareClassic mfc = MifareClassic.get(tag);
    for (String tech : tag.getTechList()) {
      System.out.println(tech);
    }
    boolean auth = false;
    //读取TAG

    try {
      String metaInfo = "";
      //Enable I/O operations to the tag from this TagTechnology object.
      mfc.connect();
      int type = mfc.getType();//获取TAG的类型
      int sectorCount = mfc.getSectorCount();//获取TAG中包含的扇区数
      String typeS = "";
      switch (type) {
        case MifareClassic.TYPE_CLASSIC:
          typeS = "TYPE_CLASSIC";
          break;
        case MifareClassic.TYPE_PLUS:
          typeS = "TYPE_PLUS";
          break;
        case MifareClassic.TYPE_PRO:
          typeS = "TYPE_PRO";
          break;
        case MifareClassic.TYPE_UNKNOWN:
          typeS = "TYPE_UNKNOWN";
          break;
      }
      metaInfo += "卡片类型:" + typeS + "\n共" + sectorCount + "个扇区\n共"
          + mfc.getBlockCount() + "个块\n存储空间: " + mfc.getSize()
          + "B\n";
      for (int j = 0; j < sectorCount; j++) {
        //Authenticate a sector with key A.
        auth = mfc.authenticateSectorWithKeyA(j,
            MifareClassic.KEY_NFC_FORUM);
        int bCount;
        int bIndex;
        if (auth) {
          metaInfo += "Sector " + j + ":验证成功\n";
          // 读取扇区中的块
          bCount = mfc.getBlockCountInSector(j);
          bIndex = mfc.sectorToBlock(j);
          for (int i = 0; i < bCount; i++) {
            byte[] data = mfc.readBlock(bIndex);
            metaInfo += "Block " + bIndex + " : "
                + bytesToHexString(data) + "\n";
            bIndex++;
          }
        } else {
          metaInfo += "Sector " + j + ":验证失败\n";
        }
      }
      return metaInfo;
    } catch (Exception e) {
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
      e.printStackTrace();
    } finally {
      if (mfc != null) {
        try {
          mfc.close();
        } catch (IOException e) {
          Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG)
              .show();
        }
      }
    }
    return null;

  }
}
清单文件: 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="mobile.android.mifareultralight"
  android:versionCode="1"
  android:versionName="1.0" >

  <uses-sdk
    android:minSdkVersion="15"
    android:targetSdkVersion="15" />

  <uses-permission android:name="android.permission.NFC" />

  <application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
      android:name=".MifareultralightMainActivity"
      android:label="Mifareultralight"
      android:launchMode="singleTop"
      android:screenOrientation="portrait" >
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
  </application>

</manifest>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值