How to write NDEF records to NFC tag?
Asked Answered
K

1

8

How do I write NDEF message to NFC tag? Do I have to change manifest file? So far I have code to generate NDEF message:

    public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
    byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
    Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
    byte[] textBytes = payload.getBytes(utfEncoding);
    int utfBit = encodeInUtf8 ? 0 : (1 << 7);
    char status = (char) (utfBit + langBytes.length);
    byte[] data = new byte[1 + langBytes.length + textBytes.length];
    data[0] = (byte) status;
    System.arraycopy(langBytes, 0, data, 1, langBytes.length);
    System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
    NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
            NdefRecord.RTD_TEXT, new byte[0], data);
    return record;
}

How do I discover the TAG? Can someone help me with that?

Katharinakatharine answered 19/11, 2020 at 21:29 Comment(0)
S
22

I would ignore what the Google documentation says for reading at https://developer.android.com/guide/topics/connectivity/nfc/nfc and for read/write at https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#read-write as this provides a very poor user experience for writing to Tags and leads to a lot of failed writes because of user behaviour.

To get reliable writing to NFC with Android you should use the newer and much better enableReaderMode API https://developer.android.com/reference/android/nfc/NfcAdapter

Using this newer API leads to a lot less failed writes and corrupted cards because you can control when the notification sound happens. With the old Intent based system the system pauses your app and then reads the card and makes a notification sound, the user then removes the card before your App is resumed and has a chance to handle the card data and write to the card.

With the newer enableReaderMode API you turn off the system notification sound and your App is never paused to read the NFC card, you can then read and write to the card and then when you have successfully written to the card you can make the notification sound yourself.

Because any errors are silent, the user will keep trying to present the card until the success Notification. Additional logic is needed not to write the same message every time a single card or different cards are presented.

Some example code (adapted from my NFC app to handle low level reading and writing (not Ndef Tag technology))


public class NFCActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{

private NfcAdapter mNfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nfc);

        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        // Rest of Activity setup
    }

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

        if(mNfcAdapter!= null) {
            Bundle options = new Bundle();
            // Work around for some broken Nfc firmware implementations that poll the card too fast
            options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

            // Enable ReaderMode for all types of card and disable platform sounds
            mNfcAdapter.enableReaderMode(this,
                    this,
                    NfcAdapter.FLAG_READER_NFC_A |
                            NfcAdapter.FLAG_READER_NFC_B |
                            NfcAdapter.FLAG_READER_NFC_F |
                            NfcAdapter.FLAG_READER_NFC_V |
                            NfcAdapter.FLAG_READER_NFC_BARCODE |
                            NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                    options);
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableReaderMode(this);
    }
    
   // This method is run in another thread when a card is discovered
   // !!!! This method cannot cannot direct interact with the UI Thread
   // Use `runOnUiThread` method to change the UI from this method
   @Override
   public void onTagDiscovered(Tag tag) {

      // Read and or write to Tag here to the appropriate Tag Technology type class
      // in this example the card should be an Ndef Technology Type 
      Ndef mNdef = Ndef.get(tag);

      // Check that it is an Ndef capable card
      if (mNdef!= null) {

      // If we want to read
      // As we did not turn on the NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
      // We can get the cached Ndef message the system read for us.

      NdefMessage mNdefMessage = mNdef.getCachedNdefMessage();


      // Or if we want to write a Ndef message

      // Create a Ndef Record
      NdefRecord mRecord = NdefRecord.createTextRecord("en","English String");

      // Add to a NdefMessage
      NdefMessage mMsg = new NdefMessage(mRecord);
      
      // Catch errors
      try {
          mNdef.connect();
          mNdef.writeNdefMessage(mMsg);

          // Success if got to here
          runOnUiThread(() -> {
             Toast.makeText(getApplicationContext(),
               "Write to NFC Success",
               Toast.LENGTH_SHORT).show();
          });

          // Make a Sound
          try {
              Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
              Ringtone r = RingtoneManager.getRingtone(getApplicationContext(),
                  notification);
              r.play();
              } catch (Exception e) {
                 // Some error playing sound
              }
          
      } catch (FormatException e) {
        // if the NDEF Message to write is malformed
      } catch (TagLostException e) {
        // Tag went out of range before operations were complete
      } catch (IOException e){
        // if there is an I/O failure, or the operation is cancelled
      } catch (SecurityException e){
        // The SecurityException is only for Android 12L and above
        // The Tag object might have gone stale by the time
        // the code gets to process it, with a new one been
        // delivered (for the same or different Tag)
        // The SecurityException is thrown if you are working on
        // a stale Tag
      } finally {
        // Be nice and try and close the tag to
        // Disable I/O operations to the tag from this TagTechnology object, and release resources. 
         try {
                mNdef.close();
             } catch (IOException e) {
               // if there is an I/O failure, or the operation is cancelled
             }
      }

   }

}

Superinduce answered 19/11, 2020 at 23:13 Comment(11)
I am getting the error in line " mNdef.writeNdefMessage(mMsg);" Error: W/Binder: Caught a RuntimeException from the binder stub implementation. java.lang.IllegalStateException: Call connect() first!Katharinakatharine
Sorry forgot to transfer that across when adapting my Nfca code to Ndef tag technology, example updated with mNdef.connect(); just before the mNdef.writeNdefMessage(mMsg);Superinduce
Hey @Andrew, I have an additional question to your provided answer. How can I set up the techlist more precisely? For me, I need only tags with NfcA AND MifareClassic. I am only able to filter for NfcA with your solution.Survey
@Survey MifareClassic Tags are also NfcA Tags, so just remove the B,F,V,BARCODE Flags to enableReaderMode are treat them as NfcA tags. If you really want to use the MifareClassic class try MifareClassic.get(Tag) but handle the exception if the phone does not support MifareClassic (Because support is optional) and handle a null return if the Tag is not MifareClassicSuperinduce
I am using different kind of tags, some are MifareUltralight and some MifareClassic, but both NfcA. I wish to use a tech-filter again, to just allow MifareClassic. Hope you understand the problem i am facing. But I am solving it with an additional if-clause.Survey
You cannot use a tech-filter with enablereadermode, you have to do the filtering yourself between the various different NfcA Tags by doing if-else on the return of the get method of the tech class.Superinduce
Hi Andrew, may I ask you please to take a look on my question regarding NFC? I see you are an expert in Android NFC) #76015082Bicyclic
There's one more NDEF related flag: NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, while both NfcAdapter.CreateNdefMessageCallback and NfcAdapter.OnNdefPushCompleteCallback were deprecated.Uriisa
@MartinZeitler Yes there is also the NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK as mentioned in the code comments but when this option is enabled then the getCachedNdefMessage method will probably not work (which this example uses)Superinduce
@Superinduce Either this is an assumption or undocumented behavior; haven't tried so far with and without: developer.android.com/reference/android/nfc/…Uriisa
@MartinZeitler It is documented developer.android.com/reference/android/nfc/tech/… - Get the NdefMessage that was read from the tag at discovery time - this function only returns what was discovered when the tag entered the field. So if you turn off discovery it won't be able to return any data. And you can see from android.googlesource.com/platform/packages/apps/Nfc/+/… that it skip reading of the Ndef Message at discovery time.Superinduce

© 2022 - 2024 — McMap. All rights reserved.