4. Introducing NDEF – Beginning NFC [Book]
In order to understand NFC, you need to know about the NFC Data Exchange Format (NDEF), which is the lingua franca for NFC devices and tags. In this chapter, you’ll learn about the structure of NDEF and the records it carries. You’ll also write a couple of apps that read and write NDEF-formatted messages.
NDEF is a binary format structured in messages, each of which can contain several records, as shown in Figure 4-1. Each record is made up of a header, which contains metadata about the record, such as the record type, length, and so forth, and the payload, which contains the content of the message. Think of an NDEF message like a paragraph, and records like the sentences within it. A well-formed paragraph is made up of sentences pertaining to one topic. Similarly, it’s good practice to use one NDEF message made up of several records to describe one subject, say, an address book entry.
NFC transactions are generally short. Each exchange generally consists of only one message, and each tag carries just one message. Keep in mind the physical circumstances of an NFC exchange: you tap your device to another device or tag, and the whole exchange happens while you’re in contact with the other device or tag. You don’t want to send a whole novel in a single exchange, so think of your NDEF messages as paragraph-length, not book-length. You’ll see a workaround to this for sending large files in one of the final chapters of this book, but for now, consider one NFC exchange as one NDEF message, and think of one NDEF message as one or more short records.
Figure 4-1. The structure of an NDEF message made up of several records; this is a typical example—an address book entry with three records (name, phone number, address)
An NDEF record contains a payload of data and metadata describing how to interpret the payload. Each record’s payload can be one of several different data types. The header for each record contains metadata describing the record and its place in the message, followed by its type and ID. After the header comes the payload. Figure 4-2 gives you the full picture of the bits and bytes of an NDEF record.
As you can see from Figure 4-2, an NDEF record consists of a type name format (TNF), payload type, payload identifier, and the payload. The payload is the most important part of an NDEF record; it’s the content that you’re transmitting. The TNF tells you how to interpret the payload type. The payload type is an NFC-specific type, MIME media-type, or URI that tells you how to interpret the payload. Another way to think about this is that the TNF is the metadata about the payload type, and the payload type is the metadata about the payload. The payload identifier is optional and allows multiple payloads to be associated or cross referenced.
NDEF records begin with a type name format. The TNF indicates the structure of the value of the type field. The TNF tells you how to interpret the type field. There are seven possible TNF values:
0 Empty
- Empty record that has no type or payload.
1 Well-Known
- One of several pre-defined types laid out in the NFC Forum RTD specification.
2 MIME media-type
- An Internet media type as defined in RFC 2046.
3 Absolute URI
- A URI as defined in RFC 3986.
4 External
- A user-defined value, based on rules in the NFC Forum Record Type Definition specification.
5 Unknown
- Type is unknown. Type length must be 0.
6 Unchanged
- Only for middle and terminating records of chunked payloads. Type length must be 0.
7 Reserved
- Reserved by the NFC Forum for future use.
For many applications, you’ll probably use TNF 01 (Well-Known) or TNF 02 (MIME media-type) for various Internet media. You’ll see the TNF 04 (External) frequently as well, since Android uses an External type called an Android Application Record to trigger apps to open.
The Payload type, also known as the Record type, describes the content of the payload more specifically. The TNF defines the format of the payload type. The type can be an NDEF-specified type, MIME type, URI, or External type. The NDEF Record Type Definition (RTD) specification describes Well-Known Record types and sets the rules for creating External types. The MIME RFC and the URI RFC set the rules for the other types.
For example, a record of TNF 01 (Well-Known) could have a Record type of “T” for a text message, “U” for a URI message, or “Sp” if the payload is a Smart Poster. A record of TNF 02 (MIME media-type) might be one of several different Record types, among them “text/html,” “text/json,” and “image/gif.” A record of TNF 03 (Absolute URI) would have a literal URI, http://schemas.xmlsoap.org/soap/envelope/, as the type. A record of TNF 04 (External) might also have one of several different Record types, the most common of which you’ll see in this book is “android.com:pkg.”
For more detail, consult the NDEF Specification document on formats for NFC Forum Record Type Definitions. You can find a list of common NFC RTD specifications in Appendix A as well.
NDEF messages can contain multiple Payload types, but by convention, the type of the first record determines how the whole message is processed. For example, Android’s intent filtering for NDEF messages only looks at the first record.
You’ll hear the terms URI, URL, and URN frequently in this book. A URI, or uniform resource identifier, is a string of characters that identifies a web resource. A URN, or uniform resource name, names the URI. A URL, or uniform resource locator, also tells you the type of transport protocol needed to get the resource. If the URN is your name, then the URI is your address, and a URL is telling someone to take the bus to get to your address. Or in web terms:
URN
- mySpecialApp
URI
- net.tigoe.mySpecialApp (in reverse domain format)
URL
- http://tigoe.net/mySpecialApp
The type name format “Absolute URI” is a bit misleading. The Absolute URI TNF means that the Record type, not the payload, is a URI. The URI in the type field describes the payload, similar to the way a MIME type describes the payload for TNF 02. For example: Windows and Windows Phone use TNF 03 (Absolute URI) for LaunchApp records using the URI “windows.com/LaunchApp.” LaunchApp records prompt the user to launch an app, much like Android uses Android Application Records to launch apps. If the app is not installed, the user is prompted to download it from the store.
Android breaks the NDEF specification a bit with Absolute URI records. Although the NDEF specification says the type describes the payload, Android devices handle TNF 03 (Absolute URI) records by opening the browser with the URI in the type field; basically, Android treats the URI in the type field as if it were the payload. BlackBerry and Windows Phone do not open the browser when an Absolute URI tag is scanned.
If you want to send a URI or URL as a payload, then you shouldn’t use TNF 03 (Absolute URI). You should encode them as TNF 01 (Well-Known) with NFC RTD “U” (URI). The NDEF specification provides a URI Record Type Definition specification with URI Identifier Codes for encoding URIs more efficiently. For example, 0x01 is the code for http://www. 0x02 is the code for https://www. In Writing Different Record Types you’ll see an example in which you add a URL to a payload, then add a single byte with the value 0x01 to add the http://www. You’ll see these in more detail in the application that follows, and you can see them all in Appendix A.
You can also encode URIs onto a TNF 01 (Well-Known) with NFC RTD “Sp” (Smart Poster). Using Smart Poster records allows additional information to accompany the URI, such as text descriptions in multiple languages, icons, and optional processing instructions.
The payload identifier, which is an optional field, should be a valid URI. It can be a relative URI, so even “foo” would work. It’s used to let your applications identify the payload within the record by ID or to allow other payloads to reference other payloads. It’s up to you to decide on the payload ID, but you also don’t have to include it.
The payload is your content. It can be anything you want that fits in a stream of bytes. A properly constructed NDEF library doesn’t care what’s in the payload, it just passes it on. You can encrypt your payload, you can send plain text, you can send a binary blob, or anything else you can think of. It’s up to the sending application and the receiving application to agree on what the payload means and how it’s formatted.
Figure 4-2 provides some more details about the binary format of the record header’s first byte. You will hopefully never need to use this information, since any well-written NDEF library should handle all of this for you. If you’re using the software libraries in this book, this is true. The finer details of the binary formatting will be taken care of for you, and can safely skip ahead to NDEF in Practice. If you want to understand more of the structure of messages and records, read on.
The first five bits of the NDEF record are flags that tell how to process the record and information about the record’s location in the message.
The bit flags in the first byte of the record header are as follows:
MB (Message Begin)
- True when this is the first record in a message.
ME (Message End)
- True when this is the last record in a message.
CF (Chunk Flag)
- True when this record is chunked.
SR (Short Record)
- True if the short record format is used for payload length.
IL (ID Length is present)
- True if the ID Length field is present.
Note that the IL bit is not the length of the ID field, it just indicates the presence of the length field. If the IL bit is 0, there is no ID length field or ID field.
The Message Begin and Message End bit flags are used for processing the records within a message. Since an NDEF message is just a collection of one or more NDEF records, there is no binary format for an NDEF message. The MB and ME flags let you determine when the message begins and ends. The first record in a message will have its MB flag set to true. The middle records will have both flags set to false. The end record in a message will have the ME set to true. A message with one record will have both the Message Begin and Message End bits set true.
Since there are only eight possible type name formats, you only need 3 bits to store it. The TNF is stored in the last three bits of the Message Flags byte.
NDEF records are variable length data structures. The record header contains the information required to read the data.
An NDEF record begins with the TNF byte, which includes the bit flags. After the TNF, the NDEF record header includes the type length. The type length is a one byte field that specifies the length of the payload type in bytes. The type length is required, but may be zero.
The payload length comes next. The Short Record (SR) bit flag in the first byte of the record header determines the length of the payload record. If SR is true, the payload length is one byte, otherwise it’s four bytes. Payload length is required, but may be zero.
If the ID length field is present and the (IL) flag is true, the next byte in the header is the ID length.
The record type field is a variable length field after ID length field (or after the payload length field if the IL flag is false). The type length field determines how many bytes should be read.
If there is a record ID, it comes after the type. This variable length field is determined by the ID length byte.
This is the end of the header. The payload comes next.
How Big Can an NDEF Message Be?
NDEF record payloads are limited in size to 232–1 bytes long, which is why the payload length field of the header is four bytes (or 232 bits). However, records can be chained together in a message to form longer payloads. In theory, there is no limit to the length of an NDEF message. In practice, the capabilities of your devices and tags define your limits. If you’re exchanging peer-to-peer messages between devices and no tags are involved, your NDEF messages are limited only by the computational capacity of your devices, and the patience of the person holding the two devices together. If you’re communicating between a device and a tag, however, your messages are limited by the tag’s memory capacity.
When you’re using NFC tags, the size limits of your records are well below the 232–1 byte limit. NFC tag types are based on a few different RFID tag standards. Most NFC tag types are based on ISO-14443A standard. These vary from 96 bytes of memory, expandable to 4K depending on the tag type. The Philips/NXP Mifare family of tags are compatible with NFC, including the Mifare Ultralights, Mifare Classic 1K and 4K tags, and the Classic Mini. There is one type of NFC tag based on the Japanese Industrial Standard (JIS) X 6319-4. These have up to 1MB of memory. Sony FeliCa tags are typical of this type. For more details on the tag type specifications, see the specifications section of the NFC Forum website.
Generally, NFC exchanges are short. A person holds her device to a tag or another device, a brief exchange happens, and she moves on. It’s not a protocol designed for long exchanges, because the devices need to be held literally in contact with each other. When you send large messages, the user has to hold the device in place for as long as the message takes to transfer. This can get tedious, so people generally prefer to use NFC to exchange capabilities between devices, then switch, or handover, to WiFi or Bluetooth to exchange data or media files.
Here’s a typical example of where NFC and WiFi might work in tandem: imagine you’ve got an NFC-enabled home music player that can sync tracks with your smartphone or tablet via WiFi from a central home media server. You’re listening to an album on the home stereo, but you have to leave for work. You tap your phone to the stereo, and the stereo tells the phone via NFC what track is playing and what time it’s up to in the song. Your phone then checks to see if it’s got the song in its playlist, and if not, downloads the song via cellular or WiFi. You go out the door, and when you’ve got your headphones on, you pick up the album where you left off.
If you need to send content that’s larger than the 232–1 byte limit, you can break a payload into chunks and send it in several records. When you do so, you set the Chunk Flag (one of the TNF flag bits) to 1 for the first chunked record and all following records that are chunked, except the last chunk. You can’t chunk content across multiple NDEF messages.
The TNF is set in the first chunked record. Subsequent chunks must use TNF 06 (Unchanged). Middle and terminating chunks must have a type length of 0.
The payload length of each record indicates the payload for that chunk.
Having a message that exceeds 500MB (that’s 232 bits) seems unlikely, so you may not use chunking for that too often. However, chunking can also be used for dynamically generated content, especially where the payload length is not known in advance.
Chunking is used relatively rarely. The libraries used in this book don’t implement chunking. Android’s parser will read chunked messages and combine them into logical NDEF records.
For more on the structure of NDEF, including a handy set of tests for writing your own NDEF parsing engines, visit the NFC Forum specifications page.
In order to see NDEF in practice, it’s useful to look at how existing applications do it. For this section, you’ll need to download a few existing apps to your device so you can write some tags with them and compare their work.
One of the most common tasks in many popular tag writer apps is the Foursquare check-in. When you tap a tag formatted with this task, your device will automatically connect to the social media app Foursquare and check you in at a venue. However, each app manages this task slightly differently, and the differences show up in their results.
For this project, you need the following:
- An NFC-enabled Android phone
- Five NFC tags
- The apps that are listed next (you can install these to your device directly from Google Play on your computer or other device)
We also expect that you’ve gone through Chapter 3 and installed all the software needed to complete the examples in that chapter.
Note
For best results across multiple devices, stick with the NFC Forum tag types; Mifare Classic tags won’t work with many newer devices. See Device-to-Tag Type Matching for more on which tags work with which devices. Avoid Type 2 (Mifare Ultralight) tags as they don’t have enough memory for this project.
For this comparison, the following apps were used:
You’ll also need NXP TagInfo to read the tags back, Foursquare for Android, and a Foursquare account.
Be sure to log in to the Foursquare app before you test any of these. If you run into any problems (such as the Foursquare app flashing on the screen and disappearing), go back to the app and log in again. This is a good example of the sort of problems that can occur entirely outside of your control. After the payload is handed off to Android, an app (such as Foursquare) is responsible for what happens next. If that app doesn’t deal with it gracefully, you might be scratching your head.
Don’t worry if you’re not really in the location you’re checking into. For one, each app will have to request permission to use Foursquare before actually performing the check-in, and you can always go and delete the check-in on the Foursquare website afterward. There’s nothing worse than confusing your friends as to your whereabouts while you’re testing NFC!
The process of writing the tags for each of these apps is fairly straightforward, with the exception of TagWriter. Here are the basic steps:
NFC Task Launcher
- Open the app and click the + at the top to make a new task. Choose “NFC” from the task category list. Name your task, then click the + button. From the list, choose “Social Media,” then “Foursquare Check-in at a venue.” Choose a venue by typing in a name, or click the magnifying glass and the app will look for nearby venues. Click “OK,” then click “Add to Task.” Click the right arrow to write to tag. Place the phone over the tag, and the app will write to the tag.
TagStand Writer
- Open the app, click “Foursquare Venue,” and pick a venue. When the screen changes to the writing screen, click the venue name to see the tag content. Write down the URL as shown in Figure 4-3 because you’ll need it for NXP TagWriter. Place the phone over the tag and the app will write to the tag.
Figure 4-3. When you’re on the tagwriting screen of TagStand Writer, you can click on the venue name to see the full URL of the venue
NFC TagWriter
- Open the app and choose “Create, Write, and Store.” Choose “New,” and from the menu, choose “URI.” Enter “Foursquare Check-in” in the description field. A description is required for this exercise so that a Smart Poster record is written to the tag. Enter the Foursquare venue as follows: http://m.foursquare.com/venue/venueid where venueid is a long hexadecimal string. Copy it from the one you wrote down from TagStand Writer previously. Don’t forget the http://. Click “Next.” Place the phone over the tag, and the app will write to the tag.
Samsung TecTiles
- Make sure you already have Foursquare installed. Open the TecTiles app and click “New Task.” Name it, then click “OK.” Click “Add” to create a new task. Click “Application.” Choose Foursquare from the list of apps. Click “Write to Tag.” Place the phone over the tag, and the app will write to the tag.
AppLauncher NFC
- Open the app, choose Foursquare. Place the phone over the tag, and the app will write to the tag.
Once you’ve written and labeled five tags, try them out. Close all apps and place the phone over the tag. They should respond as detailed in Table 4-1.
Next, open NXP TagInfo. Place the phone over the tag, and the app will read the tag. Choose NDEF and you can see the NDEF details of any tag (as illustrated in Table 4-1).
Table 4-1. The output of each app
App | Record | TNF | Record type | Payload | Action |
NFC Task Launcher |
1 |
MIME |
x/nfctl |
enZ:Foursquare;c:4a917563f964a520401a20e3 |
Attempts to launch Task Launcher app; if successful, passes URL to Foursquare app |
2 |
External |
android.com:pkg |
com.jwsoft.nfcactionlauncher |
||
Tagstand Writer: |
1 |
Well-Known |
U |
Launches Foursquare app, takes you to venue check-in screen |
|
NXP TagWriter |
1 |
Well-Known |
Sp |
Launches Foursquare app, takes you to venue check-in screen |
|
1.1 |
U |
||||
1.2 |
T |
Foursquare check-in |
|||
Samsung TecTiles |
1 |
Well-Known |
U |
tectile://www/samsung.com/tectiles |
Attempts to launch TecTiles app |
2 |
Well-Known |
T |
·enTask····Foursquare·com.joelapenna…[] |
||
App Launcher NFC |
1 |
External |
android.com:pkg |
com.joelapenna.foursquared |
Launches Foursquare app only |
As you can see, each app does the job differently. There are four basic approaches represented here, however:
- Launch the Foursquare app and let the user do the rest (App Launcher NFC)
- Send a URI and let the operating system do the rest (Tagstand Writer)
- Launch the original app which in turn launches the Foursquare app (NFC Task Launcher, Samsung TecTiles)
- Send a Smart Poster (NXP TagWriter)
The first method just uses one NDEF record, with the TNF set to “External.” The record type is then the Android Application Record for the application you want to launch, and the content is the actual name of the app, like so:
TNF: External Record type: android.com:pkg com.joelapenna.foursquared
Using the first method, you’re telling Android what application to launch only.
The second method also uses just one NDEF record, with the TNF set to “Well-Known” and the record type set to “U” for URI. Again, the content is the actual address, like so:
TNF: Well-Known Record Type: U http://m.foursquare.com/venue/4a917563f964a520401a20e3
Using the second method, you’re telling Android the URI of the thing you want to open and letting the operating system decide what application is best to open it. It’s a bit like letting Windows decide what application opens a file with a particular extension. If Foursquare weren’t on your device, Google Play would open to handle these URLs.
The third method uses an NDEF message composed of two NDEF records. For both NFC Task Launcher and Samsung TecTiles, the original application handles the tag read and then launches Foursquare. NFC Task Launcher uses a MIME-type record that includes the Foursquare venue information and an External AAR record that ensures that the application is installed. TecTiles takes a similar approach with a different implementation. TecTiles uses a URI record with a custom tectile:// URL to launch the application. It encodes the Foursquare information into a second text record. Unfortunately, TecTiles only launches the application; it does not store the venue information. Both applications use intent filters to launch when their tag is scanned. NFC Task Launcher registers for MIME-type x/nfctl
. TecTiles registers for their custom tectile:// URI. You’ll learn more about intent filters in Android’s Tag Dispatch System.
The fourth method uses a Smart Poster record. Smart Posters are a more complex type of NDEF record, where the payload is actually another NDEF message. The message embedded in the Smart Poster payload contains two NDEF records of its own, a URI and a text record. Since Smart Poster records have multiple records, they can deliver additional information about the URI, such as a title, icon, or suggested processing actions.
You can see that some of the applications, like TecTiles and NFC Task Launcher, write Android Application Records to launch their own app, not Foursquare. They then have their app launch Foursquare. This presumably allows them to track when their app is used, even if the end result is to open a different app. It’s more complicated, but it arguably allows for gathering metrics about your app’s use.
A Tag Writer Application: Foursquare Check-In
In this section, you’ll write your own tag writer app in order to get a better understanding of how this works in practice. This app is very simple; it looks for a tag that can be formatted as an NDEF tag, and if it finds one, it writes an NDEF message to the tag.
For this project, you need the following:
Start by making a new project as you did in Chapter 3. Use Cordova to create a project, add the Android platform, and install the plug-in:
$
cordova create ~/FoursquareCheckin com.example.checkin FoursquareCheckin$
cd
~/FoursquareCheckin$
cordova platform add android$
cordova plugin add https://github.com/chariotsolutions/phonegap-nfc
Now you’re ready to write your app by editing the HTML and JavaScript files. The index.html file is in the www directory of the app directory you just created and the index.js is in www/js. Open them both and delete everything to start your own app from scratch. Start with an index.html file like so:
Foursquare Check-In Tag Writer
{
margin
:
20px
}
Foursquare Check-In Tag Writer
class=
"app"
>
id=
"messageDiv"
>
No tag found"text/javascript"
src=
"cordova.js"
>
"text/javascript"
src=
"js/index.js"
>
"text/javascript"
>
app
.
initialize
();
Writing an NDEF Record to a Tag
Next, you’re going to write the index.js file to format an NDEF record and write an NDEF message to any tag it encounters. For now, you’ll keep it simple and hardcode most of the parameters. You’ve seen many methods for triggering a Foursquare check-in; to start with, just do the simplest thing: launch the Foursquare app using an Android Application Record.
Start with a variable to write when a tag shows up:
var
app
=
{
messageToWrite
:
[],
// message to write on next NFC event
Next comes an initialize()
function to start things off and a bindEvents()
function to set up an event listener to detect when the device is ready:
// Application constructor
initialize
:
function
()
{
this
.
bindEvents
();
console
.
log
(
"Starting Foursquare Checkin app"
);
},
/*
bind any events that are required on startup to listeners:
*/
bindEvents
:
function
()
{
document
.
addEventListener
(
'deviceready'
,
this
.
onDeviceReady
,
false
);
},
After that comes a handler to clear the screen and add an event listener to listen for discovered tags:
/*
this runs when the device is ready for user interaction:
*/
onDeviceReady
:
function
()
{
app
.
clear
();
nfc
.
addTagDiscoveredListener
(
app
.
onNfc
,
// tag successfully scanned
function
(
status
)
{
// listener successfully initialized
app
.
makeMessage
();
app
.
display
(
"Tap an NFC tag to write data"
);
},
function
(
error
)
{
// listener fails to initialize
app
.
display
(
"NFC reader failed to initialize "
+
JSON
.
stringify
(
error
));
}
)
},
The NFC event handler, onNfc()
, will write to the tag, like so:
/*
called when a NFC tag is read:
*/
onNfc
:
function
(
nfcEvent
)
{
app
.
writeTag
(
app
.
messageToWrite
);
},
Next come the display()
and clear()
functions, just as you wrote them in PhoneGap Meets NFC: NFC Reader:
/*
appends @message to the message div:
*/
display
:
function
(
message
)
{
var
label
=
document
.
createTextNode
(
message
),
lineBreak
=
document
.
createElement
(
"br"
);
messageDiv
.
appendChild
(
lineBreak
);
// add a line break
messageDiv
.
appendChild
(
label
);
// add the text
},
/*
clears the message div:
*/
clear
:
function
()
{
messageDiv
.
innerHTML
=
""
;
},
The methods for the makeMessage()
and writeTag()
functions that follow make use of functions from two objects defined by the NFC plug-in. These are the NFC object, which gives you access to your device’s NFC reader, and the NDEF object, which defines and formats NDEF records and messages.
makeMessage
:
function
()
{
// Put together the pieces for the NDEF message:
var
tnf
=
ndef
.
TNF_EXTERNAL_TYPE
,
// NDEF Type Name Format
recordType
=
"android.com:pkg"
,
// NDEF Record Type
payload
=
"com.joelapenna.foursquared"
,
// content of the record
record
,
// NDEF record object
message
=
[];
// NDEF Message to pass to writeTag()
// create the actual NDEF record:
record
=
ndef
.
record
(
tnf
,
recordType
,
[],
payload
);
// put the record in the message array:
message
.
push
(
record
);
app
.
messageToWrite
=
message
;
},
writeTag
:
function
(
message
)
{
// write the record to the tag:
nfc
.
write
(
message
,
// write the record itself to the tag
function
()
{
// when complete, run this callback function:
app
.
display
(
"Wrote data to tag."
);
// write to the message div
},
// this function runs if the write command fails:
function
(
reason
)
{
alert
(
"There was a problem "
+
reason
);
}
);
}
};
// end of app
You already know that an NDEF record consists of a TNF, a record type, and a payload. The NDEF object has functions for creating NDEF#Records. An NDEF#Message is simply an array of NDEF#Records. To create a new NDEF record, you need to pass in four parameters:
Type name format
- A 3-bit value. The TNF values are contained as constants in the NDEF object, so you can refer to them using those constants.
Record type
- A string or byte array containing zero to 255 bytes, representing the record type. The NDEF object has built-in constants for many of these as well, though not all, as you can see in this example.
Record ID
- A string or byte array containing zero to 255 bytes. As you read earlier, record IDs are optional, so you can use an empty array, but this value must not be null.
Payload
- A string or array containing zero to (232–1) bytes. Again, you can use an empty array, but this must not be null.
In the makeMessage()
function, the TNF is TNF_EXTERNAL_TYPE
, using the constants defined in the NDEF object; the record type is android.com:pkg
, meaning that your payload will be an Android Application Record; the record ID is not specified, so an empty array is sent; and the payload is the name of the application to launch, com.joelapenna.foursquared
.
Save both the index.html and index.js files, then change directories to the root of your new app, and run it:
cordova run
When you run the app and tap your device to a tag, you should get the tag ID as shown on the left side of Figure 4-4. Click the link and the app will write to the tag and give you the notification seen on the right side of Figure 4-4. Close the app, tap your device to the tag, and it should launch Foursquare.
The full source code can be found on GitHub.
Writing Different Record Types
Your app can now write a tag to launch another app from that tag, but it doesn’t do all the things you saw in the previous examples. It would be great if you could emulate all five of the other apps, for comparison. To do that, make a new project called “FoursquareAdvanced.” Install the NFC plug-in, then copy the index.html and index.js from the previous Foursquare Check-In app example:
$
cordova create ~/FoursquareAdvanced com.example.advanced FoursquareAdvanced$
cd
~/FoursquareAdvanced$
cordova platform add android$
cordova plugin add https://github.com/chariotsolutions/phonegap-nfc$
cp ~/FoursquareCheckin/www/index.html ~/FoursquareAdvanced/www/.$
cp ~/FoursquareCheckin/www/js/index.js ~/FoursquareAdvanced/www/js/.
Start by modifying the index.html page by adding a form with an option menu that lets the user choose the app she wants to emulate. Here’s the new index.html page:
Foursquare Check-In Tag Writer - Advanced
body
{
margin
:
20px
}
Foursquare Check-In Tag Writer - Advanced
class=
"app"
>
Write a tag like:
/>
id=
"messageDiv"
>
"text/javascript"
src=
"cordova.js"
>
"text/javascript"
src=
"js/index.js"
>
"text/javascript"
>
app
.
initialize
();
When the user picks one of the apps in the menu, you’ll now have a convenient form element that you can read to determine how to format your tag. Save the index.html file, and open your index.js file. You’ll need to modify makeMessage()
to handle this. The new function will emulate the apps you saw here by constructing several different types of NDEF messages. First, add a new local variable to read the HTML form element to find out which app the user wants to emulate. The rest of the local variables are the same names, but their values will be generated on the fly this time:
makeMessage
:
function
()
{
// get the app type that the user wants to emulate from the HTML form:
var
appType
=
parseInt
(
appPicker
.
value
,
10
),
tnf
,
// NDEF Type Name Format
recordType
,
// NDEF Record Type
payload
,
// content of the record
record
,
// NDEF record object
message
=
[];
// NDEF Message to pass to writeTag()
The rest of the makeMessage()
function will be totally changed. After the local variables, you’re going to format a different NDEF message depending on which app you’re emulating. Since the HTML form returns an integer from the option menu, you can use that result in a switch-case statement as follows. You can see that each case writes the same records as one of the previous apps. Case 1 creates a MIME media record containing the instructions for the Task Launcher app, then an Android Application Record to tell Android which app to launch:
switch
(
appType
)
{
case
1
:
// like NFC Task Launcher
// format the MIME media record:
recordType
=
"x/nfctl"
;
payload
=
"enZ:Foursquare;c:4a917563f964a520401a20e3"
;
record
=
ndef
.
mimeMediaRecord
(
recordType
,
payload
);
message
.
push
(
record
);
// push the record onto the message
// format the Android Application Record:
tnf
=
ndef
.
TNF_EXTERNAL_TYPE
;
recordType
=
"android.com:pkg"
;
payload
=
"com.jwsoft.nfcactionlauncher"
;
record
=
ndef
.
record
(
tnf
,
recordType
,
[],
payload
);
message
.
push
(
record
);
// push the record onto the message
break
;
Case 2 creates a Well-Known type record with the RTD set to URI. Since URIs have a standard format, the NDEF spec includes URI Identifier Codes to be used in place of some of the standard URI headers, to shorten the payload. For example, Table 4-2 shows the first few URI identifier codes.
Table 4-2. Some URI identifier codes
URI Identifier Code (UIC) | What it means |
0x00 |
Nothing, used for custom headers |
0x01 |
http://www. |
0x02 |
https://www. |
0x03 |
http:// |
The other URI headers you’re used to, like ftp:// and mailto: and file:///, are all included with their own numbers. For a complete list, see the URI Record Type Definition document, part of the NFC Specification on the NFC Forum site. It’s also listed in Appendix A.
In order to add the UIC, you need to convert the URI string to an array of bytes and push the UIC on to the beginning of it. In Case 2, add 0x03, the UIC for http://:
case
2
:
// like Tagstand Writer
// format the URI record as a Well-Known type:
tnf
=
ndef
.
TNF_WELL_KNOWN
;
recordType
=
ndef
.
RTD_URI
;
// add the URI record type
// convert to an array of bytes:
payload
=
nfc
.
stringToBytes
(
"m.foursquare.com/venue/4a917563f964a520401a20e3"
);
// add the URI identifier code for "http://":
payload
.
unshift
(
0x03
);
record
=
ndef
.
record
(
tnf
,
recordType
,
[],
payload
);
message
.
push
(
record
);
// push the record onto the message
break
;
Case 3 is special because it’s creating a Smart Poster message. Smart Posters are unique among the Well-Known types in that they’re records that contain NDEF messages. As you can see in the next example, you’re constructing the payload of the Smart Poster record as an array of records. That array is the message.
In Chapter 5, you’ll see how to extract a Smart Poster record by treating its content as a message, and extracting that message recursively:
case
3
:
// like NXP TagWriter
// The payload of a Smart Poster record is an NDEF message
// so create an array of two records like so:
var
smartPosterPayload
=
[
ndef
.
uriRecord
(
"http://m.foursquare.com/venue/4a917563f964a520401a20e3"
),
ndef
.
textRecord
(
"foursquare checkin"
),
];
// Create the Smart Poster record from the array:
record
=
ndef
.
smartPoster
(
smartPosterPayload
);
// push the Smart Poster record onto the message:
message
.
push
(
record
);
break
;
Case 4 constructs a URI record and a text record that contains some binary data. As you saw in Case 2, you need to convert the URI to a byte array and push the URI Identifier Code on to the front of the array. In this case, since Samsung is using a custom URI header (tectile://), use the URI Identifier Code 0x00, which writes the URI exactly as written. TecTiles also writes a second record with TNF 01 (Well-Known type) and type “T.”
Samsung is also using a proprietary token in the middle of the payload. You can determine this from reading a tag written with the TecTiles app with the NXP TagReader app. It’s duplicated here so that the message is formatted just as the Samsung app wants it to be:
case
4
:
// like TecTiles
// format the record as a Well-Known type
tnf
=
ndef
.
TNF_WELL_KNOWN
;
recordType
=
ndef
.
RTD_URI
;
// add the URI record type
var
uri
=
"tectiles://www.samsung.com/tectiles"
;
payload
=
nfc
.
stringToBytes
(
uri
);
var
id
=
nfc
.
stringToBytes
(
"0"
);
// URI identifier 0x00 because there's no ID for "tectile://":
payload
.
unshift
(
0x00
);
record
=
ndef
.
record
(
tnf
,
recordType
,
id
,
payload
);
message
.
push
(
record
);
// push the record onto the message
// text record with binary data
tnf
=
ndef
.
TNF_WELL_KNOWN
;
recordType
=
ndef
.
RTD_TEXT
;
payload
=
[];
// language code length
payload
.
push
(
2
);
// language code
payload
.
push
.
apply
(
payload
,
nfc
.
stringToBytes
(
"en"
));
// Task Name
payload
.
push
.
apply
(
payload
,
nfc
.
stringToBytes
(
"Task"
));
// 4-byte token proprietary to TecTiles:
payload
.
push
.
apply
(
payload
,
[
10
,
31
,
29
,
19
]);
// Application Name
payload
.
push
.
apply
(
payload
,
nfc
.
stringToBytes
(
"Foursquare"
));
// NULL terminator
payload
.
push
(
0
);
// Activity to launch
payload
.
push
.
apply
(
payload
,
nfc
.
stringToBytes
(
"com.joelapenna.foursquared.MainActivity"
));
// NULL terminator
payload
.
push
(
0
);
// Application packageName
payload
.
push
.
apply
(
payload
,
nfc
.
stringToBytes
(
"com.joelapenna.foursquared"
));
id
=
nfc
.
stringToBytes
(
"1"
);
record
=
ndef
.
record
(
tnf
,
recordType
,
id
,
payload
);
message
.
push
(
record
);
// push the record onto the message
break
;
Warning
The TecTiles record is too large for certain tags, such as the Mifare Ultralight, which has a limited capacity.
Case 5 constructs an Android Application Record to open Foursquare directly. It’s just like all the other Android Application Records you’ve made already. It’s an External TNF, with the record type android.com:pkg
and the app name as the payload:
case
5
:
// like App Launcher NFC
// format the Android Application Record:
tnf
=
ndef
.
TNF_EXTERNAL_TYPE
;
recordType
=
"android.com:pkg"
;
payload
=
"com.joelapenna.foursquared"
;
record
=
ndef
.
record
(
tnf
,
recordType
,
[],
payload
);
message
.
push
(
record
);
// push the record onto the message
break
;
}
// end of switch-case statement
Finally, finish off the makeMessage()
function by setting the message to write and notifying the user:
app
.
messageToWrite
=
message
;
app
.
display
(
"Tap an NFC tag to write data"
);
},
// end of makeMessage()
With these changes you can save the index.js file and run the app (make sure you still have the writeTag()
function in your app from the previous example):
$
cordova run
When you do, you’ll get a drop-down menu that lets you emulate any of the apps you saw in Table 4-1. To use it, pick an app to emulate from the menu, then tap the phone to a tag. Figure 4-5 shows the app.
The full source code can be found on GitHub.
PhoneGap-NFC NDEF Helper Functions, Summarized
As you can see, this app now covers multiple functions of the PhoneGap-NFC library by emulating various approaches to the same task. There are a few helper functions in the NDEF object it contains that can be used to write NDEF records. The main one is ndef.record()
, which requires you to give it the TNF, record type, ID, and payload:
// Caller specifies the TNF and record type
record
=
ndef
.
record
(
tnf
,
recordType
,
id
,
payload
);
// example:
record
=
ndef
.
record
(
ndef
.
TNF_EXTERNAL_TYPE
,
"android.com:pkg"
,
[],
"com.joelapenna.foursquared"
);
There’s also a helper for MIME media records that takes just a MIME type and the payload:
// TNF: MIME media (02)
record
=
ndef
.
mimeMediaRecord
(
mimeType
,
payload
);
// example:
record
=
ndef
.
mimeMediaRecord
(
"text/json"
,
'{"answer": 42}'
);
The URI helper builds a record using TNF 01, Well-Known, and record type “U” to send a URI as you saw in Case 3 of Writing Different Record Types. The helper will shorten your URI using a URI Identifier Code. In this example, http:// will be written to the tag as 0x03 followed by m.foursquare.com:
// TNF: Well-Known Type(01), RTD: URI ("U")
record
=
ndef
.
uriRecord
(
uri
);
// example:
record
=
ndef
.
uriRecord
(
"http://m.foursquare.com/"
);
The text record helper makes text records using a Well-Known TNF and a text record type definition. If no language is specified, it defaults to English:
// TNF: Well-Known Type(01), RTD: Text ("T")
record
=
ndef
.
textRecord
(
text
,
language
);
// example:
// defaults to English, since no language specified:
record
=
ndef
.
textRecord
(
"How are you doing?"
);
The Smart Poster helper constructs a Smart Poster from other records:
//TNF: Well-Known Type (01), RTD: Smart Poster ("Sp")
record
=
ndef
.
smartPoster
(
ndefMessage
);
// example:
record
=
ndef
.
smartPoster
(
// URI record:
ndef
.
uriRecord
(
"http://m.foursquare.com/venue/4a917563f964a520401a20e3"
),
// text record:
ndef
.
textRecord
(
"foursquare checkin"
)
);
There’s also a helper you didn’t see in this chapter, the empty record helper. This just makes an empty record for you to fill out:
// TNF: Empty (00)
record
=
ndef
.
emptyRecord
();
// example:
record
=
ndef
.
emptyRecord
();
// it's empty!
With these functions, you can construct all of the types of NDEF messages you’ll see in the rest of this book.
We’ll go further into NDEF records and messages in the following chapters. The key concepts to take away from this introduction, though, are as follows:
NFC-formatted tags contain NDEF messages. NDEF messages are composed of one or more NDEF records. You’ll be tempted to talk about tags and messages and records interchangeably, but don’t do it. Later in the book, you’ll pass messages from device to device with no tags.
All NDEF records have a type. In the specs, its sometimes called Payload type, sometimes called Record type, other times just type. The type name format (TNF) categorizes the types broadly into a few areas and tells you how to interpret the type:
For TNF 01 (Well-Known)
- The NFC Forum Record Type Definition (RTD) specification tells you what types to use.
For TNF 02 (MIME)
- The MIME RFC (RFC 2046) tells you what types are valid.
For TNF 03 (URI)
- The URI RFC (RFC 3986) tells you how to make valid types.
For TNF 04 (External)
- The NFC Forum RTD specification tells you how to make your own types.
There are eight formal type name formats of NDEF messages, but most of the work gets done with just three: Well-Known, MIME media, and External. The Well-Known TNF includes a number of useful definitions for record types, including text messages, URIs, Smart Posters, and the various carriers needed for peer-to-peer handovers. You’ll learn more about the peer-to-peer messages in Chapter 8. You’ll see text messages and URIs used throughout this book. The MIME media TNF covers all Internet media types, and you can use it to make custom types as well, as you’ll see in Chapter 6. The External TNF is used for records like the Android Application Record.
You won’t see a lot of Smart Poster types in this book. As you saw in the Smart Poster case in Writing Different Record Types, the payload of a Smart Poster record is itself an NDEF message. So in order to read a Smart Poster, you first have to parse the message that encloses it, then you have to parse the message that it encloses. We feel this is redundant for most applications, and that it’s better practice to simply use multiple records in an NDEF message.
There are multiple ways to achieve the same ends with NDEF messages. How that message is received depends on the operating system of the device that’s receiving it. In the next chapter, you’ll look at how Android offers you a few different ways to filter NDEF types to your application.
2024-12-17 08:03:48