许多提供 NFC 功能的 Android 设备已经支持 NFC 卡模拟。在大多数情况下,该卡由 称为安全元件。无线运营商提供的多张 SIM 卡 还包含安全元件
Android 4.4 及更高版本还提供了另外一种卡模拟方法, 不涉及安全元件,这称为“基于主机的卡模拟”。这个 允许任何 Android 应用模拟卡并直接与 NFC 通信 读取器本主题介绍了基于主机的卡模拟 (HCE) Android 以及如何利用它开发模拟 NFC 卡的应用 分析法。
使用安全元件进行卡模拟
使用安全元件提供 NFC 卡模拟时,要 通过 Android 设备在设备上的安全元件中进行配置 应用。然后,当用户将设备靠近 NFC 终端时,NFC 设备控制器会将读取器的所有数据直接 元素。图 1 说明了这一概念:
安全元件本身与 NFC 终端进行通信,以及 交易不涉及任何 Android 应用。完成交易后 完成后,Android 应用可以直接查询安全元件以获取 交易状态并通知用户。
基于主机的卡模拟
使用基于主机的卡模拟来模拟 NFC 卡时,系统会以路由方式发送数据 而不是路由到安全元件。图 2 展示了基于主机的卡模拟的工作原理:
支持的 NFC 卡和协议
NFC 标准支持许多不同的协议 可以模拟的不同类型的卡片
Android 4.4 及更高版本支持多种市场中常见的协议
。许多现有的感应式卡已经基于这些协议,
例如感应式支付卡许多协议也都支持这些协议
目前市场上的 NFC 读取器,包括
读者本身(请参阅IsoDep
类)。这样,您就可以围绕
仅使用 Android 设备的 HCE。
具体来说,Android 4.4 及更高版本支持模拟 NFC-Forum ISO-DEP 规范(基于 ISO/IEC 14443-4)和流程 ISO/IEC 7816-4 中定义的应用协议数据单元 (APDU) 规范Android 要求仅在 Nfc-A 之上模拟 ISO-DEP (ISO/IEC 14443-3 Type A) 技术。支持 Nfc-B(ISO/IEC 14443-4 Type B) 这项技术是可选的图 3 显示了所有这些元素的分层 。
HCE 服务
Android 中的 HCE 架构以 Android 为基础
Service
组件(称为 HCE)
服务)。Service 的主要优势之一是,它可以在
并不需要任何界面这非常适合许多 HCE
如会员卡或公交卡等,但用户无需
启动要使用的应用在这种情况下,如果让设备触碰 NFC 读取器,
正确的服务(如果尚未运行)并执行事务
当然,您也可以启动其他界面(如
用户通知)。
服务选择
当用户将设备触碰 NFC 读取器时,Android 系统需要知道 NFC 读取器想要与之通信的 HCE 服务。ISO/IEC 7816-4 定义了一种选择应用的方式, 应用 ID (AID)。一个 AID 最多包含 16 个字节。如果您要模拟 现有 NFC 读取器基础设施的存储卡、这些读取器的 AID 通常都是众所周知并公开注册的(例如, Visa 和 MasterCard 等支付网络的 AID)。
如果您想为自己的应用部署新的读取器基础架构, 必须注册您自己的 AID。AID 的注册流程请参见 ISO/IEC 7816-5 规范。我们建议您按照 7816-5 条的规定注册 AID 如果您要为 Android 部署 HCE 应用,因为它可以避免冲突 与其他应用通信
AID 群组
在某些情况下,HCE 服务可能需要注册多个 AID 并设置为 所有 AID 的默认处理程序,以便实现特定的 应用。系统不支持将群组中的部分 AID 转移至其他服务。
一起保留的 AID 列表称为“AID 群组”。所有 AID AID 群组,Android 可保证实现以下某个目标:
- 群组中的所有 AID 都会路由到此 HCE 服务。
- 群组中的 AID 都不会发送到此 HCE 服务(例如,因为 用户更喜欢另一项服务,该服务要求在您的群组中提供一个或多个 AID )。
换言之,不存在由组内某些 AID 构成的中间状态。 需要路由到一个 HCE 服务,将一些路由到另一个 HCE 服务。
AID 群组和类别
您可以将每个 AID 群组与一个类别相关联。这样,Android 系统就可以 将 HCE 服务按类别整合在一起,而这反过来又允许用户设置 在类别一级(而非 AID 一级)设置默认值。避免提及 AID 因为它们没有任何意义 普通用户的体验
Android 4.4 及更高版本支持两类:
CATEGORY_PAYMENT
(涵盖符合业界标准的付款应用)CATEGORY_OTHER
(适用于所有其他 HCE 应用)
实现 HCE 服务
要使用基于主机的卡模拟来模拟 NFC 卡,您需要创建一个
用于处理 NFC 交易的 Service
组件。
检查是否支持 HCE
您的应用可以通过检查
FEATURE_NFC_HOST_CARD_EMULATION
功能。使用 <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
声明了两个必须替换的抽象方法,
实施。其中之一
processCommandApdu()
、
每当 NFC 读取器发送应用协议数据单元 (APDU) 时,系统都会调用
。APDU 在 ISO/IEC 7816-4 规范中定义。APDU
NFC 读取器和
HCE 服务。该应用级协议是半双工的:NFC 读取器
会向您发送命令 APDU,
return。
如前所述,Android 使用 AID 来确定将哪个 HCE 服务
希望与之对话的读者通常情况下,NFC 读取器发送至您的第一个 APDU。
设备是 SELECT AID
APDU;此 APDU 包含读取器所需的 AID
可以聊天。Android 从 APDU 中提取该 AID,并将其解析为 HCE
然后将该 APDU 转发给已解析的服务。
要发送响应 APDU,您可以从以下位置返回响应 APDU 的字节:
processCommandApdu()
。请注意,此方法是在主线程上调用的
因此不应屏蔽这类应用如果无法计算并返回
响应 APDU,则返回 null。然后,您可以在
另一个线程,然后使用
sendResponseApdu()
在 HostApduService
类中定义的方法之间发送响应,
完成。
Android 不断将读取器的新 APDU 转发到您的服务,直到出现 会发生以下情况:
- NFC 读取器发送另一个
SELECT AID
APDU,操作系统会将其解析为 不同的服务 - NFC 读取器和设备之间的 NFC 链接断开。
在这两种情况下,您类的
onDeactivated()
系统会使用参数来调用实现,该参数指明发生了哪种情况。
如果您使用现有的读取器基础架构,则必须实现 读取器希望您的 HCE 服务使用的现有应用级协议。
如果您还部署了由您控制的新读取器基础架构, 可以定义您自己的协议和 APDU 序列。尝试限制 APDU 的数量 和要交换的数据大小:这可确保您的用户 让设备靠近 NFC 读取器并保持一小段时间。答 合理的上限约为 1 KB,通常可以 在 300 毫秒内交换。
服务清单声明和 AID 注册
您必须像往常一样在清单中声明服务, 服务声明的其他部分:
告知平台这是实现
HostApduService
接口,请为SERVICE_INTERFACE
操作。如需告知平台此服务请求了哪些 AID 群组,请添加 一
SERVICE_META_DATA
服务声明中的<meta-data>
标记,指向 XML 资源,其中包含有关 HCE 服务的更多信息。将
android:exported
属性设置为true
,并要求使用android.permission.BIND_NFC_SERVICE
权限。 前者可确保外部应用可以绑定该服务。 然后,后者会强制只有持有android.permission.BIND_NFC_SERVICE
权限可以绑定到您的服务。 由于android.permission.BIND_NFC_SERVICE
是系统权限,因此 有效地强制要求只有 Android OS 才能绑定到您的服务。
以下是 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>
属性
其中包含简单易懂的服务说明
应用界面您可以使用 requireDeviceUnlock
属性来指定
在您调用此服务来处理 APDU 之前,设备已解锁。
<host-apdu-service>
必须包含一个或多个 <aid-group>
标记。每个
必须使用 <aid-group>
标记才能执行以下操作:
- 包含
android:description
属性,该属性包含方便使用的 AID 组的说明,适合在界面中显示。 - 设置其
android:category
属性,以指明 AID 的类别 组所属的名称,例如由CATEGORY_PAYMENT
定义的字符串常量 或CATEGORY_OTHER
。 - 包含一个或多个
<aid-filter>
标记,每个标记都包含一个 AID。 请以十六进制格式指定 AID,并确保其中包含偶数 字符个数。
您的应用还需要
注册为以下角色的 NFC
权限:
HCE 服务。
AID 冲突解决
一台设备上可以安装多个 HostApduService
组件,并且
相同的 AID 可以由多项服务注册。Android 解析 AID
具体取决于 AID 所属的类别。每个
可能有不同的冲突解决政策。
对于某些类别(如付款),用户或许可以选择一个默认类别
服务。对于其他类别,此政策可能是
在发生冲突时始终询问用户要调用哪项服务。相关信息
有关如何查询特定类别的冲突解决政策的信息,请参阅
getSelectionModeForCategory()
。
检查您的服务是否为默认服务
应用可以检查其 HCE 服务是否为
使用
isDefaultServiceForCategory()
API。
如果您的服务不是默认服务,您可以请求将其设置为默认服务
使用
ACTION_CHANGE_DEFAULT
。
付款应用
Android 会将已声明 AID 群组的 HCE 服务视为 payment 类别为付款应用。Android 4.4 及更高版本包含一个 顶级设置菜单项,名为点按和pay:枚举所有 此类付款应用。在此设置菜单中,用户可以选择 点按付款终端时要调用的默认付款应用。
付款应用必需的资源
为了提供更具视觉吸引力的用户体验,HCE 付款应用 必须提供服务横幅
Android 13 及更高版本
为了更好地契合设置界面中的默认付款选择列表,请调整 方形图标。理想情况下,它应该与 应用启动器图标设计。这种调整可使 更加简洁
Android 12 及更低版本
将服务横幅的尺寸设置为 260x96 dp,然后设置服务横幅的尺寸
添加到元数据 XML 文件中,方法是将 android:apduServiceBanner
属性添加到
<host-apdu-service>
标记,该标记指向可绘制资源。通过
以下是一个示例:
<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)及更高版本为目标平台的应用中,您可以启用 NFC
通过设置
requireDeviceScreenOn
至
false
。
Android 10 及更高版本
搭载 Android 10(API 级别 29)或更高版本的设备支持
安全
NFC。安全可靠
NFC 处于开启状态,所有卡模拟器(主机应用和脱离主机的应用)都
在设备屏幕关闭时不可用。当安全 NFC 关闭时,脱离主机
可以在设备屏幕关闭时使用应用。您可以查看
安全 NFC 支持:
isSecureNfcSupported()
。
在搭载 Android 10 及更高版本的设备上,用于设置
android:requireDeviceUnlock
至 true
会应用于设备
搭载 Android 9 及更低版本,但仅限安全 NFC 处于关闭状态时。也就是说,如果
安全 NFC 已开启,HCE 服务在锁定屏幕中无法运行
而不会考虑 android:requireDeviceUnlock
的设置。
Android 9 及更低版本
在搭载 Android 9(API 级别 28)及更低版本的设备上,NFC 控制器和 应用处理器完全关闭 设备已关闭。因此,HCE 服务在屏幕关闭时无法运行。
此外,在 Android 9 及更低版本上,HCE 服务可以从锁定屏幕运行。
不过,这由 android:requireDeviceUnlock
属性控制,
HCE 服务的 <host-apdu-service>
标记。默认情况下,设备解锁状态为
不需要,即使设备处于锁定状态,系统也会调用您的服务。
将 HCE 的 android:requireDeviceUnlock
属性设置为 true
服务,则 Android 会在出现以下情况时提示用户解锁设备
会发生以下情况:
- 用户点按 NFC 读取器。
- NFC 读取器会选择一个已解析到您的服务的 AID。
解锁后,Android 会显示一个对话框,提示用户再次点按 以便完成交易这是必要的操作,因为用户可能已将 以便解锁设备。
与安全元件卡共存
此部分面向已部署应用的开发者 依靠安全元件进行卡模拟。Android 的 HCE 实现 旨在与实现卡片的其他方法并行运行, 包括使用安全元件。
这种共存基于一项称为 AID 路由的原则。NFC 控制器会维护一个包含路由(有限)列表的路由表 规则。每个路由规则都包含一个 AID 和一个目的地。目标位置可以 是运行 Android 应用的主机 CPU, 元素。
当 NFC 读取器发送带有 SELECT AID
的 APDU 时,NFC 控制器会解析
并检查这些 AID 是否与其路由表中的任何 AID 相匹配。如果
匹配之后,系统会将该 APDU 及其后面的所有 APDU 发送到目的地
与 AID 相关联,直到收到另一个 SELECT AID
APDU 或 NFC
链接已损坏。
图 4 展示了此架构:
NFC 控制器通常还包含 APDU 的默认路由。当 在路由表中未找到 AID,则系统会使用默认路由。虽然 设置可能因设备而异,但必须使用 Android 设备 确保您的应用注册的 AID 能正确路由到 主机。
实现 HCE 服务或使用安全元件的 Android 应用 您不必担心配置路由表;由 Android 自动同步。Android 只需知道可以处理哪些 AID 即可 哪些请求可由安全元件处理。路由 根据安装了哪些服务以及 用户已将其配置为首选
以下部分介绍了如何为使用 用于卡模拟的安全元件。
安全元件 AID 注册
使用安全元件进行卡模拟的应用可以声明 脱离主机的服务。此类服务的声明 与 HCE 服务的声明几乎完全相同例外情况包括 如下:
- intent 过滤器中使用的操作必须设置为
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>
以下是相应的 apduservice.xml
文件的示例
注册两个 AID:
<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
系统权限,那么只有操作系统可以绑定到您的服务并与之通信。
这样可确保您收到的任何 APDU 实际上都是
操作系统,并且您发回的任何 APDU 都只会转到
操作系统,后者将 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 位,而不比较
值为 0x20 的完整 SEL_RES
。
ISO-DEP 激活
Nfc-A 协议激活后,NFC 读取器启动 ISO-DEP 启用协议。它会发送 RATS(请求答案选择) 命令。NFC 控制器生成 RATS 响应,即 ATS;ATS 不是 可由 HCE 服务配置。不过,HCE 实现必须满足 NFC Forum 的要求 对 ATS 响应的要求,以便 NFC 读取器可以依赖这些参数 根据 NFC Forum 要求针对任何 HCE 设备进行设置。
以下部分详细介绍了 ATS 的各个字节 HCE 设备上的 NFC 控制器提供的响应:
- TL:ATS 响应的长度。长度不得大于 20 字节。
- T0:必须在所有 HCE 设备上设置位 5、6 和 7,表示 TA(1)、TB(1) 和 TC(1) 包含在 ATS 响应中。位 1 到位 4 表示 FSCI。 对最大帧大小进行编码在 HCE 设备上,FSCI 的值必须为 介于 0 小时到 8 小时之间。
- T(A)1:定义读取器与模拟器之间的比特率,以及 非对称。没有针对 HCE 设备的比特率要求或保证。
- T(B)1:位 1 到位 4 表示启动帧保护时间整数 (SFGI)。已开启 HCE 设备,SFGI 必须小于等于 8h。位 5 到位 8 表示“正在等待帧” 时间整数 (FWI),并对帧等待时间 (FWT) 进行编码。在 HCE 设备上,FWI 必须小于等于 8h。
- T(C)1:位 5 表示支持“高级协议功能”。HCE 设备 不一定支持“高级协议功能”。位 2 表示支持 。HCE 设备不一定支持 DID。位 1 表示支持 无。HCE 设备不得支持 NAD,也不得将位 1 设为零。
- 历史字节:HCE 设备最多可返回 15 个历史字节。近场通信 愿意与 HCE 服务互动的读者不应对 历史字节的内容或其存在。
请注意,许多 HCE 设备可能符合协议要求 联合于 EMVCo 的支付网络在其“感应式付款” “通信协议”规范具体而言:
- T0 中的 FSCI 必须在 2h 到 8h 之间。
- T(A)1 必须设置为 0x80,表示只有 106 kbit/s 比特率是 且不支持读取器和模拟器之间的不对称比特率 支持。
- T(B)1 中的 FWI 必须小于等于 7h。
APDU 数据交换
如前所述,HCE 实现仅支持单个逻辑通道。 尝试通过不同的逻辑通道选择应用不起作用 HCE 设备。