iphone+android+identifier,Android NFC not reading NDEF tag emulated via Host Card Emulation (HCE), i...

博主尝试在Android设备上使用Host Card Emulation (HCE) 模拟一个NDEF标签,能够成功被iPhone读取,但在Android手机上无法读取。问题可能涉及到Android Beam、NDEF文件系统交互或特定Android版本的限制。代码基于GitHub上的项目,并进行了调整,但问题仍然存在。寻求帮助,希望了解是否Android设备之间存在读取HCE标签的限制。
摘要由CSDN通过智能技术生成

I am trying to emulate a NDEF tag via the HCE on android. I am able to read the emulated tag with an iPhone, but when I try to read it with an Android nothing happens.

I am not using my app to read, just reading with either the default operative system behaviour or any downloaded app.

All Android used to test it were android 10 or above so android beam shouldn't be a problem, I am emulating from a pixel 3a XL updated.

I based my code on this project from github, cloning the project results in the same outcome, I am able to read it with an iPhone but nothing seems to happen using an Android phone to read it.

Is there any restriction reading a HCE from Android?

This is my Service

import android.content.Context

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.*

/**

* Created by Qifan on 05/12/2018.

*/

class NfcEmulatorService : 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(),

0x00.toByte(),

0x85.toByte(),

0x01.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(), // INS - Instruction - Instruction code

0x00.toByte(), // P1 - Parameter 1 - Instruction parameter 1

0x00.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(), 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(), // CLA - Class - Class of instruction

0xa4.toByte(), // Instruction byte (INS) for Select command

0x00.toByte(), // Parameter byte (P1), select by identifier

0x0c.toByte(), // Parameter byte (P1), 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(), 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 url = "https://facebook.com"

private var NDEF_URI = NdefMessage(NdefRecord.createUri(url))

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 onCreate() {

super.onCreate()

val prefs = getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)

url = prefs.getString("flutter.url", "").toString()

Log.i(TAG, "Service created with urlExtracted: " + url)

if (url != "https://facebook.com") {

NDEF_URI = NdefMessage(NdefRecord.createUri(url))

NDEF_URI_BYTES = NDEF_URI.toByteArray()

NDEF_URI_LEN = fillByteArrayToFixedDimension(

BigInteger.valueOf(NDEF_URI_BYTES.size.toLong()).toByteArray(), 2

)

}

}

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, commandApdu)) {

Log.i(TAG, "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, commandApdu)) {

Log.i(TAG, "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, 0, response, 0, NDEF_URI_LEN.size)

System.arraycopy(A_OKAY, 0, response, 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, 0, fullResponse, 0, NDEF_URI_LEN.size)

System.arraycopy(

NDEF_URI_BYTES,

0,

fullResponse,

NDEF_URI_LEN.size,

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, 0, response, 0, realLength)

System.arraycopy(A_OKAY, 0, response, realLength, A_OKAY.size)

Log.i(TAG, "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, 0, recordPayload, 1, languageBytes.size and 0x03F)

System.arraycopy(

textBytes,

0,

recordPayload,

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, 0, filledArray, 0, start.size)

System.arraycopy(array, 0, filledArray, start.size, array.size)

return fillByteArrayToFixedDimension(filledArray, fixedSize)

}

}

Thanks in advance, I am new to apps and willing to learn, any help would be much appreciated

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值