Android NFC - ndef.writeNdefMessage () throws an IOException and erases the tag data

My application uses a foreground dispatching system so that the user can use their NFC tag to perform read-write operations in the tag.

This works well if the user correctly labels his tag (i.e. they click on it in the right place on the phone and leave it connected long enough), but if they physically delete the tag too soon, then ndef.writeNdefMessage(...)throws an IOException.

This means that the write operation failed, which is fair enough. But the real problem is that the same unsuccessful operation also removes all ndef formatting / message from the tag!

My code is built around fragments from Advanced NFC | Android Developers (unfortunately, the link to the ForegroundDispatch sample seems to be broken, and there is no such sample project for importing to Android Studio).

Step 1. Here is the result of logcat / stacktrace when the user first selects his NFC tag, but removes it too quickly:

03-28 20:15:18.589 21278-21278/com.example.exampleapp E/NfcTestActivity: Tag error
java.io.IOException
    at android.nfc.tech.Ndef.writeNdefMessage(Ndef.java:320)
    at com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:170)
    at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)
    at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)
    at android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)
    at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)
    at android.app.ActivityThread.access$1700(ActivityThread.java:181)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:6145)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
03-28 20:15:18.599 1481-17792/? E/SecNfcJni: nfaConnectionCallback: NFA_SELECT_RESULT_EVT error: status = 3
03-28 20:15:18.599 1481-1502/? E/SecNfcJni: reSelect: tag is not active

Step 2. Then the same user enters the same tag again, but it no longer contains the ndef message (which I confirmed by changing the code and checking that it ndef.getCachedNdefMessage()returns null):

03-28 20:15:27.499 21278-21278/com.example.exampleapp E/NfcTestActivity: Tag error
java.lang.Exception: Tag was not ndef formatted: android.nfc.action.TECH_DISCOVERED
    at com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:124)
    at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)
    at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)
    at android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)
    at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)
    at android.app.ActivityThread.access$1700(ActivityThread.java:181)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:145)
    at android.app.ActivityThread.main(ActivityThread.java:6145)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

, - Samsung Galaxy Core Prime ( ) Android 5.1.1 Samsung Galaxy A5 ( ) Android 5.0.2.

NFC, , (.. !), ...

  • (. ) , ?
  • ?
  • NfcA IsoDep, Ndef?

, , , , , NFC, ?...

, , NXP MIFARE Ultralight (Ultralight C) - NTAG203 ( : ISO 14443-3A). ebay, Rapid NFC ( ), , , .

:

package com.example.exampleapp;

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;

public class NfcTestActivity extends AppCompatActivity {

    private static String LOG_TAG = NfcTestActivity.class.getSimpleName();
    private static int SUCCESS_COUNT = 0;
    private static int FAILURE_COUNT = 0;

    private NfcAdapter nfcAdapter;
    private PendingIntent pendingIntent;
    private IntentFilter[] intentFiltersArray;
    private String[][] techListsArray;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc_test);
        getSupportActionBar().setDisplayShowHomeEnabled(true);

        nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (nfcAdapter == null) {

            makeToast("NFC not available!", Toast.LENGTH_LONG);
            finish();

        }
        else {

            //makeToast("NFC available");

            pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

            IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
            try {
                ndef.addDataType("*/*");    /* Handles all MIME based dispatches.
                                           You should specify only the ones that you need. */
            } catch (IntentFilter.MalformedMimeTypeException e) {
                throw new RuntimeException("fail", e);
            }
            intentFiltersArray = new IntentFilter[]{
                    ndef
            };

            techListsArray = new String[][]{
                    new String[]{
                            Ndef.class.getName()
                    }
            };
        }

    }

    @Override
    public void onPause() {
        super.onPause();

        if (nfcAdapter != null) {
            nfcAdapter.disableForegroundDispatch(this);
        }

    }

    @Override
    public void onResume() {
        super.onResume();

        if (nfcAdapter != null) {
            nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
        }

    }

    public void onNewIntent(Intent intent) {

        Ndef ndef = null;

        try {

            String action = intent.getAction();
            //makeToast("action: " + action);

            if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

                throw new Exception("Tag was not ndef formatted: " + action); // line #124

            }
            else {

                Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
                //do something with tagFromIntent

                ndef = Ndef.get(tag);
                //makeToast("ndef: " + ndef);

                if (ndef == null) {

                    throw new Exception("ndef == null!");

                }
                else {

                    // Connect
                    ndef.connect();

                    // Get cached message
                    NdefMessage ndefMessageOld = ndef.getCachedNdefMessage();

                    if (ndefMessageOld == null) {

                        throw new Exception("No ndef message on tag!");

                    }
                    else {

                        // Get old records
                        NdefRecord[] ndefRecordsOld = ndefMessageOld.getRecords();
                        int numRecords = (ndefRecordsOld == null) ? 0 : ndefRecordsOld.length;

                        // Create/copy 'new' records

                        NdefRecord[] ndefRecordsNew = new NdefRecord[numRecords];
                        for (int i = 0; i < numRecords; i++) {
                            ndefRecordsNew[i] = ndefRecordsOld[i];
                        }

                        // Create new message
                        NdefMessage ndefMessageNew = new NdefMessage(ndefRecordsNew);

                        // Write new message
                        ndef.writeNdefMessage(ndefMessageNew); // line #170

                        SUCCESS_COUNT++;

                        // Report success
                        String msg = "Read & wrote " + numRecords + " records.";
                        makeToast(msg);
                        Log.d(LOG_TAG, msg);

                    }

                }

            }

        }
        catch(Exception e) {

            FAILURE_COUNT++;

            Log.e(LOG_TAG, "Tag error", e);
            makeToast("Tag error: " + e, Toast.LENGTH_LONG);

        }
        finally {

            try {
                if (ndef != null) {
                    ndef.close();
                }
            }
            catch(Exception e) {
                Log.e(LOG_TAG, "Error closing ndef", e);
                makeToast("Error closing ndef: " + e, Toast.LENGTH_LONG);
            }

            makeToast("Successes: " + SUCCESS_COUNT + ". Failures: " + FAILURE_COUNT);

        }

    }

    private void makeToast(final String msg) {

        makeToast(msg, Toast.LENGTH_SHORT);

    }

    private void makeToast(final String msg, final int duration) {

        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {

                Toast.makeText(NfcTestActivity.this, msg, duration).show();

            }
        });
    }

}
+1
1

, , .

(. ) , ?

"" . , undefined .

NFC NDEF. , NDEF, NDEF. ,

ndef.writeNdefMessage(ndefMessageNew);

NDEF, . NTAG203, MIFARE Ultralight MIFARE Ultralight C (, ), 4. writeNdefMessage , .

(, ), ( ). , , Android ( NDEF) NDEF , , NDEF. , TECH_DISCOVERED ( , NDEF vaild).

?

NDEF , , - ( , ). NFC- - . , NDEF , NDEF .

, ( ) NDEF (, ) . , .

NfcA IsoDep, Ndef?

. NDEF , ( NDEF). NTAG/MIFARE Ultralight ISO 14443-3A (NFC-A) ISO-DEP (ISO 14443-4). , NfcA ( MifareUltralight) / . , :

Block x: Flag indicating which section (1 or 2) contains the valid data
Block x+1: First block of section 1
Block x+2: Second block of section 1
[...]
Block x+m: Last block of section 1
Block x+m+1: First block of section 2
Block x+m+2: Second block of section 2
[...]
Block x+2*m: Last block of section 2

x - ( NDEF), m - (1 NTAG/MF Ultralight 4 ).

- :

  • x, , vaild () → s.
  • s .
  • ( s= 1: 0, s= 0: 1).
  • ( ), x .

:

  • READ

    byte[] result = nfcA.transceive(new byte[] {
            (byte)0x30,  // READ
            (byte)(blockNumber & 0x0ff)
    });
    
  • WRITE

    byte[] result = nfcA.transceive(new byte[] {
            (byte)0xA2,  // WRITE
            (byte)(blockNumber & 0x0ff),
            byte0, byte1, byte2, byte3
    });
    
+2

Source: https://habr.com/ru/post/1659088/


All Articles