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.Handler
|
||||||
import android.os.HandlerThread
|
import android.os.HandlerThread
|
||||||
import android.se.omapi.SEService
|
import android.se.omapi.SEService
|
||||||
import android.telephony.UiccCardInfo
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import im.angry.openeuicc.OpenEuiccApplication
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.sync.Mutex
|
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
|
// No-op when unprivileged
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfo, channelInfo: EuiccChannelInfo): EuiccChannel? {
|
protected fun tryOpenEuiccChannelUnprivileged(uiccInfo: UiccCardInfoCompat, channelInfo: EuiccChannelInfo): EuiccChannel? {
|
||||||
Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.slotIndex}")
|
Log.i(TAG, "Trying OMAPI for slot ${uiccInfo.physicalSlotIndex}")
|
||||||
try {
|
try {
|
||||||
return OmapiChannel(seService!!, channelInfo)
|
return OmapiChannel(seService!!, channelInfo)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// Failed
|
// 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
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfo): EuiccChannel? {
|
private suspend fun tryOpenEuiccChannel(uiccInfo: UiccCardInfoCompat): EuiccChannel? {
|
||||||
lock.withLock {
|
lock.withLock {
|
||||||
ensureSEService()
|
ensureSEService()
|
||||||
val existing = channels.find { it.slotId == uiccInfo.slotIndex }
|
val existing = channels.find { it.slotId == uiccInfo.physicalSlotIndex }
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
if (existing.valid) {
|
if (existing.valid) {
|
||||||
return existing
|
return existing
|
||||||
|
@ -83,10 +83,10 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val channelInfo = EuiccChannelInfo(
|
val channelInfo = EuiccChannelInfo(
|
||||||
uiccInfo.slotIndex,
|
uiccInfo.physicalSlotIndex,
|
||||||
uiccInfo.cardId,
|
uiccInfo.cardId,
|
||||||
"SIM ${uiccInfo.slotIndex}",
|
"SIM ${uiccInfo.physicalSlotIndex}",
|
||||||
tm.getImei(uiccInfo.slotIndex) ?: return null,
|
tm.getImei(uiccInfo.physicalSlotIndex) ?: return null,
|
||||||
uiccInfo.isRemovable
|
uiccInfo.isRemovable
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun findEuiccChannelBySlot(slotId: Int): EuiccChannel? {
|
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)
|
tryOpenEuiccChannel(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,9 +123,9 @@ open class EuiccChannelManager(protected val context: Context) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
ensureSEService()
|
ensureSEService()
|
||||||
|
|
||||||
for (uiccInfo in tm.uiccCardsInfo) {
|
for (uiccInfo in tm.uiccCardsInfoCompat) {
|
||||||
if (tryOpenEuiccChannel(uiccInfo) != null) {
|
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
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.telephony.UiccCardInfo
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import im.angry.openeuicc.OpenEuiccApplication
|
import im.angry.openeuicc.OpenEuiccApplication
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
|
@ -11,7 +10,7 @@ import java.lang.IllegalArgumentException
|
||||||
class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) {
|
class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) {
|
||||||
override fun checkPrivileges() = true // TODO: Implement proper system app check
|
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) {
|
if (uiccInfo.isRemovable) {
|
||||||
// Attempt unprivileged (OMAPI) before TelephonyManager
|
// Attempt unprivileged (OMAPI) before TelephonyManager
|
||||||
// but still try TelephonyManager in case OMAPI is broken
|
// but still try TelephonyManager in case OMAPI is broken
|
||||||
|
@ -19,13 +18,13 @@ class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(conte
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiccInfo.isEuicc) {
|
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
|
// TODO: On Tiramisu, we should also connect all available "ports" for MEP support
|
||||||
try {
|
try {
|
||||||
return TelephonyManagerChannel(channelInfo, tm)
|
return TelephonyManagerChannel(channelInfo, tm)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
// Failed
|
// 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
|
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
|
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 {
|
private val iccCloseLogicalChannelBySlot: Method by lazy {
|
||||||
TelephonyManager::class.java.getMethod(
|
TelephonyManager::class.java.getMethod(
|
||||||
"iccCloseLogicalChannelBySlot",
|
"iccCloseLogicalChannelBySlot",
|
||||||
Int::class.java, Int::class.java
|
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 {
|
private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
|
||||||
TelephonyManager::class.java.getMethod(
|
TelephonyManager::class.java.getMethod(
|
||||||
"iccTransmitApduLogicalChannelBySlot",
|
"iccTransmitApduLogicalChannelBySlot",
|
||||||
|
@ -27,15 +39,30 @@ private val iccTransmitApduLogicalChannelBySlot: Method by lazy {
|
||||||
Int::class.java, Int::class.java, Int::class.java, String::class.java
|
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(
|
fun TelephonyManager.iccOpenLogicalChannelBySlot(
|
||||||
slotId: Int, appletId: String, p2: Int
|
slotId: Int, appletId: String?, p2: Int
|
||||||
): IccOpenLogicalChannelResponse =
|
): IccOpenLogicalChannelResponse =
|
||||||
iccOpenLogicalChannelBySlot.invoke(this, slotId, appletId, p2) as 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 =
|
fun TelephonyManager.iccCloseLogicalChannelBySlot(slotId: Int, channel: Int): Boolean =
|
||||||
iccCloseLogicalChannelBySlot.invoke(this, slotId, channel) as 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(
|
fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
|
||||||
slotId: Int, channel: Int, cla: Int, instruction: Int,
|
slotId: Int, channel: Int, cla: Int, instruction: Int,
|
||||||
p1: Int, p2: Int, p3: Int, data: String?
|
p1: Int, p2: Int, p3: Int, data: String?
|
||||||
|
@ -44,6 +71,14 @@ fun TelephonyManager.iccTransmitApduLogicalChannelBySlot(
|
||||||
this, slotId, channel, cla, instruction, p1, p2, p3, data
|
this, slotId, channel, cla, instruction, p1, p2, p3, data
|
||||||
) as String?
|
) 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 {
|
private val requestEmbeddedSubscriptionInfoListRefresh: Method by lazy {
|
||||||
SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java)
|
SubscriptionManager::class.java.getMethod("requestEmbeddedSubscriptionInfoListRefresh", Int::class.java)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue