From 770083523db3cec28184edf8a169a5eafd09e1d8 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 3 Mar 2024 20:29:18 -0500 Subject: [PATCH] refactor: Extract an interface from EuiccChannelManager Eventually, we would like EuiccChannelManager to become a Service instead of just any random class. --- .../angry/openeuicc/OpenEuiccApplication.kt | 3 +- .../openeuicc/core/EuiccChannelManager.kt | 84 ++++++++++--------- .../openeuicc/core/IEuiccChannelManager.kt | 47 +++++++++++ .../im/angry/openeuicc/ui/MainActivity.kt | 13 +-- .../java/im/angry/openeuicc/util/Utils.kt | 4 +- .../PrivilegedOpenEuiccApplication.kt | 4 +- .../util/PrivilegedTelephonyUtils.kt | 6 +- 7 files changed, 105 insertions(+), 56 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt index e917aec..0b2ccec 100644 --- a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt +++ b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt @@ -5,6 +5,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import com.google.android.material.color.DynamicColors import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.util.PreferenceRepository open class OpenEuiccApplication : Application() { @@ -19,7 +20,7 @@ open class OpenEuiccApplication : Application() { getSystemService(TelephonyManager::class.java)!! } - open val euiccChannelManager: EuiccChannelManager by lazy { + open val euiccChannelManager: IEuiccChannelManager by lazy { EuiccChannelManager(this) } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 23c21b9..e031794 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.lang.IllegalArgumentException -open class EuiccChannelManager(protected val context: Context) { +open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager { companion object { const val TAG = "EuiccChannelManager" } @@ -32,9 +32,9 @@ open class EuiccChannelManager(protected val context: Context) { get() = (0..? = + runBlocking { for (card in uiccCards) { if (card.physicalSlotIndex != physicalSlotId) continue - for (port in card.ports) { - tryOpenEuiccChannel(port)?.let { return@withContext it } + return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) } + .ifEmpty { null } + } + return@runBlocking null + } + + override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = + runBlocking { + withContext(Dispatchers.IO) { + uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> + card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } } } - - null } - } - fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? = runBlocking { - for (card in uiccCards) { - 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 { - withContext(Dispatchers.IO) { - uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> - card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } - } - } - } - - suspend fun enumerateEuiccChannels() { + override suspend fun enumerateEuiccChannels() { withContext(Dispatchers.IO) { ensureSEService() for (uiccInfo in uiccCards) { for (port in uiccInfo.ports) { if (tryOpenEuiccChannel(port) != null) { - Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}") + Log.d( + TAG, + "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}" + ) } } } } } - val knownChannels: List + override val knownChannels: List get() = channels.toList() - fun invalidate() { + override fun invalidate() { for (channel in channels) { channel.close() } @@ -161,8 +171,4 @@ open class EuiccChannelManager(protected val context: Context) { seService?.shutdown() seService = null } - - open fun notifyEuiccProfilesChanged(logicalSlotId: Int) { - // No-op for unprivileged - } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt new file mode 100644 index 0000000..8a6ab54 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt @@ -0,0 +1,47 @@ +package im.angry.openeuicc.core + +interface IEuiccChannelManager { + val knownChannels: List + + /** + * Scan all possible sources for EuiccChannels and have them cached for future use + */ + suspend fun enumerateEuiccChannels() + + /** + * Returns the EuiccChannel corresponding to a **logical** slot + */ + fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? + + /** + * Returns the first EuiccChannel corresponding to a **physical** slot + * If the physical slot supports MEP and has multiple ports, it is undefined + * which of the two channels will be returned. + */ + fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? + + /** + * Returns all EuiccChannels corresponding to a **physical** slot + * Multiple channels are possible in the case of MEP + */ + fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? + + /** + * Returns the EuiccChannel corresponding to a **physical** slot and a port ID + */ + fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? + + /** + * Invalidate all EuiccChannels previously known by this Manager + */ + fun invalidate() + + /** + * If possible, trigger the system to update the cached list of profiles + * This is only expected to be implemented when the application is privileged + * TODO: Remove this from the common interface + */ + fun notifyEuiccProfilesChanged(logicalSlotId: Int) { + // no-op by default + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt index 452ed76..af6cbf4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt @@ -14,7 +14,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import im.angry.openeuicc.common.R import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -25,8 +24,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { const val TAG = "MainActivity" } - protected lateinit var manager: EuiccChannelManager - private lateinit var spinnerAdapter: ArrayAdapter private lateinit var spinner: Spinner @@ -45,8 +42,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { tm = telephonyManager - manager = euiccChannelManager - spinnerAdapter = ArrayAdapter(this, R.layout.spinner_item) lifecycleScope.launch { @@ -99,19 +94,19 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { private suspend fun init() { withContext(Dispatchers.IO) { - manager.enumerateEuiccChannels() - manager.knownChannels.forEach { + euiccChannelManager.enumerateEuiccChannels() + euiccChannelManager.knownChannels.forEach { Log.d(TAG, "slot ${it.slotId} port ${it.portId}") Log.d(TAG, it.lpa.eID) // Request the system to refresh the list of profiles every time we start // Note that this is currently supposed to be no-op when unprivileged, // but it could change in the future - manager.notifyEuiccProfilesChanged(it.logicalSlotId) + euiccChannelManager.notifyEuiccProfilesChanged(it.logicalSlotId) } } withContext(Dispatchers.Main) { - manager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel -> + euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel -> spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId)) fragments.add(createEuiccManagementFragment(channel)) } diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt index dc90ab1..3c21898 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt @@ -7,7 +7,7 @@ import android.telephony.TelephonyManager import androidx.fragment.app.Fragment import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex @@ -48,7 +48,7 @@ interface OpenEuiccContextMarker { val openEuiccApplication: OpenEuiccApplication get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication - val euiccChannelManager: EuiccChannelManager + val euiccChannelManager: IEuiccChannelManager get() = openEuiccApplication.euiccChannelManager val telephonyManager: TelephonyManager diff --git a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt index 8b3ef99..28ea49d 100644 --- a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt +++ b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt @@ -1,10 +1,10 @@ package im.angry.openeuicc -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.core.PrivilegedEuiccChannelManager class PrivilegedOpenEuiccApplication: OpenEuiccApplication() { - override val euiccChannelManager: EuiccChannelManager by lazy { + override val euiccChannelManager: IEuiccChannelManager by lazy { PrivilegedEuiccChannelManager(this) } diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt index 4675ab9..4cb4932 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt @@ -4,7 +4,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.UiccSlotMapping import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import kotlinx.coroutines.runBlocking import java.lang.Exception @@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean val TelephonyManager.dsdsEnabled: Boolean get() = activeModemCount >= 2 -fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) { +fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) { runBlocking { euiccManager.enumerateEuiccChannels() } @@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: // 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, + euiccManager: IEuiccChannelManager, newMapping: Collection, currentMapping: Collection = simSlotMapping ) { val unmapped = currentMapping.filterNot { mapping ->