android – 有没有办法检查终端是否收到了主机卡仿真中的最后一条消息?
作者:互联网
在我的Android应用程序中,我有HostApduService的实现.以下是它的实现片段:
public final class MyHostApduService extends HostApduService {
private boolean disconnected = false;
@Override
public byte[] processCommandApdu(byte[] commandApdu, @Nullable Bundle extras) {
//process apdu in a background thread and call sendResponseApdu() when ready
Single.fromCallable(() -> processInternal(commandApdu))
.subscribeOn(nfcScheduler)
.subscribe(this::sendResponseApdu, t -> Log.e("could not create response", t));
return null;
}
...
@Override
public void onDeactivated(int reason) {
disconnected = true;
}
private void processInternal(byte[] apdu) {
//business logic
if(!disconnected) {
//last message was probably received by the terminal
}
}
}
因此,在我的观察中,onDeactivated()回调可以在processCommandApdu()的中间进行,甚至那时OS似乎更早地认识到NFC字段丢失比调用onDeactivated()更早.
以下是通信过程中丢失字段的示例:
16:21:16.808 I/MyHostApduService : processApdu[request|13bytes] 0A4040007A0000000043public final class MyHostApduService extends HostApduService {
private boolean disconnected = false;
@Override
public byte[] processCommandApdu(byte[] commandApdu, @Nullable Bundle extras) {
//process apdu in a background thread and call sendResponseApdu() when ready
Single.fromCallable(() -> processInternal(commandApdu))
.subscribeOn(nfcScheduler)
.subscribe(this::sendResponseApdu, t -> Log.e("could not create response", t));
return null;
}
...
@Override
public void onDeactivated(int reason) {
disconnected = true;
}
private void processInternal(byte[] apdu) {
//business logic
if(!disconnected) {
//last message was probably received by the terminal
}
}
}
16:21:16.811 D/MyHostApduService : do business logic
16:21:16.890 D/HostEmulationManager: notifyHostEmulationDeactivated
16:21:16.890 D/HostEmulationManager: Unbinding from service ComponentInfo{app.debug/internal.MyHostApduService}
16:21:16.890 I/MyHostApduService : onDeactivated LINK_LOSS
16:21:16.898 I/MyHostApduService : processApdu[response|2bytes|90ms] 6A82
解决方法:
你不能.相反,如果您确实需要可靠地检测到该情况,则需要调整通信协议.
问题不是Android,而是基础通信协议(ISO / IEC 7844-4,ISO / IEC 14443-4).这些协议是为与常规智能卡通信而构建的.智能卡是完全无源的设备,当从读卡器中拉出或远离NFC RF场时,不能继续处理(由于缺乏能量).
协议栈设计用于询问器驱动的通信(其中询问器是终端).通信在命令 – 响应序列中执行.原则上,每个命令响应序列包括以下步骤(带有一些额外的极端情况):
>终端发送命令.
>智能卡接收并处理命令.
>智能卡发送响应数据并以状态字结束.
应用协议(ISO / IEC 7816-4)和传输协议(ISO / IEC 14443-4又名ISO-DEP)都不通过任何形式的确认来确认智能卡响应.一旦智能卡发送其响应,就认为已完成处理.
实际上,这对于经典智能卡(接触式或非接触式)来说不是问题.中断的通信将导致卡重新上电(因为链路丢失也意味着功率损耗或者因为终端执行显式重置).因此智能卡将无法在此时依赖清理序列.
但是,这并不意味着没有办法克服这种限制.经典智能卡即使在电源循环中也能保持持久状态.关键操作作为原子事务执行.在断电的情况下,通常在复位(启动)时执行清理/回滚.但是,由于链接丢失不会导致HCE端的执行被中断,因此映射到Android并不容易.因此,在将响应发送回读取器之前,无法检测到HCE智能卡被拉出.然而,也没有可能被链路丢失中断的原子事务.因此,重置(即接收选择应用程序的SELECT(通过DF名称)命令)仍然是执行清理的正确位置,例如重置应用程序状态.
关于您的特定要求,典型的方法是调整应用程序级协议并添加确认命令,以确认接收(然后是第二个)最后一个响应.即如果你现在有类似的东西:
T---> SELECT APPLICATION <---C FCI | 9000 T---> PERFORM CRITICAL OPERATION <---C CRITICAL OPERATION RESULT
您可以调整协议以包含最终确认:
T---> SELECT APPLICATION <---C FCI | 9000 T---> PERFORM CRITICAL OPERATION <---C CRITICAL OPERATION RESULT T---> CONFIRM RECEPTION OF RESULT <---C 9000
现在你真的不在乎最终的响应(9000)是否在去往终端的路上丢失了.
标签:android,nfc,apdu,contactless-smartcard,hce 来源: https://codeday.me/bug/20190701/1348009.html