Android集成ACR122U NFC读卡器与HCE通信故障排查
首先,我先梳理下你遇到的核心问题:用ACR122U读卡器连接Android设备后,发送SELECT AID的APDU根本触发不了HCE服务的processCommandApdu方法,不管是贴普通NFC标签还是Android设备,轮询后的SELECT命令都只返回一堆0,完全没法正常通信。
先拆解你的当前情况
- 目标:用ACR122U连接Android设备,实现和另一台HCE模式设备的数据交互
- 使用工具:厂商提供的
acssmc-1.1.1.jar库 - 核心操作代码:
mReader.power(slotNum, Reader.CARD_WARM_RESET); mReader.setProtocol(slotNum, Reader.PROTOCOL_T0 | Reader.PROTOCOL_T1); // 轮询ISO14443-4 A的指令 byte[] w = new byte[255]; int len = mReader.transmit(0, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, 9, w, w.length); Log.e("Response", "r: " + NfcUtils.convertBinToASCII(w, 0, len)); // 选择AID的APDU w = new byte[255]; mReader.transmit(0, new byte[] {0x00, (byte) 0xA4, 0x04, 0x00, 7, (byte)0xF0, 0x01, 0x02, 0x03, 0x04, (byte) 0x05, 0x07, 0}, 13, w, w.length); Log.e("Response", "r: " + Arrays.toString (recvBuffer)); - 测试反馈:
- 贴普通NFC标签:轮询响应为
D54B0101440004048853606A9000,但SELECT命令返回一堆0 - 贴Android设备:轮询响应为
D54B6300,SELECT命令同样返回一堆0
- 贴普通NFC标签:轮询响应为
- HCE服务情况:自定义的
MyHostApduService中processCommandApdu从未被调用,设备日志无相关记录
核心问题排查与解决建议
1. 轮询指令的兼容性问题
你发送的轮询指令是FF 00 00 00 04 D4 4A 01 00,这个指令是指定只轮询ISO14443-4 Type A的设备,但Android HCE虽然支持Type A,部分设备可能需要更灵活的轮询策略。建议修改轮询指令,试试自动适配波特率的版本:
// 自动适配波特率,查找1个目标设备 mReader.transmit(0, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x00, 0x01}, 9, w, w.length);
另外,D54B6300这个响应代码对应的是轮询失败(未找到符合要求的目标),说明读卡器没正确识别到Android的HCE设备,调整轮询策略大概率能改善这个情况。
2. APDU发送方式错误(最可能的核心问题)
你现在是直接给读卡器发送SELECT APDU,但ACR122U作为PC/SC读卡器,要和HCE设备通信,不能直接发APDU,而是需要通过读卡器的**InDataExchange控制指令(D4 40)**把APDU转发给目标设备。正确的流程应该是:
- 轮询目标设备,获取目标的CID(通信ID,从轮询响应中提取)
- 用
D4 40指令把SELECT APDU转发给目标设备
示例代码大概是这样:
// 假设轮询成功后,从响应中拿到CID(比如响应里的第5个字节,具体看ACR122U文档) byte cid = w[4]; // 要发送的SELECT APDU byte[] selectApdu = new byte[] {0x00, (byte)0xA4, 0x04, 0x00, 0x07, (byte)0xF0, 0x01, 0x02, 0x03, 0x04, (byte)0x05, 0x07, 0x00}; // 组装InDataExchange指令:FF 00 00 00 + 长度 + D4 40 + CID + APDU byte[] command = new byte[5 + 1 + 1 + selectApdu.length]; command[0] = (byte)0xFF; command[1] = 0x00; command[2] = 0x00; command[3] = 0x00; command[4] = (byte)(1 + 1 + selectApdu.length); command[5] = (byte)0xD4; command[6] = 0x40; command[7] = cid; System.arraycopy(selectApdu, 0, command, 8, selectApdu.length); // 发送指令 int len = mReader.transmit(0, command, command.length, w, w.length);
3. HCE服务配置与响应问题
检查你的AndroidManifest和HCE配置是否正确:
- 确保
MyHostApduService的声明正确:<service android:name=".MyHostApduService" android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE"> <intent-filter> <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/> </intent-filter> <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/apdu_service"/> </service> - 确保
apdu_service.xml里的AID过滤和你发送的SELECT AID一致:<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/servicedesc" android:requireDeviceUnlock="false"> <aid-group android:description="@string/aiddesc" android:category="other"> <aid-filter android:name="F0010203040507"/> </aid-group> </host-apdu-service>
另外,你的processCommandApdu返回的是new byte[2],这不符合APDU响应规范,应该返回包含状态码的响应,比如成功时返回new byte[]{(byte)0x90, 0x00},这样读卡器才能识别有效响应。
4. 读卡器固件故障
你提到在Windows、OSX上也无法正常运行,这大概率说明读卡器本身有问题(比如旧固件兼容性差,甚至硬件故障)。建议去ACR122U官方渠道下载最新固件,更新后再测试,很多类似的通信问题都是固件更新后解决的。
内容的提问来源于stack exchange,提问作者cliffroot

