• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# HCE卡模拟开发指南
2
3<!--Kit: Connectivity Kit-->
4<!--Subsystem: Communication-->
5<!--Owner: @amunra03-->
6<!--Designer: @wenxiaolin-->
7<!--Tester: @zs_111-->
8<!--Adviser: @zhang_yixin13-->
9
10## 简介
11近场通信(Near Field Communication,NFC)是一种短距高频的无线电技术,在13.56MHz频率运行,通信距离一般在10厘米距离内。HCE(Host Card Emulation),称为基于主机的卡模拟,表示不依赖安全单元芯片,电子设备上的应用程序模拟NFC卡片和NFC读卡器通信,实现NFC刷卡业务。
12
13## 场景介绍
14应用程序模拟NFC卡片,和NFC读卡器通信完成NFC刷卡业务。从使用场景上,可以分成HCE应用前台刷卡和HCE应用后台刷卡。
15- HCE应用前台刷卡<br>
16前台刷卡是指在触碰NFC读卡器之前,用户明确想使用在电子设备上打开特定的应用程序和NFC读卡器进行刷卡操作。当用户打开应用程序在前台,并且进入应用的刷卡页面时,电子设备触碰NFC读卡器后,会把刷卡交易数据分发给前台应用。若应用切换至后台或退出运行时,前台优先分发规则也随即被暂停。
17- HCE应用后台刷卡<br>
18后台刷卡是指不打开特定的HCE应用程序,当电子设备触碰NFC读卡器时,根据NFC读卡器选择的应用ID(Applet ID,AID,参考ISO/IEC 7816-4规范)匹配到HCE应用程序,并自动和匹配的HCE应用程序通信完成刷卡交易。如果NFC读卡器选择的应用ID,匹配到多个HCE应用程序时,说明存在冲突,需要用户打开指定的HCE应用,重新靠近NFC读卡器触发刷卡。
19
20## HCE应用刷卡的约束条件
211. 基于刷卡安全性考虑,不论HCE应用是前台方式还是后台方式刷卡,均不支持电子设备在灭屏或熄屏状态下的HCE刷卡操作。<br>
222. 电子设备必须具备NFC控制器芯片,才支持HCE刷卡能力。对于是否具有NFC安全单元芯片,没有约束要求。<br>
233. HCE应用程序需要声明NFC卡模拟权限,具体见示例。<br>
24
25## 接口说明
26
27NFC卡模拟完整的API说明以及实例代码请参考:[NFC卡模拟接口](../../reference/apis-connectivity-kit/js-apis-cardEmulation.md)。
28
29完成HCE卡模拟功能,可能使用到下面的接口。
30
31| 接口名                             | 功能描述                                                                       |
32| ---------------------------------- | ------------------------------------------------------------------------------ |
33| start(elementName: ElementName, aidList: string[]): void                   | 启动HCE业务功能。包括设置当前应用为前台优先,动态注册AID列表。                                                               |
34| stop(elementName: ElementName): void  | 停止HCE业务功能。包括取消APDU数据接收的订阅、退出当前应用前台优先、释放动态注册的AID列表。                                                     |
35| on(type: 'hceCmd', callback: AsyncCallback\<number[]>): void                | 订阅回调,用于接收对端读卡设备发送的APDU数据。                                                     |
36| transmit(response: number[]): Promise\<void>                  | 发送APDU数据到对端读卡设备。|
37| off(type: 'hceCmd', callback?: AsyncCallback\<number[]>): void| 取消APDU数据接收的订阅。|
38
39## 开发准备
40
41### HCE应用支持前台或后台刷卡的选择
42HCE应用开发者根据业务需要,可以选择实现前台刷卡或者后台刷卡。两种不同的刷卡方式,代码实现上会存在一些差异。
43- HCE应用前台刷卡<br>
441. 在配置文件module.json5中,不需要静态声明NFC读卡器选择的应用ID(AID,参考ISO/IEC 7816-4规范),而是通过[start](../../reference/apis-connectivity-kit/js-apis-cardEmulation.md#start9)来动态注册。
452. HCE应用的刷卡页面退出时,需要显性调用[stop](../../reference/apis-connectivity-kit/js-apis-cardEmulation.md#stop9)来释放动态注册的AID刷卡配置项。
46- HCE应用后台刷卡<br>
471. 在配置文件module.json5中,需要静态声明NFC读卡器选择的应用ID(AID)。根据业务选择, 选择声明的AID是属于Payment类型,还是Other类型。
482. 如果选择Payment类型,该HCE应用会在系统设置页面的NFC"默认付款应用"里出现。用户必须选择该HCE应用作为默认支付应用后,才能实现后台刷卡功能。由于提供了默认支付应用的选项, 因此Payment类型的HCE应用,不会出现多个冲突的情况。
493. 如果选择Other类型,该HCE应用不会出现在系统设置页面的NFC"默认付款应用"里,但是多个HCE应用如果都声明了相同的Other类型的AID时,会出现冲突的可能。
504. HCE应用后台刷卡的实现,不需要调用接口start和stop。
51
52> **注意:**
53>
54> - 从API version 9之后的应用开发新增支持Stage模型,作为目前主推并长期演进的模型。
55> - HCE示例代码的提供,全部按照Stage模型来说明。
56
57## 开发步骤
58
59### HCE应用前台刷卡
601. 在module.json5文件中声明NFC卡模拟权限,以及声明HCE特定的action。
612. import需要的NFC卡模拟模块和其他相关的模块。
623. 判断设备是否支持NFC能力和HCE能力。
634. 使能前台HCE应用程序优先处理NFC刷卡功能。
645. 订阅HCE APDU数据的接收。
656. 完成HCE刷卡APDU数据的接收和发送。
667. 退出应用程序NFC刷卡页面时,退出前台优先功能。
67
68```ts
69    "abilities": [
70      {
71        "name": "EntryAbility",
72        "srcEntry": "./ets/entryability/EntryAbility.ts",
73        "description": "$string:EntryAbility_desc",
74        "icon": "$media:icon",
75        "label": "$string:EntryAbility_label",
76        "startWindowIcon": "$media:icon",
77        "startWindowBackground": "$color:start_window_background",
78        "exported": true,
79        "skills": [
80          {
81            "entities": [
82              "entity.system.home"
83            ],
84            "actions": [
85              "ohos.want.action.home",
86
87              // actions必须包含"ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
88              "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
89            ]
90          }
91        ]
92      }
93    ],
94    "requestPermissions": [
95      {
96        // 添加使用NFC卡模拟需要的权限
97        "name": "ohos.permission.NFC_CARD_EMULATION",
98        "reason": "$string:app_name",
99      }
100    ]
101```
102
103```ts
104import { cardEmulation } from '@kit.ConnectivityKit';
105import { BusinessError } from '@kit.BasicServicesKit';
106import { hilog } from '@kit.PerformanceAnalysisKit';
107import { AsyncCallback } from '@kit.BasicServicesKit';
108import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit';
109
110let hceElementName: bundleManager.ElementName;
111let hceService: cardEmulation.HceService;
112
113const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => {
114  if (!error) {
115    if (hceCommand == null) {
116      hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.');
117      return;
118    }
119    // 应用程序根据自己业务实现,检查接收到的指令内容,发送匹配的响应数据
120    hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand));
121    let responseData = [0x90, 0x00]; // 根据接收到的不同指令更改响应数据
122    hceService.transmit(responseData).then(() => {
123      hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.');
124    }).catch((err: BusinessError) => {
125      hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err));
126    });
127  } else {
128    hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error));
129  }
130}
131
132export default class EntryAbility extends UIAbility {
133  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
134    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
135
136    // 判断设备是否支持NFC能力和HCE能力
137    if (!canIUse("SystemCapability.Communication.NFC.Core")) {
138      hilog.error(0x0000, 'testTag', 'nfc unavailable.');
139      return;
140    }
141    if (!cardEmulation.hasHceCapability()) {
142      hilog.error(0x0000, 'testTag', 'hce unavailable.');
143      return;
144    }
145
146    // hceElementName中元素不能为空,通过want获取应用的elementname或按应用实际信息填写
147    hceElementName = {
148      bundleName: want.bundleName ?? '',
149      abilityName: want.abilityName ?? '',
150      moduleName: want.moduleName,
151    }
152    hceService = new cardEmulation.HceService();
153  }
154
155  onForeground() {
156    // 应用进入前台
157    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
158    if (hceElementName != undefined) {
159      try {
160        // 调用接口使能前台HCE应用程序优先处理NFC刷卡功能
161        let aidList = ["A0000000031010", "A0000000031011"]; // 修改为正确的aid
162        hceService.start(hceElementName, aidList);
163
164        // 订阅HCE APDU数据的接收
165        hceService.on('hceCmd', hceCommandCb);
166      } catch (error) {
167        hilog.error(0x0000, 'testTag', 'hceService.start error = %{public}s', JSON.stringify(error));
168      }
169    }
170  }
171
172  onBackground() {
173    // 应用退到后台
174    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
175    // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能
176    if (hceElementName != undefined) {
177      try {
178        hceService.stop(hceElementName);
179      } catch (error) {
180        hilog.error(0x0000, 'testTag', 'hceService.stop error = %{public}s', JSON.stringify(error));
181      }
182    }
183  }
184}
185```
186
187### HCE应用后台刷卡
1881. 在module.json5文件中声明NFC卡模拟权限,声明HCE特定的action,声明应用能够处理的AID。
1892. import需要的NFC卡模拟模块和其他相关的模块。
1903. 判断设备是否支持NFC能力和HCE能力。
1914. 订阅HCE APDU数据的接收。
1925. 完成HCE刷卡APDU数据的接收和发送。
1936. 退出应用程序时,退出订阅功能。
194
195```ts
196    "abilities": [
197      {
198        "name": "EntryAbility",
199        "srcEntry": "./ets/entryability/EntryAbility.ts",
200        "description": "$string:EntryAbility_desc",
201        "icon": "$media:icon",
202        "label": "$string:EntryAbility_label",
203        "startWindowIcon": "$media:icon",
204        "startWindowBackground": "$color:start_window_background",
205        "exported": true,
206        "skills": [
207          {
208            "entities": [
209              "entity.system.home"
210            ],
211            "actions": [
212              "ohos.want.action.home",
213
214              // actions必须包含"ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
215              "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
216            ]
217          }
218        ],
219
220        // 根据业务需要至少定义一个Payment类型或Other类型的AID,可以定义多个
221        "metadata": [
222          {
223            "name": "payment-aid",
224            "value": "A0000000031010" // 定义Payment类型的AID
225          },
226          {
227            "name": "other-aid",
228            "value": "A0000000031011" // 定义Other类型的AID
229          }
230        ]
231      }
232    ],
233    "requestPermissions": [
234      {
235        // 添加使用NFC卡模拟需要的权限
236        "name": "ohos.permission.NFC_CARD_EMULATION",
237        "reason": "$string:app_name",
238      }
239    ]
240```
241
242```ts
243import { cardEmulation } from '@kit.ConnectivityKit';
244import { BusinessError } from '@kit.BasicServicesKit';
245import { hilog } from '@kit.PerformanceAnalysisKit';
246import { AsyncCallback } from '@kit.BasicServicesKit';
247import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit';
248
249let hceElementName : bundleManager.ElementName;
250let hceService: cardEmulation.HceService;
251
252const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => {
253  if (!error) {
254    if (hceCommand == null) {
255      hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.');
256      return;
257    }
258
259    // 应用程序根据自己业务实现,检查接收到的指令内容,发送匹配的响应数据
260    hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand));
261    let responseData = [0x90, 0x00]; // 根据接收到的不同指令更改响应数据
262    hceService.transmit(responseData).then(() => {
263      hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.');
264    }).catch((err: BusinessError) => {
265      hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err));
266    });
267  } else {
268    hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error));
269  }
270}
271
272export default class EntryAbility extends UIAbility {
273  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
274    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
275
276    // 判断设备是否支持NFC能力和HCE能力
277    if (!canIUse("SystemCapability.Communication.NFC.Core")) {
278      hilog.error(0x0000, 'testTag', 'nfc unavailable.');
279      return;
280    }
281    if (!cardEmulation.hasHceCapability()) {
282      hilog.error(0x0000, 'testTag', 'hce unavailable.');
283      return;
284    }
285
286    // 应用程序被运行到前台时,订阅HCE刷卡数据的接收
287    hceService = new cardEmulation.HceService();
288    hceService.on('hceCmd', hceCommandCb);
289  }
290
291  onForeground() {
292    // 应用进入前台
293    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
294  }
295
296  onDestroy() {
297    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
298    // 退出应用程序,取消订阅接受HCE刷卡数据
299    hceService.off('hceCmd', hceCommandCb);
300  }
301}
302```