Compare commits
2 commits
5f88bf089c
...
2ccfe02204
Author | SHA1 | Date | |
---|---|---|---|
2ccfe02204 | |||
19aeefaba9 |
6 changed files with 166 additions and 18 deletions
|
@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue