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>