瀏覽代碼

refactor: make EuiccChannel an interface instead of data class

Peter Cai 3 年之前
父節點
當前提交
4840236e23

+ 18 - 9
app/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt

@@ -2,15 +2,24 @@ package im.angry.openeuicc.core
 
 import com.truphone.lpa.LocalProfileAssistant
 
-interface EuiccChannelStateManager {
-    val valid: Boolean
-    fun destroy()
-}
-
-data class EuiccChannel(
+// A custom type to avoid compatibility issues with UiccCardInfo / UiccPortInfo
+data class EuiccChannelInfo(
     val slotId: Int,
     val cardId: Int,
     val name: String,
-    val lpa: LocalProfileAssistant,
-    val stateManager: EuiccChannelStateManager
-)
+    val removable: Boolean,
+)
+
+abstract class EuiccChannel(
+    info: EuiccChannelInfo
+) {
+    val slotId = info.slotId
+    val cardId = info.cardId
+    val name = info.name
+    val removable = info.removable
+
+    abstract val lpa: LocalProfileAssistant
+    abstract val valid: Boolean
+
+    abstract fun close()
+}

+ 20 - 27
app/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt

@@ -4,10 +4,7 @@ import android.content.Context
 import android.os.Handler
 import android.os.HandlerThread
 import android.se.omapi.SEService
-import android.telephony.TelephonyManager
 import android.util.Log
-import com.truphone.lpa.ApduChannel
-import com.truphone.lpa.impl.LocalProfileAssistantImpl
 import im.angry.openeuicc.OpenEuiccApplication
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.runBlocking
@@ -48,46 +45,42 @@ class EuiccChannelManager(private val context: Context) {
         ensureSEService()
         val existing = channels.find { it.slotId == slotId }
         if (existing != null) {
-            if (existing.stateManager.valid) {
+            if (existing.valid) {
                 return existing
             } else {
-                existing.stateManager.destroy()
+                existing.close()
                 channels.remove(existing)
             }
         }
 
+        val cardInfo = tm.uiccCardsInfo.find { it.slotIndex == slotId } ?: return null
+
+        val channelInfo = EuiccChannelInfo(
+            slotId, cardInfo.cardId, "SIM $slotId", cardInfo.isRemovable
+        )
+
         val (shouldTryTelephonyManager, cardId) =
-            tm.uiccCardsInfo.find { it.slotIndex == slotId }?.let {
+            cardInfo.let {
                 Pair(it.isEuicc && !it.isRemovable, it.cardId)
-            } ?: Pair(false, 0)
+            }
 
-        var apduChannel: ApduChannel? = null
-        var stateManager: EuiccChannelStateManager? = null
+        var euiccChannel: EuiccChannel? = null
 
         if (shouldTryTelephonyManager) {
             Log.d(TAG, "Using TelephonyManager for slot $slotId")
             // TODO: On Tiramisu, we should also connect all available "ports" for MEP support
-            TelephonyManagerApduChannel.tryConnectUiccSlot(tm, slotId)?.let { (_apduChannel, _stateManager) ->
-                apduChannel = _apduChannel
-                stateManager = _stateManager
-            }
+            euiccChannel = TelephonyManagerChannel.tryConnect(tm, channelInfo)
+        }
+
+        if (euiccChannel == null) {
+            euiccChannel = OmapiChannel.tryConnect(seService!!, channelInfo)
         }
 
-        if (apduChannel == null || stateManager == null) {
-            OmapiApduChannel.tryConnectUiccSlot(seService!!, slotId)
-                ?.let { (_apduChannel, _stateManager) ->
-                    apduChannel = _apduChannel
-                    stateManager = _stateManager
-                } ?: return null
+        if (euiccChannel != null) {
+            channels.add(euiccChannel)
         }
 
-        val channel = EuiccChannel(
-            slotId, cardId,
-            "SIM $slotId",
-            LocalProfileAssistantImpl(apduChannel),
-            stateManager!!)
-        channels.add(channel)
-        return channel
+        return euiccChannel
     }
 
     fun findEuiccChannelBySlotBlocking(slotId: Int): EuiccChannel? = runBlocking {
@@ -113,7 +106,7 @@ class EuiccChannelManager(private val context: Context) {
 
     fun invalidate() {
         for (channel in channels) {
-            channel.stateManager.destroy()
+            channel.close()
         }
 
         channels.clear()

+ 0 - 29
app/src/main/java/im/angry/openeuicc/core/OmapiApduChannel.kt

@@ -1,41 +1,12 @@
 package im.angry.openeuicc.core
 
 import android.se.omapi.Channel
-import android.se.omapi.SEService
-import android.util.Log
 import com.truphone.lpa.ApduChannel
 import com.truphone.lpa.ApduTransmittedListener
 import im.angry.openeuicc.util.byteArrayToHex
 import im.angry.openeuicc.util.hexStringToByteArray
-import java.lang.Exception
 
 class OmapiApduChannel(private val channel: Channel) : ApduChannel {
-    companion object {
-        private const val TAG = "OmapiApduChannel"
-        private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0)
-
-        fun tryConnectUiccSlot(service: SEService, slotId: Int): Pair<ApduChannel, EuiccChannelStateManager>? {
-            try {
-                val reader = service.getUiccReader(slotId + 1) // slotId from telephony starts from 0
-                val session = reader.openSession()
-                val channel = session.openLogicalChannel(APPLET_ID) ?: return null
-                val stateManager = object : EuiccChannelStateManager {
-                    override val valid: Boolean
-                        get() = channel.isOpen
-
-                    override fun destroy() {
-                        channel.close()
-                    }
-                }
-                return Pair(OmapiApduChannel(channel), stateManager)
-            } catch (e: Exception) {
-                Log.e(TAG, "Unable to open eUICC channel for slot ${slotId}, skipping")
-                Log.e(TAG, Log.getStackTraceString(e))
-                return null
-            }
-        }
-    }
-
     override fun transmitAPDU(apdu: String): String =
         byteArrayToHex(channel.transmit(hexStringToByteArray(apdu)))
 

+ 39 - 0
app/src/main/java/im/angry/openeuicc/core/OmapiChannel.kt

@@ -0,0 +1,39 @@
+package im.angry.openeuicc.core
+
+import android.se.omapi.Channel
+import android.se.omapi.SEService
+import android.util.Log
+import com.truphone.lpa.LocalProfileAssistant
+import com.truphone.lpa.impl.LocalProfileAssistantImpl
+import java.lang.Exception
+
+class OmapiChannel private constructor(
+    info: EuiccChannelInfo,
+    private val channel: Channel,
+) : EuiccChannel(info) {
+    companion object {
+        private const val TAG = "OmapiChannel"
+        private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0)
+
+        fun tryConnect(service: SEService, info: EuiccChannelInfo): OmapiChannel? {
+            try {
+                val reader = service.getUiccReader(info.slotId + 1) // slotId from telephony starts from 0
+                val session = reader.openSession()
+                val channel = session.openLogicalChannel(APPLET_ID) ?: return null
+                return OmapiChannel(info, channel)
+            } catch (e: Exception) {
+                Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId}, skipping")
+                Log.e(TAG, Log.getStackTraceString(e))
+                return null
+            }
+        }
+    }
+
+    override val lpa: LocalProfileAssistant by lazy {
+        LocalProfileAssistantImpl(OmapiApduChannel(channel))
+    }
+    override val valid: Boolean
+        get() = channel.isOpen // TODO: This has to be implemented properly
+
+    override fun close() = channel.close()
+}

+ 0 - 38
app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt

@@ -1,53 +1,15 @@
 package im.angry.openeuicc.core
 
-import android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL
-import android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR
 import android.telephony.TelephonyManager
-import android.util.Log
 import com.truphone.lpa.ApduChannel
 import com.truphone.lpa.ApduTransmittedListener
 import im.angry.openeuicc.util.*
-import java.lang.Exception
 
 class TelephonyManagerApduChannel(
     private val tm: TelephonyManager,
     private val slotId: Int,
     private val channelId: Int) : ApduChannel {
 
-    companion object {
-        private const val TAG = "TelephonyManagerApduChannel"
-        private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
-
-        // TODO: On Tiramisu, we need to specify the portId also if we want MEP support
-        fun tryConnectUiccSlot(tm: TelephonyManager, slotId: Int): Pair<ApduChannel, EuiccChannelStateManager>? {
-            try {
-                val channel = tm.iccOpenLogicalChannelBySlot(slotId, EUICC_APP_ID, 0)
-                if (channel.status != STATUS_NO_ERROR || channel.channel == INVALID_CHANNEL) {
-                    Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager: ${channel.status}")
-                    return null
-                }
-
-                Log.d(TAG, "channel: ${channel.channel}")
-
-                val stateManager = object : EuiccChannelStateManager {
-                    override val valid: Boolean
-                        get() = true // TODO: Fix this properly
-
-                    override fun destroy() {
-                        tm.iccCloseLogicalChannelBySlot(slotId, channel.channel)
-                    }
-
-                }
-
-                return Pair(TelephonyManagerApduChannel(tm, slotId, channel.channel), stateManager)
-            } catch (e: Exception) {
-                Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager")
-                Log.e(TAG, Log.getStackTraceString(e))
-                return null
-            }
-        }
-    }
-
     override fun transmitAPDU(apdu: String): String? {
         val cla = Integer.parseInt(apdu.substring(0, 2), 16)
         val instruction = Integer.parseInt(apdu.substring(2, 4), 16)

+ 49 - 0
app/src/main/java/im/angry/openeuicc/core/TelephonyManagerChannel.kt

@@ -0,0 +1,49 @@
+package im.angry.openeuicc.core
+
+import android.telephony.IccOpenLogicalChannelResponse
+import android.telephony.TelephonyManager
+import android.util.Log
+import com.truphone.lpa.LocalProfileAssistant
+import com.truphone.lpa.impl.LocalProfileAssistantImpl
+import im.angry.openeuicc.util.*
+import java.lang.Exception
+
+class TelephonyManagerChannel private constructor(
+    info: EuiccChannelInfo,
+    private val tm: TelephonyManager,
+    private val channelId: Int
+) : EuiccChannel(info) {
+    companion object {
+        private const val TAG = "TelephonyManagerApduChannel"
+        private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
+
+        // TODO: On Tiramisu, we need to specify the portId also if we want MEP support
+        fun tryConnect(tm: TelephonyManager, info: EuiccChannelInfo): TelephonyManagerChannel? {
+            try {
+                val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, EUICC_APP_ID, 0)
+                if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
+                    Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager: ${channel.status}")
+                    return null
+                }
+
+                Log.d(TAG, "channel: ${channel.channel}")
+
+                return TelephonyManagerChannel(info, tm, channel.channel)
+            } catch (e: Exception) {
+                Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager")
+                Log.e(TAG, Log.getStackTraceString(e))
+                return null
+            }
+        }
+    }
+
+    override val lpa: LocalProfileAssistant by lazy {
+        LocalProfileAssistantImpl(TelephonyManagerApduChannel(tm, slotId, channelId))
+    }
+    override val valid: Boolean
+        get() = true // TODO: Fix this
+
+    override fun close() {
+        tm.iccCloseLogicalChannelBySlot(slotId, channelId)
+    }
+}