Writing Tag Content

Exclusive offer: get 50% off this eBook here
Near Field Communication with Android Cookbook

Near Field Communication with Android Cookbook — Save 50%

Discover the endless possibilities of using Android NFC capabilities to enhance your apps through over 60 practical recipes with this book and ebook

$26.99    $13.50
by Vitor Subtil | June 2014 | Cookbooks Open Source

The article by Vitor Subtil, the author of Near Field Communication with Android Cookbook, explains that the NFC Forum is responsible for developing standards-based NFC specifications to allow interoperability between different devices and tags. One of the specifications created by the NFC Forum is NFC Data Exchange Format (NDEF). The NDEF specification defines the message encapsulation format to exchange information between two NFC-enabled devices or devices with the NFC tag. NDEF is a lightweight, binary message format that can be used to encapsulate several custom payloads. Using this abstraction layer allows us to create the standard tag content compatible with all the NFC Forum tag types, ignoring the fact that each tag type has different hardware setups and memory layouts.

In this article, we will cover the following topics:

  • Working with the NDEF record
  • Writing a URI-formatted record
  • Writing a text-formatted record
  • Using Android Application Record
  • Working with external types

(For more resources related to this topic, see here.)

The NDEF Message is composed by one or more NDEF records, and each record contains the payload and a header in which the data length and type and identifier are stored.

In this article, we will create some examples that will allow us to work with the NDEF standard and start writing NFC applications.

Working with the NDEF record

Android provides an easy way to read and write data when it is formatted as per the standard NDEF. This format is the easiest way for us to work with tag data because it saves us from performing lots of operations and processes of reading and writing raw bytes. So, unless we need to get our hands dirty and write our own protocol, this is the way to go (you can still build it on top of NDEF and achieve a custom, yet standard-based protocol).

Getting ready

  • Make sure you have a working Android development environment. If you don't, ADT Bundle is a good environment to start with (you can access it by navigating to http://developer.android.com/sdk/index.html).
  • Make sure you have an NFC-enabled Android device or a virtual test environment.
  • It will be assumed that Eclipse is the development IDE.

How to do it...

We are going to create an application that writes any NDEF record to a tag by performing the following steps:

  1. Open Eclipse and create a new Android application project named NfcBookCh3Example1 with the package name nfcbook.ch3.example1.
  2. Make sure the AndroidManifest.xml file is correctly configured.
  3. Open the MainActivity.java file located under com.nfcbook.ch3.example1 and add the following class member:

    private NfcAdapter nfcAdapter;

  4. Implement the enableForegroundDispatch method and filter tags by using Ndef and NdefFormatable.Invoke in the onResume method:

    private void enableForegroundDispatch() { Intent intent =
    new Intent(this, MainActivity.class).addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0); IntentFilter[] intentFilter = new IntentFilter[] {}; String[][] techList = new String[][] { { android.nfc.tech.Ndef.class.getName() },
    { android.nfc.tech.NdefFormatable.class.getName() } }; if ( Build.DEVICE.matches(".*generic.*") ) { //clean up the tech filter when in emulator since it doesn't work properly. techList = null; } nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilter, techList); }

  5. Instantiate the nfcAdapter class field in the onCreate method:

    protected void onCreate(Bundle savedInstanceState) { ... nfcAdapter = NfcAdapter.getDefaultAdapter(this); }

  6. Implement the formatTag method:

    private boolean formatTag(Tag tag, NdefMessage ndefMessage) { try { NdefFormatable ndefFormat = NdefFormatable.get(tag); if (ndefFormat != null) { ndefFormat.connect(); ndefFormat.format(ndefMessage); ndefFormat.close(); return true; } } catch (Exception e) { Log.e("formatTag", e.getMessage()); } return false; }

  7. Implement the writeNdefMessage method:

    private boolean writeNdefMessage(Tag tag, NdefMessage ndefMessage) { try { if (tag != null) { Ndef ndef = Ndef.get(tag); if (ndef == null) { return formatTag(tag, ndefMessage); } else { ndef.connect(); if (ndef.isWritable()) { ndef.writeNdefMessage(ndefMessage); ndef.close(); return true; } ndef.close(); } } } catch (Exception e) { Log.e("formatTag", e.getMessage()); } return false; }

  8. Implement the isNfcIntent method:

    boolean isNfcIntent(Intent intent) { return intent.hasExtra(NfcAdapter.EXTRA_TAG); }

  9. Override the onNewIntent method using the following code:

    @Override protected void onNewIntent(Intent intent) { try { if (isNfcIntent(intent)) { NdefRecord ndefEmptyRecord = new NdefRecord(NdefRecord.TNF_EMPTY, new byte[]{},
    new byte[]{}, new byte[]{}); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] { ndefEmptyRecord }); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (writeNdefMessage(tag, ndefMessage)) { Toast.makeText(this, "Tag written!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Failed to write tag", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { Log.e("onNewIntent", e.getMessage()); } }

  10. Override the onPause method and insert the following code:

    @Override protected void onPause() { super.onPause(); nfcAdapter.disableForegroundDispatch(this); }

  11. Open the NFC Simulator tool and simulate a few tags. The tag should be marked as modified, as shown in the following screenshot:

In the NFC Simulator, a tag name that ends with _LOCKED is not writable, so we won't be able to write any content to the tag and, therefore, a Failed to write tag toast will appear.

How it works...

NFC intents carry an extra value with them that is a virtual representation of the tag and can be obtained using the NfcAdapter.EXTRA_TAG key. We can get information about the tag, such as the tag ID and its content type, through this object.

In the onNewIntent method, we retrieve the tag instance and then use other classes provided by Android to easily read, write, and retrieve even more information about the tag. These classes are as follows:

  • android.nfc.tech.Ndef: This class provides methods to retrieve and modify the NdefMessage object on a tag
  • android.nfc.tech.NdefFormatable: This class provides methods to format tags that are capable of being formatted as NDEF

The first thing we need to do while writing a tag is to call the get(Tag tag) method from the Ndef class, which will return an instance of the same class. Then, we need to open a connection with the tag by calling the connect() method. With an open connection, we can now write a NDEF message to the tag by calling the writeNdefMessage(NdefMessage msg) method. Checking whether the tag is writable or not is always a good practice to prevent unwanted exceptions. We can do this by calling the isWritable() method. Note that this method may not account for physical write protection. When everything is done, we call the close() method to release the previously opened connection.

If the get(Tag tag) method returns null, it means that the tag is not formatted as per the NDEF format, and we should try to format it correctly.

For formatting a tag with the NDEF format, we use the NdefFormatable class in the same way as we did with the Ndef class. However, in this case, we want to format the tag and write a message. This is achieved by calling the format(NdefMessage firstMessage) method. So, we should call the get(Tag tag) method, then open a connection by calling connect(), format the tag, and write the message by calling the format(NdefMessage firstMessage) method. Finally, close the connection with the close() method.

If the get(Tag tag) method returns null, it means that the tag is the Android NFC API that cannot automatically format the tag to NDEF.

An NDEF message is composed of several NDEF records. Each of these records is composed of four key properties:

  • Type Name Format (TNF): This property defines how the type field should be interpreted
  • Record Type Definition (RTD): This property is used together with the TNF to help Android create the correct NDEF message and trigger the corresponding intent
  • Id: This property lets you define a custom identifier for the record
  • Payload: This property contains the content that will be transported to the record

Using the combinations between the TNF and the RTD, we can create several different NDEF records to hold our data and even create our custom types. In this recipe, we created an empty record.

The main TNF property values of a record are as follows:

  • - TNF_ABSOLUTE_URI: This is a URI-type field
  • - TNF_WELL_KNOWN: This is an NFC Forum-defined URN
  • - TNF_EXTERNAL_TYPE: This is a URN-type field
  • - TNF_MIME_MEDIA: This is a MIME type based on the type specified

The main RTF property values of a record are:

  • - RTD_URI: This is the URI based on the payload
  • - RTD_TEXT: This is the NFC Forum-defined record type

Writing a URI-formatted record

URI is probably the most common content written to NFC tags. It allows you to share a website, an online service, or a link to the online content. This can be used, for example, in advertising and marketing.

How to do it...

We are going to create an application that writes URI records to a tag by performing the following steps. The URI will be hardcoded and will point to the Packt Publishing website.

  1. Open Eclipse and create a new Android application project named NfcBookCh3Example2.
  2. Make sure the AndroidManifest.xml file is configured correctly.
  3. Set the minimum SDK version to 14:

    <uses-sdk android:minSdkVersion="14" />

  4. Implement the enableForegroundDispatch, isNfcIntent, formatTag, and writeNdefMessage methods from the previous recipe—steps 2, 4, 6, and 7.
  5. Add the following class member and instantiate it in the onCreate method:

    private NfcAdapter nfcAdapter; protected void onCreate(Bundle savedInstanceState) { ... nfcAdapter = NfcAdapter.getDefaultAdapter(this); }

  6. Override the onNewIntent method and place the following code:

    @Override protected void onNewIntent(Intent intent) { try { if (isNfcIntent(intent)) { NdefRecord uriRecord = NdefRecord.createUri("http://www.packtpub.com"); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] { uriRecord }); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); boolean writeResult = writeNdefMessage(tag, ndefMessage); if (writeResult) { Toast.makeText(this, "Tag written!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Tag write failed!", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { Log.e("onNewIntent", e.getMessage()); } super.onNewIntent(intent); }

  7. Run the application.
  8. Tap a tag on your phone or simulate a tap in the NFC Simulator. A Write Successful! toast should appear.

How it works...

URIs are perfect content for NFC tags because with a relatively small amount of content, we can send users to more rich and complete resources. These types of records are the most easy to create, and this is done by calling the NdefRecord.createUri method and passing the URI as the first parameter.

URIs are not necessarily URLs for a website. We can use other URIs that are quite well-known in Android such as the following:

- tel:+000 000 000 000 - sms:+000 000 000 000

If we write the tel: uri syntax, the user will be prompted to initiate a phone call.

We can always create a URI record the hard way without using the createUri method:

public NdefRecord createUriRecord(String uri) { NdefRecord rtdUriRecord = null; try { byte[] uriField; uriField = uri.getBytes("UTF-8"); byte[] payload = new byte[uriField.length + 1]; //+1 for the URI prefix payload[0] = 0x00; //prefixes the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); rtdUriRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
NdefRecord.RTD_URI, new byte[0], payload); } catch (UnsupportedEncodingException e) { Log.e("createUriRecord", e.getMessage()); } return rtdUriRecord; }

The first byte in the payload indicates which prefix should be used with the URI. This way, we don't need to write the whole URI in the tag, which saves some tag space.

The following list describes the recognized prefixes:

0x00 No prepending is done 0x01 http://www. 0x02 https://www. 0x03 http:// 0x04 https:// 0x05 tel: 0x06 mailto: 0x07 ftp://anonymous:anonymous@ 0x08 ftp://ftp. 0x09 ftps:// 0x0A sftp:// 0x0B smb:// 0x0C nfs:// 0x0D ftp:// 0x0E dav:// 0x0F news: 0x10 telnet:// 0x11 imap: 0x12 rtsp:// 0x13 urn: 0x14 pop: 0x15 sip: 0x16 sips: 0x17 tftp: 0x18 btspp:// 0x19 btl2cap:// 0x1A btgoep:// 0x1B tcpobex:// 0x1C irdaobex:// 0x1D file:// 0x1E urn:epc:id: 0x1F urn:epc:tag: 0x20 urn:epc:pat: 0x21 urn:epc:raw: 0x22 urn:epc: 0x23 urn:nfc:

Near Field Communication with Android Cookbook Discover the endless possibilities of using Android NFC capabilities to enhance your apps through over 60 practical recipes with this book and ebook
Published: June 2014
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

Writing a text-formatted record

As we saw in the Working with the NDEF record recipe, we can write different tag content formats. In this recipe, we will see how to store text in a tag.

How to do it...

We are going to create an application that creates and writes a text-formatted record to a tag:

  1. Open Eclipse and create a new Android application project named NfcBookCh3Example3 with the package name nfcbook.ch3.example3.
  2. Make sure the AndroidManifest.xml file is configured correctly.
  3. Make sure the foreground dispatch system is enabled.
  4. Open the MainActivity.java file located under nfcbook.ch3.example3 and add the following class member:

    private NfcAdapter nfcAdapter;

  5. Instantiate the nfcAdapter class field in the onCreate method:

    protected void onCreate(Bundle savedInstanceState) { ... nfcAdapter = NfcAdapter.getDefaultAdapter(this); }

  6. Implement the createTextRecord method, as shown in the following code:

    public NdefRecord createTextRecord(String content) { try { byte[] language; language = Locale.getDefault().getLanguage().getBytes("UTF-8"); final byte[] text = content.getBytes("UTF-8"); final int languageSize = language.length; final int textLength = text.length; final ByteArrayOutputStream payload =
    new ByteArrayOutputStream(1 + languageSize + textLength); payload.write((byte) (languageSize & 0x1F)); payload.write(language, 0, languageSize); payload.write(text, 0, textLength); return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT,
    new byte[0], payload.toByteArray()); } catch (UnsupportedEncodingException e) { Log.e("createTextRecord", e.getMessage()); } return null; }

  7. Implement the isNfcIntent, formatTag, and writeNdefMessage methods from step 7 of the previous recipe.
  8. Override the onNewIntent method and place the following code:

    @Override protected void onNewIntent(Intent intent) { try { if (isNfcIntent(intent)) { NdefRecord ndefRecord = createTextRecord("My string content"); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] { ndefRecord }); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); boolean writeResult = writeNdefMessage(tag, ndefMessage); if (writeResult) { Toast.makeText(this, "Tag written!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Tag write failed!", Toast.LENGTH_SHORT).show(); } } } catch (Exception e) { Log.e("onNewIntent", e.getMessage()); } }

  9. Run the application.
  10. Tap a tag or simulate a tap in the NFC Simulator.
  11. A Write Successful! toast should appear.

How it works...

Writing a string to a tag is a little tricky because the payload doesn't directly correspond to the text that we write. The payload sent to the tag is a byte concatenation that contains the language of the string, the string itself, and an additional byte—the first one—which is a bitwise AND operator on the language size (to indicate the character encoding used). This is done on the createTextRecord method.

When we read text from a tag, we need to perform the inverse operation and get the string language and the string itself from the byte array.

In this recipe, we used the system's default language to format our string, but we can use any other language independent of the user's language.

Using Android Application Record

When we develop an app to write and read NFC tags, we most probably want this to happen when the user taps on a tag on their phone, and our app starts automatically even if it isn't running in the foreground. The Android system provides us with a pretty simple solution for this—AAR.

How to do it...

We are going to create an application that creates and writes an AAR to a tag by performing the following steps:

  1. Open Eclipse and create a new Android application project named NfcBookCh3Example4 with the package name nfcbook.ch3.example4.
  2. Make sure the AndroidManifest.xml file is configured correctly.
  3. Set the minimum SDK version to 14:

    <uses-sdk android:minSdkVersion="14" />

  4. Make sure the foreground dispatch system is enabled.
  5. Open MainActivity located under nfcbook.ch3.example4 and add the following class member:

    private NfcAdapter nfcAdapter;

  6. Instantiate the nfcAdapter class field in the onCreate method:

    protected void onCreate(Bundle savedInstanceState) { ... nfcAdapter = NfcAdapter.getDefaultAdapter(this); }

  7. Implement the isNfcIntent, formatTag, and writeNdefMessage methods from the previous recipe-step 7.
  8. Override the onNewIntent method and add the following code:

    @Override protected void onNewIntent(Intent intent) { try { if (isNfcIntent(intent)) { NdefRecord aaRecord = NdefRecord.createApplicationRecord
    (this.getPackageName()); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] { aaRecord }); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); boolean writeResult = writeNdefMessage(tag, ndefMessage); if (writeResult) { Toast.makeText(this, "Tag written!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Tag write failed!", Toast.LENGTH_SHORT).show(); } } else { Log.d("onNewIntent", "received intent isn't a NFC intent."); } } catch (Exception e) { Log.e("onNewIntent", e.getMessage()); } }

  9. On the onResume method, add the following lines of code:

    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { onNewIntent(getIntent()); }

  10. Run the application.
  11. Tap a tag on your phone or simulate a tap in the NFC Simulator.
  12. Exit the application and tap your phone or simulate a tap on the written tag. The application should start automatically.

How it works...

The AAR record can be placed anywhere in the NDEF message—typically in the last record—and provides a way for the Android system to identify the correct application associated with the message. We can create an application record by calling the NdefRecord.createApplicationRecord method and placing it in the NDEF message. When the user taps on a tag, the Android system does an initial tag scan to determine its content type and verify the presence of an AAR record. If an AAR record is present, Android tries to launch the corresponding application based on the package name present in the record. If the application is not present in the device, the Play Store is launched to download the application.

Android uses the first record present in the NDEF message to determine the content type; so, it's not the best practice to place the AAR as the first record.

Working with external types

If you are developing an application that writes a specific data object into the tag, you should use the external type records. The payload doesn't need to follow any specific structure like it does in the text records. We can also use these types to create generic records for better support in both Android and non-Android devices since these are simpler ndef records.

How to do it...

We are going to create an application that creates and writes an NDEF Message that contains an NFC Forum External Type-formatted ndef record to a tag:

Please note that this recipe requires API level 16 to run and will not work with the OpenNFC Simulator since it is sunning API level 15. It should be tested using a real device.

  1. Open Eclipse and create a new Android application project named NfcBookCh3Example5 with the package name nfcbook.ch3.example5.
  2. Make sure the AndroidManifest.xml file is configured correctly.
  3. Set the minimum SDK version to 16:

    <uses-sdk android:minSdkVersion="16" />

  4. Make sure the foreground dispatch system is enabled.
  5. Open MainActivity located under nfcbook.ch3.example5 and add the following class member:

    private NfcAdapter nfcAdapter;

  6. Instantiate the nfcAdapter class field in the onCreate method:

    protected void onCreate(Bundle savedInstanceState) { ... nfcAdapter = NfcAdapter.getDefaultAdapter(this); }

  7. Implement the isNfcIntent, formatTag, and writeNdefMessage methods from step 6 of the previous recipe.
  8. Override the onNewIntent method and add the following code:

    @Override protected void onNewIntent(Intent intent) { try { if (isNfcIntent(intent)) { // additional payload byte[] payload = null; NdefRecord externalRecord = NdefRecord.createExternal("packtpub.com",
    "myexternaltype", payload); NdefMessage ndefMessage = new NdefMessage(new NdefRecord[] { externalRecord }); Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (writeNdefMessage(tag, ndefMessage)) { Toast.makeText(this, "Tag written!", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "Tag write failed!", Toast.LENGTH_SHORT).show(); } } else { Log.d("onNewIntent", "received intent isn't a NFC intent."); } } catch (Exception e) { Log.e("onNewIntent", e.getMessage()); } }

  9. Run the application.
  10. Tap a tag or simulate a tap in the NFC Simulator. A Write Successful! toast should appear.

How it works...

External type records are very similar to the URI. They are both based on the URN scheme and are basically URIs that point to a service location.

Note that the createExternal method receives two parameters, the domain and the type, which are then put together to create the urn:nfc:ext:packtpub.com:myexternaltype URI. The urn:nfc:ext: part of the URI is omitted according to the specification and it's not written to the tag.

Summary

This article covers the introduction to the NDEF records by explaining how this format is useful and the different possible usages. This article provides practical working examples that demonstrate the correct NDEF usage.

Resources for Article:


Further resources on this subject:


Near Field Communication with Android Cookbook Discover the endless possibilities of using Android NFC capabilities to enhance your apps through over 60 practical recipes with this book and ebook
Published: June 2014
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Vitor Subtil

Vitor Subtil has been a web developer since 2009 and has been working with ASP.NET, C#, Oracle, and FluentNhibernate in the development of Enterprise Management applications. He started using MVC and SOA quite recently.

He is currently pursuing his final year graduate studies in Computer Engineering, where he got introduced to the NFC technology and became a fan thereafter.

He is enthusiastic about new technologies such as HTML5, CSS3, and Android. He loves using the JQuery framework for JavaScript programming and uses new features of HTML5 such as OfflineStorage, the History API, and Canvas. His current focus is on developing Android applications.

Books From Packt


Android Application Security Essentials
Android Application Security Essentials

Android Development Tools for Eclipse
Android Development Tools for Eclipse

Asynchronous Android
Asynchronous Android

Flash Development for Android Cookbook
Flash Development for Android Cookbook

Raspberry Pi Networking Cookbook
Raspberry Pi Networking Cookbook

Raspberry Pi Projects for Kids
Raspberry Pi Projects for Kids

Raspberry Pi Cookbook for Python Programmers
Raspberry Pi Cookbook for Python Programmers

Raspberry Pi Home Automation with Arduino
Raspberry Pi Home Automation with Arduino


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software