Android NFC tutorial

Introduction

In this tutorial you will learn how to interact with an application installed on a Fidesmo Card from an Android app. During our own work in the Fidesmo App we have often found difficulties when implementing NFC interactions, so we hope this tutorial will help other developers.

This is not an attempt to replace Google’s Android NFC developer guide but more of a hands-on recipe that will get you going in no time.

This tutorial comments an example application; its source code is freely available at GitHub and you are of course welcome to download, copy and reuse it for your needs. It works with the “Hello Fidesmo!” example started in the Java Card tutorial and continued here.

What you need for this tutorial

The Android development environment is all you need. Something we like here at Fidesmo is the combination of a graphical IDE with command-line tools, and using Gradle to handle builds.

We are also using the following libraries:

Code walkthrough

This tutorial is mostly about code: how to access the phone’s NFC capabilities from your app, so it can detect a card and communicate with it.

What the example app does

This tutorial comments an example application; its source code is freely available at GitHub and you are of course welcome to download, copy and reuse it for your needs. It works with the “Hello Fidesmo!” example started in the Java Card tutorial and continued here.

If the response is not the expected one, the app assumes that the HelloFidesmo! cardlet has not been installed and calls the Fidesmo App to install it on the Fidesmo Card.

This walkthrough will focus on the NFC interactions.

Preparation

Since we are using the wonderful external library Android Annotations and the equally wonderful Nordpol library, we need to add them as dependencies in our build.gradle file:

NFC permissions

To use NFC we need to add the permission for it. This permission is not considered a dangerous permission by Google and will not show up in the 'dangerous permissions' part of the app installation flow on Google Play Store. It is also not a runtime permission which means that on Marshmallow (Android 6.0) the user will not be bothered by the permission upon install and will not be able to remove the permission from the app after install either. This code should be defined in the project specific AndroidManifest.xml file. You should also add a uses-feature tag for making sure no devices without NFC install the app (through Google Play Store) and that in turn has a required parameter which you may toggle as necessary:

Registering to NFC events

Start by declaring a TagDispatcher up top in our class, call it tagDispatcher and make it private. Like so:

Set the value of our TagDispatcher variable with the Nordpol provided TagDispatcher.get(Activity activity, OnDiscoveredTagListener onDiscoveredTagListener) method. Supply the current Activity as the first parameter to allow Nordpol to attach itself to the Activity lifecycle. The second parameter should be the callback/interface Nordpol uses to alert of any NFC interactions. Instead of a variable we can instead implement OnDiscoveredTagListener in our activity and simply supply the Activity as the second paramenter as well. OnDiscoveredTagListener requires us to override the Nordpol callback method tagDiscovered(Tag tag){ /* ... */ }. This method will be called whenever NFC interactions happen.

Leaving the tagDiscovered method empty, implementing OnDiscoveredTagListener in the Activity and setting the value of tagDispatcher in onResume() leaves us with the following:

When using Nordpol, enabling and disabling exclusive NFC for our app should be done in onResume() and onPause(). For Nordpol to function properly on all supported versions of Android you also have to pass on any received Intents in onNewIntent(Intent intent) { /* ... */ } to the library. The methods should look like this when we are done:

In the tagDiscovered method call the pre-defined setMainMessage(String message) method and pass "Reading card..." as the parameter:

When running the code and tapping a token to the phone the main TextView should add another line that says "Reading card..." If this is the case: Great! We have now successfully claimed exclusive access to the devices NFC while our app is running in foreground! Let us move on and actually send some commands.

Communicating with the card

Continue in the tagDiscovered() method, just below our setMainMessage() call, create a new IsoCard variable called isoCard. IsoCard is an interface that defines basic operations to the card. Use Nordpol's AndroidCard.get() method to create the IsoCard from the Tag that we got as a parameter in the tagDiscovered method declaration. To produce the IsoCard, Nordpol communicates with the card. If the card is removed before the library is done the generation might fail in which instance Nordpol will throw an IOException. Make sure to catch this exception by doing the call inside of a try{ /* .... */ } catch { /* ... */ }. This leaves the tagDiscovered method declaration looking like this:

To ensure legibility pass the generated IsoCard on as the sole parameter to another method called something like "communicateWithCard". Call this method just below of where we generated the IsoCard inside of our try-catch.

In this new method we will first open a connection to the card and then close it. Do this by calling isoCard.connect(); and then isoCard.close(); This too has to be done in a try-catch as the card might still be removed prematurely.

Between these two calls we need to select the app on the card that we want to talk to (the Hello Fidesmo! app) before actually doing any communication with it. We can do this very simply by using Nordpols provided Apdu.select(String appId, String versionId) method.

Using the method IsoCard.transceive(byte[] apduCommand); to transfer the select APDU command and putting the response APDU in a byte[] called response leaves us with the following communicateWithCard and tagDiscovered methods:

Make sure that the app selection went OK by comparing the status bytes of the response (the last two bytes) with the Apdu.OK_APDU variable provided by Nordpol. Nordpol has a method for exactly this called Apdu.hasStatus(byte[] statusApdu, byte[] expectedApdu); Putting the status check in the the condition of an if statement and calling setMainMessage(String message); with a status update depending on the result leaves us with the following:

Running this and tapping the card should now print "Select OK"

Additional messages

All that is left is reading the rest of the response from the app. Remove the status bytes from the response using the Nordpol method Apdu.responseData(byte[] apduResponse);. Then create a String variable and print the rest of the response to it. Finally show this message to the user with our setMainMessage(String message);

In this example we will assume that the HelloFidesmo app was not installed if the select command was not successful. Offer the user a button to install the HelloFidesmo! cardlet in that case:

Launching the Service Delivery process

As described here, calling the Fidesmo App from other app to perform a service on a card is fairly simple: we just need to create an Intent and encode the AppId and ServiceId in the Intent’s URL. In this case, we use the ones for the HelloFidesmo! cardlet:

When the Fidesmo App finishes, Android returns control to our example App, which redraws the UI to ask the user again to put the card to the phone

so that the Hello Fidesmo! message is finally shown.