Compare commits

...

2 commits

6 changed files with 166 additions and 18 deletions

View file

@ -5,9 +5,9 @@ import android.content.Context
import android.os.Handler
import android.os.HandlerThread
import android.se.omapi.SEService
import android.telephony.UiccCardInfo
import android.util.Log
import im.angry.openeuicc.OpenEuiccApplication
import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
@ -52,27 +52,27 @@ open class EuiccChannelManager(protected val context: Context) {
}
}
protected open fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
protected open fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
// No-op when unprivileged
return null
}
protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.slotIndex}")
protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.physicalSlotIndex}")
try {
return OmapiChannel(seService!!, channelInfo)
} catch (e: IllegalArgumentException) {
// Failed
Log.w(TAG, "OMAPI APDU interface unavailable for slot ${uiccInfo.slotIndex}.")
Log.w(TAG, "OMAPI APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}.")
}
return null
}
private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfo): EuiccChannel? {
private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfoCompat): EuiccChannel? {
lock.withLock {
ensureSEService()
val existing = channels.find { it.slotId == uiccInfo.slotIndex }
val existing = channels.find { it.slotId == uiccInfo.physicalSlotIndex }
if (existing != null) {
if (existing.valid) {
return existing
@ -83,10 +83,10 @@ open class EuiccChannelManager(protected val context: Context) {
}
val channelInfo = EuiccChannelInfo(
uiccInfo.slotIndex,
uiccInfo.physicalSlotIndex,
uiccInfo.cardId,
"SIM ${uiccInfo.slotIndex}",
tm.getImei(uiccInfo.slotIndex) ?: return null,
"SIM ${uiccInfo.physicalSlotIndex}",
tm.getImei(uiccInfo.physicalSlotIndex) ?: return null,
uiccInfo.isRemovable
)
@ -105,7 +105,7 @@ open class EuiccChannelManager(protected val context: Context) {
}
private suspend fun findEuiccChannelBySlot(slotId: Int): EuiccChannel? {
return tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let {
return tm.uiccCardsInfoCompat.find { it.physicalSlotIndex == slotId }?.let {
tryOpenEuiccChannel(it)
}
}
@ -123,9 +123,9 @@ open class EuiccChannelManager(protected val context: Context) {
withContext(Dispatchers.IO) {
ensureSEService()
for (uiccInfo in tm.uiccCardsInfo) {
for (uiccInfo in tm.uiccCardsInfoCompat) {
if (tryOpenEuiccChannel(uiccInfo) != null) {
Log.d(TAG, "Found eUICC on slot ${uiccInfo.slotIndex}")
Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex}")
}
}
}

View file

@ -0,0 +1,75 @@
package im.angry.openeuicc.util
import android.annotation.SuppressLint
import android.os.Build
import android.telephony.TelephonyManager
import android.telephony.UiccCardInfo
import android.telephony.UiccPortInfo
import im.angry.openeuicc.util.*
import java.lang.RuntimeException
@Suppress("DEPRECATION")
class UiccCardInfoCompat(val inner: UiccCardInfo) {
val physicalSlotIndex: Int
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
inner.physicalSlotIndex
} else {
inner.slotIndex
}
val ports: Collection<UiccPortInfoCompat>
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
inner.ports.map { UiccPortInfoCompat(it, this) }
} else {
listOf(UiccPortInfoCompat(null, this))
}
val isEuicc: Boolean
get() = inner.isEuicc
val isRemovable: Boolean
get() = inner.isRemovable
val isMultipleEnabledProfilesSupported: Boolean
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
inner.isMultipleEnabledProfilesSupported
} else {
false
}
val cardId: Int
get() = inner.cardId
}
class UiccPortInfoCompat(private val _inner: Any?, val card: UiccCardInfoCompat) {
init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
check(_inner != null && _inner is UiccPortInfo) {
"_inner is not UiccPortInfo on TIRAMISU"
}
}
}
val inner: UiccPortInfo
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
_inner as UiccPortInfo
} else {
throw RuntimeException("UiccPortInfo does not exist before T")
}
val portIndex: Int
get() =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
inner.portIndex
} else {
0
}
}
val TelephonyManager.uiccCardsInfoCompat: List<UiccCardInfoCompat>
@SuppressLint("MissingPermission")
get() = uiccCardsInfo.map { UiccCardInfoCompat(it) }

View file

@ -1,7 +1,6 @@
package im.angry.openeuicc.core
import android.content.Context
import android.telephony.UiccCardInfo
import android.util.Log
import im.angry.openeuicc.OpenEuiccApplication
import im.angry.openeuicc.util.*
@ -11,7 +10,7 @@ import java.lang.IllegalArgumentException
class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) {
override fun checkPrivileges() = true // TODO: Implement proper system app check
override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
override fun tryOpenEuiccChannelPrivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
if (uiccInfo.isRemovable) {
// Attempt unprivileged (OMAPI) before TelephonyManager
// but still try TelephonyManager in case OMAPI is broken
@ -19,13 +18,13 @@ class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(conte
}
if (uiccInfo.isEuicc) {
Log.i(TAG, "Trying TelephonyManager for slot ${uiccInfo.slotIndex}")
Log.i(TAG, "Trying TelephonyManager for slot ${uiccInfo.physicalSlotIndex}")
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
try {
return TelephonyManagerChannel(channelInfo, tm)
} catch (e: IllegalArgumentException) {
// Failed
Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${uiccInfo.slotIndex}, falling back")
Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${uiccInfo.physicalSlotIndex}, falling back")
}
}
return null

View file

@ -0,0 +1,39 @@
package im.angry.openeuicc.util
import android.os.Build
import android.telephony.IccOpenLogicalChannelResponse
import android.telephony.TelephonyManager
// TODO: Usage of *byPort APIs will still break build in-tree on lower AOSP versions
// Maybe older versions should simply include hidden-apis-shim when building?
fun TelephonyManager.iccOpenLogicalChannelByPortCompat(
slotIndex: Int, portIndex: Int, aid: String?, p2: Int
): IccOpenLogicalChannelResponse =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
iccOpenLogicalChannelByPort(slotIndex, portIndex, aid, p2)
} else {
iccOpenLogicalChannelBySlot(slotIndex, aid, p2)
}
fun TelephonyManager.iccCloseLogicalChannelByPortCompat(
slotIndex: Int, portIndex: Int, channel: Int
) =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
iccCloseLogicalChannelByPort(slotIndex, portIndex, channel)
} else {
iccCloseLogicalChannelBySlot(slotIndex, channel)
}
fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat(
slotIndex: Int, portIndex: Int, channel: Int,
cla: Int, inst: Int, p1: Int, p2: Int, p3: Int, data: String?
): String? =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
iccTransmitApduLogicalChannelByPort(
slotIndex, portIndex, channel, cla, inst, p1, p2, p3, data
)
} else {
iccTransmitApduLogicalChannelBySlot(
slotIndex, channel, cla, inst, p1, p2, p3, data
)
}

View file

@ -14,12 +14,24 @@ private val iccOpenLogicalChannelBySlot: Method by lazy {
Int::class.java, String::class.java, Int::class.java
)
}
private val iccOpenLogicalChannelByPort: Method by lazy {
TelephonyManager::class.java.getMethod(
"iccOpenLogicalChannelByPort",
Int::class.java, Int::class.java, String::class.java, Int::class.java
)
}
private val iccCloseLogicalChannelBySlot: Method by lazy {
TelephonyManager::class.java.getMethod(
"iccCloseLogicalChannelBySlot",
Int::class.java, Int::class.java
)
}
private val iccCloseLogicalChannelByPort: Method by lazy {
TelephonyManager::class.java.getMethod(
"iccCloseLogicalChannelByPort",
Int::class.java, Int::class.java, Int::class.java
)
}
private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
TelephonyManager::class.java.getMethod(
"iccTransmitApduLogicalChannelBySlot",
@ -27,15 +39,30 @@ private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
Int::class.java, Int::class.java, Int::class.java, String::class.java
)
}
private val iccTransmitApduLogicalChannelByPort: Method by lazy {
TelephonyManager::class.java.getMethod(
"iccTransmitApduLogicalChannelByPort",
Int::class.java, Int::class.java, Int::class.java, Int::class.java, Int::class.java,
Int::class.java, Int::class.java, Int::class.java, String::class.java
)
}
fun TelephonyManager.iccOpenLogicalChannelBySlot(
slotId: Int, appletId: String, p2: Int
slotId: Int, appletId: String?, p2: Int
): IccOpenLogicalChannelResponse =
iccOpenLogicalChannelBySlot.invoke(this, slotId, appletId, p2) as IccOpenLogicalChannelResponse
fun TelephonyManager.iccOpenLogicalChannelByPort(
slotId: Int, portId: Int, appletId: String?, p2: Int
): IccOpenLogicalChannelResponse =
iccOpenLogicalChannelByPort.invoke(this, slotId, portId, appletId, p2) as IccOpenLogicalChannelResponse
fun TelephonyManager.iccCloseLogicalChannelBySlot(slotId: Int, channel: Int): Boolean =
iccCloseLogicalChannelBySlot.invoke(this, slotId, channel) as Boolean
fun TelephonyManager.iccCloseLogicalChannelByPort(slotId: Int, portId: Int, channel: Int): Boolean =
iccCloseLogicalChannelByPort.invoke(this, slotId, portId, channel) as Boolean
fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
slotId: Int, channel: Int, cla: Int, instruction: Int,
p1: Int, p2: Int, p3: Int, data: String?
@ -44,6 +71,14 @@ fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
this, slotId, channel, cla, instruction, p1, p2, p3, data
) as String?
fun TelephonyManager.iccTransmitApduLogicalChannelByPort(
slotId: Int, portId: Int, channel: Int, cla: Int, instruction: Int,
p1: Int, p2: Int, p3: Int, data: String?
): String? =
iccTransmitApduLogicalChannelByPort.invoke(
this, slotId, portId, channel, cla, instruction, p1, p2, p3, data
) as String?
private val requestEmbeddedSubscriptionInfoListRefresh: Method by lazy {
SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java)
}