NFC Tag in Android: Read NDEF Tag – Text,Link, Smart Poster

This post describes how to read NFC tag in Android. Android SDK provides a set of API that can be used to read the NFC payload in Android.  Reading the NFC specification, there are several types of nfc tag format, depending on the type of the tag.
This post describes how to read complex NFC tag in Android.
As we said in the previous post, explaining how to implement Android NFC app , there are several types 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]

It is possible to 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.

nfc record structure

Getting started NFC Tag in Android: NDEF Record Structure

Before analyzing how to read NDEF content, it is important to know the NDEF record structure. The picture below shows the structure:
nfc ndef record 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 is 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.

NFC Well-known type: Text Record

The NFC Well-Known type is the simplest record type to read NFC tag in Android. To get the NFC payload, the first thing to do is reading 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";
  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) {

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 NFC payload, and if we parse it we get:

create nfc tag app


NFC Forum external type

This is another simple NDEF content. This type of content is built for an organization that wants to create a custom namespace. This type of content can be useful when we want to create a special namespace 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 NFC payload is the content.

NFC 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);
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);

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:

nfc technology: read smart poster

We can suppose now we have a tag containing a telephone number with a label:

nfc tag with telephone number


Now, you know how to read NFC tag in Android, extract the content from the payload and develop an NFC application in Android.

More posts...
  • adilah

    Hi, is it possible to show the full program code please? 🙂

    • At the end of the post there’s a link to github. There you will find the source code.

  • Gregory Kogan

    I’m trying to send APDU to Mifare DESFIRE card using Galaxy Grand Prime(model SM-G530T and Android version 5.1.1). It response with some data and 91 AF at the end which is correct. But after second command with instruction AF it shows 91 1C. It looks like my card got reset, but still connected. I’m using Android Studio for the development and debug. But if using different tool (written in C#)on computer just for sending APDUs, I got no problem.