更新记录
1.0.0(2026-09-29)
- 首次发布 Android 与 HarmonyOS 手机 NFC 虚拟卡功能,支持 HCE / ISO-DEP / ISO 7816-4 APDU 卡模拟。
- 支持动态 AID、内置测试卡、自定义 APDU 请求与响应、超时保护、测试数据读写及生命周期诊断。
- Android 使用系统
HostApduService,支持后台恢复内置测试卡;停止后占位 AID 返回6A82,自定义请求超时返回6400。 - HarmonyOS 额外提供
ndefTag模式,以标准 AIDD2760000850101模拟可重复写入的 NFC Forum Type 4 NDEF 标签。 - Type 4 NDEF 模式支持 CC 文件、NDEF 文件、
READ BINARY与UPDATE BINARY,可供 Android 系统 NDEF 接口读写。 - 提供 uni-app 与 uni-app x 中文示例,包含状态诊断、运行配置、测试数据和读卡端 APDU 指令。
- iOS、Web 和小程序暂不支持,调用时返回结构化不支持错误。
- 本插件包含 Android 原生服务、Manifest/AID 资源和 HarmonyOS 原生 ETS;接入后必须重新制作对应平台的自定义基座、HAP 或正式安装包。
平台兼容性
uni-app(4.84)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | × | × | √ | × | √ | × | √ |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(4.84)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | √ | × | √ | × |
lizhao-hce-card
lizhao-hce-card 是面向 Android 与 HarmonyOS 的 HCE 卡模拟 UTS 插件。它把手机模拟成自定义的 ISO 14443-4 / ISO-DEP / ISO 7816-4 APDU 卡;HarmonyOS 还可模拟可重复写入的 NFC Forum Type 4 NDEF 标签。
ndefTag是基于 HCE 的 Type 4 软件标签,只在 HarmonyOS 提供。它可用于 AndroidNdef读写验证,但不是 MIFARE、NdefFormatable或可永久锁定的实体标签。
能力与边界
- Android
HostApduService与 HarmonyOSHceService原生 HCE / ISO-DEP / ISO 7816-4 短 APDU 卡模拟。 - 运行时动态注册 5~16 字节 AID。
- 内置测试 Applet:SELECT、GET、PUT、ECHO、CLEAR。
custom模式把 APDU 交给页面,并按requestId返回业务响应。- HarmonyOS
ndefTag模式:标准 NDEF AID、CC 文件、NDEF 文件、READ BINARY、UPDATE BINARY与 NLEN 写事务。 - 单待响应请求、超时
6400、过期请求拒绝和生命周期状态诊断。 - HarmonyOS 测试数据保存在当前应用进程;Android 为承接系统后台唤起,会保存在应用私有目录,卸载应用后清除。
- APDU 单次数据仍按短 APDU 处理,单条最多 255 字节;
ndefTag可通过多次UPDATE BINARY写入最长 2046 字节的 NDEF Message。 - 不模拟 MIFARE、
NdefFormatable、永久只读锁定和断电保存;NDEF 内容仅在当前 HCE 运行会话内保留。
平台支持
| 平台 | 状态 | 说明 |
|---|---|---|
| HarmonyOS App | 支持 | testApplet、custom、ndefTag;使用系统 HceService,需要宿主 Ability 配置和权限 |
| Android App | 支持 | testApplet、custom;使用系统 HostApduService 和 CardEmulation 动态 AID,最低 API 21 |
| iOS App | 不支持 | 返回 9071001 |
| Web / 小程序 | 不支持 | 返回 9071001,不伪造成功 |
插件同时提供 uni-app 与 uni-app x 示例,公开 API 统一从插件根目录导入。
Android 宿主配置
Android 所需的 NFC 权限、HCE feature、HostApduService、service metadata 和 AID XML 已随
插件放在 utssdk/app-android,HBuilderX 原生联编时会合并进入宿主安装包。接入或从 1.0.0
升级后必须重新制作自定义基座或正式 APK;只更新 wgt/appResource 不会把 Service 和 XML
加入旧基座。
Android 使用 CardEmulation.CATEGORY_OTHER,不注册支付类 AID。XML 中只声明内部占位 AID
F04C495A48434500,业务 AID 由 startHce 动态注册并在 stopHce 时注销;插件停止时即使
系统把内部占位 AID 路由到 Service,也只返回 6A82。
Android 的 android.permission.NFC 是普通权限,不会出现运行时授权框。startHce 前仍会
检查设备 HCE 能力、NFC 开关和宿主 Service 声明;缺失时分别返回稳定错误,不会伪造启动成功。
HarmonyOS 宿主配置
HCE 的 Ability action、静态默认 AID 和权限属于宿主应用配置,不能仅依靠 HAR 内部声明。请把以下内容合并到项目根目录的 harmony-configs/entry/src/main/module.json5,保留原有 home skill 和其他权限:
{
"module": {
"abilities": [
{
"name": "EntryAbility",
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
},
{
"actions": [
"ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
]
}
],
"metadata": [
{
"name": "other-aid",
"value": "F0010203040506"
}
]
}
],
"requestPermissions": [
{
"name": "ohos.permission.NFC_CARD_EMULATION",
"reason": "$string:card_emulation_reason"
}
]
}
}
同时在 harmony-configs/entry/src/main/resources/base/element/string.json 的 string 数组中加入:
{
"name": "card_emulation_reason",
"value": "用于在用户主动启动测试时模拟 ISO-DEP APDU 卡并与读卡设备通信"
}
默认 AID 使用 other-aid,不声明支付类 payment-aid。
ndefTag 启动时会把运行期 AID 固定为 NFC Forum NDEF Application AID
D2760000850101,无需把它写成业务 AID。宿主仍必须保留上述 HCE action、默认 metadata 和权限,
以便系统识别当前 Ability 为 HCE 服务。
最小启动示例
import {
startHce,
stopHce,
onApduCommand,
offApduCommand
} from '@/uni_modules/lizhao-hce-card'
const handleApdu = (event) => {
console.log('收到 APDU', event.apduHex)
}
onApduCommand(handleApdu)
startHce({
mode: 'testApplet',
aidList: ['F0010203040506'],
initialDataHex: '6C697A68616F2D6863652D63617264',
success: (res) => console.log('HCE 已启动', res),
fail: (err) => console.error('HCE 启动失败', err),
complete: () => console.log('启动调用结束')
})
// 示例页选择在卸载时释放动态 AID;业务若需要 testApplet 后台响应,可在合适的业务时机再停止。
stopHce({
complete: () => offApduCommand(handleApdu)
})
进入示例页只检查状态,不会自动启动 HCE;必须由用户主动点击启动。
HarmonyOS 可写 Type 4 NDEF
import { startHce } from '@/uni_modules/lizhao-hce-card'
startHce({
mode: 'ndefTag',
// 原始 NDEF Message,不包含两字节 NLEN。这里是文本 lizhao-hce-card。
initialNdefMessageHex: 'D1011254027A686C697A68616F2D6863652D63617264',
ndefCapacityBytes: 2046,
success: (res) => console.log('Type 4 NDEF 已启动', res),
fail: (err) => console.error('启动失败', err),
complete: () => {}
})
此模式固定使用以下协议对象:
| 对象 | 值 / 行为 |
|---|---|
| NDEF Application AID | D2760000850101 |
| CC 文件 | E103,Mapping Version 2.0,MLe/MLc 均为 255 |
| NDEF 文件 | E104,默认文件总长 2048 字节,其中 NDEF Message 容量 2046 字节 |
| 读取 | READ BINARY |
| 写入 | UPDATE BINARY;支持先写 NLEN=0、分段写正文、最后提交 NLEN |
Android 端通过 Ndef.writeNdefMessage() 写入后,内容会保留在当前 HCE 运行会话中;两台手机移开再重新贴合,仍可读到新内容。停止 HCE、应用进程结束或重新启动 ndefTag 后,会恢复为新一轮启动时传入的初始内容。
ndefTag 模式的 AID 由协议固定,因此不能调用 updateHceAids();该调用会返回 9071007。
内置测试 Applet
默认 AID:F0010203040506
默认数据是 UTF-8 文本 lizhao-hce-card:
6C697A68616F2D6863652D63617264
读卡端应先 SELECT:
00A4040007F0010203040506
成功响应:
6F098407F00102030405069000
| 操作 | APDU | 成功响应 |
|---|---|---|
| SELECT 默认 AID | 00A4040007F0010203040506 |
最小 FCI + 9000 |
| GET 当前数据 | 80CA000000 |
数据 + 9000 |
PUT 010203 |
80DA000003010203 |
9000 |
ECHO A1B2C3 |
80EE000003A1B2C3 |
A1B2C39000 |
| CLEAR | 80E4000000 |
9000 |
常用状态字:
| 状态字 | 含义 |
|---|---|
9000 |
成功 |
6700 |
APDU 长度不合法 |
6985 |
尚未 SELECT 当前 Applet |
6A82 |
AID 不存在 |
6D00 |
INS 不支持 |
6E00 |
CLA 不支持 |
6400 |
custom 模式响应超时 |
6F00 |
内部处理异常时的兜底状态字 |
custom 自定义响应
import {
startHce,
onApduCommand,
sendApduResponse
} from '@/uni_modules/lizhao-hce-card'
onApduCommand((event) => {
// requestId 只对当前尚未响应的命令有效。
sendApduResponse({
requestId: event.requestId,
responseHex: '0102039000',
success: (res) => console.log('响应已发送', res),
fail: (err) => console.error('响应失败', err),
complete: () => {}
})
})
startHce({
mode: 'custom',
aidList: ['F0010203040506'],
responseTimeoutMs: 800,
success: () => {},
fail: (err) => console.error(err),
complete: () => {}
})
responseHex必须是偶数长度十六进制,至少包含 SW1/SW2,最多 257 字节。- 新 APDU 到来时,旧
requestId立即失效,插件不会把旧响应误发给新命令。 - 当前请求超时后系统发送
6400;随后再用该requestId响应会返回9071008。
动态更新 AID
import { updateHceAids } from '@/uni_modules/lizhao-hce-card'
updateHceAids({
aidList: ['F0010203040506', 'A00000000101'],
success: (res) => console.log('新 AID 已生效', res.aidList),
fail: (err) => console.error('更新失败', err),
complete: () => {}
})
更新顺序固定为停止/注销旧动态 AID,再注册新列表并恢复接收。更新失败时插件会进入明确错误状态,不会虚报旧 AID 已恢复。
API
| API | 说明 |
|---|---|
getHceStatus(options) |
获取能力、NFC、权限、状态、AID 和待响应请求快照 |
getHceDiagnostics(options) |
获取插件版本、构建、宿主状态和最近原生错误 |
startHce(options) |
启动 testApplet、custom;HarmonyOS 还支持 ndefTag |
updateHceAids(options) |
运行中动态替换 AID |
stopHce(options?) |
幂等停止并释放资源 |
onApduCommand(listener) |
监听读卡器 APDU |
offApduCommand(listener?) |
按函数身份或全部取消 APDU 监听 |
sendApduResponse(options) |
响应当前 custom 请求 |
onHceStateChange(listener) |
监听 stopped/starting/running/updating/stopping/error |
offHceStateChange(listener?) |
取消状态监听 |
getTestAppletData(options) |
读取内置 Applet 数据 |
setTestAppletData(options) |
设置内置 Applet 数据 |
resetTestAppletData(options) |
恢复默认测试数据 |
完整字段类型位于 utssdk/interface.uts。所有一次性 API 遵循单终态:成功时依次调用 success、complete,失败时依次调用 fail、complete。
错误码
| 错误码 | 说明 |
|---|---|
9071001 |
当前平台或设备不支持 HCE |
9071002 |
应用上下文或宿主 HCE 组件标识不可用 |
9071003 |
卡模拟权限或宿主配置缺失 |
9071004 |
NFC 尚未开启 |
9071005 |
AID 参数不合法 |
9071006 |
APDU 或响应 APDU 不合法 |
9071007 |
当前生命周期状态不允许操作 |
9071008 |
APDU 请求过期、不存在或已响应 |
9071009 |
自定义 APDU 响应超时 |
9071010 |
原生 HCE 状态异常或发送失败 |
9071011 |
页面监听回调异常,已被插件隔离 |
与 lizhao-nfc-pro 双机测试
需要两台支持 NFC 的设备:
- 卡端 Android 或鸿蒙手机安装包含当前源码与 HCE 原生声明的新 APK/HAP。
- 在卡端启动
lizhao-hce-card;custom 模式保持页面监听存活,testApplet 可按业务需要在后台运行。 - 另一台设备用
lizhao-nfc-pro开始读卡会话。 - 发现标签后通过
isoDepTransceive()发送默认 SELECT。 - 收到
...9000后依次发送 GET、PUT、ECHO、CLEAR,并核对响应。 - 切换 custom 模式,验证
requestId响应、超时6400和过期请求拒绝。 - 更新 AID 后确认旧 AID 返回
6A82,新 AID 可以 SELECT。
验证 Android lizhao-nfc-pro 的 NDEF 读写时,卡端必须使用 HarmonyOS ndefTag:
- 鸿蒙端启动
ndefTag,Android 端保持skipNdefCheck=false。 - Android 调用
getNdefStatus(),应识别为可写 NDEF,容量约 2046 字节。 - 调用
writeNdefOnce()写入新的 Text/URI 记录,并启用写后校验。 - 两台手机移开,再次贴合后调用
readNdefOnce(),应读回刚写入的记录。 - 再写入另一条不同内容并重复第 4 步,确认不是只成功一次。
这条链路可以覆盖 NDEF 检测、读取、写入、写后校验和重复贴合;不能覆盖 NdefFormatable、永久只读、MIFARE 技术族或真实标签断电保存。
单机能证明启动、权限、动态 AID 生命周期和无崩溃;只有双机贴卡才能证明真实射频 ISO-DEP/APDU 闭环。
FAQ
打开 NFC 后为什么另一个手机读不到?
必须先调用 startHce()。testApplet/custom 需要读卡器按 ISO-DEP 发送 SELECT/APDU;若需要让 Android 的标准 NDEF API 自动识别,鸿蒙端请选择 ndefTag。
为什么动态 AID 已传入,仍需要宿主静态声明?
系统必须先从安装包识别 HCE 服务。HarmonyOS 由 EntryAbility metadata 提供静态默认 AID;Android
由 HostApduService metadata 提供内部占位 AID。业务 AID 仍分别由 HceService.start 或
CardEmulation.registerAidsForService 动态注册。
修改后需要重新打包吗?
需要。Android 新增 Kotlin、Manifest、Service 和 AID XML,必须重新制作并安装匹配源码的 Android
自定义基座或正式 APK;HarmonyOS 新增或修改 ETS、HAR 权限、宿主 module.json5、AID metadata
时必须重新构建 HAP。仅更新 wgt/appResource 不能替换已经进入安装包的原生实现和声明。
支持支付卡模拟吗?
不支持。本插件使用 other-aid,只面向开发测试和自定义业务协议,不声明支付 AID,也不提供安全芯片或支付认证能力。

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 6486
赞赏 5
下载 12646878
赞赏 1952
赞赏
京公网安备:11010802035340号