Android - 學習操作NFC - 1

Android - 學習操作NFC - 1

在研讀了WP8 – Proximity APIs之後撰寫了幾篇文章,深深覺得NFC非常有趣,進一步希望了解

Android在這一塊的處理方式,因此,補上了該篇介紹一下自己在學習Android處理NFC時的心得。

 

〉概要

     在Android系統下,目前主要以支持NDEF NFC Data Exchange Format (NDEF)格式為主,它支持devices之間

或device與tag之間互相交換小型payload的資料。由於是屬於近距離的無線技術,典型是在4 cm或更接近時建立連線。

單一個tag就具有多種交易的方式,包括:read-only、加/解密、多種型式格式…等。但如果遇到是非預設支援的NDEF

在Android仍有自訂相關處理方式,往後進一步說明。

 

需注意的是:

     ‧Android 2.3.3 (API Level 10)才有支援完整的NFC功能;

     ‧Android 4.x 支持 Android Application Record (AAR);

     ‧Android 4.0 支持 Beam;

 

在Android developers官網裡分別有二篇文章說明NFC的使用:<NFC Basics>與<Advanced NFC>。

本篇主要根據<NFC Basics>學習在Android上:

    ‧如何傳送與接收NDEF data message;

        來自NFC tag 、 Beaming NDEF Message由一個device到另一個;

    ‧操作NFC APIs、Android Beam™

 

NDFE data從NFC tag中取出,交由tag dispatch system進行處理,包括:分析發現的NFC tags、將資料進行分類,

並且根據資料啟動應用程式。一個應用程式想要處理被掃瞄到的NFC tag,可以宣告intent filter與請求要處理的data

 

Android Beam™功能允許一個device可發佈NDEF message至另一個device,透過二個devices直接tapping。這樣的方式

比過去使用Bluetooth、Wi-Fi容易許多,因為不需要在配對或連線驗證,只需二個devices接近至一定距離則自動觸發。

Android Beam是經由NFC APIs所提供的功能,任何應用程式均能使用它在二個device之間傳送資料,例如:分享聯絡人、

web pages、videos…等。

 

有了一些Android在處理NFC tag與APIs的概論之後,往下進一步討論當取得NFC tag中的資料時,要如何識別它是那種類型,

以及具有那些特殊的識別標籤與儲存的方式。

 

 

〉The Tag Dispatch System

     在Android系統中,如果有開啟NFC的功能,當設備螢幕鎖定時,NFC功能會被取消,直到設備被喚醒後,NFC功能才會啟動。

達到省電的效果。當Android-powered device發現一個NFC tag,最好的作法是自動啟動對應的應用程式,而不是詢問用戶選擇

為什麼會這樣說呢?因為設備掃瞄NFC tags在非常近的距離,尤其是在背靠背時,要用戶移動手機回到前面選擇要啟動的程式

有可能因為移動Tags或Devices離開了連線,非常不方便。因此,你應該開發讓你的Activity只處理您在乎的NFC tags,以防止出現

Activity Chooser要用戶選擇。

 

為了達到上述的目標,Android提供了tag dispatch system協助分析發現的NFC tags、剖析它們,並嘗式指定啟動特定應用程式。

主要做的事情,如下:

(1) 拆解NFC tag與搞清除是MIME type或URI的資料payload儲存於tag中;

(2) 封裝MIME type或URI的資料payload至intent object中;

(3) 基於建立好的intent object啟動activity;

 

這三件事也就是Android處理NFC tags/devices之間的重要流程,往下逐一說明(1)與(2),加上(3)的各自處理方式。

 

 

〉How NFC tags are mapped to MIME types and URIs

     在開發NFC application之前一定要先了解有多少種NFC tag類型、tag dispatch system如何剖析它們,以及當讀取到一個

NDFE message時要執行那些動作。相對的,如果要寫入NDEF message至devices或tag也有很多種方式要特別注意。

然而,Android支持大部分NDEF標準,而這些NDEF標準由NFC Forum所定義,相關的文獻如下:

<NFC Forum Specification Download>完整說明NFC的tag規格與<Creating common types of NDEF records>說明建立NDEF record。

 

‧NFC tag結構

    NFC tag中的NDEF資料被封裝於NFC message裡,其NFC message內可有1~N個records(NdefRecord),每一個NDEF record必需是

符合標準定義所建立而成。Android支持其他類型的tags,它們可能不包含NDEF data,如果要操作它可以使用android.nfc.tech

下的類別進行處理,相關的資訊可參考<Advanced NFC>。

 

有了基本NDEF tags的背景後,由於當發現一個NFC tag包含NDEF Formatted data,它將開始剖析並識別是否為MIME type或URI。

做剖析與識別,需從NDEF message中的第一個NdefRecord中,識別它為何種資料類型,往下了解NDEF record的定義:

 

a. the First record (NdefRecord)

     由四個資訊組合:

1). 3-bit TNF (Type Name Format)

      說明如何解釋Type欄位 (Variable length type)的格式。透過下表說明the tag dispatch system如何透過TNF與type field

      來識別該NDEF message是MIME type或URI。可以被配對到的NDEF message,系統會觸發ACTION_NDEF_DISCOVERED,

      如果無法配對,系統會再倒給ACTION_TECH_DISCOVERED進行配對,如果沒有再退給ACTION_TAG_DISCOVERED。

 

2). Variable length type

      描述該record的類型。如果是TNF_WELL_KNOWN,使用該欄位需要參考Record Type Definition (RTD)。

 

3). Variable length ID

      該record的unique identifier。該欄位經常不使用,但如果需要唯一識別該tag,可以建立一個ID給他。

 

4). Variable length payload

      實際想要讀或寫的資料內容(data payload)。一個NDEF message可包括多個NDEF records,所以不要以為所有的資料

     均寫在NDEF message的第一個record。

 

the tag dispatch system透過TNF與type field嘗試去配對NDEF message符合MIME type或URI。如果配對成功,系統會封裝

這些資訊至ACTION_NDEF_DISCOVERED intent,以及包括實際的data payload。然而,如何遇到無法配對或是NFC tag不包含

NDEF data造成無法配對,系統會再倒給ACTION_TECH_DISCOVERED進行配對,如果沒有再退給ACTION_TAG_DISCOVERED。

 

以下來了解TNF具有那些類型,以及當TNF為TNF_WELL_KNOWN時搭配RTD有那些:

Type Name Format (TNF) Mapping
TNF_ABSOLUTE_URI Type欄位資料格式為:URI。
TNF_EMPTY Android系統觸發ACTION_TECH_DISCOVERED的方式處理。
TNF_EXTERNAL_TYPE Type欄位資料格式為:URN類型的URI。 URN被編碼放入NDEF type欄位,符合一個字段格式:<domain name>:<service name>
Android系統轉譯成:vnd.android.nfc://ext/<domain name>:<service name>
TNF_MIME_MEDIA Type欄位資料格式為:MIME。
TNF_UNCHANGED 第一個record為無效的,Android觸發ACTION_TECH_DISCOVERED的方式處理。
TNF_UNKNOWN Android觸發ACTION_TECH_DISCOVERED的方式處理。
TNF_WELL_KNOWN
設定在type field中的MIME type或URI,是取決於Record Type Definition (RTD)。

      如果TNF值為:TNF_WELL_KNOWN,則會相關RTD的格式可再透過下表來看:

Record Type Definition (RTD) Mapping
RTD_ALTERNATIVE_CARRIER Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_CARRIER Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_REQUEST Falls back to ACTION_TECH_DISCOVERED.
RTD_HANDOVER_SELECT Falls back to ACTION_TECH_DISCOVERED.
RTD_SMART_POSTER URI based on parsing the payload.
RTD_TEXT MIME type of text/plain.
RTD_URI URI based on payload.

 

了解了如何識別該NDEF data message 為類型之後,接著往下說明當識別之後要怎麼觸發對應的處理與呼叫應用程式。

 

〉How NFC Tags are Dispatched to Applications

     當tag dispatch system建立一個intent裡面封裝了NFC tag與識別資訊,它將傳送給filters該intent的應用程式去處理。

如果有一個以上的應用程式處理該intent,Activity Chooser將呈現一個清單對話框讓用戶選擇要執行的Activity。

tag dispatch system定義了三種intents,根據優先權由高至低進行排序

(1).  ACTION_NDEF_DISCOVERED

         當tag內容包括一個NDEF payload與具有可識別的類型被掃瞄到,該intent被用來啟動一個Activity。

         這是最高的優先權,在往下交給其他的intent之前,tag dispatch system會嘗試去開啟並夾該intent啟動一個Activity。

 

(2).  ACTION_TECH_DISCOVERED

         如果沒有activities註冊處理ACTION_NDEF_DISCOVERED該intent,tag dispatch system會發啟一個ACTION_TECH_DISCOVER

         intent給對應的應用程式進行處理。

         tag dispatch system查覺該tag具有NDEF data但卻沒有對應至MIME type或URI,或是該tag沒有包括NDEF data但是一個

         TNF_WELL_KNOWN的識別,它將會發啟一個ACTION_TECH_DISCOVERED的intent (而不會啟動ACTION_NDEF_DISCOVERED)。

 

(3).  ACTION_TAG_DISCOVERED

         如果沒有activities註冊處理ACTION_NDEF_DISCOVERED、ACTION_TECH_DISCOVERED的intents,將會發啟該intent。

 

其本的tag dispatch system處理流程如下圖:

1. 當處理到一個NFC tag時,tag dispatch system發出ACTION_NDEF_DISCOVERED或ACTION_TECH_DISCOVERED任一種,

    嘗試去啟動一個應用程式來處理該intent。

2. 如果沒有應用程式filter這個intent,嘗試再發出比較低順序的ACTION_TECH_DISCOVERED或ACTION_TAG_DISCOVERED,

    直到有應用程式來處理該intent或tag dispatch system嘗試所有可能的intents。

3. 如果沒有任何應用程式filter任何的intents,則什麼都不做。

 

 

以上介紹了Android在處理NFC Tag的機制,主要透過tag dispatch system來進行事件的觸發與流程,往下將撰寫範例程式

進行說明:

 

〉範例說明

(1). 在AppManifest.xml中增加必要的uses-permission、最低的SDK版本、設定uses-feature

     ‧指定uses-permission:

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

 

      ‧指定最低的SDK版本:

<uses-sdk android:minSdkVersion="10"/>

API Level 9雖也有支援NFC,但只限於支持ACTION_TAG_DISCOVERED與透過EXTRA_NDEF_MESSAGES取得NDEF Message。

由於完整的NFC功能從Android 2.3.3 (API Level 10)才開始支援,所以要記得宣告使用的最低SDK版本,因為API Level 10支援

Reader/Writer的功能;另外,API Level 14提供了更簡單透過Android Beam進行Push NDEF Message至另一個設備,以及更方

便的方法建立NDEF records。

 

       ‧指定uses-feature:

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

指定該應用程式必需具有NFC晶片才能安裝,如果設定android:required = true代表Google Play會自動過濾掉沒有支持NFC晶片的設備

這樣一來,具有這些設備的用戶將搜尋不到該應用程式。如果您的應用程式非以NFC為主要功能,可不設定該uses-feature或是將值

設定為false,改由程式中呼叫getDefaultAdapter()方法判斷是否為null進行功能的限制。

 

 

(2). 設定Filtering for NFC Intents

        為了讓應用程式可以處理NFC Tag被掃瞄後所發出的Intents,應用程式需要在AppManifest.xml註冊多個Filter來取得NFCT intents。

 

‧註冊ACTION_NDEF_DISCOVERED:處理大部分NDEF Message;

‧註冊ACTION_TECH_DISCOVERED:處理當沒有應用程式處理ACTION_NDEF_DISCOVERED或該Tag非NDEF message所退回的intent;

‧註冊ACTION_TAG_DISCOVERED:處理通常過於籠統的類別進行篩選。也就是上述二個intents除外的intent;

 

通常應用程式優先處理ACTION_NDEF_DISCOVERED、ACTION_TECH_DISCOVERED,除非這二個無法處理,最後才會輪到

ACTION_TAG_DISCOVERED。也因為ACTION_TAG_DISCOVERED可能偵測到的NFC Tag是比較特殊的格式,可以搭配<Advanced NFC>來做參考。

 

[注意]

由於NFC Tag部署有所不同,很多時候不是應用程式或系統所能控制之下,這並非不可能的,這就是為什麼必要時tag dispatch system

可以回退到其他兩個intents。當取得NFC Tag是有辦法識別的類型,且具有數據寫入的控制權,建議您使用的NDEF格式化標籤。

 

(2-1). 註冊filter ACTION_NDEF_DISCOVERED

            為了filter ACTION_NDEF_DISCOVERED intents,宣告intent filter要處理的資料類型。

以下例子使用處理MIME Type為text/plain:

<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;

註冊處理為http的scheme,developer.android.com的host,以及固定第一個字段為:index.html。

<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-2). 註冊filter ACTION_TECH_DISCOVERED

          如果要註冊filter ACTION_TECH_DISCOVERED,需要在專案中增加一個新的XML檔案,定義該應用程式支持的 tech-list sets

可以透過呼叫getTechList()來確認偵測到的NFC tag是否與定義的tech-list sets有所匹配。該檔案建議放罝於<project-root>/res/xml目錄下

舉例來說:

如果一個tag被偵測到,它支持三種標準:MifareClassic、NdefFormatable與NfcA,在定義的tech-list sets就需要加入這些標準,

可能是一個、二個或三個標準都支持,以確保可以處理該intent。以下列出定義常見的tech-list集合:

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


另外,也可以定義多組tech-list sets。每一個tech-list set是獨立的,如果任何一組tech-list set被匹配到,activity將會被啟動,

該activity也可以透過getTechList()來確認偵測到的NFC tag是否與定義的tech-list sets有所匹配。它提供了 AND 與OR的定義匹配技術

透過下列範例,說明支持NdefA與NdefB:

<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-list>
</resources>
 
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.Ndef</tech>
    </tech-list>
</resources>

 

定義好了resource,可以在AppManifest.xml中指定activity透過<meta-data />定義要使用那一個resource。如下:

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

更多詳細的資料可以參考<Working with Supported Tag Technologies>。

 

 

(2-3). 註冊ACTION_TAG_DISCOVERED

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

 

 

(3). 識別收到的NFC intent物件具有那些資訊

       上述介紹了應用程式要註冊的intent filter後,接著要處理是當應用程式補捉到filter所要的intent物件後,該怎麼取得我們要的資訊。

一個NFC intent以下的資訊:

EXTRA_TAG:(必要),一個Tag物件代表掃瞄到的tag;

EXTRA_NDEF_MESSAGES:(選擇),代表tag中所存在的NDEF Message陣列;

{@link android.nfc.NfcAdapter#EXTRA_ID:(選擇),代表一個tag的low-level ID;

 

如果您的應用程式被一個NFC tag被掃瞄到且建立了NFC intent所啟動,那該應用程式可以取得到上述三個資訊,

以ACTION_NDEF_DISCOVERED為例:

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 < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        }
    }
    //process the msgs array
}

至於要取得Tag物件的話,可用下列方式:

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

======

以上是介紹了Android在偵測NFC tag的機制,包括如果註冊intent filter讓應用程式可以取得系統所廣播出來的intent,

進一步接收NDEF message intent。往下一篇,針對本篇所介紹的概念,進行對NFC tag的實作包括Reader/Writer。

 

References

NFC Demo - Android sample code

Near Field Communication (重要)

NFC Basics & Advanced NFC

Android NFC 开发教程(1):概述

Android NFC 开发教程(2): ApiDemos->NFC->ForegoundDispatch

Android NFC 開發教程(3): Mifare Tag 讀寫示例

NFC Programming in Android

[Android] 簡單範例: NFC Push

Developer Document & NXP TagWriter & StickyNotes sample code

[Android]NFC能做什麼?阿達流NFC基本教學告訴你

 

Dotblogs Tags: