Skip to content
Language: EN

Which controller limits must Android code check at runtime?

Everything the reference exposes as a getter rather than a constant: the maximum transceive length, support for extended length APDUs, the timeout, and which optional technology classes appear at all. Two phones on the same Android version can answer differently, so code reads these values on the tag in hand instead of copying those of the test phone.

How much one command can carry

getMaxTransceiveLength(), on NfcA, NfcV or IsoDep, returns the largest number of bytes one transceive() call can send. It depends on the phone's controller, and the reference gives no value to assume.

APDUs add a second limit. The IsoDep reference explains that a standard APDU carries at most 255 bytes of payload, 261 bytes in all, while an extended length APDU carries up to 65535, and that some adapters do not support the extended form. isExtendedLengthApduSupported() answers for the phone in hand.

A card that expects extended APDUs therefore works on one phone and fails on another running the same code. Where the card also accepts the standard form, fall back to it rather than assuming support.

How long the phone waits

setTimeout() on NfcA or IsoDep sets how long transceive() waits for an answer. The NfcA reference suggests a longer value for operations that take the tag time, such as key generation, and notes that the value returns to its default when the connection closes.

The default itself is not documented. Read it with getTimeout() rather than hardcoding the figure measured on one phone, and set your own value again after every connect().

Which classes exist

The optional classes are the other variable. MifareClassic, MifareUltralight and NfcBarcode may be missing from a phone altogether, and NdefFormatable appears only for tags the phone can format.

The only reliable test is getTechList() on the tag in hand, since the absence is silent. The technology classes page covers what each class gives you.

What the user and the system switch off

getDefaultAdapter() returns null when the phone has no NFC controller, and isEnabled() returns false when the user has switched NFC off. The NfcAdapter reference suggests sending the user to Settings.ACTION_NFC_SETTINGS in the second case.

From Android 16, a user can also refuse tag intents for one app, which isTagIntentAllowed() reports. The adapter is on, the app is installed, and tags still never arrive, so this is worth checking before any debugging.

From API level 34, getNfcAntennaInfo() returns what the phone knows about its antennas, including their position relative to the device, and may return null. Where it answers, an app can show the user where to hold the tag instead of describing it in words.

Frequently asked questions

What transceive length is safe to assume?

None. Read getMaxTransceiveLength on the connected technology and split commands to fit.

Why does an APDU work on one phone and fail on another?

It may use the extended length form, which some adapters do not support. Check isExtendedLengthApduSupported and fall back to standard APDUs where the card allows it.

Does a timeout set with setTimeout persist?

No. The reference says it returns to the default when the connection is closed, so set it again after each connect.

Can an app find out where the NFC antenna is?

From API level 34, getNfcAntennaInfo returns antenna information, including relative position, when the phone provides it. It may return null, so the app needs a fallback.