通过 NFC 发送 NdefRecord 但接收 android.nfc.tech.IsoDep

Sending NdefRecord via NFC but Receiving android.nfc.tech.IsoDep

我已经为 NFC 创建了两个应用程序。 1. NFC 发送器 2. NFC 接收器。 NFC Sender 向 NFC Receiver App 发送简单的文本数据。但是我这里有一个大问题。

我无法从 NFC 读取简单的文本。我使用 NFC 发送的文本格式是 NdefRecord。

1. NFC 发送器 Activity 代码:

    public class MainActivity extends AppCompatActivity {

    private Button btnPay;
    private LinearLayout llRootView;
    private NfcAdapter mNfcAdapter;
    private EditText edtAmount;

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

        btnPay = findViewById(R.id.btnPay);
        llRootView = findViewById(R.id.rootView);
        edtAmount = findViewById(R.id.etAmount);
        btnPay.setOnClickListener(view -> {

            hideKeyboard(MainActivity.this);

            if (TextUtils.isEmpty(edtAmount.getText().toString().trim())) {
                Snackbar.make(llRootView, "Please enter valid value.", Snackbar.LENGTH_SHORT).show();
                return;
            }

            startNFC();
        });
    }

    private void startNFC() {
        //Check if NFC is available on device
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter != null) {
            
            //This will be called if the message is sent successfully
            mNfcAdapter.setOnNdefPushCompleteCallback(event -> runOnUiThread(() ->
                    Toast.makeText(MainActivity.this, "Message sent.", Toast.LENGTH_SHORT)
                            .show()), this);

            //This will refer back to createNdefMessage for what it will send
            mNfcAdapter.setNdefPushMessageCallback(event -> new NdefMessage(createRecords()), this);

            Snackbar.make(llRootView, "Please attach receiver device to back of your device.", Snackbar.LENGTH_INDEFINITE).show();

        } else {
            Toast.makeText(this, "NFC not available on this device",
                    Toast.LENGTH_SHORT).show();
        }
    }


    private NdefRecord[] createRecords() {

        //Api is high enough that we can use createMime, which is preferred.
        NdefRecord record = NdefRecord.createMime("text/plain", edtAmount.getText().toString().trim().getBytes(StandardCharsets.UTF_8));

        return new NdefRecord[]{
                record
                , NdefRecord.createApplicationRecord("com.example.nfcreceiver")
        };
    }


    public static void hideKeyboard(Activity activity) {
        InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
        //Find the currently focused view, so we can grab the correct window token from it.
        View view = activity.getCurrentFocus();
        //If no view currently has focus, create a new one, just so we can grab a window token from it
        if (view == null) {
            view = new View(activity);
        }
        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
    }
}

2。 NFC 接收器清单:

    <activity
        android:name=".MainActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
            <action android:name="android.nfc.action.TAG_DISCOVERED" />

            <action android:name="android.nfc.action.NDEF_DISCOVERED" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

3。 NFC 接收器 Activity 代码:

public class MainActivity extends AppCompatActivity {

    NfcAdapter mAdapter;
    PendingIntent mPendingIntent;

    TextView tvReceived, tvTagTech;

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

        tvReceived = findViewById(R.id.tvReceived);
        tvTagTech = findViewById(R.id.tvTagList);

        Initialization();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!WebConstant.isOnline()) {
            Snackbar.make(tvReceived, R.string.str_no_internet_connection, Snackbar.LENGTH_LONG).show();
        }
        resolveIntent(getIntent());
        if (mAdapter != null) {
            if (!mAdapter.isEnabled()) {
                Toast.makeText(this, "NFC Not Enabled", Toast.LENGTH_LONG).show();
            }

            mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);

        }
    }


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

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

    private void Initialization() {
        mAdapter = NfcAdapter.getDefaultAdapter(this);

        if (mAdapter == null) {
            Toast.makeText(this, "NFC Not found", Toast.LENGTH_LONG).show();
            // finish();
            return;
        }
        mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
                getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
    }


    @Override
    protected void onNewIntent(final Intent intent) {
        super.onNewIntent(intent);
        new Handler().postDelayed(() -> {
            setIntent(intent);
        }, 0);
    }


    private void resolveIntent(Intent intent) {
        String action = intent.getAction();
        Log.d("ActionType", action);
        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            getTagInfo(intent);
        }
    }

    private void getTagInfo(Intent intent) {

        sendToFirebase(intent);
    }

    private void sendToFirebase(Intent intent) {


        String title, message;
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            NdefMessage[] messages = getNdefMessages(intent);
            if (messages != null && messages.length > 0) {
                title = "Success";
                message = "Received NDEF Messages";
                showAlert(title, message);
                try {
                    String msg = new String(messages[0].getRecords()[0].getPayload(), StandardCharsets.UTF_8);
                    message = String.format("You received %sQAR", msg);
                    tvReceived.setText(message);
                } catch (Exception e) {
                    e.printStackTrace();
                    title = "Error";
                    message = e.getLocalizedMessage();
                    showAlert(title, message);
                }
            } else {
                title = "Failure";
                message = "Received empty NDEF Messages";
                showAlert(title, message);
            }
        } else {
            title = "Failure";
            message = "Received non NDEF Messages";
            showAlert(title, message);
        }

        Map<String, Object> body = new HashMap<>();
        body.put("Title", title);
        body.put("Message", message);
        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        if (tag != null) {
            body.put("TagList", Arrays.toString(tag.getTechList()));
        } else {
            body.put("TagList", "");
        }

        RestClient.getInstance().push("NFCDetails", body, mResponse -> {

        });
    }

    private NdefMessage[] getNdefMessages(Intent intent) {
        Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMessages != null) {
            NdefMessage[] messages = new NdefMessage[rawMessages.length];
            for (int i = 0; i < messages.length; i++) {
                messages[i] = (NdefMessage) rawMessages[i];
            }
            return messages;
        } else {
            return null;
        }
    }
    
    public void showAlert(String title, String message) {
        AlertDialog dialog = new AlertDialog.Builder(this)
                .create();
        dialog.setTitle(title);
        dialog.setMessage(message);
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, "OK", (dialog1, which) -> dialog1.dismiss());
        dialog.show();
    }
}

请帮我解决这个难题。

我在 Receiver 应用程序中接收到的标签技术如下:

[android.nfc.tech.IsoDep, android.nfc.tech.NfcA]

Android 光束是 deprecated in Android 10

因此 Pixel phone 无法通过您尝试使用的此方法发送或接收从其他旧 Android phone 发送的 NDEF 消息。

更新: 您看到的 IsoDep 标签很可能是由设备中 NFC 硬件的安全元件部分生成的,作为 NFC 钱包类型功能的一部分,可以保存您的非接触式银行卡详细信息。你会像阅读非接触式银行卡一样阅读这篇文章,使用正确的 AID 和更高级别的协议和标准(但如果你没有将 credit/debit 卡加载到 Google 钱包,它会赢不响应 credit/debit 卡片的任何标准 AID)

您在 IsoDep 级别真正可以读取的唯一内容是随机生成的 UID。

Android 允许您进行主机卡仿真 (HCE),它允许您设置对选定 AID 查询的响应。 NDef 数据有一个 AID,因此使用 HCE 您可以复制 Android 光束类型功能,但这很复杂。

在 2 台设备之间发送数据的推荐方式是通过蓝牙或 Wifi Direct