Monday, March 16, 2015

NFC Android: Read NDEF Tag - Text,Link, Smart Poster

Near Field Communication (NFC) in Android - Advanced tutorial: Use Android smartphone to read complex NFC payload

Topics covered

NFC

NDEF Record structure

Read NFC payload

NFC Smart poster, text, link

This post describes how to read NFC tag. 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 type 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 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]



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.

Master NFC in Android

NFC - 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:

record data structure in NFC

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.

NFC Well-known type: Text Record

The NFC Well-Known type is the simplest record type and we will start from here. 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";
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 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 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 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);
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:

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


Source code available @github

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


NFC Android: Read NDEF Tag - Text,Link, Smart Poster Rating: 4.5 Diposkan Oleh: Unknown

0 comments:

Post a Comment