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刷卡的条件是电子设备需要亮屏解锁。<br>2.module.json5文件中需要声明nfc卡模拟权限,具体见示例。<br>3.前台应用时需要调用start和stop注册和去注册AID,具体见示例。<br> 14 15## 接口说明 16 17NFC卡模拟完整的JS API说明以及实例代码请参考:[NFC卡模拟接口](../../reference/apis-connectivity-kit/js-apis-cardEmulation.md)。 18 19完成HCE卡模拟功能,可能使用到下面的接口。 20 21| 接口名 | 功能描述 | 22| ---------------------------------- | ------------------------------------------------------------------------------ | 23| start(elementName: ElementName, aidList: string[]): void | 启动HCE业务功能。包括设置当前应用为前台优先,动态注册AID列表。 | 24| stop(elementName: ElementName): void | 停止HCE业务功能。包括取消APDU数据接收的订阅,退出当前应用前台优先,释放动态注册的AID列表。 25| on(type: 'hceCmd', callback: AsyncCallback\<number[]>): void | 订阅回调,用于接收对端读卡设备发送的APDU数据。 26| transmit(response: number[]): Promise\<void> | 发送APDU数据到对端读卡设备。| | 27 28## 开发步骤 29 30### HCE应用前台刷卡 311. 在module.json5文件中声明NFC卡模拟权限,以及声明HCE特定的action。 322. import需要的NFC卡模拟模块和其他相关的模块。 333. 判断设备是否支持NFC能力和HCE能力。 344. 使能前台HCE应用程序优先处理NFC刷卡功能。 355. 订阅HCE APDU数据的接收。 366. 完成HCE刷卡APDU数据的接收和发送。 377. 退出应用程序NFC刷卡页面时,退出前台优先功能。 38 39```ts 40 "abilities": [ 41 { 42 "name": "EntryAbility", 43 "srcEntry": "./ets/entryability/EntryAbility.ts", 44 "description": "$string:EntryAbility_desc", 45 "icon": "$media:icon", 46 "label": "$string:EntryAbility_label", 47 "startWindowIcon": "$media:icon", 48 "startWindowBackground": "$color:start_window_background", 49 "exported": true, 50 "skills": [ 51 { 52 "entities": [ 53 "entity.system.home" 54 ], 55 "actions": [ 56 "action.system.home", 57 58 // actions须包含"ohos.nfc.cardemulation.action.HOST_APDU_SERVICE" 59 "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE" 60 ] 61 } 62 ], 63 "metadata": [ 64 { 65 "name": "payment-aid", 66 "value": "A0000000031010" // 修改为正确的aid 67 }, 68 { 69 "name": "other-aid", 70 "value": "A0000000031011" // 修改为正确的aid 71 } 72 ] 73 } 74 ], 75 "requestPermissions": [ 76 { 77 // 添加使用nfc卡模拟需要的权限 78 "name": "ohos.permission.NFC_CARD_EMULATION", 79 "reason": "$string:app_name", 80 } 81 ] 82``` 83 84```ts 85import { cardEmulation } from '@kit.ConnectivityKit'; 86import { BusinessError } from '@kit.BasicServicesKit'; 87import { hilog } from '@kit.PerformanceAnalysisKit'; 88import { AsyncCallback } from '@kit.BasicServicesKit'; 89import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit'; 90 91let hceElementName: bundleManager.ElementName; 92let hceService: cardEmulation.HceService; 93 94const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => { 95 if (!error) { 96 if (hceCommand == null || hceCommand == undefined) { 97 hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.'); 98 return; 99 } 100 // 检查指令,发送response 101 hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand)); 102 let responseData = [0x90, 0x00]; // 根据接收到的不同命令更改response 103 hceService.transmit(responseData).then(() => { 104 hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.'); 105 }).catch((err: BusinessError) => { 106 hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err)); 107 }); 108 } else { 109 hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error)); 110 } 111} 112 113export default class EntryAbility extends UIAbility { 114 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 115 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 116 117 // 判断设备是否支持NFC能力和HCE能力 118 if (!canIUse("SystemCapability.Communication.NFC.Core")) { 119 hilog.error(0x0000, 'testTag', 'nfc unavailable.'); 120 return; 121 } 122 if (!cardEmulation.hasHceCapability()) { 123 hilog.error(0x0000, 'testTag', 'hce unavailable.'); 124 return; 125 } 126 127 hceElementName = { 128 bundleName: want.bundleName ?? '', 129 abilityName: want.abilityName ?? '', 130 moduleName: want.moduleName, 131 } 132 hceService = new cardEmulation.HceService(); 133 } 134 135 onForeground() { 136 // 应用进入前台 137 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 138 if (hceElementName != undefined) { 139 try { 140 // 调用接口使能前台HCE应用程序优先处理NFC刷卡功能 141 let aidList = ["A0000000031010", "A0000000031011"]; // 修改为正确的aid 142 hceService.start(hceElementName, aidList); 143 144 // 订阅HCE APDU数据的接收 145 hceService.on('hceCmd', hceCommandCb); 146 } catch (error) { 147 hilog.error(0x0000, 'testTag', 'hceService.start error = %{public}s', JSON.stringify(error)); 148 } 149 } 150 } 151 152 onBackground() { 153 // 应用退到后台 154 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); 155 // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能 156 if (hceElementName != undefined) { 157 try { 158 hceService.stop(hceElementName); 159 } catch (error) { 160 hilog.error(0x0000, 'testTag', 'hceService.stop error = %{public}s', JSON.stringify(error)); 161 } 162 } 163 } 164} 165``` 166 167### HCE应用后台刷卡 1681. 在module.json5文件中声明NFC卡模拟权限,声明HCE特定的action,声明应用能够处理的AID。 1692. import需要的NFC卡模拟模块和其他相关的模块。 1703. 判断设备是否支持NFC能力和HCE能力。 1714. 订阅HCE APDU数据的接收。 1725. 完成HCE刷卡APDU数据的接收和发送。 1736. 退出应用程序时,退出订阅功能。 174 175```ts 176 "abilities": [ 177 { 178 "name": "EntryAbility", 179 "srcEntry": "./ets/entryability/EntryAbility.ts", 180 "description": "$string:EntryAbility_desc", 181 "icon": "$media:icon", 182 "label": "$string:EntryAbility_label", 183 "startWindowIcon": "$media:icon", 184 "startWindowBackground": "$color:start_window_background", 185 "exported": true, 186 "skills": [ 187 { 188 "entities": [ 189 "entity.system.home" 190 ], 191 "actions": [ 192 "action.system.home", 193 194 // actions须包含"ohos.nfc.cardemulation.action.HOST_APDU_SERVICE" 195 "ohos.nfc.cardemulation.action.HOST_APDU_SERVICE" 196 ] 197 } 198 ], 199 "metadata": [ 200 { 201 "name": "payment-aid", 202 "value": "A0000000031010" // 修改为正确的aid 203 }, 204 { 205 "name": "other-aid", 206 "value": "A0000000031011" // 修改为正确的aid 207 } 208 ] 209 } 210 ], 211 "requestPermissions": [ 212 { 213 // 添加使用nfc卡模拟需要的权限 214 "name": "ohos.permission.NFC_CARD_EMULATION", 215 "reason": "$string:app_name", 216 } 217 ] 218``` 219 220```ts 221import { cardEmulation } from '@kit.ConnectivityKit'; 222import { BusinessError } from '@kit.BasicServicesKit'; 223import { hilog } from '@kit.PerformanceAnalysisKit'; 224import { AsyncCallback } from '@kit.BasicServicesKit'; 225import { AbilityConstant, UIAbility, Want, bundleManager } from '@kit.AbilityKit'; 226 227let hceElementName : bundleManager.ElementName; 228let hceService: cardEmulation.HceService; 229 230const hceCommandCb : AsyncCallback<number[]> = (error : BusinessError, hceCommand : number[]) => { 231 if (!error) { 232 if (hceCommand == null || hceCommand == undefined) { 233 hilog.error(0x0000, 'testTag', 'hceCommandCb has invalid hceCommand.'); 234 return; 235 } 236 237 // 检查指令,发送response 238 hilog.info(0x0000, 'testTag', 'hceCommand = %{public}s', JSON.stringify(hceCommand)); 239 let responseData = [0x90, 0x00]; // change the response depend on different received command. 240 hceService.transmit(responseData).then(() => { 241 hilog.info(0x0000, 'testTag', 'hceService transmit Promise success.'); 242 }).catch((err: BusinessError) => { 243 hilog.error(0x0000, 'testTag', 'hceService transmit Promise error = %{public}s', JSON.stringify(err)); 244 }); 245 } else { 246 hilog.error(0x0000, 'testTag', 'hceCommandCb error %{public}s', JSON.stringify(error)); 247 } 248} 249 250export default class EntryAbility extends UIAbility { 251 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { 252 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); 253 254 // 判断设备是否支持NFC能力和HCE能力 255 if (!canIUse("SystemCapability.Communication.NFC.Core")) { 256 hilog.error(0x0000, 'testTag', 'nfc unavailable.'); 257 return; 258 } 259 if (!cardEmulation.hasHceCapability()) { 260 hilog.error(0x0000, 'testTag', 'hce unavailable.'); 261 return; 262 } 263 264 hceElementName = { 265 bundleName: want.bundleName ?? '', 266 abilityName: want.abilityName ?? '', 267 moduleName: want.moduleName, 268 } 269 hceService = new cardEmulation.HceService(); 270 hceService.on('hceCmd', hceCommandCb); 271 } 272 273 onForeground() { 274 // 应用进入前台 275 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); 276 } 277 278 onDestroy() { 279 // 退出应用 280 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); 281 // 退出应用程序NFC标签页面时,调用tag模块退出前台优先功能 282 if (hceElementName != undefined) { 283 try { 284 hceService.stop(hceElementName); 285 } catch (error) { 286 hilog.error(0x0000, 'testTag', 'hceService.stop error = %{public}s', JSON.stringify(error)); 287 } 288 } 289 } 290} 291```