Browse Source

refactor: Extract an interface from EuiccChannelManager

Eventually, we would like EuiccChannelManager to become a Service
instead of just any random class.
Peter Cai 1 year ago
parent
commit
770083523d

+ 2 - 1
app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt

@@ -5,6 +5,7 @@ import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
 import android.telephony.TelephonyManager
 import com.google.android.material.color.DynamicColors
 import com.google.android.material.color.DynamicColors
 import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.core.IEuiccChannelManager
 import im.angry.openeuicc.util.PreferenceRepository
 import im.angry.openeuicc.util.PreferenceRepository
 
 
 open class OpenEuiccApplication : Application() {
 open class OpenEuiccApplication : Application() {
@@ -19,7 +20,7 @@ open class OpenEuiccApplication : Application() {
         getSystemService(TelephonyManager::class.java)!!
         getSystemService(TelephonyManager::class.java)!!
     }
     }
 
 
-    open val euiccChannelManager: EuiccChannelManager by lazy {
+    open val euiccChannelManager: IEuiccChannelManager by lazy {
         EuiccChannelManager(this)
         EuiccChannelManager(this)
     }
     }
 
 

+ 42 - 36
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 kotlinx.coroutines.withContext
 import java.lang.IllegalArgumentException
 import java.lang.IllegalArgumentException
 
 
-open class EuiccChannelManager(protected val context: Context) {
+open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager {
     companion object {
     companion object {
         const val TAG = "EuiccChannelManager"
         const val TAG = "EuiccChannelManager"
     }
     }
@@ -32,9 +32,9 @@ open class EuiccChannelManager(protected val context: Context) {
         get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
         get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
 
 
     private suspend fun ensureSEService() {
     private suspend fun ensureSEService() {
-         if (seService == null) {
-             seService = connectSEService(context)
-         }
+        if (seService == null) {
+            seService = connectSEService(context)
+        }
     }
     }
 
 
     protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
     protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
@@ -52,7 +52,10 @@ open class EuiccChannelManager(protected val context: Context) {
             return OmapiChannel(seService!!, port)
             return OmapiChannel(seService!!, port)
         } catch (e: IllegalArgumentException) {
         } catch (e: IllegalArgumentException) {
             // Failed
             // Failed
-            Log.w(TAG, "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}.")
+            Log.w(
+                TAG,
+                "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
+            )
         }
         }
 
 
         return null
         return null
@@ -61,7 +64,8 @@ open class EuiccChannelManager(protected val context: Context) {
     private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
     private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
         lock.withLock {
         lock.withLock {
             ensureSEService()
             ensureSEService()
-            val existing = channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
+            val existing =
+                channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
             if (existing != null) {
             if (existing != null) {
                 if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
                 if (existing.valid && port.logicalSlotIndex == existing.logicalSlotId) {
                     return existing
                     return existing
@@ -90,7 +94,7 @@ open class EuiccChannelManager(protected val context: Context) {
         }
         }
     }
     }
 
 
-    fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
+    override fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? =
         runBlocking {
         runBlocking {
             withContext(Dispatchers.IO) {
             withContext(Dispatchers.IO) {
                 for (card in uiccCards) {
                 for (card in uiccCards) {
@@ -105,54 +109,60 @@ open class EuiccChannelManager(protected val context: Context) {
             }
             }
         }
         }
 
 
-    fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? = runBlocking {
-        withContext(Dispatchers.IO) {
-            for (card in uiccCards) {
-                if (card.physicalSlotIndex != physicalSlotId) continue
-                for (port in card.ports) {
-                    tryOpenEuiccChannel(port)?.let { return@withContext it }
+    override fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? =
+        runBlocking {
+            withContext(Dispatchers.IO) {
+                for (card in uiccCards) {
+                    if (card.physicalSlotIndex != physicalSlotId) continue
+                    for (port in card.ports) {
+                        tryOpenEuiccChannel(port)?.let { return@withContext it }
+                    }
                 }
                 }
-            }
 
 
-            null
+                null
+            }
         }
         }
-    }
 
 
-    fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? = runBlocking {
-        for (card in uiccCards) {
-            if (card.physicalSlotIndex != physicalSlotId) continue
-            return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
-                .ifEmpty { null }
+    override fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List<EuiccChannel>? =
+        runBlocking {
+            for (card in uiccCards) {
+                if (card.physicalSlotIndex != physicalSlotId) continue
+                return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) }
+                    .ifEmpty { null }
+            }
+            return@runBlocking 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) }
+    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) }
+                }
             }
             }
         }
         }
-    }
 
 
-    suspend fun enumerateEuiccChannels() {
+    override suspend fun enumerateEuiccChannels() {
         withContext(Dispatchers.IO) {
         withContext(Dispatchers.IO) {
             ensureSEService()
             ensureSEService()
 
 
             for (uiccInfo in uiccCards) {
             for (uiccInfo in uiccCards) {
                 for (port in uiccInfo.ports) {
                 for (port in uiccInfo.ports) {
                     if (tryOpenEuiccChannel(port) != null) {
                     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<EuiccChannel>
+    override val knownChannels: List<EuiccChannel>
         get() = channels.toList()
         get() = channels.toList()
 
 
-    fun invalidate() {
+    override fun invalidate() {
         for (channel in channels) {
         for (channel in channels) {
             channel.close()
             channel.close()
         }
         }
@@ -161,8 +171,4 @@ open class EuiccChannelManager(protected val context: Context) {
         seService?.shutdown()
         seService?.shutdown()
         seService = null
         seService = null
     }
     }
-
-    open fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
-        // No-op for unprivileged
-    }
 }
 }

+ 47 - 0
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<EuiccChannel>
+
+    /**
+     * 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<EuiccChannel>?
+
+    /**
+     * 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
+    }
+}

+ 4 - 9
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 androidx.lifecycle.lifecycleScope
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.common.R
 import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.core.EuiccChannel
-import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.util.*
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.launch
@@ -25,8 +24,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
         const val TAG = "MainActivity"
         const val TAG = "MainActivity"
     }
     }
 
 
-    protected lateinit var manager: EuiccChannelManager
-
     private lateinit var spinnerAdapter: ArrayAdapter<String>
     private lateinit var spinnerAdapter: ArrayAdapter<String>
     private lateinit var spinner: Spinner
     private lateinit var spinner: Spinner
 
 
@@ -45,8 +42,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
 
 
         tm = telephonyManager
         tm = telephonyManager
 
 
-        manager = euiccChannelManager
-
         spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
         spinnerAdapter = ArrayAdapter<String>(this, R.layout.spinner_item)
 
 
         lifecycleScope.launch {
         lifecycleScope.launch {
@@ -99,19 +94,19 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker {
 
 
     private suspend fun init() {
     private suspend fun init() {
         withContext(Dispatchers.IO) {
         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, "slot ${it.slotId} port ${it.portId}")
                 Log.d(TAG, it.lpa.eID)
                 Log.d(TAG, it.lpa.eID)
                 // Request the system to refresh the list of profiles every time we start
                 // 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,
                 // Note that this is currently supposed to be no-op when unprivileged,
                 // but it could change in the future
                 // but it could change in the future
-                manager.notifyEuiccProfilesChanged(it.logicalSlotId)
+                euiccChannelManager.notifyEuiccProfilesChanged(it.logicalSlotId)
             }
             }
         }
         }
 
 
         withContext(Dispatchers.Main) {
         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))
                 spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId))
                 fragments.add(createEuiccManagementFragment(channel))
                 fragments.add(createEuiccManagementFragment(channel))
             }
             }

+ 2 - 2
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 androidx.fragment.app.Fragment
 import im.angry.openeuicc.OpenEuiccApplication
 import im.angry.openeuicc.OpenEuiccApplication
 import im.angry.openeuicc.core.EuiccChannel
 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.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.Mutex
@@ -48,7 +48,7 @@ interface OpenEuiccContextMarker {
     val openEuiccApplication: OpenEuiccApplication
     val openEuiccApplication: OpenEuiccApplication
         get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
         get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication
 
 
-    val euiccChannelManager: EuiccChannelManager
+    val euiccChannelManager: IEuiccChannelManager
         get() = openEuiccApplication.euiccChannelManager
         get() = openEuiccApplication.euiccChannelManager
 
 
     val telephonyManager: TelephonyManager
     val telephonyManager: TelephonyManager

+ 2 - 2
app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt

@@ -1,10 +1,10 @@
 package im.angry.openeuicc
 package im.angry.openeuicc
 
 
-import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.core.IEuiccChannelManager
 import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
 import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
 
 
 class PrivilegedOpenEuiccApplication: OpenEuiccApplication() {
 class PrivilegedOpenEuiccApplication: OpenEuiccApplication() {
-    override val euiccChannelManager: EuiccChannelManager by lazy {
+    override val euiccChannelManager: IEuiccChannelManager by lazy {
         PrivilegedEuiccChannelManager(this)
         PrivilegedEuiccChannelManager(this)
     }
     }
 
 

+ 3 - 3
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.TelephonyManager
 import android.telephony.UiccSlotMapping
 import android.telephony.UiccSlotMapping
 import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.core.EuiccChannel
-import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.core.IEuiccChannelManager
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.runBlocking
 import java.lang.Exception
 import java.lang.Exception
 
 
@@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean
 val TelephonyManager.dsdsEnabled: Boolean
 val TelephonyManager.dsdsEnabled: Boolean
     get() = activeModemCount >= 2
     get() = activeModemCount >= 2
 
 
-fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) {
+fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) {
     runBlocking {
     runBlocking {
         euiccManager.enumerateEuiccChannels()
         euiccManager.enumerateEuiccChannels()
     }
     }
@@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled:
 // Disable eSIM profiles before switching the slot mapping
 // Disable eSIM profiles before switching the slot mapping
 // This ensures that unmapped eSIM ports never have "ghost" profiles enabled
 // This ensures that unmapped eSIM ports never have "ghost" profiles enabled
 fun TelephonyManager.updateSimSlotMapping(
 fun TelephonyManager.updateSimSlotMapping(
-    euiccManager: EuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
+    euiccManager: IEuiccChannelManager, newMapping: Collection<UiccSlotMapping>,
     currentMapping: Collection<UiccSlotMapping> = simSlotMapping
     currentMapping: Collection<UiccSlotMapping> = simSlotMapping
 ) {
 ) {
     val unmapped = currentMapping.filterNot { mapping ->
     val unmapped = currentMapping.filterNot { mapping ->