javanfcapduhceacr122

NFC reader "SELECT (by AID)" APDU is not routed to Android device


I have an ACR122U NFC reader/writer connected to my Windows machine with ACR122 driver installed.

I try to use javax.smartcardio API to send an SELECT (by AID) ADPU to my Android device (which should be in HCE mode).

This is my code:

TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
CardTerminal terminal = terminals.get(0);
System.out.println(terminal.getName());
Card card = terminal.connect("*");
CardChannel channel = card.getBasicChannel();
execute(channel, new byte[] { (byte) 0xFF, 0x00, 0x51, (byte) 195, 0x00}, card);
execute(channel, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, card); //InListPassiveTarget
execute(channel, new byte[] { (byte)0xFF, 0x00, 0x00, 0x00, 0x04,(byte)0xD4, 0x4A, 0x01, 0x00}, card); //InListPassiveTarget
execute(channel, new byte[] {0x00, (byte) 0xA4, 0x04, 0x00, 7,
                (byte)0xF0, 0x01, 0x02, 0x03, 0x04, (byte) 0x05, 0x07, 0}, card); //select AID

...

public static void execute(CardChannel channel, byte[] command, Card...cards) throws CardException {
    ByteBuffer r = ByteBuffer.allocate(1024);
    channel.transmit(bufferFromArray(command), r);
    System.out.println(convertBinToASCII(r.array(), 0, r.position()));
}

This is the output I get:

ACS ACR122 0
3B8F8001804F0CA000000306030000000000006B
C3
D54B6300
D54B010108032004010203049000

I guess 01020304 is the UID presented by my Android device to the NFC reader. The SELECT APDU returns no response, it's 0 bytes long.

On my Android device I have this service:

public class MyHostApduService extends HostApduService {

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("APDU", "APDU service was created.");
    }

    @Override
    public byte[] processCommandApdu(byte[] apdu, Bundle extras) {
       Log.e("APDU", "command apdu: " + Arrays.toString(apdu));
       return new byte[2];
    }
    @Override
    public void onDeactivated(int reason) {
       Log.e("APDU", "ON DEACTIVATED.");
    }
}

But processCommandAdpu is not getting called. Looking through the logs I wasn't able to find anything when the SELECT ADPU is supposedly sent to the reader, so it seems like the ADPU doesn't even get to the Android device.

This is the apduservice.xml for the Android project:

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

Besides there are several ADPUs which when transmitted make the NFC reader somewhat stuck. For example,

execute(channel, new byte[] {(byte) 0xFF, 0x00, 0x00, 0x00, 0x02, (byte) 0xd4, 0x04}, card);

which is a pseudo APDU to query current status of PN532 chip does not return any response. Can it be that this particular reader is flawed? How can I check it?


UPDATE (based on discussion in chat):

A test with a second reader (same model, same version) just worked. So it might either be some obscure settings on the first reader or the reader was just malfunctioning.

Both readers have the same version information:


Solution

  • You used InListPassiveTarget to directly instruct the PN532 NFC chip inside the ACR122U to manually poll for tags. This essentially bypasses the abstraction layers of the ACR122U that allow you to automatically poll for tags and use "standard PC/SC" to exchange APDU commands with the enumerated smartcard. Consequently, sending plain APDUs over the PC/SC interface won't work and the SELECT APDU will never arrive at the Android HCE side.

    Instead, you will also need to exchange APDU commands by speaking directly with the PN532 transmission module. You can do this by wrapping your APDU command inside an InDataExchange command (or InCommunicateThru if you need control over ISO/IEC 14443-4 header fields). In your case, the wrapped SELECT (by AID) command APDU would look something like:

    execute(channel, new byte[] {
        (byte)0xFF, 0x00, 0x00, 0x00,  // direct PN532 command
        16,                // Lc = command length
        (byte)0xD4, 0x40,  // InDataExchange
        0x01,              // Tag #1 (equal to the tag number from the InListPassiveTarget response)
        0x00, (byte)0xA4, 0x04, 0x00,                         // APDU: SELECT (by AID)
              7,                                              // Lc = AID length
              (byte)0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x07, // AID = F0010203040507
              0x00,                                           // Le = max
    }, card);
    

    Can it be that this particular reader is flawed?

    Yes, this could be the case though I rather doubt that. Note that there are many different versions of the ACR122U firmware and most of them seem to be flawed by design. Particularly the fact that some versions of the reader perform automatic tag enumeration and some don't, and that the available API changed drastically across different versions of that reader make it difficult to program for that device.

    UPDATE: Some more observations ...