卡模拟功能
场景介绍
设备可以模拟卡片,替代卡片完成对应操作,如模拟门禁卡、公交卡等。应用或者其他模块可以通过接口完成以下功能:
查询是否支持指定安全单元的卡模拟功能,安全单元包括HCE(Host Card Emulation)、ESE(Embedded Secure Element)和SIM(Subscriber Identity Module)卡。
打开或关闭指定技术类型的卡模拟,并查询卡模拟状态。
获取NFC信息,包括当前激活的安全单元、Hisee上电状态、是否支持RSSI(Received Signal Strength Indication)查询等。
根据NFC服务的类型获取刷卡时选择服务的方式,包括支付(Payment)类型和非支付(Other)类型。
动态设置和注销前台优先应用。
NFC应用的AID(Application Identifier,应用标识)相关操作,包括注册和删除应用的AID、查询应用是否是指定AID的默认应用、获取应用的AID等。
定义Host和OffHost服务的抽象类,应用可以通过继承抽象类来实现NFC卡模拟功能。
接口说明
NFC卡模拟功能的主要接口说明如下,在使用对应的接口前,需要申请ohos.permission.NFC_CARD_EMULATION权限。
表1 NFC卡模拟功能的主要接口
查询是否支持卡模拟功能
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用isSupported(int feature)接口去查询是否支持HCE、UICC、ESE卡模拟。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 查询是否支持HCE、UICC、ESE卡模拟,返回值表示是否支持对应安全单元的卡模拟 boolean isSupportedHce = cardEmulation.isSupported(CardEmulation.FEATURE_HCE); boolean isSupportedUicc = cardEmulation.isSupported(CardEmulation.FEATURE_UICC); boolean isSupportedEse = cardEmulation.isSupported(CardEmulation.FEATURE_ESE);
开关卡模拟及查询卡模拟状态
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用setListenMode(int mode)接口去打开或者关闭卡模拟。
4.调用isListenModeEnabled()接口去查询卡模拟是否打开。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 打开卡模拟 cardEmulation.setListenMode(CardEmulation.ENABLE_MODE_ALL); // 调用查询卡模拟开关状态的接口,返回值为卡模拟是否是打开的状态 boolean isEnabled = cardEmulation.isListenModeEnabled(); // 关闭卡模拟 cardEmulation.setListenMode(CardEmulation.DISABLE_MODE_A_B); // 调用查询卡模拟开关状态的接口,返回值为卡模拟是否是打开的状态 isEnabled = cardEmulation.isListenModeEnabled();
获取NFC信息
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用getNfcInfo(String key)接口去获取NFC信息。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 查询本机当前使能的安全单元类型 String seType = cardEmulation.getNfcInfo(CardEmulation.KEY_ENABLED_SE_TYPE); // ENABLED_SE_TYPE_ESE // 查询Hisee上电状态 String hiseeState = cardEmulation.getNfcInfo(CardEmulation.KEY_HISEE_READY); // 查询是否支持RSSI的查询 String rssiAbility = cardEmulation.getNfcInfo(CardEmulation.KEY_RSSI_SUPPORTED);
根据NFC服务的类型获取刷卡时选择服务的方式
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用getSelectionType(Sring category)接口去获取选择服务的方式。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 获取选择服务的方式 int result = cardEmulation.getSelectionType(CardEmulation.CATEGORY_PAYMENT); // SELECTION_TYPE_PREFER_DEFAULT result = cardEmulation.getSelectionType(CardEmulation.CATEGORY_OTHER); // SELECTION_TYPE_ASK_IF_CONFLICT
动态设置和注销前台优先应用
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用registerForegroundPreferred(Ability appAbility, ElementName appName)接口去动态设置前台优先应用。
4.调用unregisterForegroundPreferred(Ability appAbility)接口去取消设置前台优先应用。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 动态设置前台优先应用 Ability ability = new Ability(); cardEmulation.registerForegroundPreferred(ability, new ElementName()); // 注销前台优先应用 cardEmulation.unregisterForegroundPreferred(ability);
NFC应用的AID相关操作
静态注册AID
在配置文件config.json注册HCE服务,具体可参考Ability。
在配置文件的module中,添加metaData对象,并配置customizeData。
使用paymentAid字段静态注册支付类型的AID,多个支付类型的AID使用‘|’符号隔开。
使用otherAid字段静态注册其他类型的AID,多个其他类型的AID使用‘|’符号隔开。
示例代码如下:
"metaData": { "customizeData": [ { "name": "paymentAid", "value": "325041592E5359532E4444463031" }, { "name": "otherAid", "value": "0123456789|535558494E2E4D46|1234567890" } ] }
动态注册AID
1.调用NfcController类的getInstance(Context context)接口,获取NfcController实例。
2.调用CardEmulation类的getInstance(NfcController controller)接口,获取CardEmulation实例,去管理本机卡模拟模块操作。
3.调用registerAids(ElementName appName, String type, List<String> aids)接口去给应用注册指定类型的AID。
4.调用removeAids(ElementName appName, String type)接口去删除应用的指定类型的AID。
// 获取NFC控制对象 NfcController nfcController = NfcController.getInstance(context); // 获取卡模拟控制对象 CardEmulation cardEmulation = CardEmulation.getInstance(nfcController); // 给应用注册指定类型的AID List<String> aids = new ArrayList<String>(); aids.add(0, "A0028321901280"); aids.add(1, "A0028321901281"); Element element = new ElementName(); try { cardEmulation.registerAids(element, CardEmulation.CATEGORY_PAYMENT, aids); } catch (IllegalArgumentException e) { HiLog.error(TAG, "IllegalArgumentException when registerAids"); } // 删除应用的指定类型的AID cardEmulation.removeAids(element, CardEmulation.CATEGORY_PAYMENT); cardEmulation.removeAids(element, CardEmulation.CATEGORY_OTHER);
AID查询
调用isDefaultForAid(ElementName appName, String aid)接口,可判断应用是否是指定AID的默认处理应用:
// 判断应用是否是指定AID的默认处理应用 String aid = "A0028321901280"; cardEmulation.isDefaultForAid(element, aid);
调用getAids(ElementName appName, String type)接口去获取应用中指定类型的AID列表:
// 获取应用中指定类型的AID列表 try { cardEmulation.getAids(element, CardEmulation.CATEGORY_PAYMENT); } catch (IllegalArgumentException e) { HiLog.error(TAG, "IllegalArgumentException when getAids"); }
Host服务的抽象类
应用的服务继承HostService,实现HCE卡模拟功能。
应用自定义实现抽象方法handleRemoteCommand(byte[] cmd, IntentParams params)和disabledCallback()。
应用自定义功能。
// HCE应用的服务继承HostService,实现HCE卡模拟功能 public class AppService extends HostService { @Override public byte[] handleRemoteCommand(byte[] cmd, IntentParams params) { HiLog.info(TAG, "handleRemoteCommand" ); if (Arrays.equals(SELECT_PPSE, cmd)) { HiLog.info(TAG, "Matched PPSE select" ); return PPSE_RESP; } else if (Arrays.equals(SELECT_MASTERCARD, cmd)) { HiLog.info(TAG, "Matched Mastercard select" ); return SELECT_MASTERCARD_RESP; } else if (Arrays.equals(GET_PROC_OPT, cmd)) { HiLog.info(TAG, "Matched get processing options" ); return GET_PROC_OPT_RESP; } else if (Arrays.equals(READ_REC, cmd)) { HiLog.info(TAG, "Matched read rec" ); return READ_REC_RESP; } else if (cmd.length >= 5 && cmd[0] == (byte)0x80 && cmd[1] == (byte)0x2a && cmd[2] == (byte)0x8e && cmd[3] == (byte)0x80 && cmd[4] == 0x0f) { return COMPUTE_CHECKSUM_RESP; } else { return new byte[] {(byte)0x90, 0x00}; } } @Override public void disabledCallback(int errCode) { // 应用自定义接口实现。 } // 应用自定义功能 }
NFC消息通知
场景介绍
NFC消息通知是HarmonyOS内部或者与应用之间跨进程通讯的机制,注册者在注册消息通知后,一旦符合条件的消息被发出,注册者即可接收到该消息。
接口说明
表1 NFC消息通知的相关广播介绍
注册并获取NFC状态改变消息
- 1.构建消息通知接收者NfcStateEventSubscriber。
- 2.注册NFC状态改变消息。
- 3.NfcStateEventSubscriber接收并处理NFC状态改变消息。
// 构建消息接收者/注册者 class NfcStateEventSubscriber extends CommonEventSubscriber { NfcStateEventSubscriber (CommonEventSubscribeInfo info) { super(info); } @Override public void onReceiveEvent(CommonEventData commonEventData) { if (commonEventData == null || commonEventData.getIntent() == null) { return; } if (NfcController.STATE_CHANGED.equals(commonEventData.getIntent().getAction())) { IntentParams params = commonEventData.getIntent().getParams(); int currState = commonEventData.getIntent().getIntParam(NfcController.EXTRA_NFC_STATE, NfcController.STATE_OFF); } } } // 注册消息 MatchingSkills matchingSkills = new MatchingSkills(); // 增加获取NFC状态改变消息 matchingSkills.addEvent(NfcController.STATE_CHANGED); matchingSkills.addEvent(CommonEventSupport.COMMON_EVENT_NFC_ACTION_ADAPTER_STATE_CHANGED); CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills); NfcStateEventSubscriber subscriber = new NfcStateEventSubscriber(subscribeInfo); try { CommonEventManager.subscribeCommonEvent(subscriber); } catch (RemoteException e) { HiLog.error(TAG, "doSubscribe occur exception: %{public}s" ,e.toString()); }
注册并获取NFC场强消息
- 1.构建消息通知接收者NfcFieldOnAndOffEventSubscriber。
- 2.注册NFC场强消息。
- 3.NfcFieldOnAndOffEventSubscriber接收并处理NFC场强消息。
// 构建消息接收者/注册者 class NfcFieldOnAndOffEventSubscriber extends CommonEventSubscriber { NfcFieldOnAndOffEventSubscriber (CommonEventSubscribeInfo info) { super(info); } @Override public void onReceiveEvent(CommonEventData commonEventData) { if (commonEventData == null || commonEventData.getIntent() == null) { return; } if (NfcController.FIELD_ON_DETECTED.equals(commonEventData.getIntent().getAction())) { IntentParams params = commonEventData.getIntent().getParams(); if (params == null) { HiLog.info(TAG, "Pure FIELD_ON_DETECTED"); } else { HiLog.info(TAG, "Transaction FIELD_ON_DETECTED"); Intent transactionIntent = (Intent) params.getParam("transactionIntent"); } } else if (NfcController.FIELD_OFF_DETECTED.equals(commonEventData.getIntent().getAction())) { HiLog.info(TAG, "FIELD_OFF_DETECTED"); } HiLog.info(TAG, "NfcFieldOnAndOffEventSubscriber onReceiveEvent: %{public}s", commonEventData.getIntent().getAction()); } } // 注册消息 MatchingSkills matchingSkills = new MatchingSkills(); // 增加获取NFC状态改变消息 matchingSkills.addEvent(NfcController.FIELD_ON_DETECTED); matchingSkills.addEvent(NfcController.FIELD_OFF_DETECTED); CommonEventSubscribeInfo subscribeInfo = new CommonEventSubscribeInfo(matchingSkills); HiLog.info(TAG, "subscribeInfo permission: %{public}s", subscribeInfo.getPermission()); NfcFieldOnAndOffEventSubscriber subscriber = new NfcFieldOnAndOffEventSubscriber(subscribeInfo); try { CommonEventManager.subscribeCommonEvent(subscriber); } catch (RemoteException e) { HiLog.error(TAG, "doSubscribe occur exception: %{public}s", e.toString()); }