NFC Android: Read NDEF Tag
In this post we want to describe how to read NFC tag. Android SDK provides a set of API that can be used to read the NFC payload, anyway if we look at the NFC spec we can notice there are several type of payload depending on the type of the tag. Android SDK doesn’t provide any support to parse the payload and extract information, so in this post we will describe how we can read the NFC tag content. As we said in the previous post, explaining how to use NFC in Android, there are several type of NFC (NDEF) tag:
- NFC Forum well-known type [NFC RTD]
- Media-type as defined in RFC 2046
- Absolute URI as defined in RFC 3986
- NFC Forum external type [NFC RTD]
We can know the NFC type using the last three bytes in the NFC header or simpler using:
short tnf = record.getTnf();
Comparing the tnf (Type Name Format) with all possible combination we can know the record type. In the code above record is an instance of NdefRecord.
NDEF Record Structure
Before analysing how to read NDEF content, it is important to know the NDEF record structure. The picture below shows the structure:
The most important byte (7th) is the Message Begin byte, this byte is 1 if the it is the starting message otherwise is zero. The 6th byte is the Message End, this byte is 1 if the this record is the end record otherwise is 0. SR is the Short Record and it is 1 if it is a short record. This information are important if we want to handle the NDEF tag data correctly.
We know that Android SDK provides the method getPayload()
that returns an array of bytes that represent the tag content. We have to read and parse this array to extract information.
Let’s start from the simplest record structure: text record.
Well known type: Text Record
This is the simplest record type and we will start from here. We know from the specification how to read the payload. The first thing we have to read is the header (payload[0]) and parse it. The most significative byte (the 7th) represent the text encoding:
byte status = payload[0]; int enc = status & 0x80; // Bit mask 7th bit 1 String encString = null; if (enc == 0) encString = "UTF-8"; else encString = "UTF-16";
The bit from 5th to 0 represents the language length:
int ianaLength = status & 0x3F; // Bit mask bit 5..0
Now we are ready to read the “real” content:
try { String content = new String(payload, ianaLength + 1, payload.length - 1 - ianaLength, encString); record.payload = content; } catch(Throwable t) { t.printStackTrace(); }
Let us suppose we create simple text/plain content with Surviving with Android. Now if we read the content we have:
02 65 6e 53 75 72 76 69 76 69 6e 67 20 77 69 74 68 20 61 6e 64 72 6f 69 64
This is the payload, and if we parse it we get:
NFC Forum external type
This is another simple NDEF content. This type of content is built for organization that want to create a custom name space. This type of content can be useful when we want to create a special name space to run an app for example. Reading it is very simple:
StringBuffer pCont = new StringBuffer(); for (int rn=0; rn < payload.length;rn++) { pCont.append(( char) payload[rn]); }
All the payload is the content.
NDEF Smart poster
This is the most complex NDEF tag, because it can be made by several inner contents made by several types. In this case is very important to read the message header to know if the record is a Message Begin or Message end or if the record is a Short Record.
The first thing we have to do is read the header:
int[] result = getHeader(payload); // 0 = MB, 1 = ME, 2 = SR int numLenByte = 1; if (result[2] == 0) numLenByte = 4; // This is not a Short Record. Length = 4 byte
Now we know how many bytes is the payload length and then we have to get the length:
String byteLen = ""; for (int p = 2; p <= 2 + numLenByte - 1; p++) byteLen = byteLen + payload[p]; // We simply append the bytes
Then we read the record type to know how to handle it:
int pos = 2 + numLenByte; int type = payload[pos];
We can parse the payload according to the record type:
if (type == 'U') { RDTUrl url = new RDTUrl(); result = getHeader(payload); url.MB = result[0]; url.ME = result[1]; url.SR = result[2]; url.prefix = payload[pos]; Log.d("NFC", "Action:" + "0x" + Integer.toHexString(url.prefix)); url.url = new String(payload, pos + 1, Integer.parseInt(byteLen) - 1); Log.d("NFC", "Content:" + url.url); record.records.add(url); } else if (type == 'T') { RDTTextRecord text = new RDTTextRecord(); result = getHeader(payload); text.MB = result[0]; text.ME = result[1]; text.SR = result[2]; int len = payload[pos]; Log.d("Nfc", "Lan len ["+len+"]"); text.language = ""; for (int i = pos + 1; i <= pos + len; i++) text.language = text.language + (char) payload[i]; Log.d("Nfc", "Lang ["+text.language+"]"); text.payload = new String(payload, pos + len + 1, Integer.parseInt(byteLen) - len - 1); Log.d("NFC", "Content:" + text.payload); record.records.add(text); } }
And finally we move to the next message part:
payload = Arrays.copyOfRange(payload, pos + Integer.parseInt(byteLen), payload.length);
…and of course we repeat all these things until the payload length is greater than 0. That’s all.
Let us suppose we a NDEF Tag that has a link that points to this website and a text part like surviving.
The payload is:
ffffff91 1 19 55 1 73 75 72 76 69 76 69 6e 67 77 69 74 68 61 6e 64 72 6f 69 64 2e 63 6f 6d 51 1 c 54 2 65 6e 73 75 72 76 69 76 69 6e 67
Now, if we run our app we get:
We can suppose now we have a tag containing a telephone number with a label:
Reference: | NFC Android: Read NDEF Tag from our JCG partner Francesco Azzola at the Surviving w/ Android blog. |