refactor: IEuiccChannelManager -> EuiccChannelManager
All checks were successful
/ build-debug (push) Successful in 3m27s
All checks were successful
/ build-debug (push) Successful in 3m27s
This commit is contained in:
parent
4dd14d23f2
commit
7c6b4ebee5
|
@ -0,0 +1,174 @@
|
|||
package im.angry.openeuicc.core
|
||||
|
||||
import android.content.Context
|
||||
import android.se.omapi.SEService
|
||||
import android.telephony.SubscriptionManager
|
||||
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
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
open class DefaultEuiccChannelManager(protected val context: Context) : EuiccChannelManager {
|
||||
companion object {
|
||||
const val TAG = "EuiccChannelManager"
|
||||
}
|
||||
|
||||
private val channels = mutableListOf<EuiccChannel>()
|
||||
|
||||
private var seService: SEService? = null
|
||||
|
||||
private val lock = Mutex()
|
||||
|
||||
protected val tm by lazy {
|
||||
(context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager
|
||||
}
|
||||
|
||||
protected open val uiccCards: Collection<UiccCardInfoCompat>
|
||||
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
||||
|
||||
private suspend fun ensureSEService() {
|
||||
if (seService == null) {
|
||||
seService = connectSEService(context)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
// No-op when unprivileged
|
||||
return null
|
||||
}
|
||||
|
||||
protected fun tryOpenEuiccChannelUnprivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
if (port.portIndex != 0) {
|
||||
Log.w(TAG, "OMAPI channel attempted on non-zero portId, this may or may not work.")
|
||||
}
|
||||
|
||||
Log.i(TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
|
||||
try {
|
||||
return OmapiChannel(seService!!, port)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Failed
|
||||
Log.w(
|
||||
TAG,
|
||||
"OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
lock.withLock {
|
||||
ensureSEService()
|
||||
val existing =
|
||||
channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
||||
if (existing != null) {
|
||||
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
||||
return existing
|
||||
} else {
|
||||
existing.close()
|
||||
channels.remove(existing)
|
||||
}
|
||||
}
|
||||
|
||||
if (port.logicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
|
||||
// We can only open channels on ports that are actually enabled
|
||||
return null
|
||||
}
|
||||
|
||||
var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(port)
|
||||
|
||||
if (euiccChannel == null) {
|
||||
euiccChannel = tryOpenEuiccChannelUnprivileged(port)
|
||||
}
|
||||
|
||||
if (euiccChannel != null) {
|
||||
channels.add(euiccChannel)
|
||||
}
|
||||
|
||||
return euiccChannel
|
||||
}
|
||||
}
|
||||
|
||||
override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
for (card in uiccCards) {
|
||||
for (port in card.ports) {
|
||||
if (port.logicalSlotIndex == logicalSlotId) {
|
||||
return@withContext tryOpenEuiccChannel(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
for (card in uiccCards) {
|
||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||
for (port in card.ports) {
|
||||
tryOpenEuiccChannel(port)?.let { return@withContext it }
|
||||
}
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? =
|
||||
runBlocking {
|
||||
for (card in uiccCards) {
|
||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||
return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
|
||||
.ifEmpty { null }
|
||||
}
|
||||
return@runBlocking null
|
||||
}
|
||||
|
||||
override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
||||
card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun enumerateEuiccChannels() {
|
||||
withContext(Dispatchers.IO) {
|
||||
ensureSEService()
|
||||
|
||||
for (uiccInfo in uiccCards) {
|
||||
for (port in uiccInfo.ports) {
|
||||
if (tryOpenEuiccChannel(port) != null) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val knownChannels: List<EuiccChannel>
|
||||
get() = channels.toList()
|
||||
|
||||
override fun invalidate() {
|
||||
for (channel in channels) {
|
||||
channel.close()
|
||||
}
|
||||
|
||||
channels.clear()
|
||||
seService?.shutdown()
|
||||
seService = null
|
||||
}
|
||||
}
|
|
@ -1,174 +1,47 @@
|
|||
package im.angry.openeuicc.core
|
||||
|
||||
import android.content.Context
|
||||
import android.se.omapi.SEService
|
||||
import android.telephony.SubscriptionManager
|
||||
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
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.IllegalArgumentException
|
||||
interface EuiccChannelManager {
|
||||
val knownChannels: List<EuiccChannel>
|
||||
|
||||
open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager {
|
||||
companion object {
|
||||
const val TAG = "EuiccChannelManager"
|
||||
}
|
||||
/**
|
||||
* Scan all possible sources for EuiccChannels and have them cached for future use
|
||||
*/
|
||||
suspend fun enumerateEuiccChannels()
|
||||
|
||||
private val channels = mutableListOf<EuiccChannel>()
|
||||
/**
|
||||
* Returns the EuiccChannel corresponding to a **logical** slot
|
||||
*/
|
||||
fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel?
|
||||
|
||||
private var seService: SEService? = null
|
||||
/**
|
||||
* Returns the first EuiccChannel corresponding to a **physical** slot
|
||||
* If the physical slot supports MEP and has multiple ports, it is undefined
|
||||
* which of the two channels will be returned.
|
||||
*/
|
||||
fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel?
|
||||
|
||||
private val lock = Mutex()
|
||||
/**
|
||||
* Returns all EuiccChannels corresponding to a **physical** slot
|
||||
* Multiple channels are possible in the case of MEP
|
||||
*/
|
||||
fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>?
|
||||
|
||||
protected val tm by lazy {
|
||||
(context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager
|
||||
}
|
||||
/**
|
||||
* Returns the EuiccChannel corresponding to a **physical** slot and a port ID
|
||||
*/
|
||||
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel?
|
||||
|
||||
protected open val uiccCards: Collection<UiccCardInfoCompat>
|
||||
get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
|
||||
/**
|
||||
* Invalidate all EuiccChannels previously known by this Manager
|
||||
*/
|
||||
fun invalidate()
|
||||
|
||||
private suspend fun ensureSEService() {
|
||||
if (seService == null) {
|
||||
seService = connectSEService(context)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
// No-op when unprivileged
|
||||
return null
|
||||
}
|
||||
|
||||
protected fun tryOpenEuiccChannelUnprivileged(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
if (port.portIndex != 0) {
|
||||
Log.w(TAG, "OMAPI channel attempted on non-zero portId, this may or may not work.")
|
||||
}
|
||||
|
||||
Log.i(TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
|
||||
try {
|
||||
return OmapiChannel(seService!!, port)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// Failed
|
||||
Log.w(
|
||||
TAG,
|
||||
"OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
|
||||
lock.withLock {
|
||||
ensureSEService()
|
||||
val existing =
|
||||
channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
|
||||
if (existing != null) {
|
||||
if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
|
||||
return existing
|
||||
} else {
|
||||
existing.close()
|
||||
channels.remove(existing)
|
||||
}
|
||||
}
|
||||
|
||||
if (port.logicalSlotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
|
||||
// We can only open channels on ports that are actually enabled
|
||||
return null
|
||||
}
|
||||
|
||||
var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(port)
|
||||
|
||||
if (euiccChannel == null) {
|
||||
euiccChannel = tryOpenEuiccChannelUnprivileged(port)
|
||||
}
|
||||
|
||||
if (euiccChannel != null) {
|
||||
channels.add(euiccChannel)
|
||||
}
|
||||
|
||||
return euiccChannel
|
||||
}
|
||||
}
|
||||
|
||||
override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
for (card in uiccCards) {
|
||||
for (port in card.ports) {
|
||||
if (port.logicalSlotIndex == logicalSlotId) {
|
||||
return@withContext tryOpenEuiccChannel(port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
for (card in uiccCards) {
|
||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||
for (port in card.ports) {
|
||||
tryOpenEuiccChannel(port)?.let { return@withContext it }
|
||||
}
|
||||
}
|
||||
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? =
|
||||
runBlocking {
|
||||
for (card in uiccCards) {
|
||||
if (card.physicalSlotIndex != physicalSlotId) continue
|
||||
return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
|
||||
.ifEmpty { null }
|
||||
}
|
||||
return@runBlocking null
|
||||
}
|
||||
|
||||
override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? =
|
||||
runBlocking {
|
||||
withContext(Dispatchers.IO) {
|
||||
uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card ->
|
||||
card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun enumerateEuiccChannels() {
|
||||
withContext(Dispatchers.IO) {
|
||||
ensureSEService()
|
||||
|
||||
for (uiccInfo in uiccCards) {
|
||||
for (port in uiccInfo.ports) {
|
||||
if (tryOpenEuiccChannel(port) != null) {
|
||||
Log.d(
|
||||
TAG,
|
||||
"Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val knownChannels: List<EuiccChannel>
|
||||
get() = channels.toList()
|
||||
|
||||
override fun invalidate() {
|
||||
for (channel in channels) {
|
||||
channel.close()
|
||||
}
|
||||
|
||||
channels.clear()
|
||||
seService?.shutdown()
|
||||
seService = null
|
||||
/**
|
||||
* If possible, trigger the system to update the cached list of profiles
|
||||
* This is only expected to be implemented when the application is privileged
|
||||
* TODO: Remove this from the common interface
|
||||
*/
|
||||
fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
|
||||
// no-op by default
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package im.angry.openeuicc.core
|
||||
|
||||
interface IEuiccChannelManager {
|
||||
val knownChannels: List<EuiccChannel>
|
||||
|
||||
/**
|
||||
* Scan all possible sources for EuiccChannels and have them cached for future use
|
||||
*/
|
||||
suspend fun enumerateEuiccChannels()
|
||||
|
||||
/**
|
||||
* Returns the EuiccChannel corresponding to a **logical** slot
|
||||
*/
|
||||
fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel?
|
||||
|
||||
/**
|
||||
* Returns the first EuiccChannel corresponding to a **physical** slot
|
||||
* If the physical slot supports MEP and has multiple ports, it is undefined
|
||||
* which of the two channels will be returned.
|
||||
*/
|
||||
fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel?
|
||||
|
||||
/**
|
||||
* Returns all EuiccChannels corresponding to a **physical** slot
|
||||
* Multiple channels are possible in the case of MEP
|
||||
*/
|
||||
fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>?
|
||||
|
||||
/**
|
||||
* Returns the EuiccChannel corresponding to a **physical** slot and a port ID
|
||||
*/
|
||||
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel?
|
||||
|
||||
/**
|
||||
* Invalidate all EuiccChannels previously known by this Manager
|
||||
*/
|
||||
fun invalidate()
|
||||
|
||||
/**
|
||||
* If possible, trigger the system to update the cached list of profiles
|
||||
* This is only expected to be implemented when the application is privileged
|
||||
* TODO: Remove this from the common interface
|
||||
*/
|
||||
fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
|
||||
// no-op by default
|
||||
}
|
||||
}
|
|
@ -2,12 +2,12 @@ package im.angry.openeuicc.di
|
|||
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.util.*
|
||||
|
||||
interface AppContainer {
|
||||
val telephonyManager: TelephonyManager
|
||||
val euiccChannelManager: IEuiccChannelManager
|
||||
val euiccChannelManager: EuiccChannelManager
|
||||
val subscriptionManager: SubscriptionManager
|
||||
val preferenceRepository: PreferenceRepository
|
||||
val uiComponentFactory: UiComponentFactory
|
||||
|
|
|
@ -3,8 +3,8 @@ package im.angry.openeuicc.di
|
|||
import android.content.Context
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyManager
|
||||
import im.angry.openeuicc.core.DefaultEuiccChannelManager
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||
import im.angry.openeuicc.util.*
|
||||
|
||||
open class DefaultAppContainer(context: Context) : AppContainer {
|
||||
|
@ -12,8 +12,8 @@ open class DefaultAppContainer(context: Context) : AppContainer {
|
|||
context.getSystemService(TelephonyManager::class.java)!!
|
||||
}
|
||||
|
||||
override val euiccChannelManager: IEuiccChannelManager by lazy {
|
||||
EuiccChannelManager(context)
|
||||
override val euiccChannelManager: EuiccChannelManager by lazy {
|
||||
DefaultEuiccChannelManager(context)
|
||||
}
|
||||
|
||||
override val subscriptionManager by lazy {
|
||||
|
|
|
@ -7,7 +7,7 @@ import android.telephony.TelephonyManager
|
|||
import androidx.fragment.app.Fragment
|
||||
import im.angry.openeuicc.OpenEuiccApplication
|
||||
import im.angry.openeuicc.core.EuiccChannel
|
||||
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.di.AppContainer
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -52,7 +52,7 @@ interface OpenEuiccContextMarker {
|
|||
val appContainer: AppContainer
|
||||
get() = openEuiccApplication.appContainer
|
||||
|
||||
val euiccChannelManager: IEuiccChannelManager
|
||||
val euiccChannelManager: EuiccChannelManager
|
||||
get() = appContainer.euiccChannelManager
|
||||
|
||||
val telephonyManager: TelephonyManager
|
||||
|
|
|
@ -7,7 +7,7 @@ import im.angry.openeuicc.util.*
|
|||
import java.lang.Exception
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) {
|
||||
class PrivilegedEuiccChannelManager(context: Context): DefaultEuiccChannelManager(context) {
|
||||
override val uiccCards: Collection<UiccCardInfoCompat>
|
||||
get() = tm.uiccCardsInfoCompat
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package im.angry.openeuicc.di
|
||||
|
||||
import android.content.Context
|
||||
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
|
||||
|
||||
class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) {
|
||||
override val euiccChannelManager: IEuiccChannelManager by lazy {
|
||||
override val euiccChannelManager: EuiccChannelManager by lazy {
|
||||
PrivilegedEuiccChannelManager(context)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import android.telephony.SubscriptionManager
|
|||
import android.telephony.TelephonyManager
|
||||
import android.telephony.UiccSlotMapping
|
||||
import im.angry.openeuicc.core.EuiccChannel
|
||||
import im.angry.openeuicc.core.IEuiccChannelManager
|
||||
import im.angry.openeuicc.core.EuiccChannelManager
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.lang.Exception
|
||||
|
||||
|
@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean
|
|||
val TelephonyManager.dsdsEnabled: Boolean
|
||||
get() = activeModemCount >= 2
|
||||
|
||||
fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) {
|
||||
fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
|
||||
runBlocking {
|
||||
euiccManager.enumerateEuiccChannels()
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled:
|
|||
// Disable eSIM profiles before switching the slot mapping
|
||||
// This ensures that unmapped eSIM ports never have "ghost" profiles enabled
|
||||
fun TelephonyManager.updateSimSlotMapping(
|
||||
euiccManager: IEuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
|
||||
euiccManager: EuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
|
||||
currentMapping: Collection<UiccSlotMapping> = simSlotMapping
|
||||
) {
|
||||
val unmapped = currentMapping.filterNot { mapping ->
|
||||
|
|
Loading…
Reference in a new issue