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:
- ACR122U firmware version: 41435231323255323135 (-> ACR122U215)
- PN532 version: D503 32010607 9000 (-> PN532 v1.6)
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 ...
The response to the InListPassiveTarget command does not contain ATS data (after the UID field). Maybe your reader does not perform automatic RATS during tag selection. This can be enabled using the SetParameters command (before InListPassiveTarget):
execute(channel, new byte[] {
(byte)0xFF, 0x00, 0x00, 0x00, // direct PN532 command
3, // Lc = command length
(byte)0xD4, 0x12, // InDataExchange
(1<<4), // fAutomaticRATS = 1
}, card);
You could also try to manually send a RATS command using InCommunicateThru:
execute(channel, new byte[] {
(byte)0xFF, 0x00, 0x00, 0x00, // direct PN532 command
4, // Lc = command length
(byte)0xD4, 0x42, // InCommunicateThru
(byte)0xE0, 0x80, // RATS (FSD = 256, CID = 0)
}, card);
After that you could try to communicate with the card using InCommunicateThru and raw ISO/IEC 14443-4 blocks:
execute(channel, new byte[] {
(byte)0xFF, 0x00, 0x00, 0x00, // direct PN532 command
16, // Lc = command length
(byte)0xD4, 0x42, // InCommunicateThru
0x02, // PCB (I-block, change to 0x03 for the next block)
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);
The ATQA 0803
looks rather odd. Especially the 0x03 in the bit frame anticollision field suggests that there is more than one target in the field (since a standards compliant tag would only set a single bit in the bit frame anticollision field). Note that this is not true. The ATQA in the response to InListPassiveTarget is transmitted little endian. Consequently, the bit frame anticollision value is 0x08 (= valid/compliant) and the value in the proprietary field is 0x03.
It's, indeed, strange that your reader does not respond to certain PN532 commands (particularly since the firmware version 32010607
looks fine). I've tested some of the commands that fail for you with another ACR122U and they completed successfully ...