"Select AID" 命令并不总是路由到 Android HCE 中的 HostApduService

"Select AID" command is not always routed to HostApduService in Android HCE

问题陈述

我试图理解为什么 "Select AID" 命令并不总是成功路由到我启用 HCE 的应用程序的 HostApduService。

有时,"Select AID" 会正确路由到 HostApduService,从以下 logcat 输出可以看出:

07-11 20:11:36.386: D/NxpNfcJni(1486): RoutingManager::stackCallback: event=0x18
07-11 20:11:36.396: D/HostEmulationManager(1486): notifyHostEmulationActivated
07-11 20:11:36.396: D/NxpNfcJni(1486): RoutingManager::stackCallback: event=0x17
07-11 20:11:36.396: D/NxpNfcJni(1486): RoutingManager::stackCallback: NFA_CE_DATA_EVT; stat=0x0; h=0x301; data len=18
07-11 20:11:36.396: D/HostEmulationManager(1486): notifyHostEmulationData
07-11 20:11:36.396: D/HostEmulationManager(1486): Binding to service ComponentInfo{email.HCE/email.HCE.MyHCEService}
07-11 20:11:36.416: D/HostEmulationManager(1486): Waiting for new service.
07-11 20:11:36.466: D/MYHCESERVICE(25504): APDU RECEIVED
07-11 20:11:36.466: D/HostEmulationManager(1486): Sending data
07-11 20:11:37.276: E/SMD(284): DCD ON
07-11 20:11:37.436: D/NxpNfcJni(1486): RoutingManager::stackCallback: event=0x19
07-11 20:11:37.436: D/NxpNfcJni(1486): RoutingManager::stackCallback: NFA_DEACTIVATED_EVT, NFA_CE_DEACTIVATED_EVT
07-11 20:11:37.446: D/HostEmulationManager(1486): notifyHostEmulationDeactivated
07-11 20:11:37.446: D/MYHCESERVICE(25504): DISCONNECT
07-11 20:11:37.446: D/HostEmulationManager(1486): Unbinding from service ComponentInfo{email.HCE/email.HCE.MyHCEService}
07-11 20:11:37.446: D/MYHCESERVICE(25504): Disconnect by DEACTIVATION_LINK_LOSS
07-11 20:11:37.466: E/NfcNfa(1486): UICC/ESE[0x402] is not activated

然而,更频繁的是,"Select AID" 命令似乎并没有到达 HostApduService。更糟糕的是,在 LogCat 输出中似乎根本没有来自 NxpNfcJni 的 activity。鉴于成功尝试的 LogCat 输出摘录,我认为 NxpNfcJni 是切入点。

必须说明的是,成功的频率比失败的频率低很多。

测试设置

下面是HCE设备的描述和相应的相关代码,后面是控制reader的应用程序的描述。

1. HCE 设备

public class MyHCEService extends HostApduService {
    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
        Log.d("MYHCESERVICE", "APDU RECEIVED");
        return new byte[]{(byte)0x6F,(byte)0x25}; //returned byte array reduced for this example
    }

    @Override
    public void onDeactivated(int reason) {
        Log.d("MYHCESERVICE", "DISCONNECT");
        switch(reason) {
        case MyHCEService.DEACTIVATION_DESELECTED:
            Log.d("MYHCESERVICE", "Disconnect by DEACTIVATION_DESELECTED");
        case MyHCEService.DEACTIVATION_LINK_LOSS:
            Log.d("MYHCESERVICE", "Disconnect by DEACTIVATION_LINK_LOSS");
        }
    }    
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="email.HCE"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="19"
        android:targetSdkVersion="22" />

    <uses-permission android:name="android.permission.NFC" />

    <uses-feature android:name="android.hardware.nfc" android:required="true" />
    <uses-feature android:name="android.hardware.nfc.hce" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".MyHCEService" 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/apduservice"/>
        </service>

        <activity
            android:name=".ListBluetoothDevicesActivity"
            android:label="@string/app_name" 
            android:screenOrientation="portrait"
            android:configChanges="screenSize|orientation|keyboardHidden" >
        </activity>

    </application>

</manifest>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
           android:description="@string/servicedesc"
           android:requireDeviceUnlock="false">
    <aid-group android:description="@string/aiddescription"
               android:category="other">
        <aid-filter android:name="11223344556677889900112233"/>
    </aid-group>
</host-apdu-service>

2。 Reader申请

byte[] CMD_SelectApp = { 
    0x00, //CLA 
    0xA4, //INS
    0x04, //P1
    0x00, //P2
    0x0D, //LC
    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33 //AID
};
SCardContext _Context = new SCardContext();
_Context.Establish(SCardScope.System);
SCardReader _Reader = new SCardReader(_Context);
_Reader.Connect(SelectedReader, SCardShareMode.Direct, SCardProtocol.Any);
_Reader.Transmit(CMD_SelectApp, CMD_SelectApp.Length, RecvBuffer, ref ReceiveBufferLength);

问题

有人知道为什么 "Select AID" 命令并不总是成功吗?或者有人可以给出一些调试这个的指示?

我强烈建议使用这个开源项目,它允许您通过 运行 这个应用程序在另一个 Android phone 上测试 AID 选择。这在过去对我很有帮助。应用程序中有用于支付应用程序的预编程 AID,但您也可以手动输入自定义 AID。这比必须使用 USB reader.

更容易进行测试(在我看来)

https://github.com/doc-rj/smartcard-reader

似乎通过 'ASK RDR 417 (PC/SC) Properties'(见下图)为 ASK RDR 417 选择仅一种类型的轮询卡,HostApduService 响应正确且符合预期。

我很想知道为什么,乍一看这似乎限制了 reader 的功能。