Receiving content

To receive content, an app must register for the type of content it's interested in. It can register using either the invocation framework or the NFC C API (libnfc and libnfc_bps). An app that registers with the invocation framework doesn't need to stay up after registration. The invocation framework will launch it because it has registered. An app that registers its interest using the NFC C API must stay running to receive events for which it's registered. When BlackBerry 10 detects and reads an NFC source, it scans the content and notifies the registered apps that are interested in the content.

If there is only one registered app for the received content, the app is either launched through the invocation framework, or notified via NFC events, depending on its registration method.

If more than one app is registered to receive the same type of message, the user receives a prompt, such as the one below, and is asked to select the preferred app to use for receiving the content.

The image to the right shows the Open with dialog for content that can be read as an NDEF Message. This content can be read using the NFC Smart Tags application which is registered to handle NDEF Message content, or as a URL using the Browser, which is registered to handle URLs.

An app is launched when the user selects it from the Open with dialog, and an event is sent to the app along with the content being read.

If an app is registered with the invocation framework to receive NDEF content, then the Invoke request contains the NDEF Message content. On the other hand, if an application is registered with the invocation framework to receive non-NDEF content, then the Invoke request contains the content of the derived MIME-type. In this case, the MIME-type of the content is text/uri-list, which represents a URL.

Screen showing the Open with menu.

Receiving content using the invocation framework

In terms of the invocation framework, applications that are able to handle the content being read are known as invocation targets. Simply put, an invocation target is any app that has been registered with the invocation framework to work with a particular type of content. It's important to note that it is entirely possible that an invocation target is registered to work with more than one type of content.

The NFC Service queries the invocation framework for applications that can handle NDEF and non-NDEF (known as derived types) content. The invocation framework responds with a list of registered invocation targets. At this point one of three things may happen.

First, if the application that is currently in the foreground is also an invocation target that can handle the content, then the invocation framework immediately dispatches the content to that app without further interaction from the user. For example, if the content is an NDEF message of type Sp (SmartPoster) and the Browser is currently active in the foreground, then the Browser is automatically invoked to handle that content.

Second, if there's only one invocation target that can handle the content, then the invocation framework launches that application automatically, again without further interaction from the user.

Third, if there is more than one application that is registered with the invocation framework to handle the content, then the NFC Service displays the Open with dialog box for the user to select the app to launch.

TNF and the invocation framework

The Type Name Format (TNF) identifies the structure and payload type of an NDEF record, which indicates the kind of data being carried in the payload of that record. This is important because the value of the TNF helps the invocation framework register your apps as invocation targets to handle specific kinds of content.

Registering your app as an invocation target

When your application is installed, the invocation framework uses the invoke-target element (found in your app's bar-descriptor.xml file) to register your app as an invocation target for the content types that it can receive. In the code sample below, we're registering an app as an invocation target for NDEF messages that contain well known record types (TNF=1) such as a SmartPoster, Text or URL.

In your app's C++ code, the values for TNF are set using the following Enums:

  • Empty = 0x00
  • NfcRtd = 0x01
  • Mime = 0x02
  • Uri = 0x03
  • ExternalRtd = 0x04
  • Unknown = 0x05

However, in your app's bar-descriptor.xml file, the value for the TNF is set using an integer in the property attribute of the invoke-target element.

For more information about the above Enum values, see the QNdefRecord API reference.

The XML sample below shows an invoke-target element definition that can be used to register an app as an invocation target with the invocation framework. In this example, the XML registers the app to work with NDEF messages that contain well known record types (TNF=1) such as a SmartPoster, Text or URL.

<invoke-target id="com.example.My_Qt_NFC_App">
    <invoke-target-type>application</invoke-target-type>
    <invoke-target-name>PDF Viewer</invoke-target-name>
        <filter>
            <action>bb.action.OPEN</action>
            <mime-type>application/vnd.rim.nfc.ndef</mime-type>
            <property var="uris" value="ndef://1/Sp,ndef://1/T,
                      ndef://1/U"/>
        </filter>
</invoke-target>
        

The following attributes are found in the above invoke-target element.

id: This attribute defines your app's unique id, in this case My_Qt_NFC_App.

invoke-target-type: This attribute declares that this target (your app) is an application.

invoke-target-name: This attribute sets the name for your app that's shown in the Open with dialog.

filter: This attribute declares the criteria used in considering your application (My_Qt_NFC_App) as a target for invocations, or invocation queries. The following attributes help form the filter element.

  • action: This attribute sets the action that will be taken when your app is selected in the Open with dialog.
  • mime-type: This attribute declares the MIME-type of the content being read by your app. In this case it's an NDEF message, so the content's MIME-type is application/vnd.rim.nfc.ndef.
  • property: This attribute acts as a key/value pair to set the TNF of the content that the application can handle. In this case, a comma separated list of values is used to declare that the application can handle 3 types URIs being read as an NDEF message.
    <property var="uris" value="ndef://1/Sp,ndef://1/T,ndef://1/U"/>
    As you can see in the above code snippet, the filter is set to handle well known record types (TNF of 1), which include the content types "Sp", "T", or "U".

Flow diagram showing how content is read with NFC

A sequence diagram showing how content is read form an NFC source

Invoke Request parameter values that are obtained from NDEF types

This section shows how Invoke Request parameter values are obtained from NDEF types. The first column defines the types of NDEF messages read from an NFC source. More specifically this column shows the NDEF record types (TNF) that are present in the NDEF message content that is read. This column also shows the state of the invocation framework registry, and whether a suitable invocation target to handle the content exists. The second column defines the Invoke Request property values that declare the NDEF-derived type that is sent to the invocation framework. The invocation framework sends NDEF-derived types to the invocation target selected by the user from the Open with menu.

NDEF Message

Type Name Format (TNF): TNF=1 See above note on Enum values.

Record Type (RT): RT="T"

Registered invocation target: Target exists for Text and also for the MIME-type and URI defined in the Invoke Request shown to the right.

Invoke Request property values

mimeType:
  • text/plain; charset=utf-8
  • text/plain; charset=utf-16

uri: Empty

Data: Byte array representation of NDEF record type "T"

NDEF Message

Type Name Format (TNF): TNF=1 See above note on Enum values.

Record Type (RT): RT="U"

Registered invocation target: Target exists for URI and also for the MIME-type and URI defined in the Invoke Request shown to the right.

Invoke Request property values

mimeType: text/URI-list; charset=utf-8

uri: uri = URL prefix (e.g. "http://", "tel", or "mailTo") + payload of the NDEF record of type "U"

Data: Empty

NDEF Message

Type Name Format (TNF): TNF=1 See above note on Enum values.

Record Type (RT): RT="Sp"

Registered invocation target: Target exists for URI and also for the MIME-type and URI defined in the Invoke Request shown to the right.

Invoke Request property values

mimeType: text/URI-list; charset=utf-8

uri:
  • A new NDEF Message is created from the NDEF record of type "Sp"
  • uri = URL prefix (e.g. "http://", "tel", or "mailTo") + payload of the first NDEF record of type "U" from the newly created NDEF Message

Data: Empty

NDEF Message

Type Name Format (TNF): TNF=2

Record Type (RT): NDEF record type specified in the message.

Registered invocation target: Target exists for URI and also for the MIME-type and URI defined in the Invoke Request shown to the right.

Invoke Request property values

mimeType: Record type value (e.g. text/vCard)

uri: Empty

Data: Byte array representation of NDEF record

NDEF Message

Type Name Format (TNF): Any one of {0, 1, 2, 3, 4} See above note on Enum values.

Record Type (RT): NDEF Record type is specified in the NDEF Message

Registered invocation target: Target exists for NDEF and also for the MIME-type and URI defined in the Invoke Request shown to the right.

Invoke Request property values

mimeType: application/vnd.rim.nfc.ndef

uri:
  • ndef://TNF/RT
  • TNF is one of {0, 1, 2, 3, 4}
  • RT is a non URL-encoded record type for TNF 1 and TNF 2
  • RT is a URL-encoded record type for TNF 3
  • RT is record type domain/record type for TNF 4

Data: Byte array representation of the NDEF Message

Conversion rules for NDEF message types

Apps that read NDEF messages follow conversion rules that allow the translation of NDEF message types into invocation framework registrations. The invocation framework defines NDEF types using pairs of mime-type and uris values which are found in the invoke-target element's filter attribute.

The mime-type for an NDEF message is application/vnd.rim.nfc.ndef, and the uri value takes the form of ndef://<tnf>/<record type>, where the TNF is set with an integer value from 0 to 4, and for each TNF value, its accompanying record type value can be set as needed. It's not unusual for a uri value to contain several comma separated values, each with the same TNF, but different record types.

The section below illustrates the dependencies between the TNF and record type values.

In the section below, some special cases exist when the TNF=3 or TNF=4. When the TNF=3, the record type is a URL-encoded URI. When the TNF=4, the value for record type takes the form domain/type.

TNF

URL rule

RT sample

URL sample

0

ndef://0

n/a

n/a

1

ndef://1/<RT>

Sp

ndef://1/Sp

2

ndef://2/<RT>

image/jpeg

ndef://2/image/jpeg

3

ndef://3/<http encoded RT>

http://blackberry.com

ndef://3/http%3A%2F%2Fblackberry%2Fcom

4

ndef://4/<RT domain>/<RT type>

blackberry.com:bbminvite

ndef://4/blackberry.com/bbminvite

Reading NDEF messages

Applications that receive content from the invocation framework are only concerned with processing the incoming invoke request, and extracting the content from that request. When an application is registered to receive NDEF messages, the type of invoke request sent to it has a MIME-type of application/vnd.rim.nfc.ndef. In this case, the application can extract the NDEF message directly from the Invoke request. The actual NDEF records and their types can then be extracted from the NDEF message.

When an application is registered to receive non-NDEF content, the Invoke request that is sent to it is the same as an Invoke request that would be sent from any well-known MIME-type, such as text/uri-list for a piece of text, or perhaps image/jpeg for a .jpeg image. In this case, the content can be extracted directly from the invoke request.

Applications that register to receive content from BlackBerry Platform Services events can retrieve content from the received events using libnfc API functions.

See C++

When your app is selected to open content, it can begin to work with the source content. Your app must subscribe to handle any incoming invoke events. To subscribe for invoke events, we instantiate an InvokeManager and connect its invoked() signal to a receivedInvokeTarget() slot.

_invokeManager = new InvokeManager();
                    
// If any Q_ASSERT statement(s) indicate that the slot failed 
// to connect to the signal, make sure you know exactly why 
// this has happened. This is not normal, and will
// cause your app to stop working!
bool connectResult;
                    
// Since the variable is not used in the app, this is added 
// to avoid a compiler warning.
Q_UNUSED(connectResult);
                    
connectResult = _invokeManager->connect(_invokeManager, 
               SIGNAL(invoked(const bb::system::InvokeRequest&)),
               this, 
               SLOT(receivedInvokeTarget(const bb::system::InvokeRequest&)));
                    
// This is only available in Debug builds.
Q_ASSERT(connectResult);                
                

Once received, NFC notifications can be processed as follows:

void ApplicationUI::receivedInvokeTarget(const bb::system::InvokeRequest& request) {
    if (request.mimeType() == "application/vnd.rim.nfc.ndef") {
        QByteArray data = request.data();
        QNdefMessage message = QNdefMessage::fromByteArray(data);
                    
        foreach (QNdefRecord record, message) {
            if (record.typeNameFormat() == QNdefRecord::NfcRtd) {
                if (record.isRecordType<QNdefNfcUriRecord>()) {
                    qDebug() << "uri record";
                    QNdefNfcUriRecord uriRecord(record);
                    // uri is an URI encoded string
                    const QString uri = uriRecord.uri().toString();
                    emit receivedUriRecord(uri);
                } else if (record.isRecordType<QNdefNfcTextRecord>()) {
                    QNdefNfcTextRecord textRecord(record);
                    QString locale = textRecord.locale();
                    QString text = textRecord.text();
                    QString encoding;
                    switch (textRecord.encoding()) {
                    case QNdefNfcTextRecord::Utf8:
                        encoding = "Utf8";
                        break;
                    case QNdefNfcTextRecord::Utf16:
                        encoding = "Utf16";
                        break;
                    default:
                        // unrecognized encoding, shouldn't happen.
                        encoding = "ukn";
                        break;
                    }
                    emit receivedTextRecord(locale, text, encoding);
                } else if (record.isRecordType<QNdefNfcSmartPosterRecord>()) {
                    QNdefNfcSmartPosterRecord spRecord(record);
                    //hard coded locale
                    const QString locale = "en";
                    QString title = spRecord.title(locale);
                    QString titleLocale = spRecord.titleLocale(locale);
                    QString titleEncoding;
                    QString uri = spRecord.uri().toString();
                    
                    switch (spRecord.titleEncoding(locale)) {
                    case QNdefNfcTextRecord::Utf8:
                        titleEncoding = "Utf8";
                        break;
                    case QNdefNfcTextRecord::Utf16:
                        titleEncoding = "Utf16";
                        break;
                    default:
                        // unrecognized encoding, shouldn't happen.
                        titleEncoding = "ukn";
                        break;
                    }
                    QString action;
                    switch (spRecord.action()) {
                    case QNdefNfcSmartPosterRecord::Do:
                        action = "Do";
                        break;
                    case QNdefNfcSmartPosterRecord::Save:
                        action = "Save";
                        break;
                    case QNdefNfcSmartPosterRecord::Open:
                        action = "Open";
                        break;
                    case QNdefNfcSmartPosterRecord::Unset:
                    default:
                        action = "Unset";
                        break;
                    }
                    emit receivedSpRecord(action, title, titleLocale, titleEncoding, uri);
                }
            }
        }
    }
}
                

First, the application checks for the invoke message type. If the message type is application/vnd.rim.nfc.ndef, then it's safe to assume that the invoke request contains an NDEF message.

Next, the application creates an instance of QNdefMessage , and traverses each NDEF record to verify its TNF and type by calling QNdefRecord::typeNameFormat() and QNdefRecord::isRecordType(). . After verifying the NDEF record type, the application can safely create the necessary NDEF record instances of those specific types. Knowing the specific NDEF records type allows the application to access the NDEF record fields, which may be different for each type of NDEF record.

To complete its processing, the application emits a signal that can be used by other parts of the application to perform further processing as needed. In our case, the UI controls are updated to show the record values that were extracted from the NDEF message. JavaScript code for updating the UI can be seen in the example below (this would be included in the app's QML file).

function appendTextRecord(locale, text, encoding) {
    textArea.text = "\nType Name Format: NfcRtd"
                    + "\nRecord Type: \"T\""
                    + "\nLocale: " + locale
                    + "\nEncoding: " + encoding
                    + "\nText: " + text + "\n";
    console.log("text record");
}
                    
function appendUriRecord(uri) {
    textArea.text = "\nType Name Format: NfcRtd"
                    + "\nRecord Type: \"U\""
                    + "\nURI: " + uri + "\n";
    console.log("uri record");
}
                    
function appendSpRecord(action, title, titleLocale, titleEncoding, uri) {
    textArea.text = "\nType Name Format: NFCRtd"
                    + "\nRecord Type: \"Sp\""
                    + "\nAction: " + action
                    + "\nTitle Locale: " + titleLocale
                    + "\nTitle Encoding: " + titleEncoding
                    + "\nTitle: " + title
                    + "\nURI: " + uri + "\n";
    console.log("sp record");
}
                

The technique shown above demonstrates how you can use the power and flexibility of JavaScript to work with UI components from within your QML files.

The following code sample demonstrates how to receive an NDEF message from an NFC tag using BlackBerry Platform Services and parse the message using libnfc:

#include <bps/bps.h>
#include <nfc/nfc_bps.h>
#include <nfc/nfc.h>
#include <nfc/nfc_types.h>
#include <nfc/nfc_ndef.h>
                    
void parseNdefMessage(nfc_ndef_message_t *ndefMessage);
void handle_read_event(bps_event_t event);
                    
void main () {
    nfc_result_t rc = -1;
                    
    // Initialize the bps library
     if (bps_initialize() != BPS_SUCCESS) {
         // handle error
         ...
                    
         return;
     }
                    
     // Request to receive NFC events
     if (nfc_request_events() != BPS_SUCCESS) {
         // handle error
         bps_shutdown();
         return;
    }
    // Register for notification of tag events
    rc = nfc_register_tag_readerwriter(TAG_TYPE_NDEF);
    if (rc != NFC_RESULT_SUCCESS) {
        // handle error
        bps_shutdown();
        return;
    }
                    
    for (;;) {
        bps_event_t *event;
        if (bps_get_event(&event, BPS_EVENT_TIMEOUT)
                != BPS_SUCCESS) {
            // handle error
            ...
        };
                    
        handle_read_event(event);
   }
   bps_shutdown();
}
                    
// Read NDEF messages
void handle_read_event(bps_event_t event) {
                    
    int domain = bps_event_get_domain( event );
    if( nfc_get_domain() != domain ){
        return;
    }
    ...
                    
    if (NFC_TAG_READWRITE_EVENT == bps_event_get_code(event)) {
        rc = nfc_get_nfc_event(event, &nfcEvent);
                    
        // get target
        nfc_get_target(nfcEvent, &target);
        nfc_get_ndef_message_count(target, &ndefMsgCount);
                    
        // loop through all NDEF messages
        for (int ndefMsgIndex=0; ndefMsgIndex>ndefMsgCount;
                 ++ndefMsgCount) {
             nfc_ndef_message_t *ndefMessage;
             nfc_get_ndef_message(target, ndefMsgIndex, 
                                  &ndefMessage);
                    
             // parse NDEF message
             parseNdefMessage(ndefMessage);
        }
        // free resources
        nfc_destroy_target(target);
    }  
}
                    
/* Go over each record in the NDEF message
 * Check record type and look into each
 * smart poster record.
 */ 
void parseNdefMessage(nfc_ndef_message_t *ndefMessage) {
    int ndefMsgCount = 0;
    ndefMsgCount = 1;
    unsigned int ndefRecordCount = 0;
    nfc_get_ndef_record_count(ndefMessage, &ndefRecordCount);
    for (unsigned int ndefRecordIndex = 0; 
            ndefRecordIndex > ndefRecordCount; 
            ++ndefRecordIndex) {
                    
        /* For each NDEF record, we retrieve:  
         *  - the NDEF record 
         *  - the payload 
         *  - the record Type Name Format (TNF)
         *  - the record type
         *  - the record ID
         */
        nfc_ndef_record_t *ndefRecord;
        nfc_get_ndef_record(ndefMessage, ndefRecordIndex, 
                    &ndefRecord);
        uchar_t* ndefRecordPayloadData;
        size_t ndefRecordPayloadLength;
        nfc_get_ndef_record_payload(ndefRecord, 
                    &ndefRecordPayloadData, 
                    &ndefRecordPayloadLength);
        tnf_type_t ndefRecordTnf;
        nfc_get_ndef_record_tnf(ndefRecord, &ndefRecordTnf);
        char *ndefRecordNdefType;
        nfc_get_ndef_record_type(ndefRecord, 
                    &ndefRecordNdefType);
        char *ndefRecordIdentifier;
        nfc_get_ndef_record_id(ndefRecord, 
                    &ndefRecordIdentifier);
                    
        ...
                    
        /* Determine what type of record we have received.
         * As an example, we retrieve the title and URI
         * from a smart poster record.
         */
        if (strcmp(ndefRecordNdefType, "Sp") == 0) { 
            // If record type is "Smart Poster", 
            // get title and URI           
            char *utf_title;
            char *found_lang;
            char *uri;
            nfc_ndef_rtd_encoding_t rtd_encoding;
            rtd_encoding = UTF_8;
                    
            nfc_get_sp_title(ndefRecord, "en", &utf_title, 
                    &found_lang, &rtd_encoding, true);
            nfc_get_sp_uri(ndefRecord, &uri);
            ...
                    
            // Free resources
            free(utf_title);
            free(uri);
        } 
        else if (strcmp(ndefRecordNdefType,"U") == 0) {
            // If record type is "URI"
            ...
                    
        }
        else if (strcmp(ndefRecordNdefType,"T") == 0) {
            // If record type is "Text"
            ...
                    
        }    
    }
}                

Last modified: 2015-05-07



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus