ソースを参照

Move eUICC vendor handling to an interface in util

...instead of ad-hoc functions
Peter Cai 11 ヶ月 前
コミット
889b08767c

+ 5 - 10
app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt

@@ -23,8 +23,6 @@ import im.angry.openeuicc.common.R
 import im.angry.openeuicc.core.EuiccChannel
 import im.angry.openeuicc.core.EuiccChannelManager
 import im.angry.openeuicc.util.*
-import im.angry.openeuicc.vendored.getESTKmeInfo
-import im.angry.openeuicc.vendored.getSIMLinkVersion
 import kotlinx.coroutines.launch
 import net.typeblog.lpac_jni.impl.PKID_GSMA_LIVE_CI
 import net.typeblog.lpac_jni.impl.PKID_GSMA_TEST_CI
@@ -104,14 +102,11 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
         add(Item(R.string.euicc_info_access_mode, channel.type))
         add(Item(R.string.euicc_info_removable, formatByBoolean(channel.port.card.isRemovable, YES_NO)))
         add(Item(R.string.euicc_info_eid, channel.lpa.eID, copiedToastResId = R.string.toast_eid_copied))
-        getESTKmeInfo(channel.apduInterface)?.let {
-            add(Item(R.string.euicc_info_sku, it.skuName))
-            add(Item(R.string.euicc_info_sn, it.serialNumber, copiedToastResId = R.string.toast_sn_copied))
-            add(Item(R.string.euicc_info_bl_ver, it.bootloaderVersion))
-            add(Item(R.string.euicc_info_fw_ver, it.firmwareVersion))
-        }
-        getSIMLinkVersion(channel.lpa.eID, channel.lpa.euiccInfo2?.euiccFirmwareVersion)?.let {
-            add(Item(R.string.euicc_info_sku, "9eSIM $it"))
+        channel.tryParseEuiccVendorInfo()?.let { vendorInfo ->
+            vendorInfo.skuName?.let { add(Item(R.string.euicc_info_sku, it)) }
+            vendorInfo.serialNumber?.let { add(Item(R.string.euicc_info_sn, it)) }
+            vendorInfo.firmwareVersion?.let { add(Item(R.string.euicc_info_fw_ver, it)) }
+            vendorInfo.bootloaderVersion?.let { add(Item(R.string.euicc_info_bl_ver, it)) }
         }
         channel.lpa.euiccInfo2.let { info ->
             add(Item(R.string.euicc_info_sgp22_version, info?.sgp22Version.toString()))

+ 112 - 0
app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt

@@ -0,0 +1,112 @@
+package im.angry.openeuicc.util
+
+import android.util.Log
+import im.angry.openeuicc.core.ApduInterfaceAtrProvider
+import im.angry.openeuicc.core.EuiccChannel
+import net.typeblog.lpac_jni.Version
+
+data class EuiccVendorInfo(
+    val skuName: String?,
+    val serialNumber: String?,
+    val bootloaderVersion: String?,
+    val firmwareVersion: String?,
+)
+
+private val EUICC_VENDORS: Array<EuiccVendor> = arrayOf(EstkMe(), SimLink())
+
+fun EuiccChannel.tryParseEuiccVendorInfo(): EuiccVendorInfo? {
+    EUICC_VENDORS.forEach { vendor ->
+        vendor.tryParseEuiccVendorInfo(this@tryParseEuiccVendorInfo)?.let {
+            return it
+        }
+    }
+
+    return null
+}
+
+interface EuiccVendor {
+    fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo?
+}
+
+private class EstkMe : EuiccVendor {
+    companion object {
+        private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex()
+        private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray()
+    }
+
+    private fun checkAtr(channel: EuiccChannel): Boolean {
+        val iface = channel.apduInterface
+        if (iface !is ApduInterfaceAtrProvider) return false
+        val atr = iface.atr ?: return false
+        for (index in atr.indices) {
+            if (atr.size - index < PRODUCT_ATR_FPR.size) break
+            if (atr.sliceArray(index until index + PRODUCT_ATR_FPR.size)
+                    .contentEquals(PRODUCT_ATR_FPR)
+            ) return true
+        }
+        return false
+    }
+
+    private fun decodeAsn1String(b: ByteArray): String? {
+        if (b.size < 2) return null
+        if (b[b.size - 2] != 0x90.toByte() || b[b.size - 1] != 0x00.toByte()) return null
+        return b.sliceArray(0 until b.size - 2).decodeToString()
+    }
+
+    override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? {
+        if (!checkAtr(channel)) return null
+
+        val iface = channel.apduInterface
+        return try {
+            iface.withLogicalChannel(PRODUCT_AID) { transmit ->
+                fun invoke(p1: Byte) =
+                    decodeAsn1String(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)))
+                EuiccVendorInfo(
+                    skuName = invoke(0x03),
+                    serialNumber = invoke(0x00),
+                    bootloaderVersion = invoke(0x01),
+                    firmwareVersion = invoke(0x02),
+                )
+            }
+        } catch (e: Exception) {
+            Log.d(TAG, "Failed to get ESTKmeInfo", e)
+            null
+        }
+    }
+}
+
+private class SimLink : EuiccVendor {
+    companion object {
+        private val EID_PATTERN = Regex("^89044045(84|21)67274948")
+    }
+
+    override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? {
+        val eid = channel.lpa.eID
+        val version = channel.lpa.euiccInfo2?.euiccFirmwareVersion
+        if (version == null || EID_PATTERN.find(eid, 0) == null) return null
+        val versionName = when {
+            // @formatter:off
+            version >= Version(37,  1, 41) -> "v3.1 (beta 1)"
+            version >= Version(36, 18,  5) -> "v3 (final)"
+            version >= Version(36, 17, 39) -> "v3 (beta)"
+            version >= Version(36, 17,  4) -> "v2s"
+            version >= Version(36,  9,  3) -> "v2.1"
+            version >= Version(36,  7,  2) -> "v2"
+            // @formatter:on
+            else -> null
+        }
+
+        val skuName = if (versionName == null) {
+            "9eSIM"
+        } else {
+            "9eSIM $versionName"
+        }
+
+        return EuiccVendorInfo(
+            skuName = skuName,
+            serialNumber = null,
+            bootloaderVersion = null,
+            firmwareVersion = null
+        )
+    }
+}

+ 0 - 49
app-common/src/main/java/im/angry/openeuicc/vendored/estkme.kt

@@ -1,49 +0,0 @@
-package im.angry.openeuicc.vendored
-
-import android.util.Log
-import im.angry.openeuicc.core.ApduInterfaceAtrProvider
-import im.angry.openeuicc.util.TAG
-import im.angry.openeuicc.util.decodeHex
-import net.typeblog.lpac_jni.ApduInterface
-
-data class ESTKmeInfo(
-    val serialNumber: String?,
-    val bootloaderVersion: String?,
-    val firmwareVersion: String?,
-    val skuName: String?,
-)
-
-fun isESTKmeATR(iface: ApduInterface): Boolean {
-    if (iface !is ApduInterfaceAtrProvider) return false
-    val atr = iface.atr ?: return false
-    val fpr = "estk.me".encodeToByteArray()
-    for (index in atr.indices) {
-        if (atr.size - index < fpr.size) break
-        if (atr.sliceArray(index until index + fpr.size).contentEquals(fpr)) return true
-    }
-    return false
-}
-
-fun getESTKmeInfo(iface: ApduInterface): ESTKmeInfo? {
-    if (!isESTKmeATR(iface)) return null
-    fun decode(b: ByteArray): String? {
-        if (b.size < 2) return null
-        if (b[b.size - 2] != 0x90.toByte() || b[b.size - 1] != 0x00.toByte()) return null
-        return b.sliceArray(0 until b.size - 2).decodeToString()
-    }
-    return try {
-        iface.withLogicalChannel("A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex()) { transmit ->
-            fun invoke(p1: Byte) = decode(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)))
-            ESTKmeInfo(
-                invoke(0x00), // serial number
-                invoke(0x01), // bootloader version
-                invoke(0x02), // firmware version
-                invoke(0x03), // sku name
-            )
-        }
-    } catch (e: Exception) {
-        Log.d(TAG, "Failed to get ESTKmeInfo", e)
-        null
-    }
-}
-

+ 0 - 20
app-common/src/main/java/im/angry/openeuicc/vendored/simlink.kt

@@ -1,20 +0,0 @@
-package im.angry.openeuicc.vendored
-
-import net.typeblog.lpac_jni.Version
-
-private val prefix = Regex("^89044045(84|21)67274948") // SIMLink EID prefix
-
-fun getSIMLinkVersion(eid: String, version: Version?): String? {
-    if (version == null || prefix.find(eid, 0) == null) return null
-    return when {
-        // @formatter:off
-        version >= Version(37,  1, 41) -> "v3.1 (beta 1)"
-        version >= Version(36, 18,  5) -> "v3 (final)"
-        version >= Version(36, 17, 39) -> "v3 (beta)"
-        version >= Version(36, 17,  4) -> "v2s"
-        version >= Version(36,  9,  3) -> "v2.1"
-        version >= Version(36,  7,  2) -> "v2"
-        // @formatter:on
-        else -> null
-    }
-}