PrivilegedTelephonyUtils.kt 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package im.angry.openeuicc.util
  2. import android.telephony.SubscriptionManager
  3. import android.telephony.TelephonyManager
  4. import android.telephony.UiccSlotMapping
  5. import im.angry.openeuicc.core.EuiccChannel
  6. import im.angry.openeuicc.core.EuiccChannelManager
  7. import kotlinx.coroutines.runBlocking
  8. import java.lang.Exception
  9. val TelephonyManager.supportsDSDS: Boolean
  10. get() = supportedModemCount == 2
  11. val TelephonyManager.dsdsEnabled: Boolean
  12. get() = activeModemCount >= 2
  13. fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
  14. runBlocking {
  15. euiccManager.enumerateEuiccChannels()
  16. }
  17. // Disable all eSIM profiles before performing a DSDS switch (only for internal eSIMs)
  18. euiccManager.knownChannels.forEach {
  19. if (!it.removable) {
  20. it.lpa.disableActiveProfileWithUndo()
  21. }
  22. }
  23. switchMultiSimConfig(if (enabled) { 2 } else { 1 })
  24. }
  25. // Disable eSIM profiles before switching the slot mapping
  26. // This ensures that unmapped eSIM ports never have "ghost" profiles enabled
  27. fun TelephonyManager.updateSimSlotMapping(
  28. euiccManager: EuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
  29. currentMapping: Collection<UiccSlotMapping> = simSlotMapping
  30. ) {
  31. val unmapped = currentMapping.filterNot { mapping ->
  32. // If the same physical slot + port pair is not found in the new mapping, it is unmapped
  33. newMapping.any {
  34. it.physicalSlotIndex == mapping.physicalSlotIndex && it.portIndex == mapping.portIndex
  35. }
  36. }
  37. val undo = unmapped.mapNotNull { mapping ->
  38. euiccManager.findEuiccChannelByPortBlocking(mapping.physicalSlotIndex, mapping.portIndex)?.let { channel ->
  39. if (!channel.removable) {
  40. return@mapNotNull channel.lpa.disableActiveProfileWithUndo()
  41. } else {
  42. // Do not do anything for external eUICCs -- we can't really trust them to work properly
  43. // with no profile enabled.
  44. return@mapNotNull null
  45. }
  46. }
  47. }
  48. try {
  49. simSlotMapping = newMapping
  50. } catch (e: Exception) {
  51. e.printStackTrace()
  52. undo.forEach { it() } // Undo what we just did
  53. throw e // Rethrow for caller to handle
  54. }
  55. }
  56. fun SubscriptionManager.tryRefreshCachedEuiccInfo(cardId: Int) {
  57. if (cardId != 0) {
  58. try {
  59. requestEmbeddedSubscriptionInfoListRefresh(cardId)
  60. } catch (e: Exception) {
  61. // Ignore
  62. }
  63. }
  64. }
  65. // Every EuiccChannel we use here should be backed by a RealUiccPortInfoCompat
  66. val EuiccChannel.removable
  67. get() = (port as RealUiccPortInfoCompat).card.isRemovable
  68. val EuiccChannel.cardId
  69. get() = (port as RealUiccPortInfoCompat).card.cardId
  70. val EuiccChannel.isMEP
  71. get() = (port as RealUiccPortInfoCompat).card.isMultipleEnabledProfilesSupported