Reading and Writing NFC Smart Tags

by BlackBerry Development Advisor (Retired) ‎10-24-2011 09:36 AM - edited ‎01-30-2012 11:19 AM (26,349 Views)

Introduction

This article is part of a series intended to act as a short and basic introduction to the world of NFC as it applies to BlackBerry® smartphones such as the BlackBerry® Bold™ 9900 and is aimed at developers who wish to take advantage of this exciting new technology. No prior knowledge of NFC is assumed but we do assume that the reader is familiar with Java® in some places. It would also be beneficial to have read the first article in this series, entitled: NFC Primer for Developers, since we will be following the thread that was outlined in this article but in greater depth. This article will look specifically at the reading and writing of NFC smart tags.

 

The Authors

This article was co-authored by Martin Woolley and John Murray both of whom work in the RIM Developer Relations team. Both Martin and John specialise in NFC applications development (amongst other things).

 

What is a Smart Tag?

In order to understand this it's probably best to look at some of the most common uses of smart tags such as:

 

Smart Posters - these embed small electronic "tags" containing data such as a URL in posters. An NFC enabled BlackBerry device can read the data and act upon it just by holding the device close to the poster. The action taken when reading a tag will vary according to the type of data the tag contains and the nature of the application reading the tag but it could for example involve taking the user directly to a web site which contains more information about the advertised event/product (etc) using the BlackBerry® Browser, automatically sending an SMS requesting someone call the user back, and so on.

 

smart_poster.jpg

 

Figure 1 - a smart poster

 

Smart Tags are really very simple and lightweight objects. They are often constructed from layers of stiff paper,  thin card, or plastic to protect the embedded antenna and chip. They may be sticky on one side to allow them to be attached to objects fairly unobtrusively; they are fairly inexpensive when bought in bulk and may even be supplied in rolls much like adhesive tape.

developer_tag.jpg

 

Figure 2 - a smart tag as used by a developer

 

Their size and simple nature means that the amount of data that can be stored in a smart tag may be relatively small, perhaps no more than a few hundred characters or about the same amount of information that you might see printed on the face of a business card. Like a business card, common patterns have been developed for encoding information such as plain text, URLs, telephone numbers, and contact details like addresses to name but a few.

 

 

smart tags app.jpg

Figure 3 - The BlackBerry Smart Tags app on the home screen



All BlackBerry® 7 NFC enabled devices include a built in Smart Tags Application, as shown above, that allows the reading and writing of simple smart poster tags that follow the pattern of a URI and associated plain text.

This article will demonstrate how to perform similar functions from within a BlackBerry application. We will not discuss how to enable a BlackBerry device to emulate a smart tag which could be read by others using an NFC capable device. That topic will be covered in a later article.


 

Standards and Specifications

The standards that relate to smart tags are managed by the NFC Forum. They have adopted a set of 15 technical specifications arranged into the following categories:

 

  • ·         Protocol technical specifications
  • ·         Data Exchange Format technical specifications
  • ·         Reference Application technical specifications
  • ·         NFC Forum Tag Type technical specifications
  • ·         Record Type Definition technical specifications

To understand how to read and write smart tags the "Record Type Definition" (RTD) set is the group to look at. In particular the following:

 

  •  NFC Data Exchange Format (NDEF) - the specification of the data structure and rules used to construct valid NDEF Messages
  • NFC Record Type Definition (RTD) - the specification of the data structures and rules used to construct valid NDEF Records
  •  Text Record Type Definition - the specifications and rules used to encode text strings including attributes like localisation
  • URI Record Type Definition - the specifications and rules used to encode URIs (e.g. http://, https://, sms://, etc.)
  • Smart Poster Record Type Definition - the specifications and rules used to organise and place URIs and Text onto a tag using NDEF Messages and Records.

The NFC Forum Data Exchange Format (NDEF) is a lightweight binary message format designed to encapsulate one or more application-defined payloads into a single message construct. An NDEF message contains one or more NDEF records of different types. There are separate specifications for record types that describe a Smart Poster or a URI or Plain Text.

So, a simple Smart Poster tag may use a Text record and a URI record to encode a URL and some descriptive text.

NDEF messages are encoded using a technique called Type, Length, Value (TLV) so parsing such messages can be a little more challenging than simpler data structures. The advantage of TLV format is that messages become self-describing and an NDEF message can be parsed in its entirety without foreknowledge of the particular type of message. Unfortunately for the Java programmer there are no simple wrapper classes to hide this parsing and manipulation of NDEF record structures ends up being a process of manipulating byte [] objects.


 

Use Cases

We're going to look at two simple use cases in what follows:

 

  •  Reading a Smart Poster Tag that is presented into the RF field of the BlackBerry device using a BlackBerry application that contains a text description and a URL pointing to a web site.
  • Writing data to a Smart Poster Tag that is presented into the RF field of the BlackBerry device using a BlackBerry application. The tag that will be written will contain some descriptive text and a URL to a web site

 

Reading a Smart Poster Tag

The process of reading a Smart Poster tag is fairly straightforward. Firstly your application has to register that it wants to be notified when tags of interest are presented to the NFC enabled BlackBerry device; and secondly it must take care of parsing the contents of the tag into a form that can be displayed on a screen or have some other action taken on it. Whilst there is an interface ( net.rim.device.api.io.nfc.readerwriter.DetectionListener) that allows you to register an interest in the presentation of cards it's simpler to use the interface that is provided to allow interest to be registered in NDEF smart tags specifically, namely net.rim.device.api.io.nfc.ndef.NDEFMessageListener. Note that when using this interface it’s possible to indicate that only tags which match a specific pattern are of interest to your application.

 

Implementing an NDEFMessageListener

The following code fragment shows the implementation of a typical listener for NDEF Smart Tag events. The key points are that it implements the NDEFMessageListener interface and receives notification of the presence of an NDEF smart tag in the NFC RF field via the onNDEFMessageDetected(NDEFMessage message) method. The NDEFMessage object passed to this method contains the encoded version of the Smart Tag content; in this example the parsing of the NDEF message itself is handled by another class, NfcReadNdefSmartTagUpdate, used to update the screen.

 

package nfc.sample.Ndef.Read;

import net.rim.device.api.io.nfc.ndef.NDEFMessage;
import net.rim.device.api.io.nfc.ndef.NDEFMessageListener;
import net.rim.device.api.ui.UiApplication;

/**
 * 
 * This class implements the NDEFMessageListener interface and its primary
 * function is to receive NDEF message notifications when a smart tag is brought
 * into the NFC antenna's range
 * 
 */
public class NfcReadNdefSmartTagListener implements NDEFMessageListener  {

    private NfcReadNdefSmartTagScreen _screen;
    public NfcReadNdefSmartTagListener() {
        super();
    }

    public NfcReadNdefSmartTagListener(NfcReadNdefSmartTagScreen screen) {
        this();
        this._screen = screen;

    /*
     * This is where we get passed an NDEFMessage
     * Schedule a runnable to handle it in the main event processing loop
     */
    public void onNDEFMessageDetected(NDEFMessage message) {
        
        UiApplication.getUiApplication().invokeLater(
            new NfcReadNdefSmartTagUpdate( message, _screen ));
   }
}

 

NDEFMessage and NDEFRecord

As seen above, an NDEFMessageListener implementation will receive an object of type NDEFMessage when its onNDEFMessageDetected(..) method is called. Before we go much further, it’s worth developing a basic understanding of the data structures we’re working with here.

An NDEFMessage object is a container for one or more NDEFRecord objects. Each NDEFRecord object is of a given type and contains a byte array which holds the record’s payload. The first NDEFRecord object in an NDEFMessage is special and can be used for filtering the types of NDEFMessage passed to a listener. We’ll deal with how to process the byte array payload later in this article and in fact we’ll learn that the payload itself may be an NDEF message and a container for NDEF records. Yes, it can get a little recursive.

 

Registering the NDEFMessageListener

In order to start receiving these events an application must register an instance of this interface which is typically done using a code fragment such as the following. An instance of NfcReaderNdefSmartTagListener (as defined above) is registered with the NFC ReaderWriterManager using the addNDEFMessageListener method.

 



/*
 * This registers with the NFC ReaderWriterManager indicating our
 * interest in receiving notification when smart tags come within range
 * of the NFC antenna
 */
private void registerListener(listener) {

    ReaderWriterManager nfcManager;

    try {
        nfcManager = ReaderWriterManager.getInstance();

        nfcManager.addNDEFMessageListener(
            listener, NDEFRecord.TNF_WELL_KNOWN, "Sp", true);

    } catch (NFCException e) {
	      ui_lbl_status_message.setText(“ERROR: could not register NFCStatusListener”);
    }
}

 

Notes on the Parameters used when Registering a Listener

The parameters passed through the addNDEFMessageListener method allow some filtering of the types of NDEF messages that will be presented to the listener.

The second parameter: NDEFRecord.TNF_WELL_KNOWN, indicates that we are interested in NDEF records of Type Name Format (TNF): "WELL_KNOWN". TNF_WELL_KNOWN includes NFC RTD (Record Type Definitions) types such as RTD_TEXT and RTD_URI meaning that they will contain URIs or TEXT suitably encoded according to the NFC RTD specification.

The third parameter: "Sp", identifies the record type as Smart Poster. So we're only interested in 
being notified of Smart Poster records that contain plain text or URIs. Note that record types are defined in a series of specifications from the NFC Forum. In this case, the relevant specification is entitled “NFCForum-SmartPoster_RTD_1.0”.


The final parameter "true" indicates that the application must be launched if it is not currently running when a smart tag of the relevant type is brought within range of the NFC antenna. This setting is persistent across device power resets. On presentation of a suitable smart tag to the device the application will be launched if it is not already running but note that it is necessary to re-establish the NDEFMessageListener during the start-up processing.  If your NDEFMessageListener application is already running then its interface just gets a direct call.

It is important to register your listener within about 15 seconds of the application being started. Failure to do so will result in any queued message being lost.

 

There is no way to determine if the application was automatically started as a consequence of an NDEFMessageListener event  rather than (say) the user having launched it from the home screen. In practice this usually doesn’t matter however. If your application is already running, it gets the NDEF message delivered to it immediately. If it’s not, it will be launched in exactly the same way as if a user had launched it from the home screen. On those rarer occassions where perhaps registration of the listener is conditional on something such as an explicit user choice, you’d need to maintain some state which could be checked in your main method to see whether or not you need to immediately re-establish the listener so it can receive the queued call back. The RuntimeStore is a good place to store such state.



Unregistering an NDEFMessage Listener

It's important to realise that if you requested that your application be auto-started on the addNDEFMessageListener method then this setting will persistent until the application is unregistered. Unregistering an NDEFMessageListener is similar to registering one and the same parameters that were passed on the registration must be passed on the unregistration. The following code fragment demonstrates this.



nfcManager.removeNDEFMessageListener(NDEFRecord.TNF_WELL_KNOWN, "Sp");

 

 

You can see the same filter settings are specified here as in the original registration request.



Parsing the content of an NDEFMessage read from the Smart Poster Tag

Now that we've got an NDEFMessage from the Smart Poster Tag let's examine how to parse the contents and extract the information. We’ll examine the process of parsing a particular example NDEF message here as this should illustrate the general approach taken. To deal with other types of NDEF message you will need to be armed with the relevant specification(s) from the NFC Forum.



Recall that an NDEFMessage object can consist of a number of NDEFRecord objects. In the case of a Smart Poster Tag the NDEFMessage itself consists of a single NDEFRecord. This NDEFRecord object contains 5 key pieces of information:



  • An Identifier used to identify the record. This is important if there are several records but in the case of a Smart Poster Tag such as the one we are looking at, with TEXT and an associated URI, there is only one record.
    • If record is an instance of NDEFRecord then the Id is accessed using: record.getId().

  •  A Type Name Format (TNF) -- for our Smart Poster Tag this will specify a type of NDEFRecord.TNF_WELL_KNOWN.
    • If record is an instance of NDEFRecord then the Type Name Format is accessed using: record.getTypeNameFormat().

  • A Type -- for our Smart Poster Tag this will be "Sp" identifying the tag as a Smart Poster Tag
    • If record is an instance of NDEFRecord then the Type is accessed using: record.getType().

  •  A Payload which is a byte [] object.
    • If record is an instance of NDEFRecord then the payload is accessed using: record.getPayLoad()

  • A Payload length
    • If record is an instance of NDEFRecord then the payload length is accessed using: record.getPayLoad().length.

 

The meaning of these 5 attributes should be clear, with the possible exception of “Type Name Format (TNF)” and “Type”. Simply put, the Type field indicates the kind of data which is being carried in the payload field of the record. There are however several ways in which the Type value can be expressed, such as as an Absolute URI, a MIME type (RFC 2046), or using one of the “well known types” defined by the NFC Forum amongst others. The TNF field indicates which of these applies to the Type field in a record. The “NFC Data Exchange Format (NDEF) Technical Explanation” documents the full set of possible values which can be encoded within the TNF field.



The Payload of a typical Smart Poster Tag

There are currently no Java wrapper classes in the BlackBerry NFC API that could be used to parse the NDEF Messages and Records. So, to give you some idea of what sort of object is presented in the payload of a Smart Poster Tag and hence have some idea of what information needs to be parsed here is the payload of a Smart Poster Tag that contains:



  • ·         The TEXT: "This is a title for the BBC URI"
  • ·         The URI: http://www.bbc.co.uk

 

The length of the payload is 59 bytes and the payload is as follows in both HEX and clear text and where the colour annotations are explained below:



payload_bytes.png 

Breaking down the record structure:





payload_breakdown.png 

 

It's worth keeping this in mind when examining the code that follows which is used to parse this very payload. And do note that we’re only examining one example here. In this example we’re dealing with what’s known as a “short record” as opposed to a “normal record”. The NFC Forum specifications should be consulted for a more thorough appreciation of the structure of NDEF messages.



 

Parsing the NDEF Message Payload

The code below parses an NDEF message payload. The NDEF Message itself is in the variable _message.

It’s a fragment from a larger application that allows parsed information from the tag to be displayed on a screen of the application. The screen output, showing only part of the total output looks like this:



ndef_read6.png

 

Here’s the code:

 

/*
 * The NDEF Message _message may consist of a number of NDEF records
 */
NDEFRecord[] records = _message.getRecords();
/*
 * This is the number of NDEF records in the NDEF message
 */
int numRecords = records.length;

 

A Smart Poster Tag generally contains an NDEFMEssage with a single NDEFRecord which itself contains sub-records that contain the details of the text and URL.

 

/*
 * Only unpick the message if it contains a non-zero number of records
 */
if (numRecords > 0) {
    /*
     * Work our way through each record in the message in turn
     * For a Smart Poster Tag there ought to be only a single record
     */
    for (int j = numRecords - 1; j >= 0; j--) {

        byte[] payloadBytes = records[j].getPayload();
        StringBuffer hexPayload = new StringBuffer();

   StringBuffer characterPayload = new StringBuffer();
        String hexpair;
        int numberOfHexPairs = 8;

        for (int i = 0; i < payloadBytes.length; i++) {
            hexpair = byte2HexPair(payloadBytes[i]);
            characterPayload.append(byte2Ascii(payloadBytes[i]));
            hexPayload.append(hexpair + " " +
               (((i + 1) % numberOfHexPairs == 0) ? "\n" : ""));
            characterPayload.append((((i + 1) % numberOfHexPairs == 0) ? 
               "\n" : ""));
        }

 

At this point the details of the single record that represents the Smart Poster content are extracted to be added to a field on the screen of this application. We have a Smart Poster Tag if the Type is “Sp”.



/*
         * Unpick the elements of the NDEF record. It should identify
         * WELL_KNOWN, TEXT or URI and "Sp" for smart poster.
         * Construct a string to display on the application’s screen
         */
        String record = "Root NDEF Record\nID: “
                      + records[j].getId() + "\nType: "
                      + records[j].getType() + "\nTNF: "
                      + records[j].getTypeNameFormat() + "\nPayload Len: "
                      + records[j].getPayload().length + "\nHex Payload: \n"
                      + hexPayload + "\nCharacter Payload: \n" + characterPayload;

        _screen.updateDataField(record);
        /*
         * If we recognise this as a smart tag Type "Sp"
         */
        if ("Sp".equals(records[j].getType())) {

 

On recognizing the Smart Poster type we convert the payload to an NDEFMessage object in its own right. This message itself will have NDEF Records that we will need to parse in turn.



try {
                NDEFMessage smartPosterMessage = new NDEFMessage(
                    records[j].getPayload());
                NDEFRecord[] spRecords = smartPosterMessage.getRecords();
                int numSpRecords = spRecords.length;
                
               
                if (numSpRecords > 0 ) {
                    for (int k = numSpRecords - 1; k >= 0; k--) {
                        
                        byte[] spPayloadBytes = spRecords[k].getPayload();
                        
                        hexPayload = new StringBuffer();
                        characterPayload = new StringBuffer();

                        for (int i = 0; i < spPayloadBytes.length; i++) {
                            hexpair = byte2HexPair(spPayloadBytes[i]);
                            characterPayload.append(
                                byte2Ascii(spPayloadBytes[i]));

                            hexPayload.append(hexpair + " "
                                + (((i + 1) % numberOfHexPairs == 0) 
                                            ? "\n" : ""));
                            characterPayload.append((((i + 1) 
                                % numberOfHexPairs == 0) ? "\n" : ""));
                        }

 

Once again extract the type of record from the one we’re examining. We’re interested in TEXT and URI record types.

 

/*
                         * Extract the record id, type, type name format,
                         *  payload length and payload 
                         */
                        String spRecordLog = "Subsidiary NDEF Record\nID: "
                            + spRecords[k].getId() + "\nType: "
                            + spRecords[k].getType() + "\nTNF: "
                            + spRecords[k].getTypeNameFormat() 
                            + "\nPayload Len: "
                            + spRecords[k].getPayload().length 
                            + "\nHex Payload: \n"
                            + hexPayload + "\nCharacter Payload: \n" 
                            + characterPayload;
                        _screen.updateDataField(spRecordLog);
                        /*
                         * This test checks for a TEXT record as 
                         * a well known type
                         */
                        if ((spRecords[k].getTypeNameFormat() 
                             ==   NDEFRecord.TNF_WELL_KNOWN) 
                             &&   "T".equals(spRecords[k].getType())) {
                            /*
                             * Well Known Type "T" is a TEXT record 
                             */
                            StringBuffer textBuffer = new StringBuffer();
                            StringBuffer encodingBuffer = 
                                new StringBuffer();

 

TEXT record types have a status byte which indicates the character set encoding of the text (either UTF-8 or UTF-16) and the length of an ISO/IANA language code attribute which follows the status byte.



// bit 7 indicates UTF-8 if 0, UTF-16 if 1. Bits 5..0 len of IANA language // code.
int statusByte = spPayloadBytes[0]; 
boolean is_utf16 = Utilities.isUtf16Encoded(statusByte);
int iana_language_code_len = 					        		Utilities.getIanaLanguageCodeLength(statusByte);

// extract the IANA language code as an ASCII string
byte [] iana_lang_code_bytes = new byte [iana_language_code_len];
if ( iana_language_code_len > 0 ) {
	for ( int m = 0 ; m < iana_language_code_len; m++ ) {
		iana_lang_code_bytes[m] = spPayloadBytes[m+1];
	}
}
// the language code is always ASCII
langCode = new String(iana_lang_code_bytes,"US-ASCII");                            

// extract the text which may be UTF-8 or UTF-16 encoded depending on bit 7 
// of the status byte
byte [] text_bytes = new byte[spPayloadBytes.length 
		- iana_language_code_len + 1];
int i=0;
for ( int m = iana_language_code_len + 1 ; m < spPayloadBytes.length; m++ ) {
	text_bytes[i] = spPayloadBytes[m];
	i++;
}
if (!is_utf16) {
	  text = new String(text_bytes,"UTF-8");
} else {
	  text = new String(text_bytes,"UTF-16");
}
									
_screen.updateDataField(">>> Language: " + langCode);
_screen.updateDataField(">>> Text: " + text);

 

The URL associated with the descriptive text is encoded in a URI record which is one of the well known record types in the NFC Forum documentation.



/*
* This test checks for a URI record as a 
* well known type
*/
} else if ((spRecords[k].getTypeNameFormat() 
            ==  NDEFRecord.TNF_WELL_KNOWN) &&  
            "U".equals(spRecords[k].getType())) {
/*
* Well Known Type "U" is a URI record 
*/
  StringBuffer urlBuffer = new StringBuffer();
  int urlOffset = 0;

 

As an efficiency measure for cards that have very little space on them, many common prefixes are encoded in a single byte field in the URI record called the URI Identifier Code. Here we look for a specific one, keeping things simple for the purposes of this article. In a real application, we’d probably code for all possible values of URI Identifier Code as defined in the “URI Record Type Definition Technical Specification” from the NFC Forum.



 

/*
 * The http://www. prefix is represented by 0x01
 */
if ( spPayloadBytes[0] == (byte) 0x01 ){
     urlBuffer.append("http://www.");
     urlOffset = 1;
}
									
// extract the URI which must be UTF-8 encoded
byte [] uri_bytes = new byte[spPayloadBytes.length - 1];
int i=0;
for ( int m = urlOffset ; m < spPayloadBytes.length; m++ ) {
	uri_bytes[i] = spPayloadBytes[m];
	i++;
}
urlBuffer.append(new String(uri_bytes,"UTF-8"));                            _screen.updateDataField(">>> URL: " + urlBuffer);
    }
  }
} else {
}
            } catch (BadFormatException e) {
                Utilities.log(“XXXX “+e.getClass().getName()+”:”+e.getMessage());
            } catch (NFCException e) {
                Utilities.log(“XXXX “+e.getClass().getName()+”:”+e.getMessage());
            }
        }
    }
} else {
    _screen.updateDataField("This is not the tag you're looking for!\n"
                    + "It contains no records!\n"
                    + "You should search elsewhere!");
}

 

The following is just a simple way of transforming a byte into a displayable character.

 

/*
 * Helper to represent a byte as a printable character
 */
private char byte2Ascii(byte b) {
    char character = '.'; 
    if ((20 <= b) && (126 >= b)) {
            character = (char) b;
    }
    return character;
}

 

The following is just a simple way of displaying a byte as a displayable hexadecimal pair.

 

/*
 * Helper to represent a byte as a printable hex pair.
 */
private String byte2HexPair(byte b) {
    String hex;
    hex = "00" + Integer.toHexString(b);
    hex = hex.substring(hex.length() - 2).toUpperCase();
    return hex;
}

 

 

The following methods extract data from the status byte which is found in the text record type:

 

	private static final int UTF_16_TEXT = 0x80;
	
	private static final int IANA_LANGUAGE_CODE_LEN_MASK = 0x1F;

	public static boolean isUtf16Encoded(int status_byte) {
		return (status_byte == (status_byte & UTF_16_TEXT));
	}
	
	public static int getIanaLanguageCodeLength(int status_byte) {
		return status_byte & IANA_LANGUAGE_CODE_LEN_MASK;
	}

 

So, in summary you can see that the process of parsing a Smart Poster Tag is a fairly straight-forward, if tedious, process involving working through a byte [] according to the NFC Forum specifications.

 

 

Writing a Smart Poster Tag

The process of writing a Smart Poster tag is also fairly straightforward. However we want to highlight a difference in the approach taken over reading a Smart Poster Tag.

If we were to use net.rim.device.api.io.nfc.ndef.NDEFMessageListener when expressing our interest in writing a tag any Smart Poster Tag would be read automatically once it enters the RF field of the BlackBerry device. In this example we want to avoid this and leave the decision to write the tag to the application.

So, instead of using net.rim.device.api.io.nfc.ndef.NDEFMessageListener to register an interest in tags being presented, we will use net.rim.device.api.io.nfc.readerwriter.DetectionListener. This allows us to be selective about the types of tags we will handle.

Once we have recognized a suitable smart tag we will immediately write some text and a URL to the tag using the standard NDEFMesage and NDEFRecord formats.

 

Implementing a DetectionListener

The following code fragment shows the implementation of a typical listener for the detection of smart cards or tags being presented through the device’s NFC RF field.

The key points are that it implements the DetectionListener interface and receives notification of the presence of a smart card or tag in the NFC RF field via the onTargetDetected (Target smartTagTarget) method.

 

Notice that the Target object passed to this method represents a smart card or tag. This is different to the NDEFMessageListener where an actual NDEFMessage object as read from the card is presented.

 

package nfc.sample.Ndef.Write;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.microedition.io.Connector;
import net.rim.device.api.io.nfc.NFCException;
import net.rim.device.api.io.nfc.ndef.NDEFMessage;
import net.rim.device.api.io.nfc.ndef.NDEFRecord;
import net.rim.device.api.io.nfc.ndef.NDEFTagConnection;
import net.rim.device.api.io.nfc.readerwriter.DetectionListener;
import net.rim.device.api.io.nfc.readerwriter.Target;
import net.rim.device.api.ui.UiApplication;

/**
 * 
 * This class implements the DetectionListener interface and its primary
 * function is to receive notifications when a smart tag is brought
 * into the NFC antenna's range
 */
public class NfcWriteNdefSmartTagListener implements DetectionListener {

	private NfcWriteNdefSmartTagScreen _screen;
	private final String URL_TEXT_LOCALE = "en-US";
	
	public NfcWriteNdefSmartTagListener() {
		super();
	}

	public NfcWriteNdefSmartTagListener(NfcWriteNdefSmartTagScreen screen) {
		this();
		this._screen = screen;
		Utilities.log("XXXX NfcWriteNdefSmartTagListener in constructor");
	}

 

At this point we have access to a smart card or tag Target object.  The Target object tells us the type of contactless protocol to be used when communicating with the card or tag represented by the Target object:

  • ·         Target.ISO_14443_3
  • ·         Target.ISO_14443_4
  • ·         Target.ISO_NDEF_TAG

This allows an application to be aware of tag types other than NDEF smart tags. In fact it will notify the presence of other cards provided they adhere to either the ISO 14443-3 (Parts A or B) or ISO 14443-4 (Parts A or B). Cards that do not adhere to any of the above three types are not detectable for the purpose of interacting with a BlackBerry Java application.

 

/*
	 * This is where we get informed of a tag in the proximity of 
	 * NFC antenna
	 */
	public void onTargetDetected(Target smartTagTarget) {

	}
}

 

That is not to say that other card types conforming to other standards can’t be routed from an external reader to either the UICC or eSE using Card Emulation. Card Emulation will be dealt with in a later article.


Registering the DetectionListener

In order to start receiving these events, an application must register an instance of this class which is typically done using a code fragment such as the following. An instance of nfcWriteNdefSmartTagListener which was defined in the last section is registered using the addDetectionListener method of the ReaderWriterManager.

 

/*
 * This registers with the NFC Reader Writer Manager indicating our
 * interest in receiving notification when smart tags come within range
 * of the NFC antenna
 */
private void registerListener(NfcWriteNdefSmartTagListener
                              nfcWriteNdefSmartTagListener) {

    ReaderWriterManager nfcManager;


    try {
        nfcManager = ReaderWriterManager.getInstance();


        nfcManager.addDetectionListener(
            nfcWriteNdefSmartTagListener,
            new int[]{Target.NDEF_TAG});

    } catch (NFCException e) {
        Utilities.log(“XXXX “+e.getClass().getName()+”:”+e.getMessage());
    }
}

 

The second parameter to addDetectionListener is a filter that instructs the ReaderWriterManager  to trigger detection events that match the types identified in the array of int. In this case we have specified that we are interested only in NDEF TAG events. Providing an empty array here would mean that the event target type would have to be determined when the card was presented in the listener itself.

 

Building the NDEF Message Payload

Here is an implementation of onTargetDetected() where:

  • _screen is a screen of the application that contains a number of fields:
    • Text for the URL accessed via: _screen.getTxtField()
    • A  URL for the URL accessed via: _screen.getUrlField()

 

public void onTargetDetected(Target smartTagTarget) {

    NDEFTagConnection tagConnection = null;
		
    try {

 

The process of writing a Smart Poster Tag to the card we’ve just detected (it has to be a Target.NDEF_TAG type card because that was specified when the listener was registered) consists of creating a new NDEFMessage object ( via createSmartPosterTag()  described below);  obtaining a connection to the target cast as an NDEFTagConnection object; and then write()’ing the tag to the card.

 

        NDEFMessage startPosterTag = createSmartPosterTag();

        tagConnection = (NDEFTagConnection)Connector.open(
                         smartTagTarget.getUri(Target.NDEF_TAG));

        tagConnection.write(startPosterTag);

    } catch (NFCException e) {
        Utilities.log(“XXXX “+e.getClass().getName()+”:”+e.getMessage());

	  } catch (IOException e) {
        Utilities.log(“XXXX “+e.getClass().getName()+”:”+e.getMessage());
    }
}

 

This is where the NDEFMessage that will be written to the Smart Card is assembled from information provided by the user.

 

private NDEFMessage createSmartPosterTag() throws IOException {

    NDEFMessage rootMessage   = new NDEFMessage();// (SP (TEXT, URL) message
    NDEFMessage ndefMessage   = new NDEFMessage();// (TEXT, URL) message
    NDEFRecord     rootRecord = new NDEFRecord(); // Smart Poster Record
    NDEFRecord tagTitleRecord = new NDEFRecord(); // Tag Title TEXT record
    NDEFRecord   tagUrlRecord = new NDEFRecord(); // Tag URL record
		
    ByteArrayOutputStream titlePayload = 
                               new ByteArrayOutputStream();// to build title
    ByteArrayOutputStream   urlPayload = 
                               new ByteArrayOutputStream(); // to build URL

 

Start constructing the records that will be part of the NDEFMessage from the basic elements upwards. Record 0 represents the text description of the tag.

 

	  private final String URL_TEXT_LOCALE = "en-US";
	  .....

   /*
    * ================ Record 0 ===========================================
    * 
    * This is the NDEF record that represents the title associated
    * with the URL that will the URL part of the Smart Poster Tag
    */
    titlePayload.write((byte) URL_TEXT_LOCALE.length());// status byte:
 							// char encoding 
                                                    // + length of locale
    titlePayload.write(URL_TEXT_LOCALE.getBytes(“US-ASCII”));     // locale encoding
   /*
    * This is the text to be associated with the Smart Poster Tag
    */
    titlePayload.write(_screen.getTxtField().getBytes(“UTF-8”)); // Text
    titlePayload.flush();
   /*
    * Construct the record itself 
    */
    tagTitleRecord.setId("0");
 
    tagTitleRecord.setType(NDEFRecord.TNF_WELL_KNOWN,
                           "T"); // It's TEXT type
    tagTitleRecord.setPayload(
    titlePayload.toByteArray());// construct the record

 

The next record to construct is the one that contains the URL associated with the descriptive text.

 

   /*
    * ================ Record 1 ===========================================
    *
    * This is the NDEF record that represents the URL associated
    * with the title that will the Text part of the Smart Poster Tag
    */
    urlPayload.write((byte) 0x01); // coded abbreviation for http://www.
    urlPayload.write(
        _screen.getUrlField().getBytes()); // The rest of the URL
    urlPayload.flush();
   /*
    * Construct the record itself
    */
    tagUrlRecord.setId("1");								// record Id
    tagUrlRecord.setType(NDEFRecord.TNF_WELL_KNOWN,
                        "U");                      // It's a URL(I) type
    tagUrlRecord.setPayload(
                        urlPayload.toByteArray()); // construct the record

 

Now combine the URI and Text records that we have constructed into an NDEFMessage object containing two records identified as records “0” and “1”.

 

   /*
    * ================ Construct an NDEF MEssage ==========================
    *
    * This NDEF Message comprises the Title and URL records (TEXT, URL)
    *
    */
    ndefMessage.setRecords(new NDEFRecord[] {tagTitleRecord, tagUrlRecord});

 

Once we have the single NDEF Message object representing the URL and the descriptive text it needs to be wrapped inside a Smart Poster Tag itself which is in fact just another NDEFRecord. This demonstrates that NDEF Records and NDEF Messages may be nested as long as they conform to the NFC Forum NDEF Message structures.

 

   /*
    * ================ Wrap the message as a Smart Poster Tag ============
    *
    * What we have now is a single NDEF message with two records, a 
    * URL and some text associated with it. We now need to make that into a
    * Smart poster Tag which is a well known type: "Sp"
    */
    rootRecord.setType(NDEFRecord.TNF_WELL_KNOWN, "Sp");// Smart Poster Type
    rootRecord.setPayload(ndefMessage.getBytes()); // construct the record

 

Last of all create a top level NDEF Message representing the actual Smart Poster Tag.

 

   /*
    * ================ Construct an NDEF MEssage ==========================
    *
    * This NDEF message contains a single record encoding the Smart Poster
    * Tag itself
    */
    rootMessage.setRecords(
        new NDEFRecord[] {rootRecord}); // (SP, (TEXT, URL)) 

   /*
    * Return the Smart Poster Tag
    */
    return rootMessage;
}

 

This example probably demonstrates that it is easier to construct a Smart Poster Tag than to read and parse one. Reading one involves navigating round a byte [] object and calculating offsets. Writing a Smart Poster Tag is simpler if the tag is built in reverse order from the basic elements upwards. In this case it simply involves appending data to byte [] objects and converting these to NDEF Records and NDEF Messages making appropriate use of the NDEFMessage() and NDEFRecord() constructors.

 

Summary

Hopefully this article has given you some insight into how you can use NFC Smart Poster Tags as part of your BlackBerry application. The APIs are available today so download the latest BlackBerry® JDK from

 

 

http://us.blackberry.com/developers/blackberry7/

APIs are documented here if you'd like to browse further:

 

 

http://www.blackberry.com/developers/docs/7.0.0api/

The NFC Forum Home Page can be found here: http://www.nfc-forum.org

 

Other BlackBerry developer NFC articles

 

See the NFC Article Index for the list of other articles in this series