From 6e590cfd487fd39b681258c85957fea875f28b82 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 3 Apr 2024 20:53:48 -0400 Subject: [PATCH] OpenEuiccService: stop confusing AOSP with multiple eUICCs Unfortunately, AOSP is not really good at handling more than one eUICC chips per device, even though the EuiccService interface should technically allow for such a situation. Let's do the next best thing -- only ever report one eUICC chip to AOSP. If the device has an internal one, then only report that one; otherwise, select the first available eUICC chip to report to the system. We might make this more configurable in the future, but for now I think this should work for most of the situations. Note that this does NOT affect how the rest of OpenEUICC behaves. This does mean however OpenEUICC will keep hold of some APDU channels that it will never access via OpenEuiccService. A mitigation is to make EuiccChannelManager close unused channels automatically after some timeout. --- .../openeuicc/service/OpenEuiccService.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 65d7918..e635d21 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -16,6 +16,21 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { const val TAG = "OpenEuiccService" } + private val hasInternalEuicc by lazy { + telephonyManager.uiccCardsInfoCompat.any { it.isEuicc && !it.isRemovable } + } + + // TODO: Should this be configurable? + private fun shouldIgnoreSlot(physicalSlotId: Int) = + if (hasInternalEuicc) { + // For devices with an internal eUICC slot, ignore any removable UICC + telephonyManager.uiccCardsInfoCompat.find { it.physicalSlotIndex == physicalSlotId }!!.isRemovable + } else { + // Otherwise, we can report at least one removable eUICC to the system without confusing + // it too much. + telephonyManager.uiccCardsInfoCompat.firstOrNull { it.isEuicc }?.physicalSlotIndex == physicalSlotId + } + private fun findChannel(physicalSlotId: Int): EuiccChannel? = euiccChannelManager.findEuiccChannelByPhysicalSlotBlocking(physicalSlotId) @@ -111,6 +126,11 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult { Log.i(TAG, "onGetEuiccProfileInfoList slotId=$slotId") + if (shouldIgnoreSlot(slotId)) { + Log.i(TAG, "ignoring slot $slotId") + return GetEuiccProfileInfoListResult(RESULT_FIRST_USER, arrayOf(), true) + } + val channel = findChannel(slotId)!! val profiles = channel.lpa.profiles.operational.map { EuiccProfileInfo.Builder(it.iccid).apply { @@ -142,6 +162,8 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { override fun onDeleteSubscription(slotId: Int, iccid: String): Int { Log.i(TAG, "onDeleteSubscription slotId=$slotId iccid=$iccid") + if (shouldIgnoreSlot(slotId)) return RESULT_FIRST_USER + try { val channels = findAllChannels(slotId) ?: return RESULT_FIRST_USER @@ -187,6 +209,8 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { forceDeactivateSim: Boolean ): Int { Log.i(TAG,"onSwitchToSubscriptionWithPort slotId=$slotId portIndex=$portIndex iccid=$iccid forceDeactivateSim=$forceDeactivateSim") + if (shouldIgnoreSlot(slotId)) return RESULT_FIRST_USER + try { // retryWithTimeout is needed here because this function may be called just after // AOSP has switched slot mappings, in which case the slots may not be ready yet. @@ -237,6 +261,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String, nickname: String?): Int { Log.i(TAG, "onUpdateSubscriptionNickname slotId=$slotId iccid=$iccid nickname=$nickname") + if (shouldIgnoreSlot(slotId)) return RESULT_FIRST_USER val channel = findChannel(slotId) ?: return RESULT_FIRST_USER if (!channel.profileExists(iccid)) { return RESULT_FIRST_USER