Peer to peer communication with LLCP

by BlackBerry Development Advisor (Retired) on ‎07-12-2012 09:31 AM - edited on ‎07-12-2012 09:31 AM by BlackBerry Development Advisor (Retired) (4,656 Views)

Introduction

 

This article is another in a series of articles about NFC which are intended to help applications developers exploit NFC from within their own applications for BlackBerry® smartphones such as the BlackBerry® Bold™ 9900 smartphone and the BlackBerry® Curve™ 9360.

 

Readers of this article should have some pre-existing knowledge of the fundamental architecture of NFC systems and be familiar with Java®.

 

Unless you already have good knowledge of the subjects covered, it’s recommended that you read the earlier articles in this series. Links to these articles can be found at the end of this one.

 

As well as covering some of the relevant theory, this article will look at an example application that uses the Logical Link Control Protocol (LLCP) in a peer-to-peer communication scenario. If you don’t know what these terms mean, don’t worry; all will be explained.

 

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 specialize in NFC applications development (amongst other things).

 

About this article

 

In a previous article, we looked at peer to peer communications using SNEP, the Simple NDEF Exchange Protocol. That article introduced NFC peer-to-peer mode and the relevant parts of the NFC protocol stack, with SNEP running over LLCP, which is the subject of this article.

 

About LLCP

 

Whilst SNEP provides a very easy mechanism for pushing a message in NDEF format from one device to another, LLCP gives lower level control over the process and can be used for both one-way and bidirectional exchanges of data. Before we turn our attention to the question of how we can utilize LLCP from a BlackBerry application, let’s review some LLCP theory.

 
Like other aspects of NFC, LLCP is defined by the NFC Forum and the full specification is called NFCForum-TS-LLCP_1.0.pdf. The specification explains that the primary responsibilities of LLCP are link activation (establishing an LLCP link between two compatible devices), supervision of the connection and deactivation of the link when required.

 
Two transports are defined for LLCP; a connectionless transport and a connection-oriented transport.

 
The LLCP connectionless transport operates as an unacknowledged data transmission protocol and doesn’t require any preparatory steps prior to actually sending data. As such, it has a low overhead. The application layer is required to take care of flow control should this be deemed necessary.

 
The connection-oriented transport of LLCP offers guaranteed delivery and maintains the sequence of transmitted “service data units”. This transport requires some steps to set it up.

 
So in essence, the connectionless and connection-oriented transports each trade reliability against performance.

 
LLCP is based upon a client/server model. The client is usually called either the Master or Initiator whilst the server is called the Slave or Target. Service names or service access points can be used to identify endpoints. Service access points include service access point numbers, some of which are reserved by the NFC Forum.

 
The NFC Forum attribute a “class” to devices according to the link transport type or types they can offer to remote devices:

 
Class 1: Connectionless transport service only
Class 2: Connection-oriented transport only

Class 3: both Connectionless and Connection-oriented transports available

 

BlackBerry device that support NFC are Class 2 devices in this respect.

 

LLCPDemo - the sample application

 

Overview

 

To accompany this article, we’ve written a sample application called LLCPDemo and released it in our GitHub repository. In a moment, we’ll dive straight into the code and review the LLCP APIs that were used in this application. Before we do this though, let’s introduce the application.

 

app overview.png

Figure 1 – LLCPDemo UI flow

 

As can be seen from Figure 1, LLCPDemo can be used in either of two roles, either to send data or to receive it. So to use LLCPDemo, you should install it on two devices and run one in sender mode and the other in receiver mode.

 
Role selection is made from the first screen which is displayed when the application is launched. If the sender role is selected, the next screen allows you to edit the short text message you want to send to the other device over LLCP. Clicking the Continue button causes the activity log screen to be displayed where details of the progression of the LLCP transfer process will be displayed. If the receiver role is selected, we are immediately taken to the activity log screen where we’ll see similar progress information, but from the perspective of the receiver.

 

llcp1.png

Figure 2 - The LLCPDemo role selection screen

 

OK, let’s take a look at some code, starting with the sender role.

 

Sending data over LLCP

 

llcp3.png

Figure 3 – the activity log showing the sender’s progression

 

To send data over LLCP, we use the LLCPConnection interface. It extends the java.microedition.io.Connection interface and as such should be familiar to many developers already.

 

LLCPConnection.png

Figure 4 – The LLCPConnection interface

 

In LLCPDemo, the code for sending data over LLCP is all in the “run ()” method of the nfc.sample.llcp.nfc.LLCPSender class, which implements the Runnable interface. This means the LLCP operation can be executed in a background thread. This is important since IO operations are blocking operations and we do not want to impact the responsiveness of our user interface.

 
Here’s what it looks like:

 

    public void run() {
        _screen.logEvent("LlcpSender starting...");
        LLCPConnection llcp_conn = null;
        OutputStream out = null;
        boolean completed_ok = false;
        try {
            // note mode=client
            _screen.logEvent("Obtaining connection");
            llcp_conn = (LLCPConnection) Connector.open("urn:nfc:sn:llcpdemo;mode=client");
            _screen.logEvent("Got LlcpConnection");
            out = llcp_conn.getOutputStream();
            _screen.logEvent("Got OutputStream");
            byte[] data = _message.getBytes("US-ASCII");
            out.write(data, 0, data.length);
            out.flush();
            _screen.logEvent("Sent " + data.length + " bytes of data");
            completed_ok = true;
        } catch(Exception e) {
            _screen.logEvent(e.getClass().getName() + ":" + e.getMessage());
            _screen.logEvent("LlcpSender is exiting abnormally");
        } finally {
            // make sure we don't close and interrupt the receiver before the data we sent has been read 
            delay(1000);
            _screen.logEvent("Closing resources......");
            close(llcp_conn, out);
        }
        if (completed_ok) {
            _screen.logEvent("LlcpSender finished OK");
        }
    }

Figure 5 – sending data over an LLCPConnection 

 

As you can see, this is simple stuff. All we have to do is open a connection, specifying an URN as shown:

 

llcp_conn = (LLCPConnection) Connector.open("urn:nfc:sn:llcpdemo;mode=client");

 

The URN format is per the NFC Forum specification and in this case indicates that we want to open a client connection (for sending data) and connect to a service name of “llcpdemo” on the remote device. The “mode=client” parameter indicates that we are going to act as the client end of the connection of course.

 
Once we’ve opened a connection, we obtain an OutputStream object from it and can then write our message in a byte representation to it. Note I’ve assumed ASCII characters only will be used in LLCPDemo. You may wish to modify the character to byte array conversion process to support other character sets.

 
After writing to the OutputStream, you should flush and then close the connection.

 

Important!
Make sure you close the connection no matter what happens! In my case, I have ensured this will be the case by catching all Exceptions and placing my call to a method in which I close the OutputStream and LLCPConnection in the finally block. 

Failure to close an LLCPConnection may result in you being unable to re-open the connection and errors such as “LLCP resource busy” may appear in the event log and in NfcExceptions.

You may also find it beneficial to include a short delay before closing your connection. This ensures the receiver has time to read all the data you sent across the connection before you close. If you close the connection at the sender end before the receiver has finished reading, you’ll get an IoException with a message such as “connection invalidated while reading”.

 Figure 6 – some important notes about sending data over LLCP

 

The code for the delay and close methods are as shown in Figure 7

 

   private void delay(int delay_ms) {
        try {
            Thread.sleep(delay_ms);
        } catch(InterruptedException e) {
        }
    }

    public void close(LLCPConnection llcp_conn, OutputStream out) {
        try {
            if(out != null) {
                out.close();
                _screen.logEvent("Stream closed");
            }
        } catch(Exception e) {
            _screen.logEvent(e.getClass().getName() + ":" + e.getMessage());
            _screen.logEvent("LlcpSender stream close error");
        }
        try {
            if(llcp_conn != null) {
                llcp_conn.close();
                _screen.logEvent("Connection closed");
            }
        } catch(Exception e) {
            _screen.logEvent(e.getClass().getName() + ":" + e.getMessage());
            _screen.logEvent("LlcpSender connection close error");
        }
    }

 Figure 7 – closing the connection

 

Receiving data over LLCP

 
The way we receive data over LLCP is very similar to the way in which we send it. We use the LLCPConnection class again, this time opening it with “mode=server”. We then use the LLCPConnectionNotifier class which we obtain from the LLCPConnection to “accept and open” connection requests we receive from clients. After that, it’s plane sailing. We obtain an InputStream object and read data from it. At the end of the reading process. we close the connection, taking care per the notes above; to ensure that this always happens. The receiver code is shown in below in Figure 8.

 

   public void run() {
        _screen.logEvent("LlcpReceiver starting...");
        LLCPConnectionNotifier llcp_conn_notifier = null;
        LLCPConnection llcp_conn = null;
        InputStream in = null;
        boolean completed_ok = false;
        try {
            // note mode=server
            _screen.logEvent("Obtaining connection notifier");
            llcp_conn_notifier = (LLCPConnectionNotifier) Connector.open("urn:nfc:sn:llcpdemo;mode=server;timeout=120");
            _screen.logEvent("Got LlcpConnectionNotifier");
            llcp_conn = llcp_conn_notifier.acceptAndOpen();
            _screen.logEvent("Got LlcpConnection");
            in = llcp_conn.getInputStream();
            _screen.logEvent("Got InputStream");
            byte[] data = new byte[256];
            int bytesRead = in.read(data, 0, 256);
            _screen.logEvent("Rcvd " + data.length + " bytes of data");
            String text = new String(data, "US-ASCII");
            _screen.logEvent("Rcvd text:" + text);
            completed_ok = true;
        } catch(Exception e) {
            _screen.logEvent(e.getClass().getName() + ":" + e.getMessage());
            _screen.logEvent("LlcpReceiver is exiting abnormally");
        } finally {
            close(llcp_conn_notifier, llcp_conn, in);
        }
        if(completed_ok) {
            _screen.logEvent("LlcpReceiver finished OK");
        }
    }

 Figure 8 – receiving data over LLCP

 

And that is really all there is to it. Sending and receiving data over an LLCP connection is technically and conceptually simple and, if you have experience working with other types of connection based communication APIs, instantly familiar.

 
The Sample Application Source

 

The LLCPDemo sample application which accompanies this article can be downloaded with its full source code from our blackberry/Samples-for-Java/NFC GitHub® repository:

 
https://github.com/blackberry/Samples-for-Java/tree/master/NFC

 
You’ll need to install it on two NFC enabled BlackBerry devices to be able to use it of course. On the first device, select the Sender icon and on the second select the Receiver icon. Adjust the text on the next screen on the sender device and click “Continue” and then place both devices back-to-back. You should see the data transfer take place and this illustrated by messages on the activity log screen.

 

Summary

 

In this article, we’ve reviewed the Logical Link Control Protocol and the BlackBerry 7 Java APIs which allow it to be used. We hope this has helped make it easy for you to take advantage of LLCP in your own BlackBerry applications.

 
The APIs are available today so download the latest BlackBerry Java SDK from

 
https://developer.blackberry.com/java/download/

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

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

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

 
Other BlackBerry Developer NFC Articles

 

http://supportforums.blackberry.com/t5/Java-Development/NFC-Article-and-Code-Index/ta-p/1538775

 

Glossary of NFC Terms

 

  • APDU - Application Protocol Data Unit. A message type which forms part of a protocol and which may be exchanged between peers as either a request or a response message. Applications on a BlackBerry smart phone may communicate with applets in an SE using the ISO 7816-4 APDUs for example.
  • CLF - Contactless Front-end. Part of the NFC Controller. Communicates via RF with an NFC reader.
  • HCI - Hardware Controller Interface. Amongst other things, this component of the NFC system architecture redirects radio communication to appropriate SE.
  • ISO 7816-4 - the specification which defines the protocol which allows applications to communicate with applets installed in an SE or smart card.
  • LLCP - Logical Link Control Protocol
  • NDEF - NFC Data Exchange Format
  • NFC - Near Field Communications
  • PCD - Proximity Coupling Device (also known as “card reader”)
  • PICC - Proximity Integrated Circuit Card
  • SE - Secure Element. A hardware component which can host and act as an execution environment for small software applications known, in this context, as "applets".
  • SIM - Subscriber Identity Module. In 2G modules this used to be synonymous with the SIM *card* i.e. the small smart card inserted in the handset. In 3G networks, the SIM is a software module installed on the UICC.
  • SNEP - Simple NDEF Exchange Protocol
  • SWP - Single Wire Protocol.
  • UICC - Universal Integrated Circuit Card - the smart card used in mobile terminals in GSM and UMTS networks