Peter Cai
e48f9aa828
All checks were successful
/ build-debug (push) Successful in 4m51s
* ApduInterfaces also need a concept of validity based on the underlying APDU channel. For example, OMAPI depends on SEService being still connected. * We then rely on this validity to wait for reconnection; we do not need to manually remove all channels under a slot because the rest will be invalid anyway, and the next attempt at connection will lazily recreate the channel. * We had to manage channels manually before during reconnect because `valid` may result in SIGSEGV's when the underlying APDU channel has become invalid. This is avoided by the validity concept added to APDU channels.
59 lines
2.2 KiB
Kotlin
59 lines
2.2 KiB
Kotlin
package im.angry.openeuicc.core
|
|
|
|
import android.telephony.IccOpenLogicalChannelResponse
|
|
import android.telephony.TelephonyManager
|
|
import im.angry.openeuicc.util.*
|
|
import net.typeblog.lpac_jni.ApduInterface
|
|
|
|
class TelephonyManagerApduInterface(
|
|
private val port: UiccPortInfoCompat,
|
|
private val tm: TelephonyManager
|
|
): ApduInterface {
|
|
private var lastChannel: Int = -1
|
|
|
|
override val valid: Boolean
|
|
// TelephonyManager channels will never become truly "invalid",
|
|
// just that transactions might return errors or nonsense
|
|
get() = lastChannel != -1
|
|
|
|
override fun connect() {
|
|
// Do nothing
|
|
}
|
|
|
|
override fun disconnect() {
|
|
// Do nothing
|
|
lastChannel = -1
|
|
}
|
|
|
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
|
check(lastChannel == -1) { "Already initialized" }
|
|
val hex = aid.encodeHex()
|
|
val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0)
|
|
if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
|
|
throw IllegalArgumentException("Cannot open logical channel $hex via TelephonManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}");
|
|
}
|
|
lastChannel = channel.channel
|
|
return lastChannel
|
|
}
|
|
|
|
override fun logicalChannelClose(handle: Int) {
|
|
check(handle == lastChannel) { "Invalid channel handle " }
|
|
tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle)
|
|
lastChannel = -1
|
|
}
|
|
|
|
override fun transmit(tx: ByteArray): ByteArray {
|
|
check(lastChannel != -1) { "Uninitialized" }
|
|
|
|
val cla = tx[0].toUByte().toInt()
|
|
val instruction = tx[1].toUByte().toInt()
|
|
val p1 = tx[2].toUByte().toInt()
|
|
val p2 = tx[3].toUByte().toInt()
|
|
val p3 = tx[4].toUByte().toInt()
|
|
val p4 = tx.drop(5).toByteArray().encodeHex()
|
|
|
|
return tm.iccTransmitApduLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, lastChannel,
|
|
cla, instruction, p1, p2, p3, p4)?.decodeHex() ?: byteArrayOf()
|
|
}
|
|
|
|
} |