From 4840236e238fcfc960ada79f463cf544390d6550 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 11 May 2022 18:46:33 -0400 Subject: [PATCH] refactor: make EuiccChannel an interface instead of data class --- .../im/angry/openeuicc/core/EuiccChannel.kt | 27 ++++++---- .../openeuicc/core/EuiccChannelManager.kt | 51 ++++++++----------- .../angry/openeuicc/core/OmapiApduChannel.kt | 29 ----------- .../im/angry/openeuicc/core/OmapiChannel.kt | 39 ++++++++++++++ .../core/TelephonyManagerApduChannel.kt | 38 -------------- .../openeuicc/core/TelephonyManagerChannel.kt | 49 ++++++++++++++++++ 6 files changed, 128 insertions(+), 105 deletions(-) create mode 100644 app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt create mode 100644 app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt diff --git a/app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt b/app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt index 7b9500f..a51dbde 100644 --- a/app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt +++ b/app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt @@ -2,15 +2,24 @@ package im.angry.openeuicc.core import com.truphone.lpa.LocalProfileAssistant -interface EuiccChannelStateManager { - val valid: Boolean - fun destroy() -} - -data class EuiccChannel( +// A custom type to avoid compatibility issues with UiccCardInfo / UiccPortInfo +data class EuiccChannelInfo( val slotId: Int, val cardId: Int, val name: String, - val lpa: LocalProfileAssistant, - val stateManager: EuiccChannelStateManager -) \ No newline at end of file + val removable: Boolean, +) + +abstract class EuiccChannel( + info: EuiccChannelInfo +) { + val slotId = info.slotId + val cardId = info.cardId + val name = info.name + val removable = info.removable + + abstract val lpa: LocalProfileAssistant + abstract val valid: Boolean + + abstract fun close() +} \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 80c55b3..1fcf974 100644 --- a/app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -4,10 +4,7 @@ import android.content.Context import android.os.Handler import android.os.HandlerThread import android.se.omapi.SEService -import android.telephony.TelephonyManager import android.util.Log -import com.truphone.lpa.ApduChannel -import com.truphone.lpa.impl.LocalProfileAssistantImpl import im.angry.openeuicc.OpenEuiccApplication import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -48,46 +45,42 @@ class EuiccChannelManager(private val context: Context) { ensureSEService() val existing = channels.find { it.slotId == slotId } if (existing != null) { - if (existing.stateManager.valid) { + if (existing.valid) { return existing } else { - existing.stateManager.destroy() + existing.close() channels.remove(existing) } } - val (shouldTryTelephonyManager, cardId) = - tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let { - Pair(it.isEuicc && !it.isRemovable, it.cardId) - } ?: Pair(false, 0) + val cardInfo = tm.uiccCardsInfo.find { it.slotIndex == slotId } ?: return null - var apduChannel: ApduChannel? = null - var stateManager: EuiccChannelStateManager? = null + val channelInfo = EuiccChannelInfo( + slotId, cardInfo.cardId, "SIM $slotId", cardInfo.isRemovable + ) + + val (shouldTryTelephonyManager, cardId) = + cardInfo.let { + Pair(it.isEuicc && !it.isRemovable, it.cardId) + } + + var euiccChannel: EuiccChannel? = null if (shouldTryTelephonyManager) { Log.d(TAG, "Using TelephonyManager for slot $slotId") // TODO: On Tiramisu, we should also connect all available "ports" for MEP support - TelephonyManagerApduChannel.tryConnectUiccSlot(tm, slotId)?.let { (_apduChannel, _stateManager) -> - apduChannel = _apduChannel - stateManager = _stateManager - } + euiccChannel = TelephonyManagerChannel.tryConnect(tm, channelInfo) } - if (apduChannel == null || stateManager == null) { - OmapiApduChannel.tryConnectUiccSlot(seService!!, slotId) - ?.let { (_apduChannel, _stateManager) -> - apduChannel = _apduChannel - stateManager = _stateManager - } ?: return null + if (euiccChannel == null) { + euiccChannel = OmapiChannel.tryConnect(seService!!, channelInfo) } - val channel = EuiccChannel( - slotId, cardId, - "SIM $slotId", - LocalProfileAssistantImpl(apduChannel), - stateManager!!) - channels.add(channel) - return channel + if (euiccChannel != null) { + channels.add(euiccChannel) + } + + return euiccChannel } fun findEuiccChannelBySlotBlocking(slotId: Int): EuiccChannel? = runBlocking { @@ -113,7 +106,7 @@ class EuiccChannelManager(private val context: Context) { fun invalidate() { for (channel in channels) { - channel.stateManager.destroy() + channel.close() } channels.clear() diff --git a/app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt b/app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt index d8b88d8..80ff292 100644 --- a/app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt +++ b/app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt @@ -1,41 +1,12 @@ package im.angry.openeuicc.core import android.se.omapi.Channel -import android.se.omapi.SEService -import android.util.Log import com.truphone.lpa.ApduChannel import com.truphone.lpa.ApduTransmittedListener import im.angry.openeuicc.util.byteArrayToHex import im.angry.openeuicc.util.hexStringToByteArray -import java.lang.Exception class OmapiApduChannel(private val channel: Channel) : ApduChannel { - companion object { - private const val TAG = "OmapiApduChannel" - private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0) - - fun tryConnectUiccSlot(service: SEService, slotId: Int): Pair? { - try { - val reader = service.getUiccReader(slotId + 1) // slotId from telephony starts from 0 - val session = reader.openSession() - val channel = session.openLogicalChannel(APPLET_ID) ?: return null - val stateManager = object : EuiccChannelStateManager { - override val valid: Boolean - get() = channel.isOpen - - override fun destroy() { - channel.close() - } - } - return Pair(OmapiApduChannel(channel), stateManager) - } catch (e: Exception) { - Log.e(TAG, "Unable to open eUICC channel for slot ${slotId}, skipping") - Log.e(TAG, Log.getStackTraceString(e)) - return null - } - } - } - override fun transmitAPDU(apdu: String): String = byteArrayToHex(channel.transmit(hexStringToByteArray(apdu))) diff --git a/app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt b/app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt new file mode 100644 index 0000000..281564c --- /dev/null +++ b/app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt @@ -0,0 +1,39 @@ +package im.angry.openeuicc.core + +import android.se.omapi.Channel +import android.se.omapi.SEService +import android.util.Log +import com.truphone.lpa.LocalProfileAssistant +import com.truphone.lpa.impl.LocalProfileAssistantImpl +import java.lang.Exception + +class OmapiChannel private constructor( + info: EuiccChannelInfo, + private val channel: Channel, +) : EuiccChannel(info) { + companion object { + private const val TAG = "OmapiChannel" + private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0) + + fun tryConnect(service: SEService, info: EuiccChannelInfo): OmapiChannel? { + try { + val reader = service.getUiccReader(info.slotId + 1) // slotId from telephony starts from 0 + val session = reader.openSession() + val channel = session.openLogicalChannel(APPLET_ID) ?: return null + return OmapiChannel(info, channel) + } catch (e: Exception) { + Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId}, skipping") + Log.e(TAG, Log.getStackTraceString(e)) + return null + } + } + } + + override val lpa: LocalProfileAssistant by lazy { + LocalProfileAssistantImpl(OmapiApduChannel(channel)) + } + override val valid: Boolean + get() = channel.isOpen // TODO: This has to be implemented properly + + override fun close() = channel.close() +} diff --git a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt index f7b315b..4ee2529 100644 --- a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt +++ b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt @@ -1,53 +1,15 @@ package im.angry.openeuicc.core -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 im.angry.openeuicc.util.* -import java.lang.Exception 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" - - // TODO: On Tiramisu, we need to specify the portId also if we want MEP support - fun tryConnectUiccSlot(tm: TelephonyManager, slotId: Int): Pair? { - try { - val channel = tm.iccOpenLogicalChannelBySlot(slotId, EUICC_APP_ID, 0) - if (channel.status != STATUS_NO_ERROR || channel.channel == INVALID_CHANNEL) { - Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager: ${channel.status}") - return null - } - - Log.d(TAG, "channel: ${channel.channel}") - - val stateManager = object : EuiccChannelStateManager { - override val valid: Boolean - get() = true // TODO: Fix this properly - - override fun destroy() { - tm.iccCloseLogicalChannelBySlot(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) diff --git a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt new file mode 100644 index 0000000..7be429e --- /dev/null +++ b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt @@ -0,0 +1,49 @@ +package im.angry.openeuicc.core + +import android.telephony.IccOpenLogicalChannelResponse +import android.telephony.TelephonyManager +import android.util.Log +import com.truphone.lpa.LocalProfileAssistant +import com.truphone.lpa.impl.LocalProfileAssistantImpl +import im.angry.openeuicc.util.* +import java.lang.Exception + +class TelephonyManagerChannel private constructor( + info: EuiccChannelInfo, + private val tm: TelephonyManager, + private val channelId: Int +) : EuiccChannel(info) { + companion object { + private const val TAG = "TelephonyManagerApduChannel" + private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100" + + // TODO: On Tiramisu, we need to specify the portId also if we want MEP support + fun tryConnect(tm: TelephonyManager, info: EuiccChannelInfo): TelephonyManagerChannel? { + try { + val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, EUICC_APP_ID, 0) + if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) { + Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager: ${channel.status}") + return null + } + + Log.d(TAG, "channel: ${channel.channel}") + + return TelephonyManagerChannel(info, tm, channel.channel) + } catch (e: Exception) { + Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager") + Log.e(TAG, Log.getStackTraceString(e)) + return null + } + } + } + + override val lpa: LocalProfileAssistant by lazy { + LocalProfileAssistantImpl(TelephonyManagerApduChannel(tm, slotId, channelId)) + } + override val valid: Boolean + get() = true // TODO: Fix this + + override fun close() { + tm.iccCloseLogicalChannelBySlot(slotId, channelId) + } +} \ No newline at end of file