搞懂Nfc刷卡看這篇就夠了
NFC在我們生活中出現的場景越來越多了,如藍芽耳機的連線、刷交通卡、智慧鎖開鎖等,相信在未來還會有越來越多的場景會用到NFC,所以作為開發者,掌握NFC的知識及NFC的開發技能、就顯得尤為必要
這裡放出Android
官方文件
,官方的文件講的大而全,本文是對官方文件的抽絲剝繭,相對官方文件來說會更容易理解,但是會比官方文件少一些內容,如果本文沒有你想了解的,可以自己查閱官方文件。
什麼是NFC
NFC
是Near Field Communication縮寫,即近距離無線通訊技術。由飛利浦公司和索尼公司共同開發的NFC是一種非接觸式識別和互聯技術,可以在移動裝置、消費類電子產品、PC 和智慧控制元件工具間進行近距離無線通訊。
簡單的說,
NFC
提供了一種簡單、觸控式的解決方案,可以讓消費者簡單直觀地交換資訊、訪問內容與服務。
NFC的工作模式
NFC的工作模式有三種,分別是讀卡器模式(Reader/writer mode)、模擬卡模式(Card Emulation Mode)、點對點模式(P2P mode)。
讀卡器模式
資料在NFC晶片中,可以簡單理解成“刷標籤”。本質上就是透過支援NFC的手機或其它電子裝置從帶有NFC晶片的標籤、貼紙、名片等媒介中讀寫資訊。通常NFC標籤是不需要外部供電的。當支援NFC的外設向NFC讀寫資料時,它會發送某種磁場,而這個磁場會自動的向NFC標籤供電。
模擬卡模式
資料在支援NFC的手機或其它電子裝置中,可以簡單理解成“刷手機”。本質上就是將支援NFC的手機或其它電子裝置當成借記卡、公交卡、門禁卡等IC卡使用。基本原理是將相應IC卡中的資訊憑證封裝成資料包儲存在支援NFC的外設中 。
在使用時還需要一個NFC射頻器(相當於刷卡器)。將手機靠近NFC射頻器,手機就會接收到NFC射頻器發過來的訊號,在透過一系列複雜的驗證後,將IC卡的相應資訊傳入NFC射頻器,最後這些IC卡資料會傳入NFC射頻器連線的電腦,並進行相應的處理(如電子轉帳、開門等操作)。
點對點模式
該模式與藍芽、紅外差不多,用於不同NFC裝置之間進行資料交換,不過這個模式已經沒有有“刷”的感覺了。其有效距離一般不能超過4釐米,但傳輸建立速度要比紅外和藍芽技術快很多,傳輸速度比紅外塊得多,如過雙方都使用Android4。2,NFC會直接利用藍芽傳輸。這種技術被稱為Android Beam。所以使用Android Beam傳輸資料的兩部裝置不再限於4釐米之內。
點對點模式的典型應用是兩部支援NFC的手機或平板電腦實現資料的點對點傳輸,例如,交換圖片或同步裝置聯絡人。因此,透過NFC,多個裝置如數字相機,計算機,手機之間,都可以快速連線,並交換資料或者服務。
本文會主要講解
讀卡器模式,
相信理解了這個模式,其他的兩種模式你也會觸類旁通的。在講解讀卡器模式前,有一些知識是需要提前掌握的。
卡片的知識
本文主要講解的是NFC的讀卡器模式,要想讀到卡片的內容,我們要對卡片有一些基本的瞭解,比如卡片的分類,每種卡片內部的資料結構等。
卡片的分類
現在市面上的卡片分類有IC卡、ID卡、M1卡和CPU卡,簡單的瞭解一下這些卡的區別和用途,
IC卡
IC卡又稱積體電路卡,通常是在塑膠卡片內嵌入一個或多個積體電路構成的PVC卡。積體電路晶片可以是儲存器或微處理器。帶有儲存器的IC卡又稱為記憶卡或儲存卡,帶有微處理器的IC卡又稱為智慧卡或智慧卡。記憶卡可以儲存大量資訊;智慧卡則不僅具有記憶能力,而且還具有處理資訊的功能。
IC卡可以十分方便地存汽車費、電話費、地鐵乘車費、食堂就餐費、公路付費以及購物旅遊、貿易服務等。
ID卡
ID卡又叫身份識別卡,是一種不可寫入的感應式卡,擁有一個固定卡號編號。卡號在封卡前寫入後不可再更改,絕對確保卡號的唯一性和安全性。
ID卡可以作為一般的門禁或停車場系統的使用者身份識別,因ID卡無金鑰安全認證機制,且不能寫卡,很難實現一卡通功能,同時也不合適做消費系統。
M1卡
M1是菲利浦下屬子公司恩智浦出品的晶片縮寫,目前該公司的M1晶片與國產晶片相相容,
其實M1卡也屬於非接觸式IC卡
。
M1卡,優點是可讀可寫的多功能卡,缺點是:價格稍貴,感應距離短,適合非定額消費系統、停車場系統、門禁考勤系統等。
CPU卡
CPU卡晶片是一個微處理器,它的功能相當於一臺微型計算機。CPU卡可適用於金融、保險、交警、政府行業等多個領域,CPU卡的優點是儲存空間大、讀取速度快、支援一卡多用功能等特點,CPU卡從外型上與普通IC卡,射頻卡並沒有太大差異,但是效能上卻有巨大提升,安全性和普通IC卡比,提高很多,通常CPU卡內含有隨機數發生器,硬體DES,3DES加密演算法等,配合CPU卡晶片上的COS作業系統,可以達到金融級的安全級別。
M1卡的資料結構
為什麼要介紹卡結構呢?因為用NFC讀取卡片,獲取卡片的內容的時候,你要知道卡片的資料結構,才能拿到自己想要的知識,這裡就以M1卡進行講解。
M1卡有從0到15共16個扇區,每個扇區配備了從0到3共4個段,每個段可以儲存16位元組的內容。見下圖
要想讀取對應扇區的資料,需要知道對應扇區的秘鑰,否則讀取不到資料。
Android的NFC標籤排程系統
當手機發現外部NFC的標籤(指含有NFC功能的裝置)時,Android系統會尋找可以處理這個標籤的Activity,那怎麼知道哪個Activity能處理這條NFC訊息呢?答案是清單檔案,我們需要在清單檔案中設定
intent-filter
。系統會分發NFC訊息到設定
intent-filter
的Activity中,當然,接收NFC訊息也有優先順序之分,也是透過設定
intent-filter
來設定接收NFC訊息的優先順序的。
NFC的標籤排程系統綁定了3中intent,按優先順序的高低列出,如下
ACTION_NDEF_DISCOVERED
:如果掃描到包含此Intent的Activity,並且可識別其型別,則使用此 Intent 啟動 Activity。這是優先順序最高的 Intent,NFC標籤排程系統會盡可能嘗試使用此 Intent 啟動 Activity,在找不到這個Intent時才會嘗試使用其他 Intent。
ACTION_TECH_DISCOVERED
:如果沒有登記要處理
ACTION_NDEF_DISCOVERED
Intent 的 Activity,則標籤排程系統會嘗試使用此 Intent 來啟動應用。此外,如果掃描到的標籤包含無法對映到 MIME 型別或 URI 的 NDEF 資料,或者該標籤不包含 NDEF 資料,但它使用了已知的標籤技術,那麼也會直接啟動此 Intent(無需先啟動
ACTION_NDEF_DISCOVERED
)。
ACTION_TAG_DISCOVERED
:如果沒有處理
ACTION_NDEF_DISCOVERED
或者
ACTION_TECH_DISCOVERED
Intent 的 Activity,則使用此 Intent 啟動 Activity。
它們3者關係如下圖所示
如果想處理所有的NFC標籤,上面3個可以在清單檔案中都進行設定。
實戰演練
前文我們已經知道了什麼是NFC以及NFC的幾種工作模式,也瞭解了市面上卡片的分類和M1卡的資料結構,基礎知識已經掌握了,下面就開始進入實戰演練,用Android的NFC來獲取M1卡的唯一程式碼。
在 Android 清單中請求 NFC 訪問許可權
先在
AndroidManifest。xml
檔案中宣告以下內容,然後才能訪問裝置的 NFC 硬體並正確處理 NFC Intent:
設定用於訪問 NFC 硬體的 NFC
android:name= “android。permission。NFC” /> 設定 uses-feature 元素,以便您的應用僅在那些具備 NFC 硬體的裝置的 Google Play 中顯示 xml 如果你的App不是必須要NFC功能,則 uses-feature 可以省略,然後再程式碼中透過 getDefaultAdapter() 是否為 null 來判斷 NFC 的可用性。 設定過濾 NFC Intent 以下示例展示瞭如何過濾 MIME 型別為 text/plain 的 ACTION_NDEF_DISCOVERED Intent: android:name= “android。nfc。action。NDEF_DISCOVERED” /> android:name= “android。intent。category。DEFAULT” /> android:mimeType= “text/plain” /> 另外兩種 intent-filter 的使用,可以查閱官方文件。 使用前臺排程系統 前臺排程系統的作用就是,在你開啟一個可以處理NFC標籤的Activity時,NFC的訊息會優先發送給當前的Activity,不論當前的Activity設定的是哪一個 intent-filter 。使用前臺排程系統的步驟如下 在 Activity 的 onCreate() 方法中新增以下程式碼: a。 建立一個 PendingIntent 物件。 val intent = Intent ( this , javaClass )。 apply { addFlags ( Intent 。 FLAG_ACTIVITY_SINGLE_TOP ) } var pendingIntent : PendingIntent = PendingIntent 。 getActivity ( this , 0 , intent , 0 ) b。宣告 Intent 過濾器,用來處理您要攔截的 Intent。前臺排程系統會對照裝置掃描標籤時所獲得的 Intent 來檢查所指定的 Intent 過濾器。如果匹配,那麼應用會處理該 Intent。如果不匹配,那麼前臺排程系統會回退到 Intent 排程系統。指定 Intent 過濾器和技術過濾器的 null 陣列,以指明要過濾所有回退到 TAG_DISCOVERED Intent 的標籤。以下程式碼段會處理 NDEF_DISCOVERED 的所有 MIME 型別。 val ndef = IntentFilter ( NfcAdapter 。 ACTION_NDEF_DISCOVERED )。 apply { try { addDataType ( “/” ) / 處理所有 MIME 型別的標籤 / } catch ( e : IntentFilter 。 MalformedMimeTypeException ) { throw RuntimeException ( “fail” , e ) } } intentFiltersArray = arrayOf ( ndef ) c。設定應用要處理的一組標籤技術。呼叫 Object。class。getName() 方法以獲取要支援的技術的類。 techListsArray = arrayOf ( arrayOf < String >( NfcF :: class 。 java 。 name )) 替換以下 Activity 生命週期回撥,並新增相應邏輯,以分別在 Activity 失去 ( onPause() ) 焦點和重新獲得 ( onResume() ) 焦點時啟用和停用前臺排程。 enableForegroundDispatch() 必須從主執行緒呼叫,並且只能在 Activity 在前臺執行時呼叫(在 onResume() 中呼叫可確保這一點)。您還需要實現 onNewIntent 回撥以處理掃描到的 NFC 標籤中的資料。 public override fun onPause () { super 。 onPause () adapter 。 disableForegroundDispatch ( this ) } public override fun onResume () { super 。 onResume () adapter 。 enableForegroundDispatch ( this , pendingIntent , intentFiltersArray , techListsArray ) } public override fun onNewIntent ( intent : Intent ) { val tagFromIntent : Tag = intent 。 getParcelableExtra ( NfcAdapter 。 EXTRA_TAG ) //在這裡處理NFC掃描到的內容 } 程式碼示例 下面展示核心程式碼,讀取M1卡並解析卡號,程式碼如下 //解析實體卡號 fun resolveCardNumIntent ( intent : Intent ?): String { if ( intent == null ) { return “” } //拿來裝讀取出來的資料,key代表扇區數,後面list存放四個塊的內容 //intent就是onNewIntent方法返回的那個intent val tag : Tag = intent 。 getParcelableExtra ( NfcAdapter 。 EXTRA_TAG ) val mfc = MifareClassic 。 get ( tag ) //如果當前IC卡不是這個格式的mfc就會為空 if ( null != mfc ) { try { if (! mfc 。 isConnected ) { //連結NFC mfc 。 connect () } //驗證扇區密碼,否則會報錯(連結失敗錯誤) val isOpen = mfc 。 authenticateSectorWithKeyA ( 0 , CARD_KEY_15 ) if ( isOpen ) { //獲取扇區第一個塊對應晶片儲存器的位置 //(我是這樣理解的,因為第0扇區的這個值是4而不是0) val bIndex = mfc 。 sectorToBlock ( 0 ) val data = mfc 。 readBlock ( bIndex ) val byteArrToInt = DataUtils 。 byteArrToInt ( data , 0 , 4 ) // LogUtils。print(“卡號: $byteArrToInt”) return byteArrToInt } } catch ( e : Exception ) { e 。 printStackTrace () return “” } finally { try { mfc 。 close () } catch ( e : IOException ) { e 。 printStackTrace () } } } return “” } 上面的程式碼註釋很清晰,重點看下這段程式碼 val tag : Tag = intent 。 getParcelableExtra ( NfcAdapter 。 EXTRA_TAG ) val mfc = MifareClassic 。 get ( tag ) //用於解析MifareClassic的例項 這裡的Tag是獲取卡片的Tag,NFC支援的Tag如下表 表 1. 支援的標籤技術 類 說明 TagTechnology 這是所有標籤技術類都必須實現的介面。 NfcA 提供對 NFC-A (ISO 14443-3A) 屬性和 I/O 操作的訪問許可權。 NfcB 提供對 NFC-B (ISO 14443-3B) 屬性和 I/O 操作的訪問許可權。 NfcF 提供對 NFC-F (JIS 6319-4) 屬性和 I/O 操作的訪問許可權。 NfcV 提供對 NFC-V (ISO 15693) 屬性和 I/O 操作的訪問許可權。 IsoDepc 提供對 ISO-DEP (ISO 14443-4) 屬性和 I/O 操作的訪問許可權。 Ndef 提供對 NDEF 格式的 NFC 標籤上的 NDEF 資料和操作的訪問許可權。 NdefFormatable 為可設定為 NDEF 格式的標籤提供格式化操作。 Android 裝置還可以選擇支援以下標籤技術。 表 2. 可選擇支援的標籤技術 類 說明 MifareClassic 提供對 MIFARE Classic 屬性和 I/O 操作的訪問許可權(如果此 Android 裝置支援 MIFARE)。 MifareUltralight 提供對 MIFARE Ultralight 屬性和 I/O 操作的訪問許可權(如果此 Android 裝置支援 MIFARE)。 總結 本文首先介紹了NFC是什麼以及它的幾種工作模式,然後讓大家認識了一下市面上卡片的分類,也介紹了M1卡的資料結構,最後,演示了一下怎在Android中使用NFC來讀取M1卡的卡號。 雖然文章沒有把NFC的所有使用模式都進行講解,但是挑了一個模式進行詳細的講解,相信閱讀這篇文章後,你對Android的NFC不會那麼的陌生了,其他的NFC的工作模式就由大家自行了解學習,這樣才能加深印象和理解。 最後放出本文的demo,大家可以點選這裡下載 本文已由公眾號“愛碼者說”首發