Question

I have developed a multi-purpose NFC app, which is capable of reading from and writing to different types of tags and in different types of formats, including External Type, RTD Text, and RTD URI. There is, however, a problem with parsing and reading the "TNF_WELL_KNOWN" Smart Poster contents. More specifically, and to begin with the problem I have, I used well-known "NFC TagWriter" app on Android to create a simple contact content. This contact contains a name, e.g., "Police", and a phone number, e.g., "911". When trying to read the same content using yet another popular app called "NFC TagInfo", I am able to successfully read the Smart Poster, showing exactly what it is supposed to show:

WELL-KNOWN: urn:nfc:wkt:Sp (Smart Poster)

  1. WELL-KNOWN: urn:nfc:wkt:U (URI) Identifier: 0x05 ("tel:") "911"
  2. WELL-KNOWN: urn:nfc:wkt:U (Text) Encoding: UTF-8 Language: en "Police"

On the other hand, I am not able to achieve the same. Here is the code I came up with as an attempt to decode the aforementioned content:

First I extract the NDEF Message(s) and their corresponding NDEF Record(s):

 // Use NdefMessage[] getNdefMessages(Intent) method to extract NDEF Message(s)

 NdefMessage[] ndefMessages = getNdefMessages(intent);
 for(int i = 0; i < ndefMessages.length; i++) {

    ndefRecords = ndefMessages[i].getRecords();

    for (int j = 0; j < ndefRecords.length; j++) { 

        // NDEF Record parsing snippet comes here!
        if (ndefRecords[j].getTnf() == NdefRecord.TNF_WELL_KNOWN) {

           // If TNF indicates a "Well-known" type,
           // determine the type using "Type" field  

           if (Arrays.equals(ndefRecords[j].getType(),  NdefRecord.RTD_SMART_POSTER)) {

               // First try to decode the content
               payload = new String(ndefRecords[j].getPayload(), 1, ndefRecords[j].getPayload().length - 1, Charset.forName("UTF-8"));
               Log.i(TAG, "Content: " + payload);

               // Second try to decode the content
               Log.i(TAG, "Content: " + ndefRecords[j].toUri());
           }
         }
       }
    }

When looking at the Logcat output, the first Log.i prints:

U911Q   TenPolice

While the second prints:

tel:911

It appears to me that the NFC TagWriter app creates a nested NDEF Record, in which one URI type and one Text type are concatenated. I am trying to figure out how to get this content decoded successfully. The first attempt removes the payload header and decodes the remaining part of NDEF Record payload. Clearly, the first part contains the "tel:911" content and the second part contains "Police", where "en" indicates the language. The second attempt uses the NDEFRecord class' toUri() helper method, which according to API documentation is able to extract RTD URIs. I am wondering in order to extract all nested records, we could decode one, convert that to byte[] array and remove from the payload, which will be re-iterated for yet another decoding. Though, this approach looks really inefficient. So, I would like to know what other experts suggest on how to approach this problem.

[UPDATE] Following is a short snippet to compose an Smart Poster NDEF Record for those interested:

NdefRecord uriNdefRecord = NdefRecord.createUri(Uri.parse((String) o[0])); //RTD_URI
NdefRecord textNdefRecord = createTextRecord((String) o[1], Locale.US, true); //RTD_TEXT
NdefRecord[] recs = new NdefRecord[]{uriNdefRecord, textNdefRecord};
NdefMessage smartPosterpContentMessage = new NdefMessage(recs);
byte[] bytes = smartPosterpContentMessage.toByteArray();
NdefRecord newRec = new NdefRecord((short) 0x01, NdefRecord.RTD_SMART_POSTER, null, bytes);
ndefMessageWriter(new NdefMessage(newRec));   

In the above snippet, the "ndefMessageWriter" uses a thread to communicate with the tag and write the content to it.

Was it helpful?

Solution

As you correctly found out the Smart poster record contains a Text record and a URI record as its payload. So all you have to do to get these records is to decode the Smart poster records payload (btw. if you had Googled for "Smart poster record" you would have quickly come up with the Smart Poster Record Type Definition Specification, grab it here).

You already found out that the SP record contains an NDEF message, so you would decode the record's payload as an NDEF message:

NdefMessage spContentMsg = new NdefMessage(spRecord.getPayload());

Now you have an NDEF message which, per specification, contains at least one URI record and some other optional records (e.g. one descriptive Text record for each language):

NdefRecord[] spContentRecs = spContentMsg.getRecords();
for (NdefRecord rec : spContentRecs) {
    if (rec.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
       if (Arrays.equals(rec.getType(),  NdefRecord.RTD_URI)) {
           // do something
       } else if (Arrays.equals(rec.getType(),  NdefRecord.RTD_TEXT)) {
           // do something
       }
     }
   }
}

Btw, most of the NFC Forum's specifications are well written and easy to follow (particularly the various NDEF and RTD related specifications).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top