refactor: make EuiccChannel an interface instead of data class
This commit is contained in:
parent
058b01533d
commit
4840236e23
|
@ -2,15 +2,24 @@ package im.angry.openeuicc.core
|
||||||
|
|
||||||
import com.truphone.lpa.LocalProfileAssistant
|
import com.truphone.lpa.LocalProfileAssistant
|
||||||
|
|
||||||
interface EuiccChannelStateManager {
|
// A custom type to avoid compatibility issues with UiccCardInfo / UiccPortInfo
|
||||||
val valid: Boolean
|
data class EuiccChannelInfo(
|
||||||
fun destroy()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class EuiccChannel(
|
|
||||||
val slotId: Int,
|
val slotId: Int,
|
||||||
val cardId: Int,
|
val cardId: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val lpa: LocalProfileAssistant,
|
val removable: Boolean,
|
||||||
val stateManager: EuiccChannelStateManager
|
)
|
||||||
)
|
|
||||||
|
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()
|
||||||
|
}
|
|
@ -4,10 +4,7 @@ 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.impl.LocalProfileAssistantImpl
|
|
||||||
import im.angry.openeuicc.OpenEuiccApplication
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -48,46 +45,42 @@ class EuiccChannelManager(private val context: Context) {
|
||||||
ensureSEService()
|
ensureSEService()
|
||||||
val existing = channels.find { it.slotId == slotId }
|
val existing = channels.find { it.slotId == slotId }
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
if (existing.stateManager.valid) {
|
if (existing.valid) {
|
||||||
return existing
|
return existing
|
||||||
} else {
|
} else {
|
||||||
existing.stateManager.destroy()
|
existing.close()
|
||||||
channels.remove(existing)
|
channels.remove(existing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val (shouldTryTelephonyManager, cardId) =
|
val cardInfo = tm.uiccCardsInfo.find { it.slotIndex == slotId } ?: return null
|
||||||
tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let {
|
|
||||||
Pair(it.isEuicc && !it.isRemovable, it.cardId)
|
|
||||||
} ?: Pair(false, 0)
|
|
||||||
|
|
||||||
var apduChannel: ApduChannel? = null
|
val channelInfo = EuiccChannelInfo(
|
||||||
var stateManager: EuiccChannelStateManager? = null
|
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) {
|
if (shouldTryTelephonyManager) {
|
||||||
Log.d(TAG, "Using TelephonyManager for slot $slotId")
|
Log.d(TAG, "Using TelephonyManager for slot $slotId")
|
||||||
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
|
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
|
||||||
TelephonyManagerApduChannel.tryConnectUiccSlot(tm, slotId)?.let { (_apduChannel, _stateManager) ->
|
euiccChannel = TelephonyManagerChannel.tryConnect(tm, channelInfo)
|
||||||
apduChannel = _apduChannel
|
|
||||||
stateManager = _stateManager
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apduChannel == null || stateManager == null) {
|
if (euiccChannel == null) {
|
||||||
OmapiApduChannel.tryConnectUiccSlot(seService!!, slotId)
|
euiccChannel = OmapiChannel.tryConnect(seService!!, channelInfo)
|
||||||
?.let { (_apduChannel, _stateManager) ->
|
|
||||||
apduChannel = _apduChannel
|
|
||||||
stateManager = _stateManager
|
|
||||||
} ?: return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val channel = EuiccChannel(
|
if (euiccChannel != null) {
|
||||||
slotId, cardId,
|
channels.add(euiccChannel)
|
||||||
"SIM $slotId",
|
}
|
||||||
LocalProfileAssistantImpl(apduChannel),
|
|
||||||
stateManager!!)
|
return euiccChannel
|
||||||
channels.add(channel)
|
|
||||||
return channel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findEuiccChannelBySlotBlocking(slotId: Int): EuiccChannel? = runBlocking {
|
fun findEuiccChannelBySlotBlocking(slotId: Int): EuiccChannel? = runBlocking {
|
||||||
|
@ -113,7 +106,7 @@ class EuiccChannelManager(private val context: Context) {
|
||||||
|
|
||||||
fun invalidate() {
|
fun invalidate() {
|
||||||
for (channel in channels) {
|
for (channel in channels) {
|
||||||
channel.stateManager.destroy()
|
channel.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
channels.clear()
|
channels.clear()
|
||||||
|
|
|
@ -1,41 +1,12 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import android.se.omapi.Channel
|
import android.se.omapi.Channel
|
||||||
import android.se.omapi.SEService
|
|
||||||
import android.util.Log
|
|
||||||
import com.truphone.lpa.ApduChannel
|
import com.truphone.lpa.ApduChannel
|
||||||
import com.truphone.lpa.ApduTransmittedListener
|
import com.truphone.lpa.ApduTransmittedListener
|
||||||
import im.angry.openeuicc.util.byteArrayToHex
|
import im.angry.openeuicc.util.byteArrayToHex
|
||||||
import im.angry.openeuicc.util.hexStringToByteArray
|
import im.angry.openeuicc.util.hexStringToByteArray
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class OmapiApduChannel(private val channel: Channel) : ApduChannel {
|
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<ApduChannel, EuiccChannelStateManager>? {
|
|
||||||
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 =
|
override fun transmitAPDU(apdu: String): String =
|
||||||
byteArrayToHex(channel.transmit(hexStringToByteArray(apdu)))
|
byteArrayToHex(channel.transmit(hexStringToByteArray(apdu)))
|
||||||
|
|
||||||
|
|
39
app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt
Normal file
39
app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt
Normal file
|
@ -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()
|
||||||
|
}
|
|
@ -1,53 +1,15 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL
|
|
||||||
import android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR
|
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import android.util.Log
|
|
||||||
import com.truphone.lpa.ApduChannel
|
import com.truphone.lpa.ApduChannel
|
||||||
import com.truphone.lpa.ApduTransmittedListener
|
import com.truphone.lpa.ApduTransmittedListener
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import java.lang.Exception
|
|
||||||
|
|
||||||
class TelephonyManagerApduChannel(
|
class TelephonyManagerApduChannel(
|
||||||
private val tm: TelephonyManager,
|
private val tm: TelephonyManager,
|
||||||
private val slotId: Int,
|
private val slotId: Int,
|
||||||
private val channelId: Int) : ApduChannel {
|
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<ApduChannel, EuiccChannelStateManager>? {
|
|
||||||
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? {
|
override fun transmitAPDU(apdu: String): String? {
|
||||||
val cla = Integer.parseInt(apdu.substring(0, 2), 16)
|
val cla = Integer.parseInt(apdu.substring(0, 2), 16)
|
||||||
val instruction = Integer.parseInt(apdu.substring(2, 4), 16)
|
val instruction = Integer.parseInt(apdu.substring(2, 4), 16)
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue