OpenEuiccService: stop confusing AOSP with multiple eUICCs
All checks were successful
/ build-debug (push) Successful in 4m12s

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.
This commit is contained in:
Peter Cai 2024-04-03 20:53:48 -04:00
parent 35e543ff70
commit 6e590cfd48

View file

@ -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