其他分享
首页 > 其他分享> > android – 有没有办法检查终端是否收到了主机卡仿真中的最后一条消息?

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] 0A4040007A0000000043
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
        }
    }
}

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