agentsmith
agentsmith

Reputation: 1326

Write ndef message to NFC Tag fails with java.io.exception

I want to write simple text data to my NXP MiFARE DesFire EV1 (NDEF Type 4 Tag). However, the writing process always fails with an IOExcetion

For writing I get the NFC-Tag I use the function write:

private void write(String mimeType, String text, Tag tag) throws IOException, FormatException {
    NdefRecord[] records = {createRecord(mimeType, text)};
    NdefMessage message = new NdefMessage(records);
    Ndef ndef = Ndef.get(tag);
    ndef.connect();
    ndef.writeNdefMessage(message);
    ndef.close();
}

The result in the third line (Ndef ndef = Ndef.get(tag)) is the following:

TAG: Tech [android.nfc.tech.IsoDep, android.nfc.tech.NfcA, android.nfc.tech.Ndef]

From this I assumed, the Tag is formatted correclty (as NDEF). Now, when calling ndef.connect() it just says java.io.exception without any additional information about the error. The other parts of the code, that get called is appended.

@Override
public void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    String action = intent.getAction();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
        tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tag != null) {
            String serialId = Utility.bytesToHex(tag.getId());
            Log.d("[WriteCard]", "Serial Number: " + serialId);
            Toast.makeText(this, serialId, Toast.LENGTH_SHORT).show();
        }
    }
}

// When the write Button is clicked
public void onClick(View view)    {
    if (nfc_adapter == null) {
        Toast.makeText(this, "No NFC", Toast.LENGTH_SHORT).show();
        return;
    } 
    int id = view.getId();
    Intent intent = getIntent();
    try {
        write("type/1", spinner_location.toString(), tag);
    }
    catch(Exception e)  {
        Log.d("[WriteCard]", e.toString());
        Toast.makeText(this,  e.toString(), Toast.LENGTH_SHORT).show();
    }
}

The NXP Tag Info App reports the following:

Additional Info: The writing process with Android-Apps like NFC TagWriter by NXP or wakdev NFC Tools works without any problem, thus I assume, the Tag is working correctly.

Upvotes: 0

Views: 1287

Answers (1)

Andrew
Andrew

Reputation: 10152

Really trying to write to a Tag on a Button click will always be unreliable and also using enableForeGroundDispatch is also unreliable in real life as it is highly likely that the Tag will be moved slightly that it will go out of range and thus generate I/O errors. The two Apps you mention don't do it the way you are trying to do.

Also the documentation says connect and writeNdefMessage

May cause RF activity and may block. Must not be called from the main application thread.

and you are calling these from the main (UI) thread.

Your button just needs to setup the action that "when Tag comes in to range, immediately write text"

e.g. some thing like

private String text;

@Override
public void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    String action = intent.getAction();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
        tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tag != null) {
            if(text.isEmpty()) {
               // Nothing to write so read
               String serialId = Utility.bytesToHex(tag.getId());
               Log.d("[WriteCard]", "Serial Number: " + serialId);
               Toast.makeText(this, serialId, Toast.LENGTH_SHORT).show();
             } else {
               // Have some text to write 
               try {
                  write("type/1", text, tag);
                } catch(Exception e)  {
                  Log.d("[WriteCard]", e.toString());
                  Toast.makeText(this,  e.toString(), Toast.LENGTH_SHORT).show();
                }
               // Reset the write trigger
               text = "";
        }
    }
}

// When the write Button is clicked
public void onClick(View view)    {
    text = spinner_location.toString();
}

Also you really need to check that your Tag is a Formatted Ndef Tag before your try and write to it.

e.g. something like

private void write(String mimeType, String text, Tag tag) throws IOException, FormatException {
    NdefRecord[] records = {createRecord(mimeType, text)};
    NdefMessage message = new NdefMessage(records);
    Ndef ndef = Ndef.get(tag);
    if(ndef != null) {
       // It's an already formatted Ndef Tag
       ndef.connect();
       ndef.writeNdefMessage(message);
       ndef.close();
    } else {
       // Try and format at write
       .... "get(tag)" for Ndef formattable type and check not null
    }
}

The final point is using the old enableForegroundDispatch is unreliable, so use the Newer and better enableReaderMode and onTagDiscovered API instead.

This also solves the calling connect etc on the wrong thread as onTagDiscovered is automatically in it's own thread.

Also enableReaderMode when you disable the "Platform" sounds it does not prompt the user to remove the Tag from range before you have had a chance to write to it (You can play your own sound after a successful write)

See https://stackoverflow.com/a/64921434/2373819 for an example of enableReaderMode

Upvotes: 1

Related Questions