Compare commits

...

2 commits

4 changed files with 60 additions and 11 deletions

View file

@ -135,6 +135,16 @@ open class EuiccChannelManager(protected val context: Context) {
}
}
fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? = runBlocking {
if (!checkPrivileges()) return@runBlocking null
for (card in tm.uiccCardsInfoCompat) {
if (card.physicalSlotIndex != physicalSlotId) continue
return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
.ifEmpty { null }
}
return@runBlocking null
}
fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = runBlocking {
if (!checkPrivileges()) return@runBlocking null
withContext(Dispatchers.IO) {

View file

@ -27,6 +27,10 @@ class OpenEuiccService : EuiccService() {
openEuiccApplication.euiccChannelManager
.findEuiccChannelByPortBlocking(slotId, portId)
private fun findAllChannels(physicalSlotId: Int): List<EuiccChannel>? =
openEuiccApplication.euiccChannelManager
.findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId)
override fun onGetEid(slotId: Int): String? =
findChannel(slotId)?.lpa?.eID
@ -141,23 +145,25 @@ class OpenEuiccService : EuiccService() {
override fun onDeleteSubscription(slotId: Int, iccid: String): Int {
Log.i(TAG, "onDeleteSubscription slotId=$slotId iccid=$iccid")
try {
val channel = findChannel(slotId) ?: return RESULT_FIRST_USER
val channels = findAllChannels(slotId) ?: return RESULT_FIRST_USER
if (!channel.profileExists(iccid)) {
if (!channels[0].profileExists(iccid)) {
return RESULT_FIRST_USER
}
val profile = channel.lpa.profiles.find {
it.iccid == iccid
} ?: return RESULT_FIRST_USER
// If the profile is enabled by ANY channel (port), we cannot delete it
channels.forEach { channel ->
val profile = channel.lpa.profiles.find {
it.iccid == iccid
} ?: return RESULT_FIRST_USER
if (profile.state == LocalProfileInfo.State.Enabled) {
// Must disable the profile first
// TODO: Need to check "other port" as well for MEP
return RESULT_FIRST_USER
if (profile.state == LocalProfileInfo.State.Enabled) {
// Must disable the profile first
return RESULT_FIRST_USER
}
}
return if (channel.lpa.deleteProfile(iccid)) {
return if (channels[0].lpa.deleteProfile(iccid)) {
RESULT_OK
} else {
RESULT_FIRST_USER

View file

@ -98,7 +98,9 @@ class SlotMappingFragment: DialogFragment(), OnMenuItemClickListener {
lifecycleScope.launch(Dispatchers.Main) {
try {
withContext(Dispatchers.IO) {
tm.simSlotMapping = adapter.mappings
// Use the utility method from PrivilegedTelephonyUtils to ensure
// unmapped ports have all profiles disabled
tm.updateSimSlotMapping(openEuiccApplication.euiccChannelManager, adapter.mappings)
}
} catch (e: Exception) {
Toast.makeText(requireContext(), R.string.slot_mapping_failure, Toast.LENGTH_LONG).show()

View file

@ -2,6 +2,9 @@ package im.angry.openeuicc.util
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.UiccSlotMapping
import im.angry.openeuicc.core.EuiccChannelManager
import net.typeblog.lpac_jni.LocalProfileInfo
import java.lang.Exception
val TelephonyManager.supportsDSDS: Boolean
@ -13,6 +16,34 @@ var TelephonyManager.dsdsEnabled: Boolean
switchMultiSimConfig(if (value) { 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: EuiccChannelManager, newMapping: Collection<UiccSlotMapping>) {
val unmapped = simSlotMapping.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 ->
channel.lpa.profiles.find { it.state == LocalProfileInfo.State.Enabled }?.let { profile ->
channel.lpa.disableProfile(profile.iccid)
return@mapNotNull { channel.lpa.enableProfile(profile.iccid) }
}
}
}
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 {