主機型卡片模擬總覽

許多提供 NFC 功能的 Android 裝置已支援 NFC 卡模擬。在多數情況下,系統會透過裝置上的獨立方塊模擬資訊卡,稱為「安全元素」。許多無線電信業者提供的 SIM 卡也含有安全元件。

Android 4.4 以上版本提供另一種不涉及安全元素的卡片模擬方法,稱為「主機卡片模擬」。這可讓任何 Android 應用程式模擬卡片,並直接與 NFC 讀取器進行通訊。本主題說明主機卡片模擬 (HCE) 在 Android 上的運作方式,以及如何利用這套技巧開發模擬 NFC 卡的應用程式。

使用安全元素的卡片模擬

使用安全元件提供 NFC 卡片模擬時,系統會透過 Android 應用程式,將要模擬的卡片佈建到裝置上的安全元件中。然後,當使用者透過 NFC 終端機持有裝置時,裝置中的 NFC 控制器會將所有資料直接傳送至安全元素。圖 1 說明這個概念:

NFC 讀卡機經過 NFC 控制器,從安全元素擷取資訊的圖表
圖1. 具有安全元素的 NFC 卡片模擬。

安全元素本身會與 NFC 終端機通訊,且沒有任何 Android 應用程式參與交易。交易完成後,Android 應用程式可以直接查詢安全元素是否有交易狀態,並通知使用者。

主機型卡片模擬

使用主機型卡片模擬模擬 NFC 卡時,資料會直接轉送到主機 CPU,而不會轉送至安全元素。圖 2 說明主機卡片模擬的運作方式:

NFC 讀卡機經過 NFC 控制器並從 CPU 擷取資訊的圖表
圖2. 沒有安全元件的 NFC 卡片模擬。

支援的 NFC 卡和通訊協定

顯示 HCE 通訊協定堆疊的圖表
圖3.Android 的 HCE 通訊協定堆疊。

NFC 標準支援多種不同的通訊協定,還有不同類型的卡片可模擬。

Android 4.4 以上版本支援市面上常見的幾種通訊協定。許多現有的感應式卡片均已根據這些通訊協定,例如感應支付卡片。現今市面上有許多 NFC 讀取器都支援這些通訊協定,包括以讀取器運作的 Android NFC 裝置 (請參閱 IsoDep 類別)。這可讓您僅使用 Android 裝置,建構及部署 HCE 附近的端對端 NFC 解決方案。

具體來說,Android 4.4 以上版本支援根據 NFC-Forum ISO-DEP 規格 (依據 ISO/IEC 14443-4) 的模擬卡片,並依照 ISO/IEC 7816-4 規範中定義的應用程式通訊協定資料單位 (APDU) 處理。Android 規定只會在 Nfc-A (ISO/IEC 14443-3 Type A) 技術上模擬 ISO-DEP。您可以選擇支援 Nfc-B (ISO/IEC 14443-4 Type B) 技術。圖 3 說明所有規格的分層。

HCE 服務

Android 的 HCE 架構是以 Android Service 元件 (稱為 HCE 服務) 為基礎。服務的主要優勢之一,在於服務可以在無使用者介面的情況下在背景執行。許多 HCE 應用程式 (例如會員卡或大眾運輸票證) 都很自然,因此使用者不需要啟動應用程式即可使用。如果裝置尚未執行,並在背景執行交易,只要將裝置貼近 NFC 讀卡機即可啟動正確的服務。當然,您也可以視情況從服務啟動其他 UI (例如使用者通知)。

選取服務

使用者將裝置貼近 NFC 讀卡機時,Android 系統需要知道 NFC 讀卡機要與哪項 HCE 服務進行通訊。ISO/IEC 7816-4 規格定義了選取應用程式的方法,以應用程式 ID (AID) 為中心。AID 最多可包含 16 個位元組。如果要為現有的 NFC 讀取器基礎架構模擬卡片,這些讀者尋找的 AID 通常都是知名且公開註冊 (例如 Visa 和 MasterCard 等付款網路的 AID)。

如要為自己的應用程式部署新的讀取器基礎架構,您必須註冊自己的 AID。AID 的註冊程序依照 ISO/IEC 7816-5 規格所定義。如果您要為 Android 部署 HCE 應用程式,建議依 7816-5 註冊 AID,以免與其他應用程式發生衝突。

AID 群組

在某些情況下,HCE 服務可能需要註冊多個 AID,並設為所有 AID 的預設處理常式,才能實作特定應用程式。系統不支援群組內有部分 AID 傳送至其他服務的 AID。

多個相關的 AID 清單稱為 AID 群組。針對 AID 群組中的所有 AID,Android 會保證下列其中一種行為:

  • 群組中的所有 AID 都會轉送至這項 HCE 服務。
  • 群組中的任何 AID 都不會轉送至這項 HCE 服務 (例如,因為使用者偏好另一項服務,且該群組也要求了一或多個 AID)。

換句話說,狀態並不介於兩個狀態之間,群組中的某些 AID 可以轉送至一項 HCE 服務,有些可以轉送至另一個 HCE 服務。

AID 群組和類別

您可以將每個 AID 群組與類別建立關聯。如此一來,Android 就能依類別將 HCE 服務分組,讓使用者在類別層級 (而不是 AID 層級) 設定預設值。請避免在應用程式的任何面向使用者部分提及 AID,因為這類 ID 對一般使用者來說不會有任何意義。

Android 4.4 以上版本支援兩種類別:

實作 HCE 服務

如要使用主機卡模擬模擬 NFC 卡,您必須建立處理 NFC 交易的 Service 元件。

檢查 HCE 支援情形

應用程式可透過檢查 FEATURE_NFC_HOST_CARD_EMULATION 功能,確認裝置是否支援 HCE。在應用程式資訊清單中使用 <uses-feature> 標記,宣告應用程式使用 HCE 功能,以及應用程式是否必須正常運作。

服務導入

Android 4.4 以上版本提供便利的 Service 類別,可用於實作 HCE 服務:HostApduService 類別。

第一步是擴充 HostApduService,如以下程式碼範例所示:

Kotlin

class MyHostApduService : HostApduService() {

    override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle?): ByteArray {
       ...
    }

    override fun onDeactivated(reason: Int) {
       ...
    }
}

Java

public class MyHostApduService extends HostApduService {
    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
       ...
    }
    @Override
    public void onDeactivated(int reason) {
       ...
    }
}

HostApduService 會宣告兩個必須覆寫及實作的抽象方法。每當 NFC 讀取器傳送應用程式通訊協定資料單元 (APDU) 至您的服務時,系統就會呼叫其中一個 processCommandApdu()。APDU 符合 ISO/IEC 7816-4 規格的定義。APDU 是在 NFC 讀取器和 HCE 服務之間交換的應用程式層級封包。應用程式層級的通訊協定是半雙工:NFC 讀取器會傳送 APDU 指令給您,並等候您傳送回應 APDU。

如前所述,Android 會使用 AID 判斷讀取器想要通訊的 HCE 服務。通常 NFC 讀取器傳送給裝置的第一個 APDU 是 SELECT AID APDU,這個 APDU 包含讀取器要連線的 AID。Android 會從 APDU 擷取該 AID,將其解析為 HCE 服務,然後將 APDU 轉送至已解析的服務。

如要傳送回應 APDU,請從 processCommandApdu() 傳回回應 APDU 的位元組。請注意,這個方法會在應用程式的主要執行緒上呼叫,請不要封鎖該執行緒。如果您無法立即計算及傳回回應 APDU,請回傳空值。接著,您可以在其他執行緒上執行必要作業,並使用 HostApduService 類別中定義的 sendResponseApdu() 方法,在完成後傳送回應。

在發生下列任一情況前,Android 會持續將新的 APDU 轉送到您的服務:

  • NFC 讀取器傳送另一個 SELECT AID APDU,該 OS 會解析至其他服務。
  • NFC 讀卡機與裝置之間的 NFC 連結已損毀。

在這兩種情況下,系統會使用引數呼叫類別的 onDeactivated() 實作,指出兩者的是哪一個。

如果您使用的是現有的讀取器基礎架構,則必須實作 HCE 服務中讀取者預期的現有應用程式層級通訊協定。

如果您打算部署同樣由您控管的新讀者基礎架構,可以定義自己的通訊協定和 APDU 序列。請限制 APDU 數量和要交換的資料大小,這可確保使用者僅在短時間內將裝置貼近 NFC 讀卡機。合理的上限約為 1 KB 的資料,通常可在 300 毫秒內交換。

服務資訊清單宣告和 AID 註冊

您必須照常在資訊清單中宣告服務,但您也必須在服務宣告中加入一些其他內容:

  1. 如要告知平台這是實作 HostApduService 介面的 HCE 服務,請在服務宣告中新增 SERVICE_INTERFACE 動作的意圖篩選器。

  2. 如要告知平台要求哪些 AID 群組,請在服務宣告中加入 SERVICE_META_DATA <meta-data> 標記,該標記會指向 XML 資源,並提供 HCE 服務的相關額外資訊。

  3. android:exported 屬性設為 true,並在服務宣告中要求 android.permission.BIND_NFC_SERVICE 權限。前者可確保服務受外部應用程式繫結。後者會強制執行只有具備 android.permission.BIND_NFC_SERVICE 權限的外部應用程式可以繫結至您的服務。android.permission.BIND_NFC_SERVICE 是系統權限,因此這能有效強制執行只有 Android 作業系統可繫結至您的服務。

以下是 HostApduService 資訊清單宣告的範例:

<service android:name=".MyHostApduService" android:exported="true"
         android:permission="android.permission.BIND_NFC_SERVICE">
    <intent-filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
    </intent-filter>
    <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
               android:resource="@xml/apduservice"/>
</service>

這個中繼資料標記指向 apduservice.xml 檔案。以下範例檔案包含單一 AID 群組宣告,且包含兩個專屬 AID:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc"
           android:requireDeviceUnlock="false">
    <aid-group android:description="@string/aiddescription"
               android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

<host-apdu-service> 標記必須包含 <android:description> 屬性,其中提供使用者容易理解的服務說明,並會顯示在應用程式 UI 中。您可以使用 requireDeviceUnlock 屬性,指定在叫用此服務來處理 APDU 前,裝置已解鎖。

<host-apdu-service> 必須包含一或多個 <aid-group> 標記。每個 <aid-group> 標記都需要執行下列操作:

  • 包含 android:description 屬性,其中包含使用者容易理解的 AID 群組說明,適合在 UI 中顯示。
  • 設定 android:category 屬性來指出 AID 群組所屬的類別,例如 CATEGORY_PAYMENTCATEGORY_OTHER 定義的字串常數。
  • 包含一或多個 <aid-filter> 標記,每個標記都含有單一 AID。以 16 進位格式指定 AID,並確認 AID 包含偶數字元。

此外,應用程式也必須擁有 NFC 權限,才能註冊為 HCE 服務。

AID 衝突解決方案

單一裝置上可安裝多個 HostApduService 元件,且同一個 AID 可由多項服務註冊。Android 會根據 AID 所屬的類別,以不同方式解析 AID 衝突。每個類別可能有不同的衝突解決政策。

針對某些類別 (例如付款),使用者或許可以在 Android 設定 UI 中選取預設服務。至於其他類別,如果發生衝突,則政策可能一律要求使用者叫用哪項服務。如要瞭解如何查詢特定類別的衝突解決政策,請參閱 getSelectionModeForCategory()

檢查服務是否為預設服務

應用程式可以使用 isDefaultServiceForCategory() API,檢查其 HCE 服務是否為特定類別的預設服務。

如果服務並非預設服務,您可以使用 ACTION_CHANGE_DEFAULT 要求將其設為預設服務。

付款應用程式

Android 會認定 HCE 服務已宣告以付款類別為 AID 群組的 AID 群組,做為付款應用程式。Android 4.4 以上版本包含稱為「感應支付」的頂層「Settings」選單項目,會列舉所有這類付款應用程式。使用者可以透過這個設定選單,選取要在感應支付終端機時叫用的預設付款應用程式。

付款應用程式的必要資產

為了提供更引人入勝的使用者體驗,HCE 付款應用程式必須提供服務橫幅。

Android 13 以上版本

如要更符合「設定」使用者介面中的預設付款選項清單,請將橫幅規定調整為正方形圖示。在理想情況下,此元素應與應用程式啟動器圖示設計相同。這項調整可以提高一致性,並提供簡潔的外觀。

Android 12 以下版本

將服務橫幅的大小設為 260x96 dp,然後將 android:apduServiceBanner 屬性新增至 <host-apdu-service> 標記 (指向可繪製資源),在中繼資料 XML 檔案中設定服務橫幅的大小。範例如下:

<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:description="@string/servicedesc"
        android:requireDeviceUnlock="false"
        android:apduServiceBanner="@drawable/my_banner">
    <aid-group android:description="@string/aiddescription"
               android:category="payment">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</host-apdu-service>

螢幕關閉和螢幕鎖定行為

HCE 服務的行為會因裝置上執行的 Android 版本而異。

Android 12 以上版本

在指定 Android 12 (API 級別 31) 以上版本的應用程式中,您可以將 requireDeviceScreenOn 設為 false,即可在不開啟裝置螢幕的情況下啟用 NFC 付款。

Android 10 以上版本

搭載 Android 10 (API 級別 29) 以上版本的裝置支援安全 NFC。安全 NFC 開啟時,當裝置螢幕關閉時,所有卡片模擬器 (主機應用程式和外部主機應用程式) 都無法使用。安全 NFC 關閉時,則當裝置螢幕關閉時,即可使用非主機應用程式。您可以使用 isSecureNfcSupported() 檢查安全 NFC 支援情形。

在搭載 Android 10 以上版本的裝置上,與搭載 Android 9 以下版本的裝置相同,將 android:requireDeviceUnlock 設為 true 的功能同樣適用,但前提是必須關閉安全 NFC。也就是說,無論 android:requireDeviceUnlock 的設定為何,如果啟用了安全 NFC,HCE 服務都就無法從螢幕鎖定畫面運作。

Android 9 以下版本

在搭載 Android 9 (API 級別 28) 以下版本的裝置上,當裝置螢幕關閉時,NFC 控制器和應用程式處理器會完全關閉。因此,HCE 服務在螢幕關閉時就無法運作。

此外,Android 9 以下版本的 HCE 服務也能在螢幕鎖定時運作。不過,這是由 HCE 服務 <host-apdu-service> 標記中的 android:requireDeviceUnlock 屬性控制。根據預設,裝置不需要解鎖,即使裝置處於鎖定狀態,系統也會叫用服務。

如果將 HCE 服務的 android:requireDeviceUnlock 屬性設為 true,Android 會在以下情形時提示使用者解鎖裝置:

  • 使用者輕觸 NFC 讀卡機。
  • NFC 讀卡機選取已解析為服務的 AID。

解鎖後,Android 會顯示對話方塊,提示使用者再次輕觸以完成交易。這是必要的,因為使用者可能將裝置移出 NFC 讀卡機即可解鎖。

與安全元素資訊卡共存

如果開發人員部署的應用程式需要透過安全元素模擬卡片模擬,請參閱本節說明。Android 的 HCE 實作項目可以與其他實作卡片模擬的方法 (包括使用安全元素) 並行運作。

這種共存基礎是以 AID 轉送原則為基礎。NFC 控制器會保留由一份 (有限的) 轉送規則清單組成的路由表。每個轉送規則都包含一個 AID 和一個目的地。目的地可以是主機 CPU (執行 Android 應用程式) 或已連線的安全元素。

NFC 讀取器傳送具有 SELECT AID 的 APDU 時,NFC 控制器會剖析,並檢查 AID 是否與路由表中的任何 AID 相符。如果比對相符,則 APDU 和後續的所有 APDU 都會傳送至與 AID 相關聯的目的地,直到接收到另一個 SELECT AID APDU 或 NFC 連結損毀為止。

圖 4 說明這個架構:

透過 NFC 讀取器與安全元素和 CPU 通訊的圖表
圖4. 具備安全元素和主機卡片模擬機制的 Android 運作。

NFC 控制器通常也會包含 APDU 的預設路徑。如果在路徑表中找不到 AID,系統會使用預設路徑。雖然這項設定可能因裝置而異,但 Android 裝置需要確保應用程式所註冊的 AID 能正確轉送至主機。

若是實作 HCE 服務或使用安全元素的 Android 應用程式,您不必擔心會如何設定路由表;此功能是由 Android 自動處理。Android 只需要知道 HCE 服務能夠處理哪些 AID,以及哪些 AID 可由安全元素處理。系統會根據已安裝的服務以及使用者設為偏好選項,自動設定路由表格。

本節說明如何針對使用安全元素模擬卡片模擬的應用程式宣告 AID。

安全元素 AID 註冊

使用安全元素模擬卡片模擬的應用程式可在資訊清單中宣告外部代管服務。這類服務的宣告與 HCE 服務的宣告幾乎相同。例外狀況如下:

  • 意圖篩選器中使用的動作必須設為 SERVICE_INTERFACE
  • 中繼資料名稱屬性必須設為 SERVICE_META_DATA
  • 中繼資料 XML 檔案必須使用 <offhost-apdu-service> 根標記。

    <service android:name=".MyOffHostApduService" android:exported="true"
           android:permission="android.permission.BIND_NFC_SERVICE">
      <intent-filter>
          <action android:name="android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE"/>
      </intent-filter>
      <meta-data android:name="android.nfc.cardemulation.off_host_apdu_service"
                 android:resource="@xml/apduservice"/>
    </service>
    

以下是註冊兩個 AID 的對應 apduservice.xml 檔案範例:

<offhost-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc">
    <aid-group android:description="@string/subscription" android:category="other">
        <aid-filter android:name="F0010203040506"/>
        <aid-filter android:name="F0394148148100"/>
    </aid-group>
</offhost-apdu-service>

android:requireDeviceUnlock 屬性不適用於外部代管服務,因為主機 CPU 並未涉及交易,因此無法防止安全元素在裝置鎖定時執行交易。

如果是付款應用程式以外的代管服務,則需要 android:apduServiceBanner 屬性才能選取做為預設付款應用程式。

非代管服務叫用

Android 絕不會啟動或繫結至宣告為「外部主機」的服務,因為實際交易是由安全元素執行,而非 Android 服務。服務宣告只是允許應用程式註冊安全元素上的 AID。

HCE 和安全性

HCE 架構提供一項核心安全防護:您的服務受 BIND_NFC_SERVICE 系統權限保護,因此只有 OS 可以繫結至服務並與服務通訊。這可確保您收到的任何 APDU 都是 OS 從 NFC 控制器接收的 APDU,而且您傳送的任何 APDU 只會傳送至 OS,進而將 APDU 直接轉送到 NFC 控制器。

最後一個問題是,您將應用程式傳送至 NFC 讀取器的資料。這是刻意在 HCE 設計中分離的,不在乎資料的來源,只是確保資料安全地傳輸到 NFC 控制器,並傳送至 NFC 讀卡機。

舉例來說,如要安全地儲存及擷取您想從 HCE 服務傳送的資料,您可以仰賴 Android 應用程式沙箱,隔離您的應用程式資料。如要進一步瞭解 Android 安全性,請參閱安全性提示

通訊協定參數和詳細資料

本節旨在協助開發人員瞭解 HCE 裝置在 NFC 通訊協定的反衝突和啟用階段所使用的通訊協定參數。這可讓您建構與 Android HCE 裝置相容的讀取器基礎架構。

Nfc-A (ISO/IEC 14443 類型 A) 通訊協定反衝突與啟用

系統在啟用 Nfc-A 通訊協定時,會交換多個影格。

在交換的第一個部分,HCE 裝置會顯示其 UID;應假設 HCE 裝置擁有隨機 UID。這表示使用者每次輕觸時,向他們呈現的 UID 是隨機產生的 UID。因此,NFC 讀取器不應依賴 HCE 裝置的 UID 做為驗證或識別身分。

NFC 讀取器之後可以傳送 SEL_REQ 指令以選取 HCE 裝置。HCE 裝置的 SEL_RES 回應至少設定為第 6 位元 (0x20),表示裝置支援 ISO-DEP。請注意,SEL_RES 中的其他位元也可以設定,表示支援 NFC-DEP (p2p) 通訊協定的範例。由於可能有其他位元,因此想與 HCE 裝置互動的讀者只應明確檢查第 6 位元,而「不要」比較完整的 SEL_RES 和 0x20 的值。

啟用 ISO-DEP

啟用 Nfc-A 通訊協定之後,NFC 讀取器就會啟動 ISO-DEP 通訊協定啟用作業。它會傳送 RATS (請求答案) 指令。NFC 控制器會產生 RATS 回應,也就是 ATS;HCE 服務無法設定 ATS。然而,HCE 實作必須符合 ATS 回應的 NFC 論壇要求,因此 NFC 讀取器可以根據任何 HCE 裝置的 NFC 論壇要求來設定這些參數。

以下針對 HCE 裝置上 NFC 控制器提供的 ATS 回應個別位元組,提供更詳細的資訊:

  • TL:ATS 回應的長度。不得指出超過 20 個位元組的長度。
  • T0:所有 HCE 裝置都必須設定位元 5、6 和 7,表示 ATS 回應中包含 TA(1)、TB(1) 和 TC(1)。位元 1 到 4 表示 FSCI,編寫影格大小上限。在 HCE 裝置上,FSCI 的值必須介於 0 小時到 8 小時之間。
  • T(A)1:定義讀取器和模擬器之間的位元率,以及是否可不對稱。HCE 裝置沒有位元率或保證。
  • T(B)1:位元 1 到 4 表示啟動影格保護時間整數 (SFGI)。在 HCE 裝置上,SFGI 的值必須小於 8 小時。位元 5 到 8 表示影格等待時間整數 (FWI),並表示影格等待時間 (FWT)。在 HCE 裝置上,FWI 必須小於 8 小時。
  • T(C)1: bit 5 表示支援「進階通訊協定功能」。HCE 裝置不一定支援「進階通訊協定功能」。位元 2 表示支援 DID。HCE 裝置不一定支援 DID。位元 1 表示支援 NAD。HCE 裝置不得支援 NAD 並將位元 1 設為零。
  • 歷來位元組:HCE 裝置最多可傳回 15 過去的位元組。願意與 HCE 服務互動的 NFC 讀取器不應假設歷來位元組的內容或其狀態。

請注意,許多 HCE 裝置可能已符合 EMVCo 旗下付款網路在「無接觸通訊通訊協定」規格中指定的通訊協定需求。請特別注意以下幾點:

  • T0 中的 FSCI 必須介於 2 小時到 8 小時之間。
  • T(A)1 必須設為 0x80,表示僅支援 106 kbit/s 位元率,且不支援讀取器和模擬器之間的非對稱位元率。
  • T(B)1 中的 FWI 值必須小於 7 小時。

APDU 資料交換

如前文所述,HCE 實作僅支援單一邏輯管道。無法在 HCE 裝置上嘗試選取不同邏輯管道上的應用程式。