微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Android 上的 NFC 主机卡仿真 (HCE) 不适用于特定设备

如何解决Android 上的 NFC 主机卡仿真 (HCE) 不适用于特定设备

我想开发一个 Android 应用,其中一个设备充当 NFC 标签,另一个移动设备读取它。 我有两个安卓设备,A 和 B - NFC HCE 功能适用于设备 A,即使用正确的 NDEF 数据模拟标签,设备 B 可以读取它。设备 A 的 NFC 标签 (HCE) 的规格是 -

NFC TAG (HCE) SPECS DEVICE A

但是现在,当我尝试使用设备 B 执行此操作时,NDEF 数据未写入且设备 A 无法读取。设备 B 的 NFC 标签 (HCE) 的规格为 -

NFC TAG (HCE) SPECS DEVICE B


HostApduService代码如下-

package com.example.cn

import android.app.Service
import android.content.Intent
import android.nfc.NdefMessage
import android.nfc.NdefRecord
import android.nfc.cardemulation.HostApduService
import android.os.Bundle
import android.util.Log
import java.io.UnsupportedEncodingException
import java.math.BigInteger
import java.util.*

class CardService : HostApduService() {

    private val TAG = "HostApduService"

    private val APDU_SELECT = byteArrayOf(
            0x00.toByte(),// CLA   - Class - Class of instruction
            0xA4.toByte(),// INS   - Instruction - Instruction code
            0x04.toByte(),// P1    - Parameter 1 - Instruction parameter 1
            0x00.toByte(),// P2    - Parameter 2 - Instruction parameter 2
            0x07.toByte(),// Lc field  - Number of bytes present in the data field of the command
            0xD2.toByte(),0x76.toByte(),0x00.toByte(),0x85.toByte(),0x01.toByte(),// NDEF Tag Application name
            0x00.toByte()  // Le field  - Maximum number of bytes expected in the data field of the response to the command
    )

    private val CAPABILITY_CONTAINER_OK = byteArrayOf(
            0x00.toByte(),// CLA   - Class - Class of instruction
            0xa4.toByte(),// INS   - Instruction - Instruction code
            0x00.toByte(),// P1    - Parameter 1 - Instruction parameter 1
            0x0c.toByte(),// P2    - Parameter 2 - Instruction parameter 2
            0x02.toByte(),// Lc field  - Number of bytes present in the data field of the command
            0xe1.toByte(),0x03.toByte() // file identifier of the CC file
    )

    private val READ_CAPABILITY_CONTAINER = byteArrayOf(
            0x00.toByte(),// CLA   - Class - Class of instruction
            0xb0.toByte(),// P2    - Parameter 2 - Instruction parameter 2
            0x0f.toByte()  // Lc field  - Number of bytes present in the data field of the command
    )

    // In the scenario that we have done a CC read,the same byte[] match
    // for ReadBinary would trigger and we don't want that in succession
    private var READ_CAPABILITY_CONTAINER_CHECK = false

    private val READ_CAPABILITY_CONTAINER_RESPONSE = byteArrayOf(
            0x00.toByte(),0x11.toByte(),// cclEN length of the CC file
            0x20.toByte(),// Mapping Version 2.0
            0xFF.toByte(),0xFF.toByte(),// MLe maximum
            0xFF.toByte(),// MLc maximum
            0x04.toByte(),// T field of the NDEF File Control TLV
            0x06.toByte(),// L field of the NDEF File Control TLV
            0xE1.toByte(),0x04.toByte(),// File Identifier of NDEF file
            0xFF.toByte(),0xFE.toByte(),// Maximum NDEF file size of 65534 bytes
            0x00.toByte(),// Read access without any security
            0xFF.toByte(),// Write access without any security
            0x90.toByte(),0x00.toByte() // A_OKAY
    )

    private val NDEF_SELECT_OK = byteArrayOf(
            0x00.toByte(),// Instruction byte (INS) for Select command
            0x00.toByte(),// Parameter byte (P1),select by identifier
            0x0c.toByte(),select by identifier
            0x02.toByte(),// Lc field  - Number of bytes present in the data field of the command
            0xE1.toByte(),0x04.toByte() // file identifier of the NDEF file retrieved from the CC file
    )

    private val NDEF_READ_BINARY = byteArrayOf(
            0x00.toByte(),// Class byte (CLA)
            0xb0.toByte() // Instruction byte (INS) for ReadBinary command
    )

    private val NDEF_READ_BINARY_NLEN = byteArrayOf(
            0x00.toByte(),// Class byte (CLA)
            0xb0.toByte(),// Instruction byte (INS) for ReadBinary command
            0x00.toByte(),// Parameter byte (P1,P2),offset inside the CC file
            0x02.toByte()  // Le field
    )

    private val A_OKAY = byteArrayOf(
            0x90.toByte(),// SW1   Status byte 1 - Command processing status
            0x00.toByte()   // SW2  Status byte 2 - Command processing qualifier
    )

    private val A_ERROR = byteArrayOf(
            0x6A.toByte(),// SW1   Status byte 1 - Command processing status
            0x82.toByte()   // SW2  Status byte 2 - Command processing qualifier
    )

    private val NDEF_ID = byteArrayOf(0xE1.toByte(),0x04.toByte())

    private var NDEF_URI = NdefMessage(createTextRecord("en","NFC Clock",NDEF_ID))
    private var NDEF_URI_BYTES = NDEF_URI.toByteArray()
    private var NDEF_URI_LEN = fillByteArrayToFixedDimension(
            BigInteger.valueOf(NDEF_URI_BYTES.size.toLong()).toByteArray(),2
    )


    override fun onStartCommand(intent: Intent,flags: Int,startId: Int): Int {

        if (intent.hasExtra("ndefMessage")) {

            Log.i(TAG,"Set NFC tag as " + intent.getStringExtra("ndefMessage"))
            NDEF_URI =
                    NdefMessage(createTextRecord("en",intent.getStringExtra("ndefMessage"),NDEF_ID))

            NDEF_URI_BYTES = NDEF_URI.toByteArray()
            NDEF_URI_LEN = fillByteArrayToFixedDimension(
                    BigInteger.valueOf(NDEF_URI_BYTES.size.toLong()).toByteArray(),2
            )
        }

        Log.i(TAG,"onStartCommand() | NDEF" + NDEF_URI.records[0].payload);

        return Service.START_STICKY
    }

    override fun processCommandApdu(commandApdu: ByteArray,extras: Bundle?): ByteArray {

        //
        // The following flow is based on Appendix E "Example of Mapping Version 2.0 Command Flow"
        // in the NFC Forum specification
        //
        Log.i(TAG,"processCommandApdu() | incoming commandApdu: " + commandApdu.toHex())

        //
        // First command: NDEF Tag Application select (Section 5.5.2 in NFC Forum spec)
        //
        if (Arrays.equals(APDU_SELECT,commandApdu)) {
            Log.i(TAG,"APDU_SELECT triggered. Our Response: " + A_OKAY.toHex())
            return A_OKAY
        }

        //
        // Second command: Capability Container select (Section 5.5.3 in NFC Forum spec)
        //
        if (Arrays.equals(CAPABILITY_CONTAINER_OK,"CAPABILITY_CONTAINER_OK triggered. Our Response: " + A_OKAY.toHex())
            return A_OKAY
        }

        //
        // Third command: ReadBinary data from CC file (Section 5.5.4 in NFC Forum spec)
        //
        if (Arrays.equals(
                        READ_CAPABILITY_CONTAINER,commandApdu
                ) && !READ_CAPABILITY_CONTAINER_CHECK
        ) {
            Log.i(
                    TAG,"READ_CAPABILITY_CONTAINER triggered. Our Response: " + READ_CAPABILITY_CONTAINER_RESPONSE.toHex()
            )
            READ_CAPABILITY_CONTAINER_CHECK = true
            return READ_CAPABILITY_CONTAINER_RESPONSE
        }

        //
        // Fourth command: NDEF Select command (Section 5.5.5 in NFC Forum spec)
        //
        if (Arrays.equals(NDEF_SELECT_OK,"NDEF_SELECT_OK triggered. Our Response: " + A_OKAY.toHex())
            return A_OKAY
        }

        if (Arrays.equals(NDEF_READ_BINARY_NLEN,commandApdu)) {
            // Build our response
            val response = ByteArray(NDEF_URI_LEN.size + A_OKAY.size)
            System.arraycopy(NDEF_URI_LEN,response,NDEF_URI_LEN.size)
            System.arraycopy(A_OKAY,NDEF_URI_LEN.size,A_OKAY.size)

            Log.i(TAG,"NDEF_READ_BINARY_NLEN triggered. Our Response: " + response.toHex())

            READ_CAPABILITY_CONTAINER_CHECK = false
            return response
        }

        if (Arrays.equals(commandApdu.sliceArray(0..1),NDEF_READ_BINARY)) {
            val offset = commandApdu.sliceArray(2..3).toHex().toInt(16)
            val length = commandApdu.sliceArray(4..4).toHex().toInt(16)

            val fullResponse = ByteArray(NDEF_URI_LEN.size + NDEF_URI_BYTES.size)
            System.arraycopy(NDEF_URI_LEN,fullResponse,NDEF_URI_LEN.size)
            System.arraycopy(
                    NDEF_URI_BYTES,NDEF_URI_BYTES.size
            )

            Log.i(TAG,"NDEF_READ_BINARY triggered. Full data: " + fullResponse.toHex())
            Log.i(TAG,"READ_BINARY - OFFSET: " + offset + " - LEN: " + length)

            val slicedResponse = fullResponse.sliceArray(offset until fullResponse.size)

            // Build our response
            val realLength = if (slicedResponse.size <= length) slicedResponse.size else length
            val response = ByteArray(realLength + A_OKAY.size)

            System.arraycopy(slicedResponse,realLength)
            System.arraycopy(A_OKAY,realLength,"NDEF_READ_BINARY triggered. Our Response: " + response.toHex())

            READ_CAPABILITY_CONTAINER_CHECK = false
            return response
        }

        //
        // We're doing something outside our scope
        //
        Log.wtf(TAG,"processCommandApdu() | I don't kNow what's going on!!!")
        return A_ERROR
    }

    override fun onDeactivated(reason: Int) {
        Log.i(TAG,"onDeactivated() Fired! Reason: $reason")
    }


    private val HEX_CHARS = "0123456789ABCDEF".tochararray()

    fun ByteArray.toHex(): String {
        val result = StringBuffer()

        forEach {
            val octet = it.toInt()
            val firstIndex = (octet and 0xF0).ushr(4)
            val secondindex = octet and 0x0F
            result.append(HEX_CHARS[firstIndex])
            result.append(HEX_CHARS[secondindex])
        }

        return result.toString()
    }

    fun String.hexStringToByteArray(): ByteArray {

        val result = ByteArray(length / 2)

        for (i in 0 until length step 2) {
            val firstIndex = HEX_CHARS.indexOf(this[i]);
            val secondindex = HEX_CHARS.indexOf(this[i + 1]);

            val octet = firstIndex.shl(4).or(secondindex)
            result.set(i.shr(1),octet.toByte())
        }

        return result
    }

    fun createTextRecord(language: String,text: String,id: ByteArray): NdefRecord {
        val languageBytes: ByteArray
        val textBytes: ByteArray
        try {
            languageBytes = language.toByteArray(charset("US-ASCII"))
            textBytes = text.toByteArray(charset("UTF-8"))
        } catch (e: UnsupportedEncodingException) {
            throw AssertionError(e)
        }

        val recordPayload = ByteArray(1 + (languageBytes.size and 0x03F) + textBytes.size)

        recordPayload[0] = (languageBytes.size and 0x03F).toByte()
        System.arraycopy(languageBytes,recordPayload,1,languageBytes.size and 0x03F)
        System.arraycopy(
                textBytes,1 + (languageBytes.size and 0x03F),textBytes.size
        )

        return NdefRecord(NdefRecord.TNF_WELL_KNowN,NdefRecord.RTD_TEXT,id,recordPayload)
    }

    fun fillByteArrayToFixedDimension(array: ByteArray,fixedSize: Int): ByteArray {
        if (array.size == fixedSize) {
            return array
        }

        val start = byteArrayOf(0x00.toByte())
        val filledArray = ByteArray(start.size + array.size)
        System.arraycopy(start,filledArray,start.size)
        System.arraycopy(array,start.size,array.size)
        return fillByteArrayToFixedDimension(filledArray,fixedSize)
    }
}

(得到了this的帮助)

使用的 AID -

<aid-filter android:name="D2760000850101"/>

编辑: NXP TagInfo 在读取设备 B-ScreenShot

标签显示内容

编辑:这可能与 NFC TAG B 相关吗?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?