Android NFC标签读写 配置 过滤器总结

NFC简介

NFC,即Near Field Communication,近距离无线通讯技术,是一种短距离的(通常<=4cm或更短)高频(13.56M Hz)无线通信技术,它提供了一种简单、触控式的解决方案,可以让消费者简单直观地交换信息、访问内容与服务。

NFC技术由非接触式射频识别(RFID)演变而来,由飞利浦半导体(现恩智浦半导体公司)、诺基亚和索尼共同研制开发,其基础是RFID及互连技术。近场通信(Near Field Communication,NFC)是一种短距高频的无线电技术,在单一芯片上结合感应式读卡器、感应式卡片和点对点的功能,能在13.56MHz频率运行于4厘米距离内,与兼容设备进行识别和数据交换。其传输速度有106 Kbit/秒、212 Kbit/秒或者424 Kbit/秒三种。目前近场通信已通过成为ISO/IEC IS 18092国际标准、ECMA-340标准与ETSI TS 102 190标准。现今这项技术在日韩被广泛应用。手机用户凭着配置了支付功能的手机就可以行遍全国:他们的手机可以用作机场登机验证、大厦的门禁钥匙、交通一卡通、信用卡、支付卡等等。

                                                                       

NFC标签的复杂度不一。简单的标签仅能够提供读写语义,有时编程域是一次性的,写完卡片就变成只读。更复杂一点的tag能够提供数学运算,拥有加密硬件保护区块的访问。最最复杂的tag包含操作环境,允许tag上执行的代码进行交互。我们还能够以各种格式来向tag中写入存储数据,但很多Android的API框架都是基于NFC论坛制定的NDEF标准。

AndroidManifest.xml

为了能够使用Android手机的NFC功能,需要在Manifest文件中添加相应的权限:

使用<uses-permission>元素允许设备访问NFC硬件:

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

使用<uses-sdk>元素设置最小SDK版本,下面是基于android 4.0环境,因此声明如下:

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

下面这项不一定需要,如果你希望你的软件可以在android market中显示有NFC硬件,可以使用<uses-feature>元素声明:

<uses-feature android:name="android.hardware.nfc" android:required="true" />

NFC数据过滤

NFC有三种过滤器分别是ACTION_NDEF_DISCOVERED,ACTION_TECH_DISCOVERED,ACTION_TAG_DISCOVERED。

1. ACTION_NDEF_DISCOVERED

当扫描到的tag中包含有NDEF载荷且为已知类型,该intent将用来启动Activity。该intent的优先级最高,tag分发系统总是先于其他intent用该intent来启动Activity。

2. ACTION_TECH_DISCOVERED

如果manifest中没有注册处理ACTION_NDEF_DISCOVERED类型的intent,该intent将被用以启动Activity。如果tag中没有包含可以映射到MIME或者URI类型的数据,或者虽然没有包含NDEF数据,但是已知的tag技术,则该intent也会被直接启用。

3. ACTION_TAG_DISCOVERED

如果以上两个intent都没人疼,那么该intent就会启动。

过滤器的作用是过滤掉杂质,剩下的就是我们需要的了。这三种过滤方式可同时配置,可以比方成从上到下三层,只要是符合某一层过滤器要求的,过滤完就停止往下一层。

                       

NFC TAG的发布系统:

当android设备扫描到一个NFC标签时,会自动寻找最适合的Activity来处理这个TAG,如果有多个Activity满足条件的话,会让用户来选择到底使用哪一个Activity来处理,可以理解为就是简单的事件响应与事件处理。

那么如何让一个Activity监听 ”当扫描到NFC标签时” 的这一个事件呢?使用intent-filter。

可以理解为当检测到一个NFC标签时系统自动创建一个相关Intent对象,含有响应intent-filter的Activity将处理这个Intent。

1.ACTION_NDEF_DISCOVERED过滤器定义如下:

<intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
</intent-filter>

下面是能够筛选URI为http://developer.android.com/index.html的ACTION_NDEF_DISCOVERED过滤器。

<intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="http" android:host="developer.android.com"
                android:pathPrefix="/index.html" />
</intent-filter>

2.ACTION_TECH_DISCOVERED过滤器定义如下:

<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" />

在res下新建xml文件夹然后新建一个nfc_tech_filter.xml文件,添加进你需要支持的标签类型。

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <!-- 可以处理所有Android支持的NFC类型 -->
    <tech-list>
    <tech>android.nfc.tech.IsoDep</tech>
    </tech-list>
    <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.NfcF</tech>
    </tech-list>
    <tech-list>
    <tech>android.nfc.tech.NfcV</tech>
    </tech-list>
    <tech-list>
    <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
    <tech-list>
    <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
    <tech-list>
    <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
    <tech-list>
    <tech>android.nfc.tech.MifareClassic</tech>
    </tech-list>
</resources>

不同数据标签类型什么意思:

MIFARE Classic数据格式就是NfcA,MIFARE DESFire数据格式是IsoDep就是各种交通卡像武汉通,羊城通,深圳通,北京市政交通卡,长安通,我们使用的二代身份证用的就是NfcB,Felica用的就是NfcF,德州仪器的VicinityCard卡用的是NfcV,而Android分享文件就是实用的Ndef格式传输数据。

3.ACTION_TAG_DISCOVERED过滤器定义如下:

<intent-filter>
    <action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>

各类NFC数据读取

1.NDEF格式数据读取

@Override 
protected void onCreate(Bundle savedInstanceState) { 
  	super.onCreate(savedInstanceState); 
  	setContentView(R.layout.activity_write); 
  	nfcTView=(TextView)findViewById(R.id.info_tv); 
  	nfcAdapter = NfcAdapter.getDefaultAdapter(this); 
  	if (nfcAdapter == null) { 
   		 nfcTView.setText("设备不支持NFC!"); 
    		inish(); 
    		return; 
 	 } 
 	 if (nfcAdapter!=null&&!nfcAdapter.isEnabled()) { 
    		nfcTView.setText("请在系统设置中先启用NFC功能!"); 
    		finish(); 
    		return; 
 	 } 
} 
@Override 
protected void onResume() { 
  	super.onResume(); 
  	if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { 
    		readFromTag(getIntent()); 
  	} 
} 

//读取内容:
private boolean readFromTag(Intent intent){ 
  	Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); 
  	NdefMessage mNdefMsg = (NdefMessage)rawArray[0]; 
  	NdefRecord mNdefRecord = mNdefMsg.getRecords()[0]; 
  	try { 
    		if(mNdefRecord != null){ 
      			readResult = new String(mNdefRecord.getPayload(),"UTF-8"); 
      			return true; 
    		 } 
  	} 
  	catch (UnsupportedEncodingException e) { 
    	e.printStackTrace(); 
 	}; 
  	return false; 
} 

//写入内容
@Override 
protected void onResume() { 
  	super.onResume(); 
  	if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { 
   		Tag tag=getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); 
   		Ndef ndef=Ndef.get(tag); 
    	try { 
     		ndef.connect(); 
      		NdefRecord ndefRecord=createTextRecord(data,Locale.US,true); 
      		NdefRecord[] records={ndefRecord}; 
      		NdefMessage ndefMessage=new NdefMessage(records); 
      		ndef.writeNdefMessage(ndefMessage); 
    	} catch (IOException e1) { 
      		e1.printStackTrace(); 
    	} catch (FormatException e) { 
      		<span style="white-space:pre">   </span>
      		<span style="white-space:pre">   </span>
      		e.printStackTrace(); 
    }
} 

2.非NDEF数据MifareUltralight读取:

public String readTagUltralight(Tag tag) {
		MifareUltralight mifare = MifareUltralight.get(tag);
		try {
			mifare.connect();
			int size=mifare.PAGE_SIZE;
			byte[] payload = mifare.readPages(0);
			String result="page1:"+ByteArrayToHexString(payload)+"\n"+"总容量:"+String.valueOf(size)+"\n";
            //这里只读取了其中几个page
			byte[] payload1 = mifare.readPages(4);
			byte[] payload2 = mifare.readPages(8);
			byte[] payload3 = mifare.readPages(12);
result+="page4:"+ByteArrayToHexString(payload1)+"\npage8:"+ByteArrayToHexString(payload2)+"\npage12:"+ByteArrayToHexString(payload3)+"\n";
            //byte[] payload4 = mifare.readPages(16);
			//byte[] payload5 = mifare.readPages(20);
			return result;
			//+ new String(payload4, Charset.forName("US-ASCII"));
			//+ new String(payload5, Charset.forName("US-ASCII"));
		} catch (IOException e) {
			Log.e(TAG, "IOException while writing MifareUltralight message...",
					e);
			return "读取失败!";
		} catch (Exception ee) {
			Log.e(TAG, "IOException while writing MifareUltralight message...",
					ee);
			return "读取失败!";
		} finally {
			if (mifare != null) {
				try {
					mifare.close();
				} catch (IOException e) {
					Log.e(TAG, "Error closing tag...", e);
				}
			}
		}
}

3.非NDEF数据MifareClassic读取:

public class NFCActivity extends Activity {
	NfcAdapter nfcAdapter;
	TextView promt;
	private PendingIntent pi;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.nfc_info);
		promt = (TextView) findViewById(R.id.promt);
		// 获取默认的NFC控制器
		nfcAdapter = NfcAdapter.getDefaultAdapter(this);
		pi = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
		if (nfcAdapter == null) {
			Toast.makeText(this, "对不起,您的设备不支持nfc功能!", Toast.LENGTH_SHORT).show();
			//promt.setText("设备不支持NFC!");
			finish();
			return;
		}
		if (!nfcAdapter.isEnabled()) {
			Toast.makeText(this, "请在系统设置中开启NFC功能!", Toast.LENGTH_SHORT).show();
			//promt.setText("请在系统设置中先启用NFC功能!");
			finish();
			return;
		}
	}

	public void btn_back(View view){
		this.finish();
	}
	
	@Override
	  protected void onNewIntent(Intent intent) {
	    super.onNewIntent(intent);
	    // 当前app正在前端界面运行,这个时候有intent发送过来,那么系统就会调用onNewIntent回调方法,将intent传送过来
	    // 我们只需要在这里检验这个intent是否是NFC相关的intent,如果是,就调用处理方法
	    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
	      processIntent(intent);
	    }
	  }

	  @Override
	  protected void onResume() {
	    super.onResume();
	    nfcAdapter.enableForegroundDispatch(this, pi, null, null);
	  }
	/*@Override
	protected void onResume() {
		super.onResume();
		//得到是否检测到ACTION_TECH_DISCOVERED触发
		if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(getIntent().getAction())) {
			//处理该intent
			processIntent(getIntent());
		}
	}*/
	//字符序列转换为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();
	}
	private String ByteArrayToHexString(byte[] inarray) {
	    int i, j, in;
	    String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
	        "B", "C", "D", "E", "F" };
	    String out = "";
	    for (j = 0; j < inarray.length; ++j) {
	      in = (int) inarray[j] & 0xff;
	      i = (in >> 4) & 0x0f;
	      out += hex[i];
	      i = in & 0x0f;
	      out += hex[i];
	    }
	    return out;
	  }
	/**
	 * Parses the NDEF Message from the intent and prints to the TextView
	 */
	private void processIntent(Intent intent) {
		//取出封装在intent中的TAG
		Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
		String CardId =ByteArrayToHexString(tagFromIntent.getId());
		String metaInfo = "";
		metaInfo+="卡片ID:"+CardId;
		for (String tech : tagFromIntent.getTechList()) {
			System.out.println(tech);
		}
		boolean auth = false;
		//读取TAG
		MifareClassic mfc = MifareClassic.get(tagFromIntent);
		try {
			//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 += "\n卡片类型:" + 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_DEFAULT);
				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";
				}
			}
			promt.setText(metaInfo);
			//Toast.makeText(this, metaInfo, Toast.LENGTH_SHORT).show();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

4.非NDEF数据IsoDep读取:

IsoDep isodep = IsoDep.get(tagFromIntent);
					isodep.connect();
					//select the card manager applet
					byte[] mf = { (byte) '1', (byte) 'P',
							(byte) 'A', (byte) 'Y', (byte) '.', (byte) 'S', (byte) 'Y',
							(byte) 'S', (byte) '.', (byte) 'D', (byte) 'D', (byte) 'F',
							(byte) '0', (byte) '1', };
	                String result="";
					byte[] mfRsp = isodep.transceive(getSelectCommand(mf));
					Log.d(TAG, "mfRsp:" + HexToString(mfRsp));
					//select Main Application
					byte[] wht = { (byte) 0x41, (byte) 0x50,
                    //此处以武汉通为例,其它的卡片参考对应的命令,网上可以查到
					(byte) 0x31, (byte) 0x2E, (byte) 0x57, (byte) 0x48, (byte) 0x43,
					(byte) 0x54, (byte) 0x43, };
		            byte[] sztRsp = isodep.transceive(getSelectCommand(wht));
		            byte[] balance = { (byte) 0x80, (byte) 0x5C, 0x00, 0x02, 0x04};
		            byte[] balanceRsp = isodep.transceive(balance);
		            Log.d(TAG, "balanceRsp:" + HexToString(balanceRsp));
		            if(balanceRsp!=null && balanceRsp.length>4)
		            {
		            	int cash = byteToInt(balanceRsp, 4);	            	
		            	float ba = cash / 100.0f;
		            	result+="  余额:"+String.valueOf(ba);
		            	 
		            }
		            setNoteBody(result); 
		            isodep.close();               

IsoDep主要用于读取各城市公交卡信息如:武汉通,羊城通,深圳通,北京市政交通卡,长安通。

深圳通:
byte[] DFN_SRV = { (byte) 'P', (byte) 'A', (byte) 'Y',
(byte) '.', (byte) 'S', (byte) 'Z', (byte) 'T' };
武汉通:
byte[] DFN_SRV = { (byte) 0x41, (byte) 0x50,
(byte) 0x31, (byte) 0x2E, (byte) 0x57, (byte) 0x48, (byte) 0x43,
(byte) 0x54, (byte) 0x43, };
羊城通:
byte[] DFN_SRV = { (byte) 'P', (byte) 'A', (byte) 'Y',
(byte) '.', (byte) 'A', (byte) 'P', (byte) 'P', (byte) 'Y', };
长安通:
byte[] DFN_SRV = { (byte) 0xA0, (byte) 0x00,
(byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x86, (byte) 0x98,
(byte) 0x07, (byte) 0x01, };
北京市政交通卡:
byte[] DFI_EP = { (byte) 0x10, (byte) 0x01 };

5.非NDEF数据NfcA读取:

 NfcA nfca = NfcA.get(tagFromIntent);
           try{
               nfca.connect();
               if (nfca.isConnected()) {//NTAG216的芯片
               byte[] SELECT = {
                       (byte) 0x30,
                       (byte) 5 & 0x0ff,//0x05
                   };
               byte[] response = nfca.transceive(SELECT);
               nfca.close();
               if(response!=null){
               setNoteBody(new String(response, Charset.forName("utf-8")));
               }
               }
           }
           catch(Exception e){
}

6.非NDEF数据NfcB读取:

nfcbTag = NfcB.get(tag);
try {
     nfcbTag.connect();
     if (nfcbTag.isConnected()) {
	   System.out.println("已连接");
	   Toast.makeText(MainActivity.this, "身份证已连接",Toast.LENGTH_SHORT).show();
	   new CommandAsyncTask().execute();
 
     }
     // nfcbTag.close();
     } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
}

7.非NDEF数据NfcF读取:

NfcF nfc = NfcF.get(tag);
           try {
               nfc.connect();
               byte[] felicaIDm = new byte[]{0};
               byte[] req = readWithoutEncryption(felicaIDm, 10);
               byte[] res = nfc.transceive(req);
               nfc.close();
               setNoteBody(ByteArrayToHexString(res));
           } catch (Exception e) {
               Log.e(TAG, e.getMessage() , e);
           }

8.非NDEF数据NfcV读取:

NfcV tech = NfcV.get(tag);
    	    if (tech != null) {
    	    	try {
    	    		tech.connect();
                    if (tech.isConnected()) {
                    	byte[] tagUid = tag.getId(); 
                    	int blockAddress = 0;
                    	int blocknum = 4;
                    	byte[] cmd = new byte[] {
                    	        (byte)0x22,  // FLAGS
                    	        (byte)0x23,  // 20-READ_SINGLE_BLOCK,23-所有块
                    	        0, 0, 0, 0, 0, 0, 0,0,
                    	        (byte)(blockAddress & 0x0ff),(byte)(blocknum-1 & 0x0ff)
                    	};
                    	System.arraycopy(tagUid, 0, cmd, 2, tagUid.length);   
                    	byte[] response = tech.transceive(cmd);
                        tech.close();
                        if(response!=null){
                        setNoteBody(new String(response, Charset.forName("utf-8")));
                        }
                    }
                } catch (IOException e) {
                   
                }
    	    }

猜你喜欢

转载自blog.csdn.net/qq_36135335/article/details/82463179
今日推荐