Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange character on Android NDEF record payload

Tags:

android

nfc

ndef

I just started coding with Android NFC, i've successfully read and write NDEF data into mifare classic tag. The problem is when app read the payload from ndef record, it always contain character '*en' at the beginning of the text. I think it is language character, but how to get the real text message without that character?

This is the screenshot what app read from the tag, the actual text is 'Hello World'

enter image description here Here is the code to read

@Override
public void onNewIntent(Intent intent) {
    Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
   // mText.setText("Discovered tag NDEF " + ++mCount + " with intent: " + intent);

    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

        if (rawMsgs != null) {
            NdefMessage[] msgs = new NdefMessage[rawMsgs.length];

            for (int i = 0; i < rawMsgs.length; i++) {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }

            NdefMessage msg = msgs[0];

            try {
            mText.setText(new String(msg.getRecords()[0].getPayload(), "UTF-8"));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
like image 809
Lorensius W. L. T Avatar asked Oct 27 '11 14:10

Lorensius W. L. T


2 Answers

Here's what I did in Kotlin on API 29:

// ... in the onIntent(...) method
val parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)

parcelables?.let {
    try {
        val inNdefMessage = parcelables[0] as NdefMessage
        val inNdefRecords = inNdefMessage.records

        // convert the payload to string and drop 3 characters to get
        // rid of the " en" prefix
        val payload = inNdefRecords[0].payload

        // figure out if we need to take out the " en" at the beginning
        val textEncoding = if(payload[0] and 128.toByte() == 0.toByte()) "UTF-8" else "UTF-16"
        val langCodeLength = payload[0] and 63.toByte()

        // create a string starting by skipping the first 3 characters
        // based on the language code length
        var inMessage = String(
            payload,
            langCodeLength + 1,
            payload.count() - langCodeLength - 1,
            charset(textEncoding))

        // try to convert the message to json
        try {
            val json = JsonParser().parse(inMessage)

            // ... use json or whatever here
        } catch (error: Exception) {
            println("NFC tag data seems to invalid:\n\n$inMessage\n\n${error.localizedMessage}")
        }

        // ... do whatever
    } catch (error: Exception) {
        println("Error attempting to pull tag info: ${error.localizedMessage}")
    }
}
like image 186
KBog Avatar answered Oct 23 '22 10:10

KBog


What you're seeing is the raw data of an NDef text-record converted to UTF8.

The NDef text-record is build like this:

First byte: Control-Byte

Bit 7: 0: The text is encoded in UTF-8 1: The text is encoded in UTF16

Bit 6: RFU (MUST be set to zero)

Bit 5..0: The length of the IANA language code.

This is followed by the language code, stored in US-ASCII (en in your case) as defined in RFC 3066. The length of the language-code is given in the control-byte.

And this is followed by the text in the format as specified by bit 7 of the control-byte.

The empty square character comes from your conversion of raw data into UTF-8. I'm almost sure that the control-byte in your case has the numeric value 2. Since there is no printable character for this numeric value it gets replaced with the non-printable placeholder character from the unicode-set. This is usually displayed as an empty square.

like image 28
Nils Pipenbrinck Avatar answered Oct 23 '22 10:10

Nils Pipenbrinck