Compare commits

...

1 commit

Author SHA1 Message Date
baf717ccfc
feat: icc get atr 2025-03-23 15:39:53 +08:00
2 changed files with 38 additions and 17 deletions

View file

@ -1,6 +1,7 @@
package im.angry.openeuicc.core package im.angry.openeuicc.core
import android.telephony.IccOpenLogicalChannelResponse import android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR
import android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL
import android.telephony.TelephonyManager import android.telephony.TelephonyManager
import android.util.Log import android.util.Log
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
@ -13,7 +14,7 @@ class TelephonyManagerApduInterface(
private val port: UiccPortInfoCompat, private val port: UiccPortInfoCompat,
private val tm: TelephonyManager, private val tm: TelephonyManager,
private val verboseLoggingFlow: Flow<Boolean> private val verboseLoggingFlow: Flow<Boolean>
): ApduInterface { ) : ApduInterface, ApduInterfaceAtrProvider {
companion object { companion object {
const val TAG = "TelephonyManagerApduInterface" const val TAG = "TelephonyManagerApduInterface"
} }
@ -31,11 +32,17 @@ class TelephonyManagerApduInterface(
// Do nothing // Do nothing
} }
private val slotIndex: Int
get() = port.card.physicalSlotIndex
private val portIndex: Int
get() = port.portIndex
override fun logicalChannelOpen(aid: ByteArray): Int { override fun logicalChannelOpen(aid: ByteArray): Int {
val hex = aid.encodeHex() val hex = aid.encodeHex()
val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0) val channel = tm.iccOpenLogicalChannelByPortCompat(slotIndex, portIndex, hex, 0)
if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) { require(channel.status == STATUS_NO_ERROR && channel.channel != INVALID_CHANNEL) {
throw IllegalArgumentException("Cannot open logical channel $hex via TelephonyManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}") "Cannot open logical channel $hex via TelephonyManager on slot $slotIndex port $portIndex"
} }
channels.add(channel.channel) channels.add(channel.channel)
return channel.channel return channel.channel
@ -45,7 +52,7 @@ class TelephonyManagerApduInterface(
check(channels.contains(handle)) { check(channels.contains(handle)) {
"Invalid logical channel handle $handle" "Invalid logical channel handle $handle"
} }
tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle) tm.iccCloseLogicalChannelByPortCompat(slotIndex, portIndex, handle)
channels.remove(handle) channels.remove(handle)
} }
@ -53,15 +60,17 @@ class TelephonyManagerApduInterface(
check(channels.contains(handle)) { check(channels.contains(handle)) {
"Invalid logical channel handle $handle" "Invalid logical channel handle $handle"
} }
if (runBlocking { verboseLoggingFlow.first() }) { val verbose = runBlocking { verboseLoggingFlow.first() }
Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}") if (verbose) Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}")
} val result = tm.iccTransmitApduLogicalChannelByPortCompat(slotIndex, portIndex, handle, tx)
val result = tm.iccTransmitApduLogicalChannelByPortCompat( if (verbose) Log.d(TAG, "TelephonyManager APDU response: $result")
port.card.physicalSlotIndex, port.portIndex, handle,
tx,
)
if (runBlocking { verboseLoggingFlow.first() })
Log.d(TAG, "TelephonyManager APDU response: $result")
return result?.decodeHex() ?: byteArrayOf() return result?.decodeHex() ?: byteArrayOf()
} }
override val atr: ByteArray?
get() = try {
tm.iccGetATR(port.card.physicalSlotIndex)
} catch (e: Exception) {
null
}
} }

View file

@ -58,6 +58,17 @@ private val setSimSlotMapping: Method by lazy {
Collection::class.java Collection::class.java
) )
} }
private val getITelephony: Method by lazy {
TelephonyManager::class.java.getMethod("getITelephony").apply {
isAccessible = true
}
}
fun TelephonyManager.iccGetATR(slotId: Int): ByteArray? {
val telephony = getITelephony.invoke(this)
val getAtrUsingSlotId = telephony.javaClass.getMethod("getAtrUsingSlotId", Int::class.java)
return getAtrUsingSlotId.invoke(telephony, slotId) as ByteArray?
}
fun TelephonyManager.iccOpenLogicalChannelBySlot( fun TelephonyManager.iccOpenLogicalChannelBySlot(
slotId: Int, appletId: String?, p2: Int slotId: Int, appletId: String?, p2: Int
@ -67,7 +78,8 @@ fun TelephonyManager.iccOpenLogicalChannelBySlot(
fun TelephonyManager.iccOpenLogicalChannelByPort( fun TelephonyManager.iccOpenLogicalChannelByPort(
slotId: Int, portId: Int, appletId: String?, p2: Int slotId: Int, portId: Int, appletId: String?, p2: Int
): IccOpenLogicalChannelResponse = ): IccOpenLogicalChannelResponse =
iccOpenLogicalChannelByPort.invoke(this, slotId, portId, appletId, p2) as IccOpenLogicalChannelResponse iccOpenLogicalChannelByPort.invoke(this, slotId, portId, appletId, p2)
as IccOpenLogicalChannelResponse
fun TelephonyManager.iccCloseLogicalChannelBySlot(slotId: Int, channel: Int) { fun TelephonyManager.iccCloseLogicalChannelBySlot(slotId: Int, channel: Int) {
iccCloseLogicalChannelBySlot.invoke(this, slotId, channel) iccCloseLogicalChannelBySlot.invoke(this, slotId, channel)
@ -101,6 +113,6 @@ private val requestEmbeddedSubscriptionInfoListRefresh: Method by lazy {
SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java) SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java)
} }
fun SubscriptionManager.requestEmbeddedSubscriptionInfoListRefresh(cardId: Int): Unit { fun SubscriptionManager.requestEmbeddedSubscriptionInfoListRefresh(cardId: Int) {
requestEmbeddedSubscriptionInfoListRefresh.invoke(this, cardId) requestEmbeddedSubscriptionInfoListRefresh.invoke(this, cardId)
} }