franckbour / Plugin.NFC

A Cross-Platform NFC (Near Field Communication) plugin to easily read and write NFC tags in your application.
MIT License
233 stars 68 forks source link

working on formatting #144

Open Gekidoku opened 12 months ago

Gekidoku commented 12 months ago

Have an issue when writing to tags that are not initialized but are NDefFormattable. For xamarin.forms i used to use my own modified version of https://github.com/poz1/NFCForms This added both mifarelight format support and regular ndefformat support. Currently attempting to replace the Publish command in a fork with his write function. So far it seems to work. Just have to re-implement clear / readonly

Gekidoku commented 12 months ago

https://github.com/Gekidoku/Plugin.NFC to track progress

Gekidoku commented 12 months ago

Note i will only do this for android. as iOS NFC is a nightmare

Gekidoku commented 12 months ago
internal void WriteMessageAndFormat(ITagInfo tagInfo, bool makeReadOnly = false)
{
    if (_currentTag == null)
    {
        throw new Exception(Configuration.Messages.NFCErrorMissingTag);
    }

    Ndef ndef = Ndef.Get(_currentTag);

    //Check if the tag can be NDEFformatted
    var formatcheck = _currentTag.GetTechList().Contains("android.nfc.tech.NdefFormatable");

    //Previous check didnt compare both just ndef being null can also mean its not formatted
    if (ndef == null && formatcheck == false)
    {
        throw new Exception(Configuration.Messages.NFCErrorNotCompliantTag);
    }
    //Not formatted but can be formatted
    if (ndef == null && formatcheck == true)
    {

        NdefFormatable formatable = NdefFormatable.Get(_currentTag);
        //For some reason this could still give null even if formatcheck gave a true so /shrug
        if (formatable == null)
        {
            throw new Exception(Configuration.Messages.NFCErrorNotCompliantTag);
        }
        try
        {
            formatable.Connect();

            OnTagConnected?.Invoke(null, EventArgs.Empty);
        }
        catch
        {
            throw new Exception(Configuration.Messages.NFCErrorMissingTag);
        }
        //old size check.
        //int size = message.ToByteArray().Length;

        try
        {
            List<Android.Nfc.NdefRecord> records = new List<Android.Nfc.NdefRecord>();
            for (int i = 0; i < tagInfo.Records.Length; i++)
            {
                var record = tagInfo.Records[i];
                if (GetAndroidNdefRecord(record) is NdefRecord ndefRecord)
                    records.Add(ndefRecord);
            };
            Android.Nfc.NdefMessage msg = new Android.Nfc.NdefMessage(records.ToArray());
            //This Format works with Mifare Classic and NTAG213. Fails with ultralight. these are the chips I tested it with.
            //Both with pre used ones and with factory new ones.
            if(makeReadOnly)
                formatable.FormatReadOnly(msg);
            else
                formatable.Format(msg);
        }

        catch (TagLostException tle)
        {
            throw new Exception(Configuration.Messages.NFCErrorMissingTag + " " + tle.Message);
        }

        catch (Android.Nfc.FormatException fe)
        {
            throw new Exception(Configuration.Messages.NFCErrorMissingTag + " " + fe.Message);
        }

        catch (Exception e)
        {
            //Assume that if we are here the tag is still touching the device, our message is correct, and the tag isnt busted.
            try
            {
                //we close this tech so we can open it as ultralight
                formatable.Close();
                //check if the connected chip is ultralight.
                if (_currentTag.GetTechList().Contains("android.nfc.tech.MifareUltralight"))
                {

                    MifareUltralight ultralight = MifareUltralight.Get(_currentTag);
                    if (ultralight != null)
                    {
                        try
                        {
                            //connect with ultralight tech
                            ultralight.Connect();
                            //Found this on a stackoverflow question that had the same problem, apparently this sets it up to be NFC Forum Type 2 tag.
                            //https://stackoverflow.com/questions/35985287/formatting-a-mifare-ultralight-to-ndef-throw-io-exception
                            ultralight.Transceive(new byte[]
                            {
                            (byte)0xA2,//Write
                            (byte)0x03,//Page Nr = 3
                            (byte)0xE1,(byte)0x10,(byte)0x06, (byte)0x00//capability container (mapping version 1.0, 48 bytes for data available, read/ write allowed)
                            });
                            ultralight.Transceive(new byte[]
                            {
                            (byte)0xA2,//Write 
                            (byte)0x04,//Page nr = 4
                            (byte)0x03, (byte)0x00,(byte)0xFE,(byte)0x00 // empty NDEF TLV, Terminator TLV
                            });
                        }
                        catch (Exception e1)
                        {

                        }
                        finally
                        {
                            //Now the tag is formatted to accept NDEF
                            try
                            {
                                //Close this tech connection as this one doesnt have WriteNdef
                                ultralight.Close();
                                //Get the tag as Ndef
                                ndef = Ndef.Get(_currentTag);
                                //Connect
                                ndef.Connect();
                                //Check the message again JIC
                                List<Android.Nfc.NdefRecord> recordsFormat = new List<Android.Nfc.NdefRecord>();
                                for (int i = 0; i < tagInfo.Records.Length; i++)
                                {
                                    var record = tagInfo.Records[i];
                                    if (GetAndroidNdefRecord(record) is NdefRecord ndefRecord)
                                        recordsFormat.Add(ndefRecord);
                                };

                                Android.Nfc.NdefMessage msg = new Android.Nfc.NdefMessage(recordsFormat.ToArray());
                                //Write and close

                                ndef.WriteNdefMessage(msg);
                                if (makeReadOnly)
                                    ndef.MakeReadOnly();
                                ndef.Close();
                            }
                            catch
                            {

                            }

                        }
                    }
                }

            }
            catch (Exception eform)
            {
                 throw new Exception(Configuration.Messages.NFCErrorWrite + " " + eform.ToString());

            }

        }

        finally
        {
            //Used to not have the ultralight code in so i had to close here
            //formatable.Close();
            OnTagDisconnected?.Invoke(null, EventArgs.Empty);
        }

    }
    else
    {
        //Rest as normal
        try
        {
            ndef.Connect();
            OnTagConnected?.Invoke(null, EventArgs.Empty);
        }

        catch
        {
            throw new Exception("Tag Error: No Tag nearby");
        }

        if (!ndef.IsWritable)
        {
            ndef.Close();
            throw new Exception(Configuration.Messages.NFCWritingNotSupported + " Locked");
        }

            try
            {
                List<Android.Nfc.NdefRecord> records = new List<Android.Nfc.NdefRecord>();
                for (int i = 0; i < tagInfo.Records.Length; i++)
                {
                var record = tagInfo.Records[i];
                if (GetAndroidNdefRecord(record) is NdefRecord ndefRecord)
                    records.Add(ndefRecord);
                }
                Android.Nfc.NdefMessage msg = new Android.Nfc.NdefMessage(records.ToArray());
                ndef.WriteNdefMessage(msg);
                if (makeReadOnly)
                    ndef.MakeReadOnly();
            }

            catch (TagLostException tle)
            {
                throw new Exception("Tag Lost Error: " + tle.Message);
            }

            catch (Android.Nfc.FormatException fe)
            {
                throw new Exception("Tag Format Error: " + fe.Message);
            }

            catch (Exception e)
            {
                throw new Exception("Tag Error: " + e.ToString());
            }

            finally
            {
                ndef.Close();
                OnTagConnected?.Invoke(null, EventArgs.Empty);
            }

    }
}