Linux‎ > ‎Android‎ > ‎

Using NFC

Our Tag

NFCV (ISO/IEC 15693 / Vicinity)
256 Bytes / Block Size = 4 / Num Blocks = 64 (0-63) (0-0x3f)
Type:  Tag-it HF-I Plus Chip  (thats hf-i)
Manufacturer:  Texas Instruments (France)
See datasheet attached at the end


Introduction

Android received limited support for NFC in version 2.3

This page was initially a copy from work done by Jesse Chen http://www.jessechen.net/blog/how-to-nfc-on-the-android-platform/
I will be updating it with my own experience as I build my own NFC based application
I am starting with the experiences detailed by Jesse.

More information on Android NFC can also be found here http://developer.android.com/guide/topics/connectivity/nfc/index.html

Someone else struggling with this

Starting with Android 2.3, the platform includes an NFC stack and framework API that allows you to read/write to NDEF (NFC Forum Data format) tags. For Android smartphones, that means the requirement is to be running at least Android 2.3 and have a NFC chip on the phone.
My application is targeted for Gingerbread so that it can be used with some of the cheaper Android devices out of China.

In the Android Manafest use the following to make an NFC application

<!-- Permission to use NFC --> 
<uses-permission android:name="android.permission.NFC" />
<!-- Must have min version 10  -->
<uses-sdk android:minSdkVersion="10"/>
<!-- Don't display in Google Play if hardware model doesnt support NFC -->
<uses-feature android:name="android.hardware.nfc" android:required="true" />


P2P Data Transfer – Add Friends Example

Two NFC-enabled devices have the ability to transfer data to each other simultaneously. For example, two people who just met with NFC-enabled phones can quickly touch their phones together to automatically add each other as friends on Facebook.

To begin, both phones have to be running the same Activity in the foreground in order to work. Let’s call this Activity ‘NfcAddFriendsActivity’.

In NfcAddFriendsActivity’s onCreate method, we have the following code:

mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
mNfcPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
// Intent filters for exchanging over p2p.
IntentFilter ndefDetected = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try
{

    ndefDetected.addDataType("text/plain");
}
catch (MalformedMimeTypeException e)
{

}
mNdefExchangeFilters = new IntentFilter[] { ndefDetected };

This sets up the different intents that are needed in order for p2p to work. mNfcPendingIntent is a generic PendingIntent that will be delivered to this activity, Android fills the intent later with the details from the discovered tag before sending it to this activity. The nDefDetected IntentFilter is set to filter for the intentNfcAdapter.ACTION_NDEF_DISCOVERED with the mime type “text/plain”. So there will be a dispatch to the foreground Activity when Android receives an intent matching the IntentFilter we just created.

The next step is to enable pushing NDEF messages and receiving NDEF messages. That means in our onResumemethod, we call enableNdefExchangeMode which looks like:

private void enableNdefExchangeMode()
{

    mNfcAdapter.enableForegroundNdefPush(NfcAddFriendsActivity.this,
    NfcUtils.getUidAsNdef(mUserId));
    mNfcAdapter.enableForegroundDispatch(this, mNfcPendingIntent, 
    mNdefExchangeFilters, null);
}

These method calls are what allows our device to initiate p2p communiation via NFC. NfcUtils.getUidAsNdef is the method that creates a NDEF message with mime type “text/plain” with the user’s UID as the payload. enableForegroundNdefPush will push this message to the other device. enableForegroundDispatch will set up the listener for the intent that we are filtering for such that when it detects an intent matching the intent filter, it calls our activity’s onNewIntent method:

@Override
protected void onNewIntent(Intent intent)
{

    // NDEF exchange mode
    if (!mWriteMode && NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))
    {

        NdefMessage[] msgs = NfcUtils.getNdefMessages(intent);
        fireFriendRequest(msgs[0]);
        Toast.makeText(this, "sent friend request via nfc!", Toast.LENGTH_LONG).show();
    }
}

Here, it parses the received NDEF message and gets the payload which should be the other user’s UID. Then I callfireFriendRequest which is a method that we can assume sends a friend request to the UID that was given.

NfcUtils.getNdefMessages is below, exactly the same as the Sticky Notes demo:

NdefMessage[] getNdefMessages(Intent intent)
{

    // Parse the intent
    NdefMessage[] msgs = null;
    String action = intent.getAction();

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

        Parcelable[] rawMsgs = 
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMsgs != null)
        {

            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++)
            {

                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        }
        else
        {

            // Unknown tag type
            byte[] empty = new byte[] {};
            NdefRecord record = 
            new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
            NdefMessage msg = new NdefMessage(new NdefRecord[] { record });
            msgs = new NdefMessage[] { msg };
        }
    }
    else
    {

        Log.d(TAG, "Unknown intent.");
        finish();
    }

    return msgs;
}

Writing to a NFC Tag – Checking into Places Example

Let’s create a way for people to check in to places simply by tapping on a NFC tag. To start, we need to write some data onto a NFC tag, such that when the user taps on the tag, an Activity will launch based on the type of data stored on the tag. Well, each Facebook “Place” has an id associated with it, so lets write those ids onto NFC tags such that when a user taps on the tag, we can launch the correct Activity, passing the id as a parameter.
Lets assume you have a list of Facebook Places, and when the user clicks on a Place, we prompt them to touch the phone to the tag to write that corresponding Place id onto the tag. So, when the user taps on a place,onListItemClick is called and this is what happens:

Place place = (Place) mListAdapter.getItem(position);

// NFC: Write id to tag
placeidToWrite = place.mPlaceId;
enableTagWriteMode();

new AlertDialog.Builder(NfcWriteCheckinActivity.this).setTitle("Touch tag to write")
.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog)
{

    disableTagWriteMode();
}
}).create().show();

I hold the placeid in placeidToWrite, and call enableTagWriteMode. An alert dialog pops up prompting the user to tap the phone to the tag to write. enableTagWriteMode below:

private void enableTagWriteMode()
{

    mWriteMode = true;
    IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
    mWriteTagFilters = new IntentFilter[] { tagDetected };
    mNfcAdapter.enableForegroundDispatch(this, mNfcPendingIntent, mWriteTagFilters, null);
}

I create a new IntentFilter for the intent NFCAdapter.ACTION_TAG_DISCOVERED, which is the intent to start an Activity when a NFC tag is discovered. Then, like in the adding friends example, enableForegroundDispatch is called to dispatch a discovered tag to the foreground activity. Once this is all set up, and the user taps the phone onto a tag, the ACTION_TAG_DISCOVERED intent is detected and onNewIntent is called:

@Override
protected void onNewIntent(Intent intent)
{

    // Tag writing mode
    if (mWriteMode && NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction()))
    {

        Tag detectedTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (NfcUtils.writeTag(NfcUtils.getPlaceidAsNdef(placeidToWrite), detectedTag))
        {

            Toast.makeText(this, "Success: Wrote placeid to nfc tag", Toast.LENGTH_LONG).show();
            NfcUtils.soundNotify(this);
        }
        else
        {

            Toast.makeText(this, "Write failed", Toast.LENGTH_LONG).show();
        }
    }
}

NfcUtils.getPlaceidAsNdef takes the placeid and creates a NDEF message with mimetype “application/vnd.facebook.places”, which is a custom and vendor-specific mimetype that I made up to distinguish the different NFC tags (places, url, pages) for Facebook. That way, when the phone reads a NFC tag, it knows which Activity to launch (I detect mimetype “application/vnd.fb.places” – I should launch this specific Activity from Facebook).
NfcUtils is my handy utility class for all things NFC. NfcUtils.getPlaceidAsNdef looks like:/*

* Converts a Long into a NdefMessage in application/vnd.facebook.places MIMEtype.
*
* for writing Places
*/
public static NdefMessage getPlaceidAsNdef(Long id)
{

    String msg = ((Long) id).toString();
    byte[] textBytes = msg.getBytes();
    NdefRecord textRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA,
        "application/vnd.facebook.places".getBytes(), new byte[] {}, textBytes);
    return new NdefMessage(new NdefRecord[] { textRecord });
}

Once you construct the NDEF message, you need to actually write it to the tag. NfcUtils.writeTag looks like:

/*
* Writes an NdefMessage to a NFC tag
*/
public static boolean writeTag(NdefMessage message, Tag tag)
{

    int size = message.toByteArray().length;
    try
    {

        Ndef ndef = Ndef.get(tag);
        if (ndef != null)
        {

            ndef.connect();

            if (!ndef.isWritable())
            {

                return false;
            }

            if (ndef.getMaxSize() < size)
            {

                return false;
            }

            ndef.writeNdefMessage(message);
            return true;
        }
        else
        {

            NdefFormatable format = NdefFormatable.get(tag);
            if (format != null)
            {

                try
                {

                    format.connect();
                    format.format(message);
                    return true;
                }
                catch (IOException e)
                {

                    return false;
                }
            }
            else
            {

                return false;
            }
        }
    }
    catch (Exception e)
    {

        return false;
    }
}

And that’s how you write to a tag. Now, any person with an Android NFC-enabled phone with this build of Facebook for Android would just have to touch their phone onto the sticker (doesn’t need Facebook to be in the foreground), and wham, it takes you straight to the checkin activity with the place already set.
Reading from a NFC tag – Checking into Places Example

Now, we’ll talk about how the phone reads the tag and launch the appropriate Activity.

In the AndroidManifest, we need to write intent-filters that captures the different intents we created with our examples above. Such that when the Android phone receives the intent, it knows where to pass it along. For checking in, we have an Activity called, lets say, CheckInActivity. Within the activity, I am going to write an intent-filter that matches the exact intent I created in the NfcWriteCheckinActivity.
This is a snippet of the AndroidManifest for the app:

<activity android:name=".CheckInActivity">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <data android:mimeType="application/vnd.facebook.places"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

Notice the exact specification of the mimetype, which is the custom vendor mimetype that I created for checking in to places. That way, I can have one application, like Facebook, pass different intents to different Activities within Facebook.

Within CheckInActivity, it is fairly simple to extract the placeid and open the checkin activity. You do it in the onResumemethod of CheckInActivity:

@Override
protected void onResume()
{

    super.onResume();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction()))
    {

        NdefMessage[] messages = NfcUtils.getNdefMessages(getIntent());
        byte[] payload = messages[0].getRecords()[0].getPayload();
        String placeId = new String(payload);
        NfcUtils.soundNotify(this);
    }
}

Note: The user ID and the place ID are all written and transmitted in plaintext with no authentication or encryption. FWIW, that may or may not be acceptable for some apps. I haven’t looked into the security implications on NFC, but if you are passing confidential data, you will have to find a way to encrypt the data before writing, verifying the integrity of the data when receiving, and authenticating the entity that sent the data. Since this was a personal hackathon project, I did not implement any security features.

Ċ
Chris Martin,
29 Jul 2013, 00:43
Ċ
scbu004b.pdf
(1227k)
Chris Martin,
30 Jul 2013, 09:20
Ċ
Chris Martin,
29 Jul 2013, 01:20
Ċ
Chris Martin,
29 Jul 2013, 00:15
Comments