Updated on 2026-08-14
This commit is contained in:
parent
4a411286e8
commit
336ec4a1ed
10 changed files with 65 additions and 42 deletions
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem
|
||||
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.commands.CommandResponse
|
||||
import com.tangem.commands.OpenSessionCommand
|
||||
import com.tangem.commands.ReadCommand
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.PinCode
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
|
|
@ -9,6 +11,7 @@ import com.tangem.common.extensions.calculateSha256
|
|||
import com.tangem.common.extensions.getType
|
||||
import com.tangem.crypto.EncryptionHelper
|
||||
import com.tangem.crypto.pbkdf2Hash
|
||||
import com.tangem.tasks.PinType
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
|
@ -19,6 +22,8 @@ interface CardSessionRunnable<T : CommandResponse> {
|
|||
|
||||
val performPreflightRead: Boolean
|
||||
|
||||
val requiresPin2: Boolean
|
||||
|
||||
/**
|
||||
* The starting point for custom business logic.
|
||||
* Implement this interface and use [TangemSdk.startSessionWithRunnable] to run.
|
||||
|
|
@ -28,10 +33,6 @@ interface CardSessionRunnable<T : CommandResponse> {
|
|||
fun run(session: CardSession, callback: (result: CompletionResult<T>) -> Unit)
|
||||
}
|
||||
|
||||
interface CardSessionStoppedListener {
|
||||
fun onSessionStopped(environment: SessionEnvironment)
|
||||
}
|
||||
|
||||
enum class CardSessionState {
|
||||
Inactive,
|
||||
Active
|
||||
|
|
@ -64,7 +65,6 @@ class CardSession(
|
|||
) {
|
||||
|
||||
var connectedTag: TagType? = null
|
||||
var sessionStoppedListener: CardSessionStoppedListener? = null
|
||||
|
||||
/**
|
||||
* True if some operation is still in progress.
|
||||
|
|
@ -96,7 +96,7 @@ class CardSession(
|
|||
return
|
||||
}
|
||||
|
||||
if ((runnable as? Command<R>)?.requiresPin2 == true && environment.pin2 == null) {
|
||||
if (runnable.requiresPin2 && environment.pin2 == null) {
|
||||
viewDelegate.onSessionStarted(cardId)
|
||||
viewDelegate.onPinRequested(PinType.Pin2) {
|
||||
environment.pin2 = PinCode(it)
|
||||
|
|
@ -231,7 +231,7 @@ class CardSession(
|
|||
}
|
||||
|
||||
private fun stopSession() {
|
||||
sessionStoppedListener?.onSessionStopped(environment)
|
||||
environment.saveCardValues()
|
||||
reader.stopSession()
|
||||
state = CardSessionState.Inactive
|
||||
scope.cancel()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem
|
|||
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.common.CardValuesService
|
||||
import com.tangem.common.CardValuesStorage
|
||||
import com.tangem.common.PinCode
|
||||
import com.tangem.common.TerminalKeysService
|
||||
import com.tangem.crypto.CryptoUtils.generatePublicKey
|
||||
|
|
@ -12,14 +12,25 @@ import com.tangem.crypto.CryptoUtils.generatePublicKey
|
|||
* Contains data relating to a Tangem card. It is used in constructing all the commands,
|
||||
* and commands can return modified [SessionEnvironment].
|
||||
*
|
||||
* @property card Current card, read by preflight [com.tangem.commands.ReadCommand].
|
||||
* @property terminalKeys generated terminal keys used in Linked Terminal feature.
|
||||
* @param cardId Card ID, if it is known before tapping the card.
|
||||
* @property config sets a number of parameters for communication with Tangem cards.
|
||||
* @param terminalKeysService is used to retrieve terminal keys used in Linked Terminal feature.
|
||||
* @param cardValuesStorage is used to save and retrieve some values relating to a particular card.
|
||||
*
|
||||
* @property pin1 An access Code, required to get access to a card. A default value is set in [Config]
|
||||
* @property pin2 A code, required to perform particular operations with a card. A default value is set in [Config]
|
||||
* @property terminalKeys generated terminal keys used in Linked Terminal feature
|
||||
* @property cardFilter a property that defines types of card that this SDK will be able to interact with
|
||||
* @property handleErrors if true, the SDK parses internal card errors into concrete [TangemSdkError]
|
||||
* @property encryptionMode preferred [EncryptionMode] for interaction with cards
|
||||
* @property encryptionKey is used for encrypted communication with a card
|
||||
*
|
||||
*/
|
||||
class SessionEnvironment(
|
||||
cardId: String?,
|
||||
private val config: Config,
|
||||
private val terminalKeysService: TerminalKeysService?,
|
||||
private val cardValuesService: CardValuesService?
|
||||
terminalKeysService: TerminalKeysService?,
|
||||
private val cardValuesStorage: CardValuesStorage?
|
||||
) {
|
||||
|
||||
var pin1: PinCode?
|
||||
|
|
@ -46,7 +57,7 @@ class SessionEnvironment(
|
|||
|
||||
encryptionMode = config.encryptionMode
|
||||
|
||||
val cardValues = cardId?.let { cardValuesService?.getValues(cardId) }
|
||||
val cardValues = cardId?.let { cardValuesStorage?.getValues(cardId) }
|
||||
|
||||
cardVerification = cardValues?.cardVerification ?: VerificationState.NotVerified
|
||||
cardValidation = cardValues?.cardValidation ?: VerificationState.NotVerified
|
||||
|
|
@ -68,7 +79,7 @@ class SessionEnvironment(
|
|||
}
|
||||
|
||||
fun restoreCardValues() {
|
||||
val cardValues = this.card?.cardId?.let { cardValuesService?.getValues(it) }
|
||||
val cardValues = this.card?.cardId?.let { cardValuesStorage?.getValues(it) }
|
||||
cardVerification = cardValues?.cardVerification ?: VerificationState.NotVerified
|
||||
cardValidation = cardValues?.cardValidation ?: VerificationState.NotVerified
|
||||
codeVerification = cardValues?.codeVerification ?: VerificationState.NotVerified
|
||||
|
|
@ -85,7 +96,7 @@ class SessionEnvironment(
|
|||
card?.cardId?.let { cardId -> TangemSdk.pin2[cardId] = pin2 }
|
||||
}
|
||||
|
||||
cardValuesService?.saveValues(this)
|
||||
cardValuesStorage?.saveValues(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -106,5 +117,5 @@ class KeyPair(val publicKey: ByteArray, val privateKey: ByteArray) {
|
|||
}
|
||||
|
||||
enum class VerificationState {
|
||||
Passed, Offline, Failed, NotVerified
|
||||
Passed, Offline, Failed, NotVerified, Cancelled
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem
|
||||
|
||||
import com.tangem.commands.PinType
|
||||
import com.tangem.tasks.PinType
|
||||
|
||||
/**
|
||||
* Allows interaction with users and shows visual elements.
|
||||
|
|
|
|||
|
|
@ -11,12 +11,14 @@ import com.tangem.commands.personalization.entities.Issuer
|
|||
import com.tangem.commands.personalization.entities.Manufacturer
|
||||
import com.tangem.commands.verifycard.VerifyCardCommand
|
||||
import com.tangem.commands.verifycard.VerifyCardResponse
|
||||
import com.tangem.common.CardValuesDbService
|
||||
import com.tangem.common.CardValuesDbStorage
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.PinCode
|
||||
import com.tangem.common.TerminalKeysService
|
||||
import com.tangem.crypto.CryptoUtils
|
||||
import com.tangem.tasks.ChangePinTask
|
||||
import com.tangem.tasks.CreateWalletTask
|
||||
import com.tangem.tasks.PinType
|
||||
import com.tangem.tasks.ScanTask
|
||||
|
||||
/**
|
||||
|
|
@ -369,7 +371,7 @@ class TangemSdk(
|
|||
pin: ByteArray? = null,
|
||||
initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<SetPinResponse>) -> Unit) {
|
||||
val command = ChangePinCommand(PinType.Pin1, pin)
|
||||
val command = ChangePinTask(PinType.Pin1, pin)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +379,7 @@ class TangemSdk(
|
|||
pin: ByteArray? = null,
|
||||
initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<SetPinResponse>) -> Unit) {
|
||||
val command = ChangePinCommand(PinType.Pin2, pin)
|
||||
val command = ChangePinTask(PinType.Pin2, pin)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +387,7 @@ class TangemSdk(
|
|||
pin: ByteArray? = null,
|
||||
initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<SetPinResponse>) -> Unit) {
|
||||
val command = ChangePinCommand(PinType.Pin3, pin)
|
||||
val command = ChangePinTask(PinType.Pin3, pin)
|
||||
startSessionWithRunnable(command, cardId, initialMessage, callback)
|
||||
}
|
||||
|
||||
|
|
@ -407,18 +409,9 @@ class TangemSdk(
|
|||
runnable: CardSessionRunnable<T>, cardId: String? = null, initialMessage: Message? = null,
|
||||
callback: (result: CompletionResult<T>) -> Unit) {
|
||||
val cardSession = CardSession(buildEnvironment(cardId), reader, viewDelegate, cardId, initialMessage)
|
||||
addSessionStoppedListener(cardSession)
|
||||
Thread().run { cardSession.startWithRunnable(runnable, callback) }
|
||||
}
|
||||
|
||||
private fun addSessionStoppedListener(session: CardSession) {
|
||||
session.sessionStoppedListener = object : CardSessionStoppedListener {
|
||||
override fun onSessionStopped(environment: SessionEnvironment) {
|
||||
environment.saveCardValues()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows running a custom bunch of commands in one [CardSession] with lightweight closure syntax.
|
||||
* Tangem SDK will start a card session and perform preflight [ReadCommand].
|
||||
|
|
@ -434,7 +427,6 @@ class TangemSdk(
|
|||
fun startSession(cardId: String? = null, initialMessage: Message? = null,
|
||||
callback: (session: CardSession, error: TangemSdkError?) -> Unit) {
|
||||
val cardSession = CardSession(buildEnvironment(cardId), reader, viewDelegate, cardId, initialMessage)
|
||||
addSessionStoppedListener(cardSession)
|
||||
Thread().run { cardSession.start(callback = callback) }
|
||||
}
|
||||
|
||||
|
|
@ -448,7 +440,7 @@ class TangemSdk(
|
|||
|
||||
private fun buildEnvironment(cardId: String?): SessionEnvironment {
|
||||
return SessionEnvironment(
|
||||
cardId, config, terminalKeysService, CardValuesDbService(sqlDriver)
|
||||
cardId, config, terminalKeysService, CardValuesDbStorage(sqlDriver)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.common.PinCode
|
|||
import com.tangem.common.apdu.*
|
||||
import com.tangem.common.extensions.toInt
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.PinType
|
||||
|
||||
|
||||
interface ApduSerializable<T : CommandResponse> {
|
||||
|
|
@ -39,7 +40,7 @@ interface CommandResponse
|
|||
abstract class Command<T : CommandResponse> : ApduSerializable<T>, CardSessionRunnable<T> {
|
||||
|
||||
override val performPreflightRead: Boolean = true
|
||||
open val requiresPin2: Boolean = false
|
||||
override val requiresPin2: Boolean = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<T>) -> Unit) {
|
||||
Log.i("Command", "Initializing ${this::class.java.simpleName}")
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import com.tangem.CardValuesEntityQueries
|
|||
import com.tangem.Database
|
||||
import com.tangem.SessionEnvironment
|
||||
|
||||
interface CardValuesService {
|
||||
interface CardValuesStorage {
|
||||
|
||||
fun saveValues(environment: SessionEnvironment)
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ interface CardValuesService {
|
|||
|
||||
}
|
||||
|
||||
class CardValuesDbService(driver: SqlDriver) : CardValuesService {
|
||||
class CardValuesDbStorage(driver: SqlDriver) : CardValuesStorage {
|
||||
|
||||
private val cardValuesQueries: CardValuesEntityQueries
|
||||
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.commands
|
||||
package com.tangem.tasks
|
||||
|
||||
import com.tangem.CardSession
|
||||
import com.tangem.CardSessionRunnable
|
||||
import com.tangem.SessionEnvironment
|
||||
import com.tangem.SessionViewDelegate
|
||||
import com.tangem.commands.SetPinCommand
|
||||
import com.tangem.commands.SetPinResponse
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.PinCode
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
|
|
@ -17,15 +20,16 @@ enum class PinType {
|
|||
;
|
||||
}
|
||||
|
||||
class ChangePinCommand(
|
||||
class ChangePinTask(
|
||||
private val pinType: PinType,
|
||||
private val pin: ByteArray? = null
|
||||
) : CardSessionRunnable<SetPinResponse> {
|
||||
override val performPreflightRead = true
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<SetPinResponse>) -> Unit) {
|
||||
session.scope.launch {
|
||||
val pin = this@ChangePinCommand.pin
|
||||
val pin = this@ChangePinTask.pin
|
||||
?: requestNewPin(pinType, session.viewDelegate).calculateSha256()
|
||||
runSetPin(pin, session, callback)
|
||||
}
|
||||
|
|
@ -61,7 +65,12 @@ class ChangePinCommand(
|
|||
}
|
||||
}
|
||||
val command = SetPinCommand(pin1, pin2, pin3)
|
||||
command.run(session, callback)
|
||||
command.run(session) { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> savePin(pin, session.environment)
|
||||
is CompletionResult.Failure -> callback(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun requestPin(pinType: PinType, viewDelegate: SessionViewDelegate): String =
|
||||
|
|
@ -78,5 +87,13 @@ class ChangePinCommand(
|
|||
}
|
||||
}
|
||||
|
||||
private fun savePin(pin: ByteArray, environment: SessionEnvironment) {
|
||||
when (pinType) {
|
||||
PinType.Pin1 -> environment.pin1 = PinCode(pin, false)
|
||||
PinType.Pin2 -> environment.pin2 = PinCode(pin, false)
|
||||
PinType.Pin3 -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import com.tangem.common.CompletionResult
|
|||
class CreateWalletTask : CardSessionRunnable<CreateWalletResponse> {
|
||||
|
||||
override val performPreflightRead = true
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<CreateWalletResponse>) -> Unit) {
|
||||
val curve = session.environment.card?.curve
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.common.CompletionResult
|
|||
internal class ScanTask : CardSessionRunnable<Card> {
|
||||
|
||||
override val performPreflightRead = false
|
||||
override val requiresPin2 = false
|
||||
|
||||
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ internal class ScanTask : CardSessionRunnable<Card> {
|
|||
is CompletionResult.Success -> {
|
||||
val card = readResult.data
|
||||
session.environment.card = card
|
||||
session.environment.restoreCardValues()
|
||||
if (session.environment.pin1 != null && session.environment.pin2 != null) {
|
||||
val checkPinCommand = SetPinCommand(session.environment.pin1!!.value, session.environment.pin2!!.value)
|
||||
checkPinCommand.run(session) { result ->
|
||||
|
|
@ -39,7 +41,6 @@ internal class ScanTask : CardSessionRunnable<Card> {
|
|||
session.environment.pin2 = null
|
||||
}
|
||||
}
|
||||
session.environment.restoreCardValues()
|
||||
runCheckWalletIfNeeded(card, session, callback)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ package com.tangem.tangem_sdk_new
|
|||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.tangem.*
|
||||
import com.tangem.commands.PinType
|
||||
import com.tangem.tangem_sdk_new.nfc.NfcReader
|
||||
import com.tangem.tangem_sdk_new.ui.NfcSessionDialog
|
||||
import com.tangem.tasks.PinType
|
||||
|
||||
/**
|
||||
* Default implementation of [SessionViewDelegate].
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue