Implement TelephonyManagerApduChannel

This does not work yet because only the LPA which implements
EuiccService can access the channel. Let's hope however everything we
did here *should* work.
This commit is contained in:
Peter Cai 2022-05-02 22:16:18 -04:00
parent e8eeb9e53d
commit 5894dc9a71
2 changed files with 118 additions and 4 deletions

View file

@ -4,9 +4,11 @@ import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.HandlerThread import android.os.HandlerThread
import android.se.omapi.SEService import android.se.omapi.SEService
import android.telephony.TelephonyManager
import android.util.Log import android.util.Log
import com.truphone.lpa.ApduChannel import com.truphone.lpa.ApduChannel
import com.truphone.lpa.impl.LocalProfileAssistantImpl import com.truphone.lpa.impl.LocalProfileAssistantImpl
import im.angry.openeuicc.OpenEuiccApplication
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -23,6 +25,10 @@ class EuiccChannelManager(private val context: Context) {
private var seService: SEService? = null private var seService: SEService? = null
private val tm by lazy {
(context.applicationContext as OpenEuiccApplication).telephonyManager
}
private val handler = Handler(HandlerThread("EuiccChannelManager").also { it.start() }.looper) private val handler = Handler(HandlerThread("EuiccChannelManager").also { it.start() }.looper)
private suspend fun connectSEService(): SEService = suspendCoroutine { cont -> private suspend fun connectSEService(): SEService = suspendCoroutine { cont ->
@ -50,12 +56,30 @@ class EuiccChannelManager(private val context: Context) {
} }
} }
val shouldTryTelephonyManager =
tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let {
it.isEuicc && !it.isRemovable
} ?: false
var apduChannel: ApduChannel? = null var apduChannel: ApduChannel? = null
var stateManager: EuiccChannelStateManager? = null var stateManager: EuiccChannelStateManager? = null
OmapiApduChannel.tryConnectUiccSlot(seService!!, slotId)?.let { (_apduChannel, _stateManager) ->
apduChannel = _apduChannel if (shouldTryTelephonyManager) {
stateManager = _stateManager Log.d(TAG, "Using TelephonyManager for slot $slotId")
} ?: return null // TODO: On Tiramisu, we should also connect all available "ports" for MEP support
TelephonyManagerApduChannel.tryConnectUiccSlot(tm, slotId)?.let { (_apduChannel, _stateManager) ->
apduChannel = _apduChannel
stateManager = _stateManager
}
}
if (apduChannel == null || stateManager == null) {
OmapiApduChannel.tryConnectUiccSlot(seService!!, slotId)
?.let { (_apduChannel, _stateManager) ->
apduChannel = _apduChannel
stateManager = _stateManager
} ?: return null
}
val channel = EuiccChannel(slotId, "SIM $slotId", LocalProfileAssistantImpl(apduChannel), stateManager!!) val channel = EuiccChannel(slotId, "SIM $slotId", LocalProfileAssistantImpl(apduChannel), stateManager!!)
channels.add(channel) channels.add(channel)

View file

@ -0,0 +1,90 @@
package im.angry.openeuicc.core
import android.telephony.IccOpenLogicalChannelResponse
import android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL
import android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR
import android.telephony.TelephonyManager
import android.util.Log
import com.truphone.lpa.ApduChannel
import com.truphone.lpa.ApduTransmittedListener
import java.lang.Exception
import java.lang.reflect.Method
class TelephonyManagerApduChannel(
private val tm: TelephonyManager,
private val slotId: Int,
private val channelId: Int) : ApduChannel {
companion object {
private const val TAG = "TelephonyManagerApduChannel"
private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
private val iccOpenLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccOpenLogicalChannelBySlot",
Int::class.java, String::class.java, Int::class.java)
private val iccCloseLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccCloseLogicalChannelBySlot",
Int::class.java, Int::class.java)
private val iccTransmitApduLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccTransmitApduLogicalChannelBySlot",
Int::class.java, Int::class.java, Int::class.java, Int::class.java,
Int::class.java, Int::class.java, Int::class.java, String::class.java)
// TODO: On Tiramisu, we need to specify the portId also if we want MEP support
fun tryConnectUiccSlot(tm: TelephonyManager, slotId: Int): Pair<ApduChannel, EuiccChannelStateManager>? {
try {
val channel = iccOpenLogicalChannelBySlot.invoke(tm, slotId, EUICC_APP_ID, 0) as IccOpenLogicalChannelResponse
if (channel.status != STATUS_NO_ERROR || channel.channel == INVALID_CHANNEL) {
Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager")
return null
}
val stateManager = object : EuiccChannelStateManager {
override val valid: Boolean
get() = true // TODO: Fix this properly
override fun destroy() {
iccCloseLogicalChannelBySlot.invoke(tm, slotId, channel.channel)
}
}
return Pair(TelephonyManagerApduChannel(tm, slotId, channel.channel), stateManager)
} catch (e: Exception) {
Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager")
Log.e(TAG, Log.getStackTraceString(e))
return null
}
}
}
override fun transmitAPDU(apdu: String): String {
val cla = Integer.parseInt(apdu.substring(0, 2), 16)
val instruction = Integer.parseInt(apdu.substring(2, 4), 16)
val p1 = Integer.parseInt(apdu.substring(4, 6), 16)
val p2 = Integer.parseInt(apdu.substring(6, 8), 16)
val p3 = Integer.parseInt(apdu.substring(8, 10), 16)
val p4 = apdu.substring(10)
return iccTransmitApduLogicalChannelBySlot.invoke(
tm, slotId, channelId,
cla, instruction, p1, p2, p3, p4) as String
}
override fun transmitAPDUS(apdus: MutableList<String>): String {
var res = ""
for (pdu in apdus) {
res = transmitAPDU(pdu)
}
return res
}
override fun sendStatus() {
}
override fun setApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
}
override fun removeApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
}
}