Browse Source

refactor: Use DI techniques for EuiccChannel's as well

Peter Cai 1 year ago
parent
commit
1a69c5294b

+ 43 - 0
app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt

@@ -0,0 +1,43 @@
+package im.angry.openeuicc.core
+
+import android.content.Context
+import android.se.omapi.SEService
+import android.util.Log
+import im.angry.openeuicc.util.*
+import java.lang.IllegalArgumentException
+
+open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccChannelFactory {
+    private var seService: SEService? = null
+
+    private suspend fun ensureSEService() {
+        if (seService == null) {
+            seService = connectSEService(context)
+        }
+    }
+
+    override suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
+        if (port.portIndex != 0) {
+            Log.w(DefaultEuiccChannelManager.TAG, "OMAPI channel attempted on non-zero portId, this may or may not work.")
+        }
+
+        ensureSEService()
+
+        Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
+        try {
+            return OmapiChannel(seService!!, port)
+        } catch (e: IllegalArgumentException) {
+            // Failed
+            Log.w(
+                DefaultEuiccChannelManager.TAG,
+                "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
+            )
+        }
+
+        return null
+    }
+
+    override fun cleanup() {
+        seService?.shutdown()
+        seService = null
+    }
+}

+ 13 - 52
app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt

@@ -1,69 +1,41 @@
 package im.angry.openeuicc.core
 
 import android.content.Context
-import android.se.omapi.SEService
 import android.telephony.SubscriptionManager
 import android.util.Log
-import im.angry.openeuicc.OpenEuiccApplication
+import im.angry.openeuicc.di.AppContainer
 import im.angry.openeuicc.util.*
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
-import java.lang.IllegalArgumentException
 
-open class DefaultEuiccChannelManager(protected val context: Context) : EuiccChannelManager {
+open class DefaultEuiccChannelManager(
+    protected val appContainer: AppContainer,
+    protected val context: Context
+) : EuiccChannelManager {
     companion object {
         const val TAG = "EuiccChannelManager"
     }
 
     private val channels = mutableListOf<EuiccChannel>()
 
-    private var seService: SEService? = null
-
     private val lock = Mutex()
 
     protected val tm by lazy {
-        (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager
-    }
-
-    protected open val uiccCards: Collection<UiccCardInfoCompat>
-        get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
-
-    private suspend fun ensureSEService() {
-        if (seService == null) {
-            seService = connectSEService(context)
-        }
+        appContainer.telephonyManager
     }
 
-    protected open fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
-        // No-op when unprivileged
-        return null
+    private val euiccChannelFactory by lazy {
+        appContainer.euiccChannelFactory
     }
 
-    protected fun tryOpenEuiccChannelUnprivileged(port: UiccPortInfoCompat): EuiccChannel? {
-        if (port.portIndex != 0) {
-            Log.w(TAG, "OMAPI channel attempted on non-zero portId, this may or may not work.")
-        }
-
-        Log.i(TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}")
-        try {
-            return OmapiChannel(seService!!, port)
-        } catch (e: IllegalArgumentException) {
-            // Failed
-            Log.w(
-                TAG,
-                "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}."
-            )
-        }
-
-        return null
-    }
+    protected open val uiccCards: Collection<UiccCardInfoCompat>
+        get() = (0..<tm.activeModemCountCompat).map { FakeUiccCardInfoCompat(it) }
 
     private suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
         lock.withLock {
-            ensureSEService()
             val existing =
                 channels.find { it.slotId == port.card.physicalSlotIndex && it.portId == port.portIndex }
             if (existing != null) {
@@ -80,17 +52,9 @@ open class DefaultEuiccChannelManager(protected val context: Context) : EuiccCha
                 return null
             }
 
-            var euiccChannel: EuiccChannel? = tryOpenEuiccChannelPrivileged(port)
-
-            if (euiccChannel == null) {
-                euiccChannel = tryOpenEuiccChannelUnprivileged(port)
+            return euiccChannelFactory.tryOpenEuiccChannel(port)?.also {
+                channels.add(it)
             }
-
-            if (euiccChannel != null) {
-                channels.add(euiccChannel)
-            }
-
-            return euiccChannel
         }
     }
 
@@ -144,8 +108,6 @@ open class DefaultEuiccChannelManager(protected val context: Context) : EuiccCha
 
     override suspend fun enumerateEuiccChannels() {
         withContext(Dispatchers.IO) {
-            ensureSEService()
-
             for (uiccInfo in uiccCards) {
                 for (port in uiccInfo.ports) {
                     if (tryOpenEuiccChannel(port) != null) {
@@ -168,7 +130,6 @@ open class DefaultEuiccChannelManager(protected val context: Context) : EuiccCha
         }
 
         channels.clear()
-        seService?.shutdown()
-        seService = null
+        euiccChannelFactory.cleanup()
     }
 }

+ 16 - 0
app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelFactory.kt

@@ -0,0 +1,16 @@
+package im.angry.openeuicc.core
+
+import im.angry.openeuicc.util.*
+
+// This class is here instead of inside DI because it contains a bit more logic than just
+// "dumb" dependency injection.
+interface EuiccChannelFactory {
+    suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel?
+
+    /**
+     * Release all resources used by this EuiccChannelFactory
+     * Note that the same instance may be reused; any resources allocated must be automatically
+     * re-acquired when this happens
+     */
+    fun cleanup()
+}

+ 2 - 0
app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt

@@ -2,6 +2,7 @@ package im.angry.openeuicc.di
 
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
+import im.angry.openeuicc.core.EuiccChannelFactory
 import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.util.*
 
@@ -11,4 +12,5 @@ interface AppContainer {
     val subscriptionManager: SubscriptionManager
     val preferenceRepository: PreferenceRepository
     val uiComponentFactory: UiComponentFactory
+    val euiccChannelFactory: EuiccChannelFactory
 }

+ 6 - 1
app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt

@@ -3,6 +3,7 @@ package im.angry.openeuicc.di
 import android.content.Context
 import android.telephony.SubscriptionManager
 import android.telephony.TelephonyManager
+import im.angry.openeuicc.core.DefaultEuiccChannelFactory
 import im.angry.openeuicc.core.DefaultEuiccChannelManager
 import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.util.*
@@ -13,7 +14,7 @@ open class DefaultAppContainer(context: Context) : AppContainer {
     }
 
     override val euiccChannelManager: EuiccChannelManager by lazy {
-        DefaultEuiccChannelManager(context)
+        DefaultEuiccChannelManager(this, context)
     }
 
     override val subscriptionManager by lazy {
@@ -27,4 +28,8 @@ open class DefaultAppContainer(context: Context) : AppContainer {
     override val uiComponentFactory by lazy {
         DefaultUiComponentFactory()
     }
+
+    override val euiccChannelFactory by lazy {
+        DefaultEuiccChannelFactory(context)
+    }
 }

+ 43 - 0
app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt

@@ -0,0 +1,43 @@
+package im.angry.openeuicc.core
+
+import android.content.Context
+import android.util.Log
+import im.angry.openeuicc.OpenEuiccApplication
+import im.angry.openeuicc.util.*
+import java.lang.IllegalArgumentException
+
+class PrivilegedEuiccChannelFactory(context: Context) : DefaultEuiccChannelFactory(context) {
+    private val tm by lazy {
+        (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager
+    }
+
+    @Suppress("NAME_SHADOWING")
+    override suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? {
+        val port = port as RealUiccPortInfoCompat
+        if (port.card.isRemovable) {
+            // Attempt unprivileged (OMAPI) before TelephonyManager
+            // but still try TelephonyManager in case OMAPI is broken
+            super.tryOpenEuiccChannel(port)?.let { return it }
+        }
+
+        if (port.card.isEuicc) {
+            Log.i(
+                DefaultEuiccChannelManager.TAG,
+                "Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}"
+            )
+            try {
+                return TelephonyManagerChannel(
+                    port, tm
+                )
+            } catch (e: IllegalArgumentException) {
+                // Failed
+                Log.w(
+                    DefaultEuiccChannelManager.TAG,
+                    "TelephonyManager APDU interface unavailable for slot ${port.card.physicalSlotIndex} port ${port.portIndex}, falling back"
+                )
+            }
+        }
+
+        return super.tryOpenEuiccChannel(port)
+    }
+}

+ 4 - 26
app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt

@@ -1,37 +1,15 @@
 package im.angry.openeuicc.core
 
 import android.content.Context
-import android.util.Log
-import im.angry.openeuicc.OpenEuiccApplication
+import im.angry.openeuicc.di.AppContainer
 import im.angry.openeuicc.util.*
 import java.lang.Exception
-import java.lang.IllegalArgumentException
 
-class PrivilegedEuiccChannelManager(context: Context): DefaultEuiccChannelManager(context) {
+class PrivilegedEuiccChannelManager(appContainer: AppContainer, context: Context) :
+    DefaultEuiccChannelManager(appContainer, context) {
     override val uiccCards: Collection<UiccCardInfoCompat>
         get() = tm.uiccCardsInfoCompat
 
-    @Suppress("NAME_SHADOWING")
-    override fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? {
-        val port = port as RealUiccPortInfoCompat
-        if (port.card.isRemovable) {
-            // Attempt unprivileged (OMAPI) before TelephonyManager
-            // but still try TelephonyManager in case OMAPI is broken
-            super.tryOpenEuiccChannelUnprivileged(port)?.let { return it }
-        }
-
-        if (port.card.isEuicc) {
-            Log.i(TAG, "Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}")
-            try {
-                return TelephonyManagerChannel(port, tm)
-            } catch (e: IllegalArgumentException) {
-                // Failed
-                Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${port.card.physicalSlotIndex} port ${port.portIndex}, falling back")
-            }
-        }
-        return null
-    }
-
     // Clean up channels left open in TelephonyManager
     // due to a (potentially) forced restart
     // This should be called every time the application is restarted
@@ -48,7 +26,7 @@ class PrivilegedEuiccChannelManager(context: Context): DefaultEuiccChannelManage
     }
 
     override fun notifyEuiccProfilesChanged(logicalSlotId: Int) {
-        (context.applicationContext as OpenEuiccApplication).appContainer.subscriptionManager.apply {
+        appContainer.subscriptionManager.apply {
             findEuiccChannelBySlotBlocking(logicalSlotId)?.let {
                 tryRefreshCachedEuiccInfo(it.cardId)
             }

+ 6 - 1
app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt

@@ -2,14 +2,19 @@ package im.angry.openeuicc.di
 
 import android.content.Context
 import im.angry.openeuicc.core.EuiccChannelManager
+import im.angry.openeuicc.core.PrivilegedEuiccChannelFactory
 import im.angry.openeuicc.core.PrivilegedEuiccChannelManager
 
 class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) {
     override val euiccChannelManager: EuiccChannelManager by lazy {
-        PrivilegedEuiccChannelManager(context)
+        PrivilegedEuiccChannelManager(this, context)
     }
 
     override val uiComponentFactory by lazy {
         PrivilegedUiComponentFactory()
     }
+
+    override val euiccChannelFactory by lazy {
+        PrivilegedEuiccChannelFactory(context)
+    }
 }