During a thug life of an engineer it may occur that you will need to estabilish communication between Android HCE (Host-based Card Emulation) and your legacy microprocessor system. In my case it was a chinese CV520 NFC Transceiver which was feature-compatible with MFRC522 chips (or RC522 family).
Android HCE works on the level of APDU (Application Protocol Data Unit) defined by ISO/IEC 7816-4 standard. However, the aforementioned frontend doesn’t provide direct hardware support for APDU. While I was studying the topic, I found a lot of contradictory statements on the Internet. Some of them claimed that using such protocol is not possible at all when using such NFC frontend. I’ve also found some posts on various forums, where people were asking for help in communication debugging. From these posts, it looked like it is actually possible if you implement the whole protocol stack on your own. However, many developers got stuck on various layers or protocol stages while doing so. Due to that, I’ve decided to dig deeper and finally realized what’s going on after studying NFC norms for over a dozen hours (i.e. whole night).
If you use a simple NFC frontend which is capable of sending raw bytes through the air, it is actually possible to communicate with Android HCE. You have to implement the whole protocol stack according to ISO/IEC 14443-3 (lowest layer), ISO/IEC 14443-4 (middle layer) and ISO/IEC 7816-4 (higher layer).
ISO/IEC 14443-3: Initialization, anticollision and card selection
Symbols: “R” -> Reader (NFC transceiver frontend side), “C” -> Card (Android HCE side).
1 2 R -> C: 0x52 (wake up all request) C -> R: 0x00 (exemplary ATQA)
1 2 R -> C: 0x93 0x20 (anticollision command, request card UID) C -> R: uid0, uid1, uid2, uid3, bcc (card sends it's UID and a checksum, uid0 ^ uid1 ^ uid2 ^ uid3 == bcc)
1 2 R -> C: 0x93 0x70 uid0 uid1 uid2 uid3 bcc crclsb crcmsb C -> R: 0x20 crclsb crcmsb (SAK - ((sak & 4) == 4 - uid not complete, (sak & 32) == 32 - uid complete, otherwise card not compliant with the norm))
Note: It’s not a full anticollision loop. For cards with double or triple length UIDs you need to issue few additional commands. It also requires some changes in order to work when multiple cards are tapped against NFC reader. Refer to the information about anticollision loop provided in the standard (or anywhere else).
Note: Provided values of ATQA and SAK are exemplary and may differ, refer to the standard to figure out what’s exactly coded in them.
Note: Bytes denoted by
crcmsb are the message CRC_A code. In case of some frontends (MRFC522 included) it’s possible to calculate CRC_A using internal coprocessor. There are also some implementations on the Internet.
ISO/IEC 14443-4: Transmission protocol
1 2 R -> C: 0xE0 0x50 + crc (Request for answer to select, lower 4 bits are CID, higher 4 bits are FSDI whcih encodes maximum size of frame accepted by the reader, in our case FSDI=5 means 64 bytes) C -> R: 0x5 0x78 0x80 0x70 0x0 crclsb crcmsb (Answer to select, lower 4 bits are FSCI which encodes maximum size of frame accepted by the card, in this case FSCI=8 which means 256 bytes)
Note: Provided value of ATS is exemplary and may differ, refer to the standard to figure out what’s exactly coded in it.
At this point (after receiving ATS) it is possible to negotiate some transmission parameters, but this part is optional and I’m not going to describe it.
ISO/IEC 7816-4: Application Protocol Data Unit
From now, we need to comply with 14443-4 standard. When talking higher-level protocol we need to include I-Block frame header as well as the CRC_A at the end of each message. Simplest I-Block frame header consists of just one byte (called Protocol Control Byte). It’s the only byte which is mandatory in such frame headers. We are going to use the value of 0x02, disabling all optional features related to addressing. Be aware that the least significant bit stands for “block parity”, so it’s required to toggle this bit after each sent message (i.e. we send first message with
0x02, then next with
0x03, next with
0x02 and so on).
1 2 R -> C: 0x02 0x00 0xA4 0x04 0x00 0x07 0xF0 0x01 0x02 0x03 0x04 0x05 0x06 crclsb crcmsb (SELECT AID) C -> R: 0xF2 0x01 crclsb crcmsb (wait a little more)
We’ve succesfully sent
SELECT AID F0010203040506 command and received response
0xF2 0x01 which means (in simplification): “retry later, I need more time to respond to that”. In the meantime, Android is seeking the service which is subscribed to the aforementioned application ID and informing it, that communication was estabilished. We have to reply with exactly the same sequence in order to poll for the actual result:
1 2 R -> C: 0xF2 0x01 crclsb crcmsb (re-request information) C -> R: 0x02 0x48 0x65 0x6C 0x6C 0x6F 0x20 0x44 0x65 0x73 0x6B 0x74 0x6F 0x70 0x21 crclsb crcmsb
Here we go, we’ve received
PCB + answer + crclsb + crcmsb. After decoding to ASCII it turns out to be
Hello desktop!. Our application is selected and we can now continue the communication using almost arbitrary APDUs. Let’s send another message, e.g.
0x00 0xB0 0x00 0x00 0x0F 0xBE 0xEF (can be anything compliant with APDU protocol). Beware that we need to toggle “block parity” bit, so now we are using
0x03 as PCB:
1 2 R -> C: 0x03 0x00 0xB0 0x00 0x00 0x0F 0xBE 0xEF crclsb crcmsb C -> R: 0x03 0x4D 0x65 0x73 0x73 0x61 0x67 0x65 0x20 0x66 0x72 0x6F 0x6D 0x20 0x61 0x6E 0x64 0x72 0x6F 0x69 0x64 0x3A 0x20 0x30 crclsb crcmsb
Still works. Now the answer is
Message from android: 0.
After finishing communication, let’s deselect the card:
1 2 R -> C: 0xC2 crclsb crcmsb (deselect) C -> R: 0xC2 crclsb crcmsb (deselect OK)
Android HCE Application dispatch
Android HCE allows multiple applications to coexist at the same time. ISO/IEC 14443-3 and ISO/IEC 14443-4 is handled by the smartphone’s hardware and/or the operating system. After issuing
SELECT AID APDU command, the system is dispatching the communication to the appropriate application, according to the AID. Each application with NFC permission can subscribe to one or more AIDs.
Exemplary Android application
We know how to perform communication from the NFC frontend (microprocessor) side. Now, how to write actual Android application which will handle the communication and send these fancy
Hello Desktop! messages? For such tests, I was using a sample application grundid/host-card-emulation-sample provided on GitHub. In src/de/grundid/hcedemo/MyHostApduService.java you may find information on how to implement Android HCE service. Remember that when testing, you need to have NFC enabled with the default processing method set as “System”. When testing aforementioned application, you need to have it run in background, because it runs NFC reader mode when put in foreground.
It turns out that it is possible to communicate with Android HCE using only a low level NFC transceiver, however it needs some real effort to implement the whole protocol stack. For some legacy embedded systems there is no other way to accomplish this without replacing the actual hardware.