Search code examples
javaandroidandroid-studionfc

IOException writing data to NFC card


I am trying to write data to an NFC card using an Android application but when I try to write the data I get a java.io.IOExcetion. The log tells me that it is null. I have added printstacktrace and it points to an error at android.nfc.tech.NdefFormatble.format(NdefFormarmatable.java:131) and android.nfc.tech.NdefFormatble.format(NdefFormarmatable.java:94). I have had a look at this class to see what the error is and Android Studio says that it cannot resolve symbol on some of the imports in that class. I can not figure out what is going wrong and I would appreciate any help in sorting this problem. I did have this working before and then all of a sudden I started getting this error. I did update Android Studio to 3.1.1 but I tried using AS 3.0 and 2.3 but I get the same error. I have included the methods which all being called as well as the stacktrace.

This is where the card is formatted and written to:

    private void formatTag(Tag tag, NdefMessage ndefMessage) {
    try {

        NdefFormatable ndefFormatable = NdefFormatable.get(tag);

        if (ndefFormatable == null) {
            Toast.makeText(this, "Tag is not ndef formatable!", Toast.LENGTH_SHORT).show();
            return;
        }


        ndefFormatable.connect();
        ndefFormatable.format(ndefMessage);   ***<----MainActivity.java:469***
        ndefFormatable.close();

        Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();

    } catch (Exception e) {
        Log.e("formatTag", "" + e.getMessage());
        e.printStackTrace();
    }

}

This is the method for writing the NDEF message

    private void writeNdefMessage(Tag tag, NdefMessage ndefMessage) {

    try {

        if (tag == null) {
            Toast.makeText(this, "Tag object cannot be null", Toast.LENGTH_SHORT).show();
            return;
        }

        Ndef ndef = Ndef.get(tag);

        if (ndef == null) {
            // format tag with the ndef format and writes the message.
            formatTag(tag, ndefMessage);      **<----- MainActivity.java 494**
        } else {
            ndef.connect();

            if (!ndef.isWritable()) {
                Toast.makeText(this, "Tag is not writable!", Toast.LENGTH_SHORT).show();

                ndef.close();
                return;
            }

            ndef.writeNdefMessage(ndefMessage);
            ndef.close();

            Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();

        }

    } catch (Exception e) {
        Log.e("writeNdefMessage", "" + e.getMessage());
        e.printStackTrace();
    }

This is where writeNdefMessage is call from

    @Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    if (intent.hasExtra(NfcAdapter.EXTRA_TAG)) {
        Toast.makeText(this, "NfcIntent!", Toast.LENGTH_SHORT).show();

        if(tglReadWrite.isChecked())
        {
            Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

            if(parcelables != null && parcelables.length > 0)
            {
                readDataFromMessage((NdefMessage) parcelables[0]);
            }else{
                Toast.makeText(this, "No NDEF messages found!", Toast.LENGTH_SHORT).show();
            }

        }else{
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

            NdefMessage ndefMessage = createNdefMessage(mToken.getText()+";");


            writeNdefMessage(tag, ndefMessage);    ***<---- MainActivity.java:406***
        }

    }
}

and here is the StackTrace

    04-13 02:50:51.112 6311-6311/com.appsolutedevelopment.labourstaff E/formatTag: null

04-13 02:50:51.112 6311-6311/com.appsolutedevelopment.labourstaff W/System.err: java.io.IOException 04-13 02:50:51.113 6311-6311/com.appsolutedevelopment.labourstaff W/System.err: at android.nfc.tech.NdefFormatable.format(NdefFormatable.java:131) at android.nfc.tech.NdefFormatable.format(NdefFormatable.java:94) at com.appsolutedevelopment.labourstaff.MainActivity.formatTag(MainActivity.java:469) at com.appsolutedevelopment.labourstaff.MainActivity.writeNdefMessage(MainActivity.java:494) at com.appsolutedevelopment.labourstaff.MainActivity.onNewIntent(MainActivity.java:406) at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1228) at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1240) at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946) at android.app.ActivityThread.performNewIntents(ActivityThread.java:2958) at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2967) at android.app.ActivityThread.-wrap15(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1648) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:156) at android.app.ActivityThread.main(ActivityThread.java:6523) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)

And finally the NdefFormatable class

package android.nfc.tech;

import android.nfc.ErrorCodes;      ***<---- Cannot resolve symbol 'ErrorCodes'***
import android.nfc.FormatException;
import android.nfc.INfcTag;     ***<---- Cannot resolve symbol 'INfcTag'***
import android.nfc.NdefMessage;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.RemoteException;
import android.util.Log;

import java.io.IOException;

/**
 * Provide access to NDEF format operations on a {@link Tag}.
 *
 * <p>Acquire a {@link NdefFormatable} object using {@link #get}.
 *
 * <p>Android devices with NFC must only enumerate and implement this
 * class for tags for which it can format to NDEF.
 *
 * <p>Unfortunately the procedures to convert unformated tags to NDEF formatted
 * tags are not specified by NFC Forum, and are not generally well-known. So
 * there is no mandatory set of tags for which all Android devices with NFC
 * must support {@link NdefFormatable}.
 *
 * <p class="note"><strong>Note:</strong> Methods that perform I/O operations
 * require the {@link android.Manifest.permission#NFC} permission.
 */
public final class NdefFormatable extends BasicTagTechnology {
    private static final String TAG = "NFC";

    /**
     * Get an instance of {@link NdefFormatable} for the given tag.
     * <p>Does not cause any RF activity and does not block.
     * <p>Returns null if {@link NdefFormatable} was not enumerated in {@link Tag#getTechList}.
     * This indicates the tag is not NDEF formatable by this Android device.
     *
     * @param tag an NDEF formatable tag
     * @return NDEF formatable object
     */
    public static NdefFormatable get(Tag tag) {
        if (!tag.hasTech(TagTechnology.NDEF_FORMATABLE)) return null;
        try {
            return new NdefFormatable(tag);
        } catch (RemoteException e) {
            return null;
        }
    }

    /**
     * Internal constructor, to be used by NfcAdapter
     * @hide
     */
    public NdefFormatable(Tag tag) throws RemoteException {
        super(tag, TagTechnology.NDEF_FORMATABLE);
    }

    /**
     * Format a tag as NDEF, and write a {@link NdefMessage}.
     *
     * <p>This is a multi-step process, an IOException is thrown
     * if any one step fails.
     * <p>The card is left in a read-write state after this operation.
     *
     * <p>This is an I/O operation and will block until complete. It must
     * not be called from the main application thread. A blocked call will be canceled with
     * {@link IOException} if {@link #close} is called from another thread.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @param firstMessage the NDEF message to write after formatting, can be null
     * @throws TagLostException if the tag leaves the field
     * @throws IOException if there is an I/O failure, or the operation is canceled
     * @throws FormatException if the NDEF Message to write is malformed
     */
    public void format(NdefMessage firstMessage) throws IOException, FormatException {
        format(firstMessage, false);    ***<----NdefFormatable.java:94***
    }

    /**
     * Formats a tag as NDEF, write a {@link NdefMessage}, and make read-only.
     *
     * <p>This is a multi-step process, an IOException is thrown
     * if any one step fails.
     * <p>The card is left in a read-only state if this method returns successfully.
     *
     * <p>This is an I/O operation and will block until complete. It must
     * not be called from the main application thread. A blocked call will be canceled with
     * {@link IOException} if {@link #close} is called from another thread.
     *
     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
     *
     * @param firstMessage the NDEF message to write after formatting
     * @throws TagLostException if the tag leaves the field
     * @throws IOException if there is an I/O failure, or the operation is canceled
     * @throws FormatException if the NDEF Message to write is malformed
     */
    public void formatReadOnly(NdefMessage firstMessage) throws IOException, FormatException {
        format(firstMessage, true);
    }

    /*package*/ void format(NdefMessage firstMessage, boolean makeReadOnly) throws IOException,
            FormatException {
        checkConnected();

        try {
            int serviceHandle = mTag.getServiceHandle();
            INfcTag tagService = mTag.getTagService();
            int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
            switch (errorCode) {
                case ErrorCodes.SUCCESS:
                    break;
                case ErrorCodes.ERROR_IO:
                    throw new IOException();     ***<---- NdefFormatable.java:131***
                case ErrorCodes.ERROR_INVALID_PARAM:
                    throw new FormatException();
                default:
                    // Should not happen
                    throw new IOException();
            }
            // Now check and see if the format worked
            if (!tagService.isNdef(serviceHandle)) {
                throw new IOException();
            }

            // Write a message, if one was provided
            if (firstMessage != null) {
                errorCode = tagService.ndefWrite(serviceHandle, firstMessage);
                switch (errorCode) {
                    case ErrorCodes.SUCCESS:
                        break;
                    case ErrorCodes.ERROR_IO:
                        throw new IOException();
                    case ErrorCodes.ERROR_INVALID_PARAM:
                        throw new FormatException();
                    default:
                        // Should not happen
                        throw new IOException();
                }
            }

            // optionally make read-only
            if (makeReadOnly) {
                errorCode = tagService.ndefMakeReadOnly(serviceHandle);
                switch (errorCode) {
                    case ErrorCodes.SUCCESS:
                        break;
                    case ErrorCodes.ERROR_IO:
                        throw new IOException();
                    case ErrorCodes.ERROR_INVALID_PARAM:
                        throw new IOException();
                    default:
                        // Should not happen
                        throw new IOException();
                }
            }
        } catch (RemoteException e) {
            Log.e(TAG, "NFC service dead", e);
        }
    }
}

If I have missed anything or anybody needs more info put up just ask and I'll get whatever is needed. Id be very grateful if someone could shed some light on this as it has been driving me around the twist for so long.

Thanks.


Solution

  • I found the answer to this question here Android NFC - ndef.writeNdefMessage() throws IOException and erases tag data. This explains what is going on in my app when I try to write to the NFC card after an unsuccessful write operation. If anyone is looking for a solution to this problem then this Android nfcA.connect(), nfcA.transceive(), nfcA.setTimeout() and nfcA.getMaxTransceiveLength() might be of some help. If reading and writing critical data to NFC tokens/cards is what you are trying to achieve then I would suggest using nfcA as oppossed to NDEF.