Tips and tricks for NFC on Android

NFC interface is becoming more popular (again) thanks to payments via NFC. Companies recognizes that this method is very friendly for transfer small packages of data or authenticate user via NFC and then transfer more data via Bluetooth.

We did some mobile applications for reading and writing device configuration into memory chip that communicates via NFC interface. Let’s look at some issues and how we solved it. 

Everything starts with chips

NDEF NFC chips are the most common type for reading some information using NFC. For example, you are in the ZOO and there is NFC tag near the animal cage, so you attach your phone on it and web browser in the phone will open some webpage. Simple. But how to work with chips that support different technology and the use-case is a little more advanced?

When you want to start using NFC chip, you must declare these lines into AndroidManifest file, that your app will use NFC and can access to NFC.

<manifest package="com.dactylgroup.someproject.android"
   xmlns:android="http://schemas.android.com/apk/res/android">

   <uses-permission android:name="android.permission.NFC" />
   <uses-feature android:name="android.hardware.nfc" />

   <application>
   .
   .
</manifest>

There are three categories of NFC technologies ordered by its priority. Highest priority has ACTION_NDEF_DISCOVERED, next ACTION_TECH_DISCOVERED and lowest ACTION_TAG_DISCOVERED.
These categories must be declared in AndroidManifest file too, specifically in activity part, where you want to scan NFC tags. You can specify only that categories which your app will support.

So paste these lines there:

<activity
   android:name="com.dactylgroup.someproject.android.controller.SomeActivity"
   android:launchMode="singleInstance"
   android:theme="@style/AppTheme ">

   <intent-filter>
  	<action android:name="android.nfc.action.NDEF_DISCOVERED" />

  	<category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
   <intent-filter>
  	<action android:name="android.nfc.action.TECH_DISCOVERED" />

  	<category android:name="android.intent.category.DEFAULT" />
   </intent-filter>
   <intent-filter>
  	<action android:name="android.nfc.action.TAG_DISCOVERED" />

  	<category android:name="android.intent.category.DEFAULT" />
   </intent-filter>

   <meta-data
  	android:name="android.nfc.action.TECH_DISCOVERED"
  	android:resource="@xml/nfc_tech_filter" />
</activity>

Files are also important

You can see that there is some resource file at the end, that I have not mentioned already. That is the list of technologies that your app will use. It is xml file that you will put into resource folder, specifically into xml folder with some name that you type into AndroidManifest too and by default the list looks like this:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>

        <tech>android.nfc.tech.IsoDep</tech>

        <tech>android.nfc.tech.NfcA</tech>

        <tech>android.nfc.tech.NfcB</tech>

        <tech>android.nfc.tech.NfcF</tech>

        <tech>android.nfc.tech.NfcV</tech>

        <tech>android.nfc.tech.Ndef</tech>

        <tech>android.nfc.tech.NdefFormatable</tech>

        <tech>android.nfc.tech.MifareClassic</tech>

        <tech>android.nfc.tech.MifareUltralight</tech>

    </tech-list>
</resources>

But before copying read this! This list declares that your NFC device/chip support all of these technologies. If not (probably on 100%) Android devices do not discover the NFC chip as a ACTION_TECH_DISCOVERED (second priority) but as ACTION_TAG_DISCOVERED (that category with lowest priority). If you want to support technologies for more types of chips, you can define more tech-lists in same xml file like this.

<tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
</tech-list>
<tech-list>
        <tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>

The priority is important

Maybe you are thinking who cares about some priority? If your app will have last priority and for example ST25 NFC Tap app (will be mentioned later) can read that same chip with higher priority. it automatically opens this app and not yours. If apps can open technology with same priority, Android will show you system dialog where can you pick desired application.

Let's play with Tech-list

One more tip. Define tech-list most specific as much as possible. We defined one tech-list like this code below, because that chip supports both of these technologies. Nexus6P reads it as ACTION_TECH_DISCOVERED, but Samsung phones only as ACTION_TAG_DISCOVERED. So there was that problem with priority mention few lines above. We worked only with NfcV technology, so we have removed NdefFormatable and now Samsung phone detects it as ACTION_TECH_DISCOVERED too.

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.NfcV</tech>

        <tech>android.nfc.tech.NdefFormatable</tech>
    </tech-list>
</resources>

If you don’t know, which technology does your NFC tag supports, the easiest way is to show it with ST25 NFC Tap app, which can show you supported technologies on one of its pages.

So this was beginning part how to detect NFC chip. Now let’s get to know how to read and write data.

Communication with a chip

If you declare android:launchMode="singleInstance" in AndroidManifest inside activity tag (activity where you want to scan NFC tags), Android do not create activity again and again after scan, but creates it as only one instance and opens it after next scans.

When you touch your device on NFC interface of memory chip, there will be called method inside activity override fun onNewIntent(intent: Intent). You can get Tag instance via intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) from that intent.

NfcV technology 

This part will be specific for NfcV technology which we used for our purpose. You can get instance of NfcV for communication with memory chip via val nfcV = NfcV.get(tag). There is a tag as a parameter that we got earlier from intent.

If you want to start communicating with NFC, call nfcV.connect(). If you want to terminate communication, just call nfcV.close(). How simple. If you want to read/write data, call method nfcV.transceive(readParams : ByteArray). These parameters for transceive method is most important in this process. You can specify there, if you want to R/W data, how many blocks do you want to R/W, where you want to start R/W and so on. This is a very complex part and it may be specified in a whole new blog post in the future.
Result of transceive method is ByteArray which you can convert into hexadecimal String and then process data for your purpose.

If you call parameters for writing in transceive method, you will get results from this method as a result of the operation. If you convert this result into hexadecimal String, for example code “00” means successful writing. Here is whole documentation with commands for R/W, all response codes and so on. Response codes are specifically on the page 22.

One more useful tip

And one more tip that I will mention in this blog post. If you have your app open and you will touch your device to the NFC chip, system shows you system dialog for choosing app for reading again. This can be very annoying. You can solve this problem with foreground dispatch system. Use this code. The same rules are applied in this code as were mentioned above: Specify NFC category with highest priority that your chip can support and specify only that NFC technologies which you are using.

So I hope we exposed you some basic advices of working with NFC interface.

You might also like