Android NFC - ndef.writeNdefMessage() 抛出 IOException 并擦除标签数据 [英] Android NFC - ndef.writeNdefMessage() throws IOException and erases tag data

查看:32
本文介绍了Android NFC - ndef.writeNdefMessage() 抛出 IOException 并擦除标签数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用使用前台调度系统来允许用户点击他们的 NFC 标签,以便在标签上执行读写操作.

如果用户正确点击他们的标签(即,他们在手机上的正确位置点击标签并保持连接足够长的时间),它会很好地工作,但如果他们过早物理删除标签,那么 ndef.writeNdefMessage(...) 抛出 IOException.

这意味着写操作失败,这很公平.但真正的问题是,同样的失败操作也会删除标签中的整个 ndef 格式/消息!

我的代码是围绕 Advanced NFC |Android 开发人员 页面(不幸的是 指向 ForegroundDispatch 示例的链接 似乎已损坏,并且没有此类示例项目可导入 Android Studio).

第 1 步.这是用户第一次点击其 NFC 标签但过早将其移开时的 logcat/stacktrace 输出:

03-28 20:15:18.589 21278-21278/com.example.exampleapp E/NfcTestActivity: 标记错误java.io.IOException在 android.nfc.tech.Ndef.writeNdefMessage(Ndef.java:320)在 com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:170)在 android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)在 android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)在 android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)在 android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)在 android.app.ActivityThread.access$1700(ActivityThread.java:181)在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)在 android.os.Handler.dispatchMessage(Handler.java:102)在 android.os.Looper.loop(Looper.java:145)在 android.app.ActivityThread.main(ActivityThread.java:6145)在 java.lang.reflect.Method.invoke(本机方法)在 java.lang.reflect.Method.invoke(Method.java:372)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)03-28 20:15:18.599 1481-17792/?E/SecNfcJni:nfaConnectionCallback:NFA_SELECT_RESULT_EVT 错误:状态 = 303-28 20:15:18.599 1481-1502/?E/SecNfcJni:重新选择:标签未激活

第 2 步. 接下来,同一用户再次点击同一标签,但它似乎不再包含 ndef 消息(我已通过更改代码并检查 ndef.getCachedNdefMessage() 返回 null):

03-28 20:15:27.499 21278-21278/com.example.exampleapp E/NfcTestActivity: 标记错误java.lang.Exception:标签不是 ndef 格式:android.nfc.action.TECH_DISCOVERED在 com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:124)在 android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)在 android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)在 android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)在 android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)在 android.app.ActivityThread.access$1700(ActivityThread.java:181)在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)在 android.os.Handler.dispatchMessage(Handler.java:102)在 android.os.Looper.loop(Looper.java:145)在 android.app.ActivityThread.main(ActivityThread.java:6145)在 java.lang.reflect.Method.invoke(本机方法)在 java.lang.reflect.Method.invoke(Method.java:372)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)

到目前为止,我在测试过的两台设备上都遇到了这个问题——运行 Android 5.1.1 的 Samsung Galaxy Core Prime(低端手机)和 Samsung Galaxy A5(中端手机)运行 Android 5.0.2.

我的应用程序使用的 NFC 标签包含重要信息(即,不能无意中删除标签数据!),所以我的问题是......

  1. 为什么我的代码(见下文)会这样擦除标签数据?
  2. 如何解决根本问题,或者是否有可接受的解决方法?
  3. 是否值得我尝试使用 NfcA 或 IsoDep 而不是 Ndef?

做了很多搜索后,我很惊讶这个问题没有在其他地方讨论过,所以如果问题与我的代码无关,那么可能与我正在使用的 NFC 标签有关?...

我使用的标签是 NXP MIFARE Ultralight (Ultralight C) - NTAG203(标签类型:ISO 14443-3A).其中一些是我从 ebay 购买的,还有一些是从 Rapid NFC(一家有信誉的公司)购买的,但我似乎都遇到了这个问题.

这是我的完整活动代码:

包 com.example.exampleapp;导入 android.app.PendingIntent;导入android.content.Intent;导入 android.content.IntentFilter;导入android.nfc.NdefMessage;导入android.nfc.NdefRecord;导入 android.nfc.NfcAdapter;导入android.nfc.Tag;导入android.nfc.tech.Ndef;导入android.os.Bundle;导入android.os.Handler;导入android.os.Looper;导入android.support.v7.app.AppCompatActivity;导入android.util.Log;导入 android.widget.Toast;公共类 NfcTestActivity 扩展 AppCompatActivity {私有静态字符串 LOG_TAG = NfcTestActivity.class.getSimpleName();私有静态 int SUCCESS_COUNT = 0;私有静态 int FAILURE_COUNT = 0;私有 NfcAdapter nfcAdapter;私有的 PendingIntent私有 IntentFilter[] intentFiltersArray;私有字符串[][] techListsArray;@覆盖protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_nfc_test);getSupportActionBar().setDisplayShowHomeEnabled(true);nfcAdapter = NfcAdapter.getDefaultAdapter(this);如果(nfcAdapter == null){makeToast("NFC 不可用!", Toast.LENGTH_LONG);结束();}别的 {//makeToast("NFC 可用");pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);尝试 {ndef.addDataType("*/*");/* 处理所有基于 MIME 的调度.您应该只指定您需要的那些.*/} 捕捉(IntentFilter.MalformedMimeTypeException e){throw new RuntimeException("失败", e);}intentFiltersArray = new IntentFilter[]{定义};techListsArray = 新字符串[][]{新字符串[]{Ndef.class.getName()}};}}@覆盖公共无效 onPause() {超级.onPause();如果(nfcAdapter!= null){nfcAdapter.disableForegroundDispatch(this);}}@覆盖公共无效 onResume() {超级.onResume();如果(nfcAdapter!= null){nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);}}公共无效onNewIntent(意图意图){ndef ndef = null;尝试 {字符串动作 = intent.getAction();//makeToast("action:" + action);if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {throw new Exception("标签不是 ndef 格式:" + action);//第 124 行}别的 {标签 tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//用tagFromIntent做一些事情ndef = Ndef.get(标签);//makeToast("ndef:" + ndef);如果(ndef == null){抛出新异常(ndef == null!");}别的 {//连接ndef.connect();//获取缓存消息NdefMessage ndefMessageOld = ndef.getCachedNdefMessage();如果(ndefMessageOld == null){throw new Exception("标签上没有 ndef 消息!");}别的 {//获取旧记录NdefRecord[] ndefRecordsOld = ndefMessageOld.getRecords();int numRecords = (ndefRecordsOld == null) ?0:ndefRecordsOld.length;//创建/复制新"记录NdefRecord[] ndefRecordsNew = new NdefRecord[numRecords];for (int i = 0; i < numRecords; i++) {ndefRecordsNew[i] = ndefRecordsOld[i];}//创建新消息NdefMessage ndefMessageNew = new NdefMessage(ndefRecordsNew);//写新消息ndef.writeNdefMessage(ndefMessageNew);//第 170 行SUCCESS_COUNT++;//报告成功String msg = "读取和写入" + numRecords + " 条记录.";制作吐司(味精);Log.d(LOG_TAG, msg);}}}}捕获(异常 e){FAILURE_COUNT++;Log.e(LOG_TAG, "标签错误", e);makeToast("标签错误:" + e, Toast.LENGTH_LONG);}最后 {尝试 {如果(ndef!= null){ndef.close();}}捕获(异常 e){Log.e(LOG_TAG, "错误关闭 ndef", e);makeToast("错误关闭 ndef:" + e, Toast.LENGTH_LONG);}makeToast("成功:" + SUCCESS_COUNT + ".失败:" + FAILURE_COUNT);}}私人无效makeToast(最终字符串味精){makeToast(味精,Toast.LENGTH_SHORT);}private void makeToast(final String msg, final int duration) {新处理程序(Looper.getMainLooper()).post(新 Runnable() {@覆盖公共无效运行(){Toast.makeText(NfcTestActivity.this, msg, duration).show();}});}}

解决方案

我真的很想知道当您在覆盖其数据的过程中移除存储设备时会发生什么.

为什么我的代码(见下文)会像这样擦除标签数据?

您的代码并不是真正擦除"数据.它只是从标签内存的开头开始覆盖数据,当您中断写入时,标签处于未定义状态.

NFC 标签一次只支持存储一条 NDEF 消息.因此,当您开始编写新的 NDEF 消息时,需要覆盖旧的 NDEF 消息.因此,

ndef.writeNdefMessage(ndefMessageNew);

将从第一个块开始覆盖现有的 NDEF 消息.对于 NTAG203、MIFARE Ultralight 和 MIFARE Ultralight C(顺便说一下,这是三种不同的标签类型),第一个块将在块 4 附近.writeNdefMessage 然后将写入新消息块,用于块替换旧数据新数据.

如果写入过程被中断(例如,通过从阅读器字段中拉出标签),则只有部分新消息被写入(并且部分旧消息可能保留在标签上).由于旧消息和新消息都不完整,Android(就像任何其他 NDEF 阅读器一样)无法从标签中读取有效的 NDEF 消息,因此不会检测到任何 NDEF 消息.您的应用仍会检测到该标签,因为您还注册了 TECH_DISCOVERED 意图(它不需要标签包含有效的 NDEF 消息).

如何解决根本问题,或者是否有可接受的解决方法?

如果您的 NDEF 消息足够长,以至于您的用户实际上能够在写入时拉取标签,那么您对拉取本身无能为力(除了指示用户不要这样做).NFC 标签也没有任何形式的开箱即用保护.IE.在新的 NDEF 消息被完全写入之前,目前没有任何标签可以可靠地存储旧的 NDEF 消息.

您可以做的是将旧的(或新的)NDEF 消息(可能映射到标签 ID)存储在您的应用程序中,并让用户在写入过程失败后重新启动写入过程.不过,这需要用户合作.

是否值得我尝试使用 NfcA 或 IsoDep 而不是 Ndef?

这可能是另一种选择:不要将 NDEF 用于关键数据,而是使用特定于应用程序的内存布局(或除 NDEF 之外).NTAG/MIFARE Ultralight 在 ISO 14443-3A (NFC-A) 之上有一个命令集,不支持 ISO-DEP (ISO 14443-4).因此,您可以使用 NfcA(或 MifareUltralight)使用低级命令直接读取/写入标签.您可以将标签内存分为两部分来存储旧数据和新数据:

<上一页>块 x:指示哪个部分(1 或 2)包含有效数据的标志块 x+1:第 1 节的第一个块区块 x+2:第 1 部分的第二个区块[...]区块 x+m:第 1 节的最后一个区块块 x+m+1:第 2 节的第一个块区块 x+m+2:第 2 节的第二个区块[...]区块 x+2*m:第 2 节的最后一个区块

其中 x 是您的自定义内存结构的第一个块(您甚至可以在一些固定的 NDEF 消息之后开始该区域),而 m 是每个部分的长度块(NTAG/MF Ultralight 上的 1 个块有 4 个字节).

然后你会使用类似这样的东西来读取和更新你的标签:

  1. 从块 x 中读取以找出哪个部分包含有效(最新)数据 -> 部分 s.
  2. s 部分读取数据并将其用作当前数据.
  3. 将新数据写入其他部分(如果 s = 1:部分 0;如果 s = 0:部分 1).
  4. 如果数据写入成功(并且完全),则使用新的节号更新块 x.

低级读写命令如下所示:

  • 阅读:

    byte[] 结果 = nfcA.transceive(new byte[] {(byte)0x30,//读(byte)(blockNumber & 0x0ff)});

  • 写:

    byte[] 结果 = nfcA.transceive(new byte[] {(byte)0xA2,//写(byte)(blockNumber & 0x0ff),字节0,字节1,字节2,字节3});

My app uses the foreground dispatch system to allow a user to tap their NFC tag in order to perform a read-then-write operation on the tag.

It works nicely if the user taps their tag properly (i.e., they tap it in the correct place on the phone and leave it connected for long enough), but if they physically remove the tag too early, then ndef.writeNdefMessage(...) throws an IOException.

That means the write operation fails, which is fair enough. But the real problem is that the same failed operation also deletes the entire ndef formatting/message from the tag!

My code is built around the snippets from the Advanced NFC | Android Developers page (as unfortunately the link to the ForegroundDispatch sample appears to be broken and there is no such sample project to import into Android Studio).

Step 1. Here is the logcat/stacktrace output when the user first taps their NFC tag, but moves it away too soon:

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. Next, the same user taps the same tag again but it appears to no longer contain an ndef message (which I have confirmed by altering the code and checking that 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)

I am getting this issue with both devices I have tested with so far - a Samsung Galaxy Core Prime (a lower end phone) running Android 5.1.1 and a Samsung Galaxy A5 (a mid range phone) running Android 5.0.2.

The NFC tags used by my app contain important information (i.e., inadvertently deleting the tag data is not an option!), so my questions are...

  1. Why is my code (see below) erasing the tag data like this?
  2. How can I fix the underlying problem, or is there an acceptable workaround?
  3. Would it be worth me trying to use NfcA or IsoDep rather than Ndef?

Having done a lot of searching, I'm very surprised that this problem has not been discussed elsewhere, so if the problem isn't to do with my code, then could it be to do with the NFC tags I am using?...

The tags I'm using are NXP MIFARE Ultralight (Ultralight C) - NTAG203 (Tag type: ISO 14443-3A). Some of these I bought from ebay, and some I bought from Rapid NFC (a reputable company), yet I seem to have this problem with all of them.

Here is my complete code for the activity:

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();

            }
        });
    }

}

解决方案

I really wonder what else you would expect to happen when you remove a storage device in the middle of overwriting its data.

Why is my code (see below) erasing the tag data like this?

You code is not really "erasing" data. It simply starts overwriting the data from the beginning of the tag memory leaving the tag in an undefined state when you interrupt writing.

An NFC tag only supports storing one NDEF message at a time. Consequently, when you start to write an new NDEF message, the old NDEF message needs to be overwritten. Thus,

ndef.writeNdefMessage(ndefMessageNew);

will overwrite the existing NDEF message starting at its first block. For NTAG203, MIFARE Ultralight and MIFARE Ultralight C (that's three different tag types by the way), this first block will be around block 4. writeNdefMessage will then write the new message block for block replacing old data with new data.

If the write procedure is interrupted (e.g. by pulling the tag from the reader field), then only parts of the new message are written (and parts of the old message may remain on the tag). Since neither the old nor the new message are complete, Android (just as any other NDEF reader) cannot read a valid NDEF message from the tag and, therefore, does not detect any NDEF message. The tag is still detected by your app since you also registered for the TECH_DISCOVERED intent (which does not require the tag to contain a vaild NDEF message).

How can I fix the underlying problem, or is there an acceptable workaround?

If your NDEF message is that long that your users are actually able to pull the tag while writing there is not much you can do against the pulling itself (except for instructing the users not to do so). NFC tags also do not have any form of pulling protection out-of-the-box. I.e. there are currently no tags that will reliably store the old NDEF message until the new NDEF message was written completely.

What you could possibly do is to store the old (or the new) NDEF message (possibly mapped to the tag ID) within your app and let the users restart the write procedure once it failed. Still, that would require user cooperation.

Would it be worth me trying to use NfcA or IsoDep rather than Ndef?

That might be another option: Don't use NDEF for the critical data but use an application-specific memory layout instead (or in addition to NDEF). NTAG/MIFARE Ultralight have a command set on top of ISO 14443-3A (NFC-A) and do not support ISO-DEP (ISO 14443-4). Thus, you could use NfcA (or MifareUltralight) to directly read from/write to the tags using low-level commands. You could structure the tag memory in two sections that you use to store the old and the new data:

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

Where x is the first block of your custom memory structure (you could even start that area after some fixed NDEF message) and m is the length of each section in blocks (1 block on NTAG/MF Ultralight has 4 bytes).

You would then use something like this to read and update your tag:

  1. Read from block x to find out which section contains the vaild (newest) data -> section s.
  2. Read the data from section s and use it as current data.
  3. Write the new data to the other section (if s = 1: section 0; if s = 0: section 1).
  4. If the data was written successfully (and completely), update block x with the new section number.

Low-level read and write commands look like this:

  • 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
    });
    

这篇关于Android NFC - ndef.writeNdefMessage() 抛出 IOException 并擦除标签数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆