Chapter 8. Peer-to-Peer Exchange

All the examples you’ve seen in this book so far have used a passive tag as an intermediary to carry NDEF data. This is a big change for RFID, as it introduces a common data format that covers a range of different tag types (though not all RFID tag types). The other major difference between RFID and NFC is the latter’s ability to make peer-to-peer exchanges. These exchanges are between two active devices directly. Prior to NFC, RFID was not used in a peer-to-peer context. In this chapter, you’ll see examples of how to program peer-to-peer (P2P) exchanges.

In each P2P exchange, there is a target and an initiator, both of which are active devices. This means that when you send a target an NDEF message via P2P, the target can manipulate the data that you send it, and send you something back in response. You can also copy information from a tag, store it on a device, and transfer it to another device or another tag. P2P offers a range of interesting possibilities.

Peer-to-peer exchanges rest on yet another NFC protocol, the Simple NDEF Exchange Protocol (SNEP). SNEP is a request-and-response protocol: the initiator sends a request about the kind of data it would like to exchange, and the target responds with the requested data. SNEP rests on the NFC Forum’s Logical Link Control Protocol (LLCP). Although you won’t have to wrestle with SNEP and LLCP directly if you’re writing application code, it’s helpful to know where they sit in the NFC architecture, because it helps explain how P2P differs from tag reading and writing. As you can see from Figure 8-1, SNEP and LLCP fit in around the same area as the various RFID tag formats: data packetization and transport in peer-to-peer exchanges are dependent on SNEP and LLCP just as they are dependent on tag formats in reader-to-tag exchanges. That layer is a crucial link between the device controllers and the NDEF layer.

SNEP and LLCP fit into the NFC architecture as a data-link layer, performing a similar function to the tag formats
Figure 8-1. SNEP and LLCP fit into the NFC architecture as a data-link layer, performing a similar function to the tag formats

SNEP and LLCP don’t define how peer-to-peer exchanges should be handled by an operating system or presented to a user. Each operating system implements SNEP and LLCP in its own way. Android’s current implementation is called Android Beam (prior to Android 4.0, Android had an implementation called the NDEF Push Protocol, or NPP). Using Beam, when you want to exchange data between two Android devices, you touch them back to back, and the “Touch to beam” interface appears. When you tap on the sending device, it initiates a P2P exchange with the receiving device and the data is sent as an NDEF message or series of messages. Beam is designed to let you operate at the upper layers of the stack shown in Figure 8-1. It handles the peer-to-peer logical link exchanges and hands NDEF messages off to the dispatch system mentioned in Chapter 5 and Chapter 6.

When you enable NFC, Beam keeps the NFC radio active and looking for connections from other devices or tags. You’ve already seen in Chapter 5 that when a tag comes close and there’s no foreground app to handle it, the tag dispatch system handles it. Beam handles peer-to-peer similarly. If the NFC radio on your device detects another radio, Android brings up the “Touch to beam” interface, no matter what app is running. Figure 8-2 shows this interface. If your app implements the P2P sharing functions (the NDEF Push Protocol in the Android Java framework), then whatever data you choose to share will be sent to the target phone. If not, then Android will send the URI of your app. If the target has the app, it will open. If not, Android will open the Google Play app store to locate it.

“Touch to beam” interface on Android (Gower Tides app, left, and the app being beamed, right)
Figure 8-2. “Touch to beam” interface on Android (Gower Tides app, left, and the app being beamed, right)

This is where Android Beam differs from previous implementations of peer-to-peer on Android, and from its implementation on other operating systems. This default can be annoying in some cases, but it can be turned off by adding the following line to your AndroidManifest.xml file right after the opening <application> tag:

 <application android:debuggable="true" android:hardwareAccelerated="true"
   android:icon="@drawable/icon" android:label="@string/app_name">
      <meta-data android:name="android.nfc.disable_beam_default"
         android:value="true" />
      <activity android:configChanges=
         "orientation|keyboardHidden|keyboard|screenSize|
         locale" android:label="@string/app_name" android:name="P2P"
         android:theme="@android:style/Theme.Black.NoTitleBar">

Sending Peer-to-Peer Messages in PhoneGap

The best way to understand P2P is to try it out. You’ll need two NFC-enabled Android devices for this. To see the default behavior, you don’t even need to write another app. You can use NXP TagInfo, or the NDEF Reader app you wrote in Chapter 5. Open either of these apps on your target device, then open any other app on your initiator device. Bring the two devices together back-to-back, and tap the initiator device’s screen. On the target, you should get the URI of the foreground app on the initiator. Figure 8-3 shows the result of beaming an app called Gower Tides from the initiator device to the NXP TagInfo app. When an app that doesn’t implement P2P is beamed, Android Beam will pass a message containing two records by default: a record with a TNF 01 containing a URI for the app on the Play Store, and a second record with a TNF 04 containing the app’s Android Application Record (AAR).

The default message sent from Gower Tides using Android Beam; this app does not implement peer-to-peer, but Android Beam sends an NDEF message for it
Figure 8-3. The default message sent from Gower Tides using Android Beam; this app does not implement peer-to-peer, but Android Beam sends an NDEF message for it

When you want to override this behavior and send your own NDEF messages in Android, you can either set an NDEF Push message or set a Beam push URI. There are methods for both in Android, with callback options for both. In the PhoneGap-NFC plug-in, NDEF push is simplified to nfc.share(), and URI Beam is simplified to nfc.handover(). When you share a message, it gets pushed, and when the target receives the message successfully, you get a success callback.

The next app uses some of the NDEF messages you’ve written to tags before so that you can see how Android behaves differently when receiving messages via P2P instead of reading them from tags. In most cases, you’ll see there’s no difference. You can also use this app to send your own custom messages via P2P to see how they behave.

To do this, you’ll need the tools you saw in earlier chapters:

  • Two NFC-enabled Android devices
  • The Android Software Developers Kit
  • A text editor
  • Cordova CLI installed on your machine
  • Node.js and npm installed as well

To get started, create a new project:

$ cordova create ~/P2P com.example.p2p P2P 1
$ cd ~/P2P 2
$ cordova platform add  android
$ cordova plugin add https://github.com/don/phonegap-nfc
1

Windows users should type %userprofile%P2P instead of ~/P2P.

2

Windows users should type /d %userprofile%P2P instead of ~/P2P.

When the project is ready to go, open the index.html and the index.js files from the www directory.

The index.html page has a simple form for entering a new NDEF record and a drop-down menu that lets you choose one of several pre-made records. You’ll populate the menu from the index.js file later:

<!DOCTYPE html>

<html>
   <head>
      <title>PhoneGap NFC P2P</title>
   </head>
   <body>
      <h2>PhoneGap NFC P2P</h2>
      <div class="app">
         <div id="messageDiv"></div>
         <form>
            <select id="sampleField"></select><br />
            <input type="hidden" id="kindField" /><br/>
            <div id="typeDiv">
              Type:<br/>
              <input type="text" id="typeField" value="" size="30" />
            </div>
            Payload:<br/>
            <textarea id="payloadField" rows="10" cols="30"></textarea>
            <br/>
         </form>
        </div>
      <script type="text/javascript" src="cordova.js"></script>
      <script type="text/javascript" src="js/index.js"></script>
      <script type="text/javascript">
        app.initialize();
      </script>
   </body>
</html>

The index.js starts with an array called data that holds several JSON-encoded NDEF records. You’ll recognize most of these from past examples. They’ll be sent as various different record types:

var data = [
      {
         name: "Text Record",
         kind: "text",
         data: "hello, world"
      },
      {
         name: "URI Record",
         kind: "uri",
         data: "http://oreilly.com"
      },
      {
         name: "Address",
         kind: "mime",
         type: 'text/x-vCard',
         data: 'BEGIN:VCARD
' +
            'VERSION:2.1
' +
            'N:Coleman;Don;;;
' +
            'FN:Don Coleman
' +
            'ORG:Chariot Solutions;
' +
            'URL:http://chariotsolutions.com
' +
            'TEL;WORK:215-555-1212
' +
            'EMAIL;WORK:[email protected]
' +
            'END:VCARD'
      },
      {
         name: "Hue Settings",
         kind: "mime",
         type: 'text/hue',
         data: JSON.stringify({
         "1":
            {"state":
               {"on":true,"bri":65,"hue":44591,"sat":254}
            },
         "2":
            {"state":
               {"on":true,"bri":254,"hue":13122,"sat":211}
            },
         "3":
            {"state":
               {"on":true,"bri":255,"hue":14922,"sat":144}
            }
         })
      },
      {
         name: "Android Application Record",
         kind: "external",
         type: "android.com:pkg",
         data: "com.joelapenna.foursquared"
      },
      {
         name: "Empty",
         kind: "empty",
         data: ""
      }
];

The main app comes next. The first few functions will look very familiar. There’s an initialize(), a bindEvents(), and an onDeviceReady() to set things up:

var app = {
   /*
      Application constructor
    */
   initialize: function() {
      this.bindEvents();
      console.log("Starting P2P app");
   },
   /*
      bind any events that are required on startup to listeners:
   */
   bindEvents: function() {
      document.addEventListener('deviceready', this.onDeviceReady, false);
      sampleField.addEventListener('change', app.showSampleData, false);
      // modify the form so it doesn't generate a submit event:
      document.forms[0].onsubmit = function(evt) {
         evt.preventDefault();      // don't submit
         payloadField.focus();      // put the payload field in focus
      };
      // if either type or payload is changed, update the share:
      typeField.onchange = app.shareMessage;
      payloadField.onchange = app.shareMessage;
   },

   /*
      this runs when the device is ready for user interaction:
   */
   onDeviceReady: function() {
      var option;

      // populate the sampleField from the data array
      sampleField.innerHTML = "";
      for (var i = 0; i < data.length; i++) {
         option = document.createElement("option");   // make an option element
         option.value = i;                            // give it this number
         option.innerHTML = data[i].name;             // get the data object
         if (i === 0) {                               // select the first element
            option.selected = true;
         }
         sampleField.appendChild(option);             // add this to sampleField
      }

      app.showSampleData();
   },

Next come the P2P functions. shareMessage() looks for the data record currently in the form, formats it as an NDEF message, and uses nfc.share() to share it via P2P:

   /*
      Share the message from the form via peer-to-peer:
   */
   shareMessage: function () {
      // get the MIME-type, and payload from the form
      // and create a new record:
      var payloadType = typeField.value,
          payloadData = payloadField.value,
          kind = kindField.value,
          record;

      app.clear();                        // clear the message div
      app.display("Publishing message");  // display the notification

      // use a different ndef helper to format the message
      // depending on the kind:
      switch (kind) {
         case "text":
            record = ndef.textRecord(payloadData);
            break;
         case "uri":
            record = ndef.uriRecord(payloadData);
            break;
         case "mime":
            record = ndef.mimeMediaRecord(payloadType, payloadData);
            break;
         case "external":
            record = ndef.record(ndef.TNF_EXTERNAL_TYPE, payloadType, [], ...);
            break;
         case "empty":
            record = ndef.emptyRecord();
            break;
         default:
            alert("ERROR: can't build record");
      }

      console.log(JSON.stringify(record));

      // share the message:
      nfc.share(
         [record],                // NDEF message to share
         function () {            // success callback
            navigator.notification.vibrate(100);
            app.display("Success! Message sent to peer.");
         },
         function (reason) {      // failure callback
            app.display("Failed to share message " + reason);
         });
   },

When a message is successfully received on the target, the success callback is called on the initiator, and you get the message “Success! Message sent to peer.” You can use this callback to trigger other behaviors as well.

The nfc.share() function has two optional callbacks, for success and failure. The success function gets called only when the target device has acknowledged that it got the message. This can be a useful way to tell the initiator when the target’s ready for something new.

The unshareMessage() function turns off sharing once the data’s been shared. In this app, it’s not used, but it’s shown here so you can see what it might look like:

   /*
      Stop sharing:
   */
   unshareMessage: function () {
      // stop sharing this message:
      nfc.unshare(
         function () {                     // success callback
            navigator.notification.vibrate(100);
            app.clear();
            app.display("message is no longer shared");
         },
         function (reason) {               // failure callback
            app.display("Failed to unshare message " + reason);
         });
   },

There is just one UI-related function. When the drop-down menu changes, it calls showSampleData() to get the appropriate date element and fill it into the form fields:

    /*
      Get data from the data array and put it in the form fields:
   */
   showSampleData: function() {
      // get the type and payload from the form
      var index = sampleField.value,
          record = data[index];

      // fill form with the data from the record:
      kindField.value = record.kind;
      typeField.value = record.type;
      payloadField.value = record.data;

      // hide type for kinds that don't need it
      if (typeof record.type === 'string') {
         typeDiv.style.display = "";
      } else {
         typeDiv.style.display = "none";
      }

      app.shareMessage();
   },

Last, but not least, comes the familiar display() and clear() functions for writing to the message div:

   /*
      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 = "";
   },
};     // end of app

You only need to run this app on one of the two devices. When you run it, you’ll get the interface shown in Figure 8-4. You can pick any of the items in the drop-down menu as your data to share, or make up your own. To share the message, pick any of the pre-made records, or make one yourself, then touch the device running this app back-to-back with your second device. If Foursquare and the Mood Setter app are on the second device, then they will open when you share the records corresponding to those apps. The address record will trigger Android to ask you to save the record in your contacts.

Your first peer-to-peer app
Figure 8-4. Your first peer-to-peer app

Receiving Peer-to-Peer Messages in PhoneGap

Receiving P2P messages is just like receiving tag-based messages. You simply need to add one or more of the NFC event listeners you learned about in Chapter 5. When the foreground app receives an NDEF message via push, it treats it just like it would a tag-based message.

To add receive capability to the app you just wrote, you’ll need to add an event listener. You’ve got at least two different types of NDEF messages you’re sending (MIME type or external type), so if you want to catch them both with one listener, you need an NdefListner. Add the following to the onDeviceReady() function:

       nfc.addNdefListener(
         app.onNfc,                 // nfcEvent received
         function (status) {        // listener successfully initialized
            app.displayMessage("Listening for NDEF messages.");
         },
         function (error) {         // listener fails to initialize
            app.displayMessage("NFC reader failed to initialize "
               + JSON.stringify(error));
         }
      );

Then add a function called onNfc() to handle this event. You can add this anywhere you like within the app variable. This is a very truncated example, but if you want something more detailed, there are good functions to display messages in the NDEF Reader example from Chapter 5:

    /*
   displays info from @nfcEvent in message div:
   */
    onNfc: function(nfcEvent) {
       // if there is an NDEF message on the tag, display it:
      var thisTag = nfcEvent.tag,
          thisMessage = thisTag.ndefMessage,
          tagData = "";

      // display the tag properties:
      tagData = "Tag ID: " + nfc.bytesToHexString(thisTag.id) + "<br />"
         + "Tag Type: " +  thisTag.type + "<br />"
         + "Max Size: " +  thisTag.maxSize + " bytes<br />"
         + "Is Writable: " +  thisTag.isWritable + "<br />"
         + "Can Make Read Only: " +  thisTag.canMakeReadOnly + "<br />";

      if (thisMessage !== null) {
         // get and display the NDEF record count:
         tagData += "<p>Tag has NDEF message with " + thisMessage.length
            + " records.</p>";
         }

      app.displayMessage(tagData);
   },

Save this file and run it. When you share messages from one device to the other, the new code will listen for those messages and display information about them as well.

The full source code can be found on GitHub.

You may have noticed that we left the tag type display and tag UID display in the code from Chapter 5 as well. But since there are no physical tags used in this exchange, you get a whole series of empty results:

Tag ID: 00
Tag Type: android.ndef.unknown
Max Size: 0 bytes
Is Writable: false
Can Make Read Only: false

The tags are replaced as a datalink layer by the LLCP and SNEP layers, as explained previously, so the tag ID, type, size, and other metadata are meaningless in peer-to-peer exchanges. But that doesn’t matter to your application because it still gets the NDEF messages it’s looking for. You could restructure this app to remove the tag metadata functionality and do no damage to it.

The fact that P2P messages show up exactly like tag messages is useful because it means you can design applications where the interaction is the same whether touching a tag or another device.

Handover

Peer-to-peer sharing works great with short messages, but when you want to transfer something larger, like an audio file or a photo, it’s not so great. You have to hold the devices together while the exchange happens, so if you’ve got a large message to transfer, you could be stuck holding the devices together for a long time. This is inconvenient.

The NFC Connection Handover Specification is designed to handle this type of situation. When a handover message is received, the NFC library checks the operating system it’s running on to see if there’s a better transfer medium, like Bluetooth or WiFi, and attempts to transfer the data over that medium instead.

Handover works like this: the initiator sends an NDEF message that starts with a Handover Request Record (TNF: Well-Known type, RTD: Handover Request (“Hr”)), followed by a number of Alternative Carrier Records (TNF: Well-Known type, RTD: Alternative Carrier (“ac”)). The alternative carrier records are all the different transport media that the initiator has available: Bluetooth, WiFi, or whatever else it has. The payload of this record is the MAC address of the alternative carrier.

The target, when it gets this message, replies with a Handover Selector Record (TNF: Well-Known type, RTD: (“Hs”)) and a set of its own alternative carrier records. These are generally listed in order of the target’s preference. The initiator then chooses an alternative carrier, and sends back a handover carrier message (TNF: Well-Known type, RTD: (“Hc”)) with the alternative carrier. The target then replies with a handover carrier message that contains the configuration data and/or credentials for that carrier, so that the data can be transferred without Bluetooth pairing, WiFi login, or other negotiations.

The negotiation to set up a handover involves a few exchanges, as you can see. In order for the handover to be successful, you have to maintain contact until those exchanges are done and the handover is complete, or the transfer will fail.

When it works, handover is magical. There’s no pairing to be done, no passwords to be exchanged. Even if your devices’ Bluetooth or WiFi radios are not on, the NFC stack will ask the operating system to turn them on and make the exchange. There’s even facility in the handover protocol for targets and initiators to state the power of the alternative carrier to preserve battery life.

There are some potential issues with handover, however. If your alternative carrier is being used for something else, it can fail. For example, if you’re streaming audio over Bluetooth, and you try to send a file over it, you’ll encounter significantly slower transfer speeds, and the transfer will sometimes fail. If your devices go out of carrier range, the transfer will also fail.

As mentioned previously, every platform implements peer-to-peer in its own way. For example, Beam Android to BB10 works, but there is an extra step to accept the remote device’s pairing request. SNEP is potentially cross-platform and is supposed to simplify pairings regardless of operating system, but until everyone has implemented it, there are bound to be quirks.

Static Handover

The NFC Forum provided for a way to manage data exchange between NFC-enabled devices and non-NFC-enabled devices using handover as well. If the non-NFC device has a passive NFC tag attached, and the tag contains a Handover Selector Record with the associated configuration data in the message, then the NFC device will attempt to begin communication with the non-NFC device over the alternative carrier. For example, imagine you wanted to exchange data from an Android NFC-enabled device to an Apple iPhone. If the iPhone has an NFC tag stuck to it with its Bluetooth or WiFi configuration data in a Handover Select Message, then the Android device can get the configuration data via NFC and try to connect.

This isn’t a perfect method, as it depends on the non-NFC device to have its alternative carrier open and ready for business. If it’s not, the connection will fail, or the iPhone user will have to explicitly enable the connection. By the time you’ve gone through the trouble to enable the connection manually, it might simply be easier to start by trying to exchange your data over Bluetooth or WiFi between the two devices, and skip the static handover request. The ideal use case might be an iPhone or iPad-driven kiosk communicating to a user’s NFC-enabled device.

Sending Handover Messages in PhoneGap

In the PhoneGap-NFC plug-in, Android’s Beam URI methods, which manage handover, are simplified to nfc.handover(). Its API is similar to nfc.share(), but it shares a file instead of an NDEF Message. The following example shows how to use it. You can either choose a file to send, or take a photo and send that. To do this, you’ll need the same tools:

  • Two NFC-enabled Android devices
  • The Android Software Developers Kit
  • A text editor
  • Cordova CLI installed on your machine
  • Node.js and npm installed

To get started, create a new project:

$ cordova create ~/FileSender com.example.filesender FileSender 1
$ cd ~/FileSender 2
$ cordova platform add android
$ cordova plugin add https://github.com/don/phonegap-nfc
$ cordova plugin add https://github.com/don/cordova-filechooser
1

Windows users should type %userprofile%FileSender instead of ~/File Sender.

2

Windows users should type /d %userprofile%FileSender instead of ~/FileSender.

You’re using an extra plug-in here, filechooser, because the PhoneGap File API is clumsy to work with, as you may have noticed in Chapter 6. The filechooser plug-in simplifies the process of choosing a file by using Android’s native file chooser UI to return a URI you can use.

The index.html has a few elements in addition to the usual message div: a button for taking a picture, a button for choosing a file, and a div for displaying the picture you take:

<!DOCTYPE html>

<html>
  <head>
    <title>File Sender</title>
  </head>

  <body>
    <h1 id="cameraButton">Take Picture</h1>
    <h1 id="filePicker">Choose File</h1>

    <div id="messageDiv"></div>
    <div id="photoDiv"></div>
    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/index.js"></script>
    <script type="text/javascript">
      app.initialize();
    </script>
   </body>
</html>

The index.js file starts in the usual way, by binding event handlers to the UI elements:

var app = {
   /*
   Application constructor
   */
   initialize: function() {
      this.bindEvents();
      console.log("Starting File Sender app");
   },

   /*
   binds events that are required on startup to listeners.
   */
   bindEvents: function() {
      // bind events to the UI elements:
      document.addEventListener('deviceready', this.onDeviceReady, false);
   },

   /*
   runs when the device is ready for user interaction.
   */
   onDeviceReady: function() {
      cameraButton.addEventListener('touchstart', app.takePicture, false);
      filePicker.addEventListener('touchstart', app.chooseFile, false);
   },

The event handlers for the filePicker button and the camera button open the appropriate Android UI for both, and return the URI of the file you choose, or the image file you create. The takePicture() handler also sets the camera settings for taking the picture. You can learn more about the camera capture API from the PhoneGap documentation page under “Camera”:

   /*
   brings up the file chooser UI:
   */
   chooseFile: function() {
      fileChooser.open(
         app.onFileSystemSuccess,   // success handler
         app.failure                // failure handler
      );
   },

   /*
   Brings up the camera app:
   */
   takePicture: function () {
      navigator.camera.getPicture(
         app.onCameraSuccess,     // camera capture success handler
         app.failure,             // failure handler
         {                        // image capture options
            quality: 75,
            destinationType: Camera.DestinationType.FILE_URL,
            sourceType: Camera.PictureSourceType.CAMERA,
            targetWidth: 300,
            targetHeight: 300,
            correctOrientation: true,
            saveToPhotoAlbum: false
         }
      );
   },

Once either handler succeeds, it calls its respective success handler. The onFileSuccess() handler doesn’t do much beyond calling shareMessage(). The onCameraSuccess() handler displays the camera image on the page before calling shareMessage(). They share the same failure() handler:

   /*
   When you get a good picture, share it:
   */
   onCameraSuccess: function (imageURI) {
      var img = document.createElement("img");
      img.src = imageURI;              // add the URI as the img src
      photoDiv.innerHTML = "";         // clear old image
      photoDiv.appendChild(img);       // add the image element to the photoDiv
      app.display(imageURI);           // show the URI
      app.shareMessage(imageURI);      // share the image
   },

   /*
   When you get a good file, share it:
   */
   onFileSystemSuccess: function (fileURI) {
      photoDiv.innerHTML = "";
      app.display(fileURI);
      app.shareMessage(fileURI);
   },

   /*
   When you fail to get a file or photo, cry:
   */
   failure: function (evt) {
      console.log(evt.target.error.code);
   },

Here’s the part you’ve been waiting for: shareMessage(). It’s almost identical to the function of the same name in the last example. This version takes the URI of the file as a parameter, and it also does a check to see if the URI contains spaces (%20 when escaped for a URI). This is because the Android Beam API has a bug in it that prevents you from sending URIs with spaces in them, even when escaped. In a more fully developed version of this app, you’d make a local copy of the file and rename it without spaces, then send the copy. But for now, you’ll have to settle for sending files with no spaces:

   /*
   Share the URI from the file or photo via P2P:
   */
   shareMessage: function (uri) {
      // Android Beam API has a bug that prevents sending files
      // with spaces in the URI:
      if (uri.search("%20") > 0) {
         app.clear();
         app.display("Sorry. Can't beam a URI with spaces. Android Beam Bug");
         return;
      }

      app.clear();
      app.display("Ready to beam " + uri);
      app.display("Place your device back to back with another device to beam.")
      // beam the file:
      nfc.handover(
         uri,
         function () {                // success callback
            navigator.notification.vibrate(100);
            // you know when the beam is sent and the other device received
            // the request but you don't know if the beam completes or fails
            app.display("Success! Beam sent.");
            app.unshareMessage();     // unshare the file when complete
         },
         function (reason) {          // failure callback
            app.clear();
            app.display("Failed to share file " + reason);
         }
      );
   },

Because there’s a shareMessage() handler, there has to be an unshareMessage() handler. It’s the same as in the previous application. Following it are display(), clear(), and clearAll(), to clear the photo div as well:

   /*
   Turns off sharing
   */
   unshareMessage: function () {
      // stop beaming:
      nfc.stopHandover(
         function () {               // success callback
            navigator.notification.vibrate(100);
            app.display("File is no longer shared");
            setTimeout(app.clearAll, 5000);   // clear the screen after 5 seconds
         },
         function (reason) {         // failure callback
            app.display("Failed to unshare file " + reason);
         }
      );
   },

   /*
   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 = "";
   },

   clearAll: function() {
      app.clear();
      photoDiv.innerHTML = "";
   }
};     // end of app

The full source code can be found on GitHub.

When you run this, you’ll see that you get a success message well before the Beam is done. That message comes after the initial handover request is made and acknowledged (Figure 8-5). If you hold until you see that, the transfer generally goes through just fine. If you have trouble sending files using handover, make sure your Bluetooth connection (or other alternate carrier) is not doing something else at the time.

Handover isn’t just for large file transfer. It can be used as a convenient way to pair devices. For example, HomeSpot makes an NFC-enabled Bluetooth audio receiver that you tap your device to in order to complete Bluetooth pairing. After that, your device streams audio to the receiver. We used this as one of the devices to test the project in Chapter 6. This method of pairing could be used by many different consumer devices as a way to turn your phone or tablet into a remote control. In addition to pairing, you could include the URI to download the controller app in the initial handover request.

The FileSender app that demonstrates handover
Figure 8-5. The FileSender app that demonstrates handover

Peer-to-Peer Using Arduino

While Peer-to-Peer is possible on Arduino using the NFC shields mentioned in Chapter 7, it has complications. As of this writing, there are no libraries that abstract the SNEP and LLCP layers sufficiently to allow you to focus only on the NDEF exchange. The most promising work on peer-to-peer for Arduino to date has been done by Michael Weir, and can be found on GitHub. While this library is functional, it lacks the abstraction usually provided by an Arduino-style API, and requires more in-depth knowledge of C than most libraries. Explaining the code in this library in its current state is beyond the scope of this book. It could theoretically serve as the base for a simpler library, however.

Seeed Studio has done some work to integrate the NDEF library seen in Chapter 7 with Weir’s work. Their work can be found on GitHub as well. There is still more work to be done to fully abstract SNEP and LLCP, but this work is looking promising, and is worth following.

Thus far, both of the peer-to-peer options work only over SPI, so the Adafruit shield needs modification to work with these libraries. You need to solder a jumper across the SEL0 and SEL1 pads on the shield to make this work. You also need to add jumpers from the MISO, MOSI, SS, and SCK headers on the shield to the pins you choose for SPI initialization in the Weir or SeeedStudio libraries.

At this time, handover is not supported using any of the libraries we’ve used with Arduino. Handover would require an additional Bluetooth shield or module or a WiFi shield to work as the alternate carrier, and a library would have to be written to integrate both NFC and either Bluetooth or WiFi. This could easily push the limits of the Arduino Uno’s available program memory, but might be possible on the Arduino Due.

Card Emulation

In addition to reading and writing tags and communicating in peer-to-peer mode, NFC devices are also able to operate in tag emulation mode. In this mode, your NFC device responds to other NFC devices exactly as if it were an NFC tag. It means that your device performs just like a contactless smart card, from the reading device’s perspective.

This has some useful applications. When your device performs like a smart card, the reader device can verify the data on your card (or card emulator) by ensuring not only that the data is authenticated, but that the card ID associated with the data is accurate. For example, if your bank account number is on the card, and the card has the same UID as the card issued by your bank, then an ATM or cash register can make a reliable transaction. Tag emulation is designed to make financial transactions like this, or like ticketing, reliable over NFC. This is the technology behind Google Wallet and other systems that let your phone act like a cash card or credit card.

In order to make tag emulation secure, banks and other clients using this technology need to know that the system is secure. So the NFC forum has specified that NFC readers include a secure element or secure access module in their hardware. This is just a simple processor that’s attached to the NFC controller. The secure element can be programmed to verify a code sent by the reader using public key encryption and decryption. Because the secure element of a reader/writer (or of a smart card) is capable of running small programs on its own (called applets or taglets), it can even generate new hashes on the fly. If you have full control over the hardware, of course you can configure the secure module any way you wish.

Part of the thinking behind card emulation is that, by having your NFC device emulate the various smart cards you might have in your wallet (credit card, employee ID/access card, transit card, etc.), you could eliminate the need to carry all those cards. While this works in theory, it’s more difficult in practice. It means that several stakeholders have to participate with your mobile phone manufacturer, who makes your phone, and your mobile service provider, who provisions the software on your phone. Your bank, your employer’s security system vendor, and your public transit operator all need access to the secure element of your phone’s NFC reader in order to emulate their cards. While this is technically possible, it’s no simple feat politically.

Access to the secure element is generally limited by the hardware vendor, often in agreement with the software or firmware vendor. The standard distribution of Android presents no API for accessing the secure element of the NFC-enabled devices on which Google has licensed Android to run. It is possible to get access to the secure element by making the right agreement with the various stakeholders—if you’re American Express and you decide to implement phone-based transactions, then Google and Samsung will be happy to work with you. If you’re a two-person crafts store in Peoria, Illinois, you might be waiting a while.

Since Android is open source, you can find modified distributions of it that open access to the NFC secure element. Access is dependent on both the operating system and the particular hardware module your device is running, though, so there’s not a universal solution.

The NXP PN532 module on the Adafruit and Seeed Studio shields has a secure element as well, and it is configurable. As of this writing, however, no Arduino libraries have been written to simplify custom programming of the PN532’s Secure Access Module (SAM). The libraries mentioned in this book simply put the SAM into normal operation mode for the reader to act as a reader/writer, not to perform card emulation.

Conclusion

NFC peer-to-peer exchanges add a valuable component to NFC that simple RFID can’t offer: the ability to send NDEF messages directly from one device to another. Because of the complexity of the exchange, it’s mostly limited to devices running an operating system at the moment, like Android, BlackBerry, or Windows Phone devices. Hopefully, the state of P2P on Arduino and other simpler microcontrollers will improve in the near future. Meanwhile, the final chapter of this book will give you a taste of what’s currently possible using embedded devices running Linux and the NFC libraries available for it.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset