NFC功能
<h4>前言</h4>
<p>Near Field Communication (NFC) 又称近距离无线通信,是一种短距离的高频无线通信技术,允许电子设备之间进行非接触式点对点数据传输,在十厘米(3.9英寸)内交换数据。
& 官方文档:<a href="https://developer.android.com/guide/topics/connectivity/nfc">https://developer.android.com/guide/topics/connectivity/nfc</a></p>
<p><strong>完整的Demo</strong>
<a href="https://github.com/fangmd/NFC_Demo">https://github.com/fangmd/NFC_Demo</a>
[<a href="http://www.szzkc.com/down_data_list_240.html](http://112.74.26.209:8099/down_data_list_240.html">http://www.szzkc.com/down_data_list_240.html](http://112.74.26.209:8099/down_data_list_240.html</a> "完整的Demo")</p>
<p>CPU卡读写DEMO
[ttp://www.szzkc.com/down_data_list_268.html](<a href="http://112.74.26.209:8099/down_data_list_268.html">http://112.74.26.209:8099/down_data_list_268.html</a> "CPU卡读写DEMO")
[](<a href="http://www.szzkc.com/down_data_list_268.html">http://www.szzkc.com/down_data_list_268.html</a> "CPU卡读写DEMO")</p>
<p><strong>参考:</strong>
<a href="https://zh.wikipedia.org/wiki/%E8%BF%91%E5%A0%B4%E9%80%9A%E8%A8%8A">https://zh.wikipedia.org/wiki/%E8%BF%91%E5%A0%B4%E9%80%9A%E8%A8%8A</a>
<a href="https://developer.android.com/guide/topics/connectivity/nfc/nfc.html">https://developer.android.com/guide/topics/connectivity/nfc/nfc.html</a>
<a href="http://www.jianshu.com/p/a3a3b3db6b37">http://www.jianshu.com/p/a3a3b3db6b37</a>
<a href="http://blog.csdn.net/menghnhhuan/article/details/16947361">http://blog.csdn.net/menghnhhuan/article/details/16947361</a>
<a href="http://blog.csdn.net/menghnhhuan/article/details/16947361">http://blog.csdn.net/menghnhhuan/article/details/16947361</a></p>
<h4>NFC 的三种工作模式</h4>
<p><strong>卡模拟模式(Card emulation mode):</strong>将设备模拟成采用RFID技术的IC卡。进行卡模拟的设备必须自带安全组件(Security Element, SE),NFC芯片。
<strong>点对点模式(P2P mode):</strong>和红外线,蓝牙类似用于数据交换。在Android Beam之后android系统支持了点对点数据传输。
<strong>读卡器模式(Reader/Writer mode):</strong>作为非接触读卡器使用,比如从海报或者展览信息电子标签上读取相关信息。</p>
<h4>Android 系统检测NFC流程</h4>
<ol>
<li>Android设备在开启NFC功能后无论屏幕是否打开都会一直监听设备附近的 NFC tag;</li>
<li>到 NFC tag 后会解析内部附带的数据:MIME/URI (不同类型的NFC tag 所携带的数据也不一样);</li>
<li>将解析到的数据存到intent中,通过intent开启应用;</li>
</ol>
<h4>intent 类型</h4>
<p>下面的排序按优先级从高到低。</p>
<p>1、TION_NDEF_DISCOVERED
:当tag中附带NDEF数据的时候
2、TION_TECH_DISCOVERED
:当tag中没有检测到NDEF数据,但是其中包含的数据是已知的类型
3、TION_TAG_DISCOVERED
:如果上面两种都没有检测到,就使用这个启动应用</p>
<p><img src="http://doc.szzkc.com/Public/Uploads/2019-05-05/5cce5be680aab.png" alt="" />
<img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=23c6f63f072c489bc79969467e226bb0&amp;file=file.png" alt="" /></p>
<h4>NFC 基本使用</h4>
<p><strong>申请权限
AndroidManifest.xml</strong></p>
<pre><code class="language-java">&lt;uses-permission android:name=&quot;android.permission.NFC&quot; /&amp;</code></pre>
<p>申明app具有nfc功能 (非必须)
下面标签作用:在应用商店显示本app需要手机支持NFC功能,如果
NFC功能是非必须的,可以不声明,在代码中通过
getDefaultAdapter()
的返回值判断手机是否支持NFC。</p>
<pre><code class="language-java">&lt;uses-feature android:name=&quot;android.hardware.nfc&quot; android:required=&quot;true&quot; /&amp;</code></pre>
<p><strong>Filter 设置</strong>
例子1:MIME:</p>
<pre><code class="language-java">&lt;intent-filter&amp;
&lt;action android:name=&quot;android.nfc.action.NDEF_DISCOVERED&quot;/&amp;
&lt;category android:name=&quot;android.intent.category.DEFAULT&quot;/&amp;
&lt;data android:mimeType=&quot;text/plain&quot; /&amp;
&lt;/intent-filter&amp;</code></pre>
<p><strong>例子2:URI:</strong>
<a href="http://developer.android.com/index.html">http://developer.android.com/index.html</a>.</p>
<pre><code class="language-java">&lt;intent-filter&amp;
&lt;action android:name=&quot;android.nfc.action.NDEF_DISCOVERED&quot;/&amp;
&lt;category android:name=&quot;android.intent.category.DEFAULT&quot;/&amp;
&lt;data android:scheme=&quot;http&quot;
android:host=&quot;developer.android.com&quot;
android:pathPrefix=&quot;/index.html&quot; /&amp;
&lt;/intent-filter&amp;</code></pre>
<h4>ACTION_TECH_DISCOVERED</h4>
<p>如果定义了这个action,必须创建一个XML资源文件定义activity所支持的协议
res/xml
文件夹下创建资源文件:(例子)</p>
<pre><code class="language-java">&lt;resources xmlns:xliff=&quot;urn:oasis:names:tc:xliff:document:1.2&quot;&amp;
&lt;tech-list&amp;
&lt;tech&amp;android.nfc.tech.IsoDep&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.NfcA&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.NfcB&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.NfcF&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.NfcV&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.Ndef&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.NdefFormatable&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.MifareClassic&lt;/tech&amp;
&lt;tech&amp;android.nfc.tech.MifareUltralight&lt;/tech&amp;
&lt;/tech-list&amp;
&lt;/resources&amp;</code></pre>
<p>在
AndroidManifest.xml
中创建</p>
<pre><code class="language-java">&lt;activity&amp;
&lt;intent-filter&amp;
&lt;action android:name=&quot;android.nfc.action.TECH_DISCOVERED&quot;/&amp;
&lt;/intent-filter&amp;
&lt;meta-data android:name=&quot;android.nfc.action.TECH_DISCOVERED&quot;
android:resource=&quot;@xml/nfc_tech_filter&quot; /&amp;
&lt;/activity&amp;</code></pre>
<h4>ACTION_TAG_DISCOVERED</h4>
<pre><code class="language-java">&lt;intent-filter&amp;
&lt;action android:name=&quot;android.nfc.action.TAG_DISCOVERED&quot;/&amp;
&lt;/intent-filter&amp;</code></pre>
<p>从Intent中获取数据intent中可能携带的数据有下面三种,具体哪个里面有数据取决于检测到的NFC tag中的数据类型</p>
<p>1、TRA_TAG
:必须
2、TRA_NDEF_MESSAGES
:可选
3、TRA_ID
:可选</p>
<p>获取数据的例子:</p>
<pre><code class="language-java">public void onResume() {
super.onResume();
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i &lt; rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
}
//process the msgs array
} </code></pre>
<p>或者:可以从Intent中获取Tag对象,tag对象中包含了数据和数据类型</p>
<pre><code class="language-java">Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);</code></pre>
<h4>处理 Tag 对象</h4>
<p>获取tag对应的技术标准对象:</p>
<p>技术标准有:
& IsoDep
MifareClassic
MifareUltralight
Ndef
NdefFormatable
NfcA
NfcB
NfcBarcode
NfcF
NfcV</p>
<h4>MifareClassic格式标签读写</h4>
<p>MifareClassic格式标签数据结构
<img src="http://doc.szzkc.com/Public/Uploads/2019-05-05/5cce5ec6a4836.png" alt="" />
<img src="http://doc.szzkc.com/Public/Uploads/2019-05-05/5cce5ecebb605.png" alt="" />
<img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=c8e3adec23f35205f05cce96f810aee0&amp;file=file.png" alt="" />
<img src="https://www.showdoc.com.cn/server/api/attachment/visitFile?sign=c02999901483cbcffd718171803c5cee&amp;file=file.png" alt="" /></p>
<p>第一扇区的第一块一般用于制造商占用块</p>
<p>0-15个扇区:一个扇区对应4个块,所以总共有64个块,序号分别为0-63,第一个扇区对应:0-3块,第二个扇区对应:4-7块…</p>
<p>每个扇区的最后一个块用来存放密码或控制位,其余为数据块,一个块占用16个字节,keyA占用6字节,控制位占用4字节,keyB占用6字节。</p>
<p>MifareClassic标签读写常用api:</p>
<p>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写入当前块;注意:data必须刚好是16Byte,末尾不能用0填充,应该用空格
readBlock(int):读取当前块的数据。
close():禁止对标签的IO操作,释放资源。</p>
<p>例子:</p>
<pre><code class="language-java">IsoDep isoDep = IsoDep.get(tag);</code></pre>
<p>获取到该对象后进行I/O流读写,注意读写代码属于耗时操作需要在子线程中执行</p>
<p>要从nfc卡中读取信息,需要发起一个指令才能收到相应的信息,不同的nfc卡的指令不同。</p>