NFC tags, NDEF, reader mode, and HCE
NFC supports several modes:
- reader/writer: phone reads or writes a nearby passive tag;
- peer-oriented mechanisms (historically available, product/platform dependent);
- card emulation: phone behaves like a contactless card, including Host Card Emulation (HCE).
NDEF: standard records
NDEF messages contain records with type/name format, type, ID, and payload. Common records represent text, URI, MIME content, or external application types.
fun parseTextRecord(record: NdefRecord): String? {
if (record.tnf != NdefRecord.TNF_WELL_KNOWN ||
!record.type.contentEquals(NdefRecord.RTD_TEXT)
) return null
val payload = record.payload
if (payload.isEmpty()) return null
val languageLength = payload[0].toInt() and 0x3F
val utf16 = payload[0].toInt() and 0x80 != 0
val textStart = 1 + languageLength
if (textStart > payload.size) return null
return payload.copyOfRange(textStart, payload.size)
.toString(if (utf16) Charsets.UTF_16 else Charsets.UTF_8)
}
Treat tag data as untrusted. Bound record count/size; allowlist schemes/types; never auto-open arbitrary URLs, deserialize objects, run commands, or trust a tag UID as an unforgeable credential.
Reader mode
For a foreground scanning screen, reader mode gives explicit lifecycle control and technology flags:
override fun onResume() {
super.onResume()
nfcAdapter?.enableReaderMode(
this,
::onTagDiscovered,
NfcAdapter.FLAG_READER_NFC_A or
NfcAdapter.FLAG_READER_NFC_B,
null,
)
}
override fun onPause() {
nfcAdapter?.disableReaderMode(this)
super.onPause()
}
Tag I/O is blocking; perform it off the main thread, enforce timeouts, close technologies, and expect TagLostException when the user moves away.
Writing safely
Check writable state, maximum NDEF size, and format support before writing. Verify by rereading if correctness matters. A physical tag can be removed mid-write; design recoverable provisioning states instead of assuming atomic replacement.
Host Card Emulation
HCE routes APDU commands from an NFC reader to your HostApduService on the host CPU. The service selects application identifiers (AIDs) and implements a strict command/response protocol.
Reader -> SELECT AID -> Android routes to HostApduService
-> command APDU -> validate state/length/instruction
<- response data + status word
HCE without a secure element does not magically protect secrets against a compromised device. Payment/access-control systems require protocol cryptography, replay protection, provisioning, server risk controls, key lifecycle, and compliance analysis.
Many identifiers can be cloned, emulated, observed, or vary by tag technology. Use cryptographic challenge-response or server validation for security-sensitive access.
Sources
Exercise
Design museum tags that open exhibit content offline and an employee door-access system. Compare threat models and show why the same simple NDEF URL is acceptable for one but not the other.