diff --git a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt index f0b1909..f06e317 100644 --- a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt +++ b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt @@ -1,6 +1,7 @@ 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.util.Log import im.angry.openeuicc.util.* @@ -13,7 +14,7 @@ class TelephonyManagerApduInterface( private val port: UiccPortInfoCompat, private val tm: TelephonyManager, private val verboseLoggingFlow: Flow -): ApduInterface { +) : ApduInterface, ApduInterfaceAtrProvider { companion object { const val TAG = "TelephonyManagerApduInterface" } @@ -31,11 +32,17 @@ class TelephonyManagerApduInterface( // Do nothing } + private val slotIndex: Int + get() = port.card.physicalSlotIndex + + private val portIndex: Int + get() = port.portIndex + override fun logicalChannelOpen(aid: ByteArray): Int { 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 TelephonyManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}") + val channel = tm.iccOpenLogicalChannelByPortCompat(slotIndex, portIndex, hex, 0) + require(channel.status == STATUS_NO_ERROR && channel.channel != INVALID_CHANNEL) { + "Cannot open logical channel $hex via TelephonyManager on slot $slotIndex port $portIndex" } channels.add(channel.channel) return channel.channel @@ -45,7 +52,7 @@ class TelephonyManagerApduInterface( check(channels.contains(handle)) { "Invalid logical channel handle $handle" } - tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle) + tm.iccCloseLogicalChannelByPortCompat(slotIndex, portIndex, handle) channels.remove(handle) } @@ -53,15 +60,21 @@ class TelephonyManagerApduInterface( check(channels.contains(handle)) { "Invalid logical channel handle $handle" } - if (runBlocking { verboseLoggingFlow.first() }) { - Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}") - } - val result = tm.iccTransmitApduLogicalChannelByPortCompat( - port.card.physicalSlotIndex, port.portIndex, handle, - tx, - ) - if (runBlocking { verboseLoggingFlow.first() }) - Log.d(TAG, "TelephonyManager APDU response: $result") + val verbose = runBlocking { verboseLoggingFlow.first() } + if (verbose) Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}") + val result = tm.iccTransmitApduLogicalChannelByPortCompat(slotIndex, portIndex, handle, tx) + if (verbose) Log.d(TAG, "TelephonyManager APDU response: $result") return result?.decodeHex() ?: byteArrayOf() } + + override val atr: ByteArray? + get() = try { + tm.iccGetAtr(slotIndex)?.decodeHex() + } catch (e: NoSuchMethodException) { + try { + tm.getAtrUsingSlotId(slotIndex) + } catch (e: Exception) { + null + } + } } \ No newline at end of file diff --git a/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt b/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt index 0d42354..0d5d463 100644 --- a/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt +++ b/libs/hidden-apis-shim/src/main/java/im/angry/openeuicc/util/TelephonyManagerHiddenApi.kt @@ -58,6 +58,23 @@ private val setSimSlotMapping: Method by lazy { Collection::class.java ) } +private val getITelephony: Method by lazy { + TelephonyManager::class.java.getMethod("getITelephony").apply { + isAccessible = true + } +} + +fun TelephonyManager.getAtrUsingSlotId(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.iccGetAtr(slotId: Int): String? { + val telephony = getITelephony.invoke(this) + val iccGetAtr = telephony.javaClass.getMethod("iccGetAtr", Int::class.java) + return iccGetAtr.invoke(telephony, slotId) as String? +} fun TelephonyManager.iccOpenLogicalChannelBySlot( slotId: Int, appletId: String?, p2: Int @@ -67,7 +84,8 @@ fun TelephonyManager.iccOpenLogicalChannelBySlot( fun TelephonyManager.iccOpenLogicalChannelByPort( slotId: Int, portId: Int, appletId: String?, p2: Int ): 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) { iccCloseLogicalChannelBySlot.invoke(this, slotId, channel) @@ -95,12 +113,17 @@ fun TelephonyManager.iccTransmitApduLogicalChannelByPort( var TelephonyManager.simSlotMapping: Collection get() = getSimSlotMapping.invoke(this) as Collection - set(new) { setSimSlotMapping.invoke(this, new) } + set(new) { + setSimSlotMapping.invoke(this, new) + } 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) } \ No newline at end of file