Android NFC Tutorial – Practical Step-by-step guide to build Android NFC app

Practical Android NFC Tutorial with a step-by-step guide to build an Android NFC app. Learn how to develop Android NFC app to read NFC Tags

This Android NFC Tutorial describes how to build an Android NFC app to read NFC Tgas. The NFC technology stands for Near Field Communication and you can find the specification at NFC Forum. In this Android NFC tutorial, we analyze some fundamental aspects of NFC in Android, and we will describe how to implement an Android NFC app to read tags.
If you want to experiment with NFC, there are several websites where you can buy NFC with a few euro.
NFC can be used in a different situation: we can use it to turn on our Wifi when we are at home or launch task actions and so on.
We will focus our attention on NDEF data that is a special type of NFC tag. There are some basic steps we have to follow before using the NFC.

Getting started with developing an Android NFC App: NFC Filter

When we develop an Android app to read NFC tags, the first thing we want is our app is notified when we get near an NFC tag. To this purpose, we use an intent filter. Android SDK provides three different filters that we can use with different level of priority:

  • ACTION_NDEF_DISCOVERED
  • ACTION_TECH_DISCOVERED
  • ACTION_TAG_DISCOVERED
We focus our attention on ACTION_NDEF_DISCOVERED, that has the highest level of priority. As said, our goal is being notified when the smartphone is near a NFC tag and, if we have only this app installed and capable to handle this NFC tag, we want that the app starts immediately. To do it, we register the filter in the Manifest.xml:
[xml]<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.survivingwithandroid.nfc" >
….
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
</intent-filter>
<manifest>
[/xml]

At line 6 we register our app so that it can be notified with ACTION_NDEF_DISCOVERED.
We can use different types of filter, in this example (at line 8) we used the mime type. In other words when an NFC tag (NDEF format) is discovered and it has a mime type text/plain then our app will be started. We can filter using several mime types not only text/pain. We can, moreover, use other types of filters like android:scheme to filter using the protocol or using a string pattern.

Android NFC – Foreground Dispatch

Filtering with intents works if our app is not in a foreground. If our app is running in the foreground, it won’t be notified, if move our smartphone near an NFC tag. In this case, we have to use a different technique called NFC Foreground dispatch.
The first step is defining in our code the intent filter (as we did in the manifest.xml):

[java] @Override
protected void onCreate(Bundle savedInstanceState) {

Intent nfcIntent = new Intent(this, getClass());
nfcIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

nfcPendingIntent =
PendingIntent.getActivity(this, 0, nfcIntent, 0);

IntentFilter tagIntentFilter =
new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
tagIntentFilter.addDataType("text/plain");
intentFiltersArray = new IntentFilter[]{tagIntentFilter};
}
catch (Throwable t) {
t.printStackTrace();
}
}
[/java]

Now we have to register our filter, and we do it in onResume method, in this way:

[java] protected void onResume() {
super.onResume();
nfcAdpt.enableForegroundDispatch(
this,
nfcPendingIntent,
intentFiltersArray,
null);
handleIntent(getIntent());
}
[/java]

We should, also, remember to disable foreground dispatch as soon as the app gets in the background and the best place to do it is in onPause method.

[java] @Override
protected void onPause() {
super.onPause();
nfcAdpt.disableForegroundDispatch(this);
}
[/java]

where nfcAdpt is the NFC Adapter.

Handle NFC Tag using NFCAdapter

Once we created our filters, we have to interact with the NFC component in our smartphone. For this purpose, we use NfcAdapter, provided by Android SDK. Using this class, we can check, for example, if the NFC is supported by our smartphone or if the NFC is turned on or off:

[java] @Override
protected void onCreate(Bundle savedInstanceState) {

nfcAdpt = NfcAdapter.getDefaultAdapter(this);
// Check if the smartphone has NFC
if (nfcAdpt == null) {
Toast.makeText(this, "NFC not supported", Toast.LENGTH_LONG).show();
finish();
}
// Check if NFC is enabled
if (!nfcAdpt.isEnabled()) {
Toast.makeText(this, "Enable NFC before using the app",
Toast.LENGTH_LONG).show();
}
}
[/java]

How to read NFC Data Payload in Android

Once we know how to handle NFC tag, we want to read the tag content. There are several type of content defined in NFC specs:

  • NFC Forum well-known type
  • Media-type
  • Absolute URI
  • NFC Forum external type

Each type has it is own payload. Generally speaking, a NFC NDEF data is composed by a Message. A message can contain one or more records. Each record is made by an header and a payload (the real information).
By now, if we want to read the data inside a NFC NDEF tag we can use:

[sociallocker] [java] @Override
public void onNewIntent(Intent intent) {
getTag(intent);
}

private void getTag(Intent i) {
if (i == null)
return ;

String type = i.getType();
String action = i.getAction();
List<ndefdata>dataList = new ArrayList<ndefdata>();

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

Parcelable[] parcs = i.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

for (Parcelable p : parcs) {
recNumberTxt.setText(String.valueOf(numRec));
NdefRecord[] records = msg.getRecords();
for (NdefRecord record: records) {
short tnf = record.getTnf();
// Here we handle the payload
}
}
}
}
[/java] [/sociallocker]

How to read an NFC Tag in Android

Now it is time to describe how to read NFC tag in Android before developing an Android NFC App.  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.

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:

[java]short tnf = record.getTnf();[/java]

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

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

[java]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";
[/java]

The bit from 5th to 0 represents the language length:

[java]int ianaLength = status && 0x3F; // Bit mask bit 5..0[/java]

Now we are ready to read the “real” content:

[java] try {
String content = new String(payload, ianaLength + 1,
payload.length – 1 – ianaLength, encString);
record.payload = content;
}
catch(Throwable t) {
t.printStackTrace();
}
[/java]

Let us suppose we create simple text/plain content with Surviving with Android. Now if we read the content we have:

[text]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[/text]

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:

[java]StringBuffer pCont = new StringBuffer();
for (int rn=0; rn < payload.length;rn++) {
pCont.append(( char) payload[rn]);
}
[/java]

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:

[java]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
[/java]

Now we know how many bytes is the payload length and then we have to get the length:

[java]String byteLen = "";
for (int p = 2; p <= 2 + numLenByte – 1; p++)
byteLen = byteLen + payload[p]; // We simply append the bytes
[/java]

Then we read the record type to know how to handle it:

[java]int pos = 2 + numLenByte;
int type = payload[pos];
[/java]

We can parse the payload according to the record type:

[java]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 &amp;lt;= 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);
}
}[/java]

And finally we move to the next message part:

[java]payload = Arrays.copyOfRange(payload,
pos + Integer.parseInt(byteLen), payload.length);[/java]

…and of course, we repeat all these things until the payload length is greater than 0. That’s all.

Let us suppose we have a NDEF Tag that has a link that points to this website and a text part like surviving.

The payload is:

[text]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[/text]

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:

 

Now, you know how to read Android NFC app, extract the content from the payload and handle the NFC tags in Android. Once you know how to handle NFC tag you can develop an Android NFC app to execute common tasks as soon as the Android app detects a tag. According to the NFC tag content, you can enable or disable the WiFi for example or open a link and so on. Yo have endless possibilities to explore and implement using the Android NFC app.

 

    1. Enrique Perez June 9, 2016
    2. Pingback: Write NFC tag in Android: How to August 7, 2016
    3. Sherp Avert August 17, 2016
      • Sherp Avert August 17, 2016
      • Francesco Azzola August 18, 2016
    4. Guillermo Herdez September 13, 2017
    5. NULL February 18, 2020

    Add Your Comment