1# HCE卡模拟开发指南
2
3## 简介
4近场通信(Near Field Communication,NFC)是一种短距高频的无线电技术,在13.56MHz频率运行,通信距离一般在10厘米距离内。HCE(Host Card Emulation),称为基于主机的卡模拟,表示不依赖安全单元芯片,电子设备上的应用程序模拟NFC卡片和NFC读卡器通信,实现NFC刷卡业务。
5
6## 场景介绍
7应用程序模拟NFC卡片,和NFC读卡器通信完成NFC刷卡业务。从使用场景上,可以分成HCE应用前台刷卡,和HCE应用后台刷卡。
8- HCE应用前台刷卡<br>
9前台刷卡是指在触碰NFC读卡器之前,用户先在电子设备上打开特定的应用程序,用户明确想使用所打开的应用程序和NFC读卡器进行刷卡操作。用户打开应用程序在前台,并且进入应用的刷卡页面之后,电子设备触碰NFC读卡器,只会把刷卡交易数据分发给前台应用。
10- HCE应用后台刷卡<br>
11后台刷卡是指不打开特定的HCE应用程序,电子设备触碰NFC读卡器后,根据NFC读卡器选择的应用ID(AID)匹配到HCE应用程序,并自动和匹配的HCE应用程序通信完成刷卡交易。如果匹配到多个HCE应用程序时,说明存在冲突,需要用户打开指定的应用才能完成刷卡。
12- HCE应用刷卡的约束条件<br>
131、不管是HCE应用前台还是后台刷卡,能够完成HCE应用程序NFC刷卡的条件是电子设备需要亮屏解锁。
142、module.json5文件中需要声明nfc卡模拟权限,具体见示例。
153、前台应用时需要调用start和stop注册和去注册AID,具体见示例。
16
17## 接口说明
18
19NFC卡模拟完整的JS API说明以及实例代码请参考:[NFC卡模拟接口](../../reference/apis-connectivity-kit/js-apis-cardEmulation.md)。
20
21完成HCE卡模拟功能,可能使用到下面的接口。
22
23| 接口名                             | 功能描述                                                                       |
24| ---------------------------------- | ------------------------------------------------------------------------------ |
25| start(elementName: ElementName, aidList: string[]): void                   | 启动HCE业务功能。包括设置当前应用为前台优先,动态注册AID列表。                                                               |
26| stop(elementName: ElementName): void  | 停止HCE业务功能。包括取消APDU数据接收的订阅,退出当前应用前台优先,释放动态注册的AID列表。
27| on(type: 'hceCmd', callback: AsyncCallback\<number[]>): void                | 订阅回调,用于接收对端读卡设备发送的APDU数据。
28| transmit(response: number[]): Promise\<void>                  | 发送APDU数据到对端读卡设备。|                                                     |
29
30## 开发步骤
31
32### HCE应用前台刷卡
331. 在module.json5文件中声明NFC卡模拟权限,以及声明HCE特定的action。
342. import需要的NFC卡模拟模块和其他相关的模块。
353. 判断设备是否支持NFC能力和HCE能力。
364. 使能前台HCE应用程序优先处理NFC刷卡功能。
375. 订阅HCE APDU数据的接收。
386. 完成HCE刷卡APDU数据的接收和发送。
397. 退出应用程序NFC刷卡页面时,退出前台优先功能。
40
41```ts
42    "abilities": [
43      {
44        "name": "EntryAbility",
45        "srcEntry": "./ets/entryability/EntryAbility.ts",
46        "description": "$string:EntryAbility_desc",
47        "icon": "$media:icon",
48        "label": "$string:EntryAbility_label",
49        "startWindowIcon": "$media:icon",
50        "startWindowBackground": "$color:start_window_background",
51        "exported": true,
52        "skills": [
53          {
54            "entities": [
55              "entity.system.home"
56            ],
57            "actions": [
58              "action.system.home",
59
60              // Add the nfc card emulation action to filter out for this application.
61              "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
62            ]
63          }
64        ]
65      }
66    ],
67    "requestPermissions": [
68      {
69        // Add the permission for nfc card emulation.
70        "name": "ohos.permission.NFC_CARD_EMULATION",
71        "reason": "$string:app_name",
72      }
73    ]
74```
75
76```ts
77import { cardEmulation } from '@kit.ConnectivityKit';
78import { BusinessError } from '@kit.BasicServicesKit';
79import { hilog } from '@kit.PerformanceAnalysisKit';
80import { AsyncCallback } from '@kit.BasicServicesKit';
81import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit';
82
83let hceElementName: bundleManager.ElementName;
84let hceService: cardEmulation.HceService;
85
86const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => {
87  if (!error) {
88    if (hceCommand == null || hceCommand == undefined) {
89      hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.');
90      return;
91    }
92    // check the command, then transmit the response.
93    hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand));
94    let responseData = [0x90, 0x00]; // change the response depend on different received command.
95    hceService.transmit(responseData).then(() => {
96      hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.');
97    }).catch((err: BusinessError) => {
98      hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err));
99    });
100  } else {
101    hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error));
102  }
103}
104
105export default class EntryAbility extends UIAbility {
106  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
107    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
108
109    // 判断设备是否支持NFC能力和HCE能力
110    if (!canIUse("SystemCapability.Communication.NFC.Core")) {
111      hilog.error(0x0000, 'testTag', 'nfc unavailable.');
112      return;
113    }
114    if (!cardEmulation.hasHceCapability()) {
115      hilog.error(0x0000, 'testTag', 'hce unavailable.');
116      return;
117    }
118
119    hceElementName = {
120      bundleName: want.bundleName ?? '',
121      abilityName: want.abilityName ?? '',
122      moduleName: want.moduleName,
123    }
124    hceService = new cardEmulation.HceService();
125  }
126
127  onForeground() {
128    // Ability has brought to foreground
129    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
130    if (hceElementName != undefined) {
131      try {
132        // 调用接口使能前台HCE应用程序优先处理NFC刷卡功能
133        let aidList = ["A0000000031010", "A0000000031011"]; // change aid tobe correct.
134        hceService.start(hceElementName, aidList);
135
136        // 订阅HCE APDU数据的接收
137        hceService.on('hceCmd', hceCommandCb);
138      } catch (error) {
139        hilog.error(0x0000, 'testTag', 'hceService.start error = %{public}s', JSON.stringify(error));
140      }
141    }
142  }
143
144  onBackground() {
145    // Ability has back to background
146    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
147    // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能
148    if (hceElementName != undefined) {
149      try {
150        hceService.stop(hceElementName);
151      } catch (error) {
152        hilog.error(0x0000, 'testTag', 'hceService.stop error = %{public}s', JSON.stringify(error));
153      }
154    }
155  }
156}
157```
158
159### HCE应用后台刷卡
1601. 在module.json5文件中声明NFC卡模拟权限,声明HCE特定的action,声明应用能够处理的AID。
1612. import需要的NFC卡模拟模块和其他相关的模块。
1623. 判断设备是否支持NFC能力和HCE能力。
1634. 订阅HCE APDU数据的接收。
1645. 完成HCE刷卡APDU数据的接收和发送。
1656. 退出应用程序时,退出订阅功能。
166
167```ts
168    "abilities": [
169      {
170        "name": "EntryAbility",
171        "srcEntry": "./ets/entryability/EntryAbility.ts",
172        "description": "$string:EntryAbility_desc",
173        "icon": "$media:icon",
174        "label": "$string:EntryAbility_label",
175        "startWindowIcon": "$media:icon",
176        "startWindowBackground": "$color:start_window_background",
177        "exported": true,
178        "skills": [
179          {
180            "entities": [
181              "entity.system.home"
182            ],
183            "actions": [
184              "action.system.home",
185
186              // Add the nfc card emulation action to filter out for this application.
187              "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE"
188            ]
189          }
190        ],
191        "metadata": [
192          {
193            "name": "payment-aid",
194            "value": "A0000000031010" // change it tobe correct
195          },
196          {
197            "name": "other-aid",
198            "value": "A0000000031011" // change it tobe correct
199          }
200        ]
201      }
202    ],
203    "requestPermissions": [
204      {
205        // Add the permission for nfc card emulation.
206        "name": "ohos.permission.NFC_CARD_EMULATION",
207        "reason": "$string:app_name",
208      }
209    ]
210```
211
212```ts
213import { cardEmulation } from '@kit.ConnectivityKit';
214import { BusinessError } from '@kit.BasicServicesKit';
215import { hilog } from '@kit.PerformanceAnalysisKit';
216import { AsyncCallback } from '@kit.BasicServicesKit';
217import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit';
218
219let hceElementName : bundleManager.ElementName;
220let hceService: cardEmulation.HceService;
221
222const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => {
223  if (!error) {
224    if (hceCommand == null || hceCommand == undefined) {
225      hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.');
226      return;
227    }
228
229    // check the command, then transmit the response.
230    hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand));
231    let responseData = [0x90, 0x00]; // change the response depend on different received command.
232    hceService.transmit(responseData).then(() => {
233      hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.');
234    }).catch((err: BusinessError) => {
235      hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err));
236    });
237  } else {
238    hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error));
239  }
240}
241
242export default class EntryAbility extends UIAbility {
243  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
244    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
245
246    // 判断设备是否支持NFC能力和HCE能力
247    if (!canIUse("SystemCapability.Communication.NFC.Core")) {
248      hilog.error(0x0000, 'testTag', 'nfc unavailable.');
249      return;
250    }
251    if (!cardEmulation.hasHceCapability()) {
252      hilog.error(0x0000, 'testTag', 'hce unavailable.');
253      return;
254    }
255
256    hceElementName = {
257      bundleName: want.bundleName ?? '',
258      abilityName: want.abilityName ?? '',
259      moduleName: want.moduleName,
260    }
261    hceService = new cardEmulation.HceService();
262    hceService.on('hceCmd', hceCommandCb);
263  }
264
265  onForeground() {
266    // Ability has brought to foreground
267    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
268  }
269
270  onDestroy() {
271    // Ability has back to destroy
272    hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
273    // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能
274    if (hceElementName != undefined) {
275      try {
276        hceService.stop(hceElementName);
277      } catch (error) {
278        hilog.error(0x0000, 'testTag', 'hceService.stop error = %{public}s', JSON.stringify(error));
279      }
280    }
281  }
282}
283```