Peter Cai
770083523d
All checks were successful
/ build-debug (push) Successful in 4m45s
Eventually, we would like EuiccChannelManager to become a Service instead of just any random class.
84 lines
2.8 KiB
Kotlin
84 lines
2.8 KiB
Kotlin
package im.angry.openeuicc.util
|
|
|
|
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 kotlinx.coroutines.runBlocking
|
|
import java.lang.Exception
|
|
|
|
val TelephonyManager.supportsDSDS: Boolean
|
|
get() = supportedModemCount == 2
|
|
|
|
val TelephonyManager.dsdsEnabled: Boolean
|
|
get() = activeModemCount >= 2
|
|
|
|
fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) {
|
|
runBlocking {
|
|
euiccManager.enumerateEuiccChannels()
|
|
}
|
|
|
|
// Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs)
|
|
euiccManager.knownChannels.forEach {
|
|
if (!it.removable) {
|
|
it.lpa.disableActiveProfileWithUndo()
|
|
}
|
|
}
|
|
|
|
switchMultiSimConfig(if (enabled) { 2 } else { 1 })
|
|
}
|
|
|
|
// 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>,
|
|
currentMapping: Collection<UiccSlotMapping> = simSlotMapping
|
|
) {
|
|
val unmapped = currentMapping.filterNot { mapping ->
|
|
// If the same physical slot + port pair is not found in the new mapping, it is unmapped
|
|
newMapping.any {
|
|
it.physicalSlotIndex == mapping.physicalSlotIndex && it.portIndex == mapping.portIndex
|
|
}
|
|
}
|
|
|
|
val undo = unmapped.mapNotNull { mapping ->
|
|
euiccManager.findEuiccChannelByPortBlocking(mapping.physicalSlotIndex, mapping.portIndex)?.let { channel ->
|
|
if (!channel.removable) {
|
|
return@mapNotNull channel.lpa.disableActiveProfileWithUndo()
|
|
} else {
|
|
// Do not do anything for external eUICCs -- we can't really trust them to work properly
|
|
// with no profile enabled.
|
|
return@mapNotNull null
|
|
}
|
|
}
|
|
}
|
|
|
|
try {
|
|
simSlotMapping = newMapping
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
undo.forEach { it() } // Undo what we just did
|
|
throw e // Rethrow for caller to handle
|
|
}
|
|
}
|
|
|
|
fun SubscriptionManager.tryRefreshCachedEuiccInfo(cardId: Int) {
|
|
if (cardId != 0) {
|
|
try {
|
|
requestEmbeddedSubscriptionInfoListRefresh(cardId)
|
|
} catch (e: Exception) {
|
|
// Ignore
|
|
}
|
|
}
|
|
}
|
|
|
|
// Every EuiccChannel we use here should be backed by a RealUiccPortInfoCompat
|
|
val EuiccChannel.removable
|
|
get() = (port as RealUiccPortInfoCompat).card.isRemovable
|
|
|
|
val EuiccChannel.cardId
|
|
get() = (port as RealUiccPortInfoCompat).card.cardId
|
|
|
|
val EuiccChannel.isMEP
|
|
get() = (port as RealUiccPortInfoCompat).card.isMultipleEnabledProfilesSupported |