diff --git a/app/src/main/java/com/tangem/ui/fragment/MainFragment.kt b/app/src/main/java/com/tangem/ui/fragment/MainFragment.kt index cecb0ddeee..b4f7e31965 100644 --- a/app/src/main/java/com/tangem/ui/fragment/MainFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/MainFragment.kt @@ -35,6 +35,9 @@ import com.tangem.tangem_card.tasks.ReadCardInfoTask import com.tangem.tangem_sdk.android.data.PINStorage import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation import com.tangem.tangem_sdk.android.reader.NfcReader +import com.tangem.tangem_sdk.android.reader.NfcVReader +import com.tangem.tangem_sdk.android.reader.ReadResult +import com.tangem.tangem_sdk.android.reader.ReadSlixTagTask import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID import com.tangem.tangem_sdk.data.loadFromBundle @@ -127,7 +130,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader tvBuyCards?.setText(spannable, TextView.BufferType.SPANNABLE) llShoppingView?.setOnClickListener { - val uri = Uri.parse ("https://www.tangemcards.com") + val uri = Uri.parse("https://www.tangemcards.com") val intent = Intent(Intent.ACTION_VIEW, uri) startActivity(intent) } @@ -170,7 +173,7 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader return } - parseNfcvTag(tag) + NfcV.get(tag)?.let { onNfcVDiscovered(it, tag.id) } try { // get IsoDep handle and run cardReader thread @@ -195,74 +198,41 @@ class MainFragment : BaseFragment(), NavigationResultListener, NfcAdapter.Reader } } - private fun parseNfcvTag(tag: Tag) { - if (NfcV.get(tag) != null) { - if (Ndef.get(tag) != null) { - try { - onNdefDiscovered(Ndef.get(tag), tag.id) - } catch (e: Exception) { - e.printStackTrace() - (activity as MainActivity).nfcManager.notifyReadResult(false) - } - return - } else { - (activity as MainActivity).nfcManager.notifyReadResult(false) - return - } - } - } - private fun onNdefDiscovered(ndef: Ndef, uid: ByteArray) { + private fun onNfcVDiscovered(nfcV: NfcV, uid: ByteArray) { scope.launch { + when (val readResult = ReadSlixTagTask(NfcVReader(nfcV)).read()) { + is ReadResult.Failure -> (activity as MainActivity).nfcManager.notifyReadResult(false) + is ReadResult.Success -> { + try { + val tlvs = readResult.tlvs + val cardDataTlv = TLVList.fromBytes((tlvs.getTLV(TLV.Tag.TAG_CardData)).Value) + Log.v(TAG, "\n" + tlvs.getParsedTLVs("")) + val card = TangemCard(uid.toString()) + card.batch = cardDataTlv.getTLV(TLV.Tag.TAG_Batch).asHexString + card.setIssuer(cardDataTlv.getTLV(TLV.Tag.TAG_Issuer_ID).Value.toString(), null) + card.blockchainID = Blockchain.StellarTag.id + card.walletPublicKey = tlvs.getTLV(TLV.Tag.TAG_Wallet_PublicKey).Value + card.status = TangemCard.Status.Loaded + card.tagSignature = tlvs.getTLV(TLV.Tag.TAG_Signature).Value + val ctx = TangemContext(card) + val engineCoin = XlmTagEngine(ctx) + engineCoin.defineWallet() + launch(Dispatchers.Main) { - try { - ndef.connect() - val records = ndef.ndefMessage.records - for (record in records) { - if (record.toUri() != null) { - when (record.toUri().toString()) { - "vnd.android.nfc://ext/tangem.com:wallet" -> { - //mConsole.write(Util.bytesToHex(record.getPayload()), MessageAdapter.MSG_OKAY, "", null, false); - val payload = record.payload - Log.v(TAG, "tangem.com:wallet[${payload.size} bytes]:") - try { - val tlvNDEF: TLVList = TLVList.fromBytes(Arrays.copyOfRange(payload, 2, payload.size)) - val cardDataTlv = TLVList.fromBytes((tlvNDEF.getTLV(TLV.Tag.TAG_CardData)).Value) - Log.v(TAG, "\n" + tlvNDEF.getParsedTLVs("")) - val card = TangemCard(uid.toString()) - card.batch = cardDataTlv.getTLV(TLV.Tag.TAG_Batch).asHexString - card.setIssuer(cardDataTlv.getTLV(TLV.Tag.TAG_Issuer_ID).Value.toString(), null) - card.blockchainID = Blockchain.StellarTag.id - card.walletPublicKey = tlvNDEF.getTLV(TLV.Tag.TAG_Wallet_PublicKey).Value - card.status = TangemCard.Status.Loaded - card.tagSignature = tlvNDEF.getTLV(TLV.Tag.TAG_Signature).Value - - val ctx = TangemContext(card) - val engineCoin = XlmTagEngine(ctx) - engineCoin.defineWallet() - launch(Dispatchers.Main) { - - val bundle = Bundle() - bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag) - ctx.saveToBundle(bundle) - navigateForResult(Constant.REQUEST_CODE_SHOW_CARD_ACTIVITY, - R.id.action_main_to_tagFragment, bundle) - } - } catch (e: TLVException) { - e.printStackTrace() - Log.v(TAG, e.message) - } - } - else -> Log.v(TAG, record.toUri().toString()) + val bundle = Bundle() + bundle.putParcelable(Constant.EXTRA_LAST_DISCOVERED_TAG, lastTag) + ctx.saveToBundle(bundle) + navigateForResult(Constant.REQUEST_CODE_SHOW_CARD_ACTIVITY, + R.id.action_main_to_tagFragment, bundle) } + } catch (e: TLVException) { + e.printStackTrace() + Log.v(TAG, e.message) } } - } catch (e: Exception) { - e.printStackTrace() - (activity as MainActivity).nfcManager.notifyReadResult(false) } - } } diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/NfcReader.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/NfcReader.java index d93099f6ac..d0ad24b4b5 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/NfcReader.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/NfcReader.java @@ -17,4 +17,5 @@ public interface NfcReader { void connect(); + boolean isConnected(); } \ No newline at end of file diff --git a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java index d071484cbd..40c36a777b 100644 --- a/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java +++ b/tangem-card-old/src/main/java/com/tangem/tangem_card/reader/TLVList.java @@ -69,4 +69,20 @@ public class TLVList extends ArrayList { while (tlv != null); return tlvList; } + + public static TLVList tryFromBytes(byte[] mData) { + TLVList tlvList = new TLVList(); + ByteArrayInputStream stream = new ByteArrayInputStream(mData); + TLV tlv = null; + do { + try { + tlv = TLV.ReadFromStream(stream); + if (tlv != null) tlvList.add(tlv); + } catch (IOException e) { + break; + } + } + while (tlv != null); + return tlvList; + } } diff --git a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcManager.kt b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcManager.kt index dffc588721..a1c6e5f49d 100644 --- a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcManager.kt +++ b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcManager.kt @@ -25,7 +25,7 @@ class NfcManager(private val activity: FragmentActivity, private val readerCallb // reader mode flags: listen for type A (not B), skipping ndef check private const val READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_NFC_V or - NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS + NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK or NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS private const val DELAY_PRESENCE = 1500 private const val REQUEST_NFC_PERMISSIONS = 1 diff --git a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcReader.kt b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcReader.kt index c7a0409b99..a628e05071 100644 --- a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcReader.kt +++ b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcReader.kt @@ -38,4 +38,8 @@ data class NfcReader( isoDep.timeout = timeout } + override fun isConnected(): Boolean { + return isoDep.isConnected + } + } \ No newline at end of file diff --git a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcVReader.kt b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcVReader.kt new file mode 100644 index 0000000000..47c9239cc7 --- /dev/null +++ b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/NfcVReader.kt @@ -0,0 +1,39 @@ +package com.tangem.tangem_sdk.android.reader + +import android.nfc.tech.NfcV + +data class NfcVReader( + var nfcV: NfcV? +) : com.tangem.tangem_card.reader.NfcReader { + + override fun getId(): ByteArray? { + return nfcV?.tag?.id + } + + override fun setTimeout(timeout: Int) { + } + + override fun getTimeout(): Int { + return 0 + } + + override fun transceive(data: ByteArray?): ByteArray? { + return nfcV?.transceive(data) + } + + override fun ignoreTag() { + nfcV?.close() + nfcV = null + } + + override fun notifyReadResult(success: Boolean) { + } + + override fun connect() { + nfcV?.connect() + } + + override fun isConnected(): Boolean { + return nfcV?.isConnected == true + } +} \ No newline at end of file diff --git a/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/ReadSlixTagTask.kt b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/ReadSlixTagTask.kt new file mode 100644 index 0000000000..a061348bd4 --- /dev/null +++ b/tangem-sdk-old/src/main/java/com/tangem/tangem_sdk/android/reader/ReadSlixTagTask.kt @@ -0,0 +1,120 @@ +package com.tangem.tangem_sdk.android.reader + +import android.nfc.NdefMessage +import android.nfc.NdefRecord +import com.tangem.tangem_card.reader.NfcReader +import com.tangem.tangem_card.reader.TLV +import com.tangem.tangem_card.reader.TLVList +import com.tangem.tangem_card.util.Util +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.util.* + +class ReadSlixTagTask(val nfcReader: NfcReader) { + + private var lastErrorCode: Int = 0 + + fun read(): ReadResult { + if (!nfcReader.isConnected) { + try { + nfcReader.connect() + } catch (e: Exception) { + return ReadResult.Failure(e) + } + } + return try { + runRead() + } catch (e: Exception) { + nfcReader.ignoreTag() + ReadResult.Failure(e) + } + } + + private fun runRead(): ReadResult { + val ndefMessage = runReadNDEF() + val records: Array = ndefMessage.records + for (record in records) { + if (record.toUri() != null && record.toUri().toString() == "vnd.android.nfc://ext/tangem.com:wallet") { + val payload = record.payload + val tlvNDEF = TLVList.fromBytes(Arrays.copyOfRange(payload, 2, payload.size)) + nfcReader.ignoreTag() + return ReadResult.Success(tlvNDEF) + } + } + return ReadResult.Failure(Exception("No parcelable Tlv has been found.")) + } + + private fun runReadNDEF(): NdefMessage { + val answerCC = readSingleBlock(0x00) + + if (answerCC.size != 4 || answerCC[0].toInt() != 0xE1 || ((answerCC[1].toInt() and 0xF0) != 0x40)) { + + } else { + throw Exception("Failed! Invalid CC read " + Util.bytesToHex(answerCC)) + } + if ((answerCC[3].toInt() and 0x01) != 0x01) { + throw Exception("Multiple block read unsupported!") + } + val areaSize = 8 * answerCC[2] + val blocksCount = areaSize / 4 + + val areaBuf = readMultipleBlocks(1, blocksCount) + + val tlvNDEF = TLVList.tryFromBytes(areaBuf) + + return NdefMessage(tlvNDEF.getTLV(TLV.Tag.TAG_CardPublicKey).Value) + } + + private fun readSingleBlock(blockNo: Int): ByteArray { + return doTransceive(0x20, blockNo)!! + } + + private fun readMultipleBlocks(startBlock: Int, blocksCount: Int): ByteArray { + val resultBuf = ByteArrayOutputStream() + val maxBlocksAtOnce = 32 + var blocksRemaining = blocksCount + var firstBlockToRead = startBlock + while (blocksRemaining > 0) { + val blocksToRead = if (blocksRemaining > maxBlocksAtOnce) { + maxBlocksAtOnce + } else { + blocksRemaining + } + val blocks = doTransceive(0x23.toByte(), firstBlockToRead, blocksToRead - 1) + blocksRemaining -= blocksToRead + firstBlockToRead += blocksToRead + resultBuf.write(blocks!!) + } + return resultBuf.toByteArray() + } + + private fun doTransceive(cmd: Byte, p1: Int?, p2: Int? = null, params: ByteArray? = null): ByteArray? { + val command: ByteArray + val res: ByteArray? + lastErrorCode = -1 + val os = ByteArrayOutputStream() + os.write(REQ_FLAG.toInt()) + os.write(cmd.toInt()) + p1?.let { os.write(p1) } + p2?.let { os.write(it) } + params?.let { os.write(params, 0, params.size) } + command = os.toByteArray() + if (!nfcReader.isConnected) throw IOException("Connection lost") + res = nfcReader.transceive(command) + lastErrorCode = res[0].toInt() + if (lastErrorCode != 0) { + throw IOException("Error! Code: " + String.format("0x%02x", lastErrorCode)) + } + return res.copyOfRange(1, res.size) + } + + companion object{ + // iso15693 flags + private const val REQ_FLAG: Byte = 0x02 + } +} + +sealed class ReadResult { + data class Success(val tlvs: TLVList) : ReadResult() + data class Failure(val exception: Exception) : ReadResult() +}