From d515b808dcdfb07eb185371885b3e34d0c5e2662 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 19:43:02 +0800 Subject: [PATCH 1/9] feat: add eastcompeace supports --- .../java/im/angry/openeuicc/util/Vendors.kt | 60 +++++++++++++++---- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 529f9ee..77acf13 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -12,23 +12,16 @@ data class EuiccVendorInfo( val firmwareVersion: String?, ) -private val EUICC_VENDORS: Array = arrayOf(EstkMe(), SimLink()) +private val EUICC_VENDORS: Array = arrayOf(ESTKme(), SIMLink9(), Eastcompeace()) -fun EuiccChannel.tryParseEuiccVendorInfo(): EuiccVendorInfo? { - EUICC_VENDORS.forEach { vendor -> - vendor.tryParseEuiccVendorInfo(this@tryParseEuiccVendorInfo)?.let { - return it - } - } - - return null -} +fun EuiccChannel.tryParseEuiccVendorInfo(): EuiccVendorInfo? = + EUICC_VENDORS.firstNotNullOfOrNull { it.tryParseEuiccVendorInfo(this) } interface EuiccVendor { fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? } -private class EstkMe : EuiccVendor { +private class ESTKme : EuiccVendor { companion object { private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex() private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray() @@ -75,7 +68,7 @@ private class EstkMe : EuiccVendor { } } -private class SimLink : EuiccVendor { +private class SIMLink9 : EuiccVendor { companion object { private val EID_PATTERN = Regex("^89044045(84|21)67274948") } @@ -109,4 +102,47 @@ private class SimLink : EuiccVendor { firmwareVersion = null ) } +} + +@Suppress("SpellCheckingInspection") +private class Eastcompeace : EuiccVendor { + companion object { + private val TAG = Eastcompeace::class.java.simpleName + private const val EID_PREFIX = "89086030" + private val PRODUCT_AID = "A000000533C000FF860000000427".decodeHex() + private val COMMAND = "80CA000050".decodeHex() + } + + class SCID(val scid: ByteArray) { + val bootloaderVersion: ByteArray + get() = scid.sliceArray(12..14) + val cosVersion: ByteArray + get() = scid.sliceArray(14..16) + val uniquelyIdentify: ByteArray + get() = scid.sliceArray(32..56) + + override fun toString() = scid.encodeHex() + } + + private fun decodeResponse(b: ByteArray): ByteArray? { + 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) + } + + override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? { + if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null + return channel.apduInterface.withLogicalChannel(PRODUCT_AID, ::parseSCID) + } + + fun parseSCID(transmit: (ByteArray) -> ByteArray): EuiccVendorInfo? { + val scid = decodeResponse(transmit(COMMAND))?.let(::SCID) ?: return null + Log.i(TAG, "Eastcompeace SCID: $scid") + return EuiccVendorInfo( + skuName = "Eastcompeace", + serialNumber = scid.uniquelyIdentify.encodeHex(), + bootloaderVersion = scid.bootloaderVersion.encodeHex(), + firmwareVersion = scid.cosVersion.encodeHex(), + ) + } } \ No newline at end of file -- 2.45.3 From e3357a188aa80c50db42691d59debb0013d43057 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 20:18:26 +0800 Subject: [PATCH 2/9] fix: slice array --- .../src/main/java/im/angry/openeuicc/util/Vendors.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 77acf13..9955dab 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -4,6 +4,8 @@ import android.util.Log import im.angry.openeuicc.core.ApduInterfaceAtrProvider import im.angry.openeuicc.core.EuiccChannel import net.typeblog.lpac_jni.Version +import java.nio.ByteBuffer +import java.nio.ByteOrder data class EuiccVendorInfo( val skuName: String?, @@ -115,11 +117,11 @@ private class Eastcompeace : EuiccVendor { class SCID(val scid: ByteArray) { val bootloaderVersion: ByteArray - get() = scid.sliceArray(12..14) + get() = scid.sliceArray(12..<14) val cosVersion: ByteArray - get() = scid.sliceArray(14..16) + get() = scid.sliceArray(14..<16) val uniquelyIdentify: ByteArray - get() = scid.sliceArray(32..56) + get() = scid.sliceArray(32..<56) override fun toString() = scid.encodeHex() } -- 2.45.3 From 56234788fb66a4e8ff20fb0c1a9d3594eaac209a Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 20:40:05 +0800 Subject: [PATCH 3/9] fix: add try catch --- .../src/main/java/im/angry/openeuicc/util/Vendors.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 9955dab..b576aa9 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -25,6 +25,7 @@ interface EuiccVendor { private class ESTKme : EuiccVendor { companion object { + private val TAG = ESTKme::class.java.simpleName private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex() private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray() } @@ -134,7 +135,12 @@ private class Eastcompeace : EuiccVendor { override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? { if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null - return channel.apduInterface.withLogicalChannel(PRODUCT_AID, ::parseSCID) + return try { + channel.apduInterface.withLogicalChannel(PRODUCT_AID, ::parseSCID) + } catch (e: Exception) { + Log.d(TAG, "Failed to get EastcompeaceInfo", e) + null + } } fun parseSCID(transmit: (ByteArray) -> ByteArray): EuiccVendorInfo? { -- 2.45.3 From 62f69cd30965d916663248d0c808291a91e4cbf7 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 20:47:33 +0800 Subject: [PATCH 4/9] refactor: clean up unused selection states and update sliceArray syntax --- .../src/main/java/im/angry/openeuicc/util/Vendors.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index b576aa9..cc2cc36 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -4,8 +4,6 @@ import android.util.Log import im.angry.openeuicc.core.ApduInterfaceAtrProvider import im.angry.openeuicc.core.EuiccChannel import net.typeblog.lpac_jni.Version -import java.nio.ByteBuffer -import java.nio.ByteOrder data class EuiccVendorInfo( val skuName: String?, @@ -118,11 +116,11 @@ private class Eastcompeace : EuiccVendor { class SCID(val scid: ByteArray) { val bootloaderVersion: ByteArray - get() = scid.sliceArray(12..<14) + get() = scid.sliceArray(12 until 14) val cosVersion: ByteArray - get() = scid.sliceArray(14..<16) + get() = scid.sliceArray(14 until 16) val uniquelyIdentify: ByteArray - get() = scid.sliceArray(32..<56) + get() = scid.sliceArray(32 until 56) override fun toString() = scid.encodeHex() } -- 2.45.3 From 11b02e9191a5ca96f86d3cba8f530eddcf3f9d7b Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 21:04:44 +0800 Subject: [PATCH 5/9] refactor: remove unused SCID class and update SCID parsing logic --- .../java/im/angry/openeuicc/util/Vendors.kt | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index cc2cc36..409a430 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -114,17 +114,6 @@ private class Eastcompeace : EuiccVendor { private val COMMAND = "80CA000050".decodeHex() } - class SCID(val scid: ByteArray) { - val bootloaderVersion: ByteArray - get() = scid.sliceArray(12 until 14) - val cosVersion: ByteArray - get() = scid.sliceArray(14 until 16) - val uniquelyIdentify: ByteArray - get() = scid.sliceArray(32 until 56) - - override fun toString() = scid.encodeHex() - } - private fun decodeResponse(b: ByteArray): ByteArray? { if (b.size < 2) return null if (b[b.size - 2] != 0x90.toByte() || b[b.size - 1] != 0x00.toByte()) return null @@ -142,13 +131,14 @@ private class Eastcompeace : EuiccVendor { } fun parseSCID(transmit: (ByteArray) -> ByteArray): EuiccVendorInfo? { - val scid = decodeResponse(transmit(COMMAND))?.let(::SCID) ?: return null - Log.i(TAG, "Eastcompeace SCID: $scid") + val scid = decodeResponse(transmit(COMMAND)) ?: return null + // TODO: Some data needs to be accumulated to distinguish SKUs + Log.i(TAG, "Eastcompeace SCID: ${scid.encodeHex()}") return EuiccVendorInfo( skuName = "Eastcompeace", - serialNumber = scid.uniquelyIdentify.encodeHex(), - bootloaderVersion = scid.bootloaderVersion.encodeHex(), - firmwareVersion = scid.cosVersion.encodeHex(), + serialNumber = null, + bootloaderVersion = null, + firmwareVersion = null, ) } } \ No newline at end of file -- 2.45.3 From a40fa930b114439e15a61bc116590dc21bf3f687 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 21:18:15 +0800 Subject: [PATCH 6/9] refactor: remove unused selection states and reorganize code style settings --- .../java/im/angry/openeuicc/util/Vendors.kt | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 409a430..b15216d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -41,12 +41,6 @@ private class ESTKme : EuiccVendor { 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 @@ -54,7 +48,8 @@ private class ESTKme : EuiccVendor { return try { iface.withLogicalChannel(PRODUCT_AID) { transmit -> fun invoke(p1: Byte) = - decodeAsn1String(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00))) + decodeResponse(transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00))) + ?.decodeToString() EuiccVendorInfo( skuName = invoke(0x03), serialNumber = invoke(0x00), @@ -114,12 +109,6 @@ private class Eastcompeace : EuiccVendor { private val COMMAND = "80CA000050".decodeHex() } - private fun decodeResponse(b: ByteArray): ByteArray? { - 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) - } - override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? { if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null return try { @@ -141,4 +130,10 @@ private class Eastcompeace : EuiccVendor { firmwareVersion = null, ) } +} + +private fun decodeResponse(b: ByteArray): ByteArray? { + 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) } \ No newline at end of file -- 2.45.3 From cbf72aae391212b62de9c6e10fac8d5e1a5d6c1d Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 21:27:18 +0800 Subject: [PATCH 7/9] fix: remove tag --- app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index b15216d..81d3415 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -23,7 +23,6 @@ interface EuiccVendor { private class ESTKme : EuiccVendor { companion object { - private val TAG = ESTKme::class.java.simpleName private val PRODUCT_AID = "A06573746B6D65FFFFFFFFFFFF6D6774".decodeHex() private val PRODUCT_ATR_FPR = "estk.me".encodeToByteArray() } @@ -103,7 +102,6 @@ private class SIMLink9 : EuiccVendor { @Suppress("SpellCheckingInspection") private class Eastcompeace : EuiccVendor { companion object { - private val TAG = Eastcompeace::class.java.simpleName private const val EID_PREFIX = "89086030" private val PRODUCT_AID = "A000000533C000FF860000000427".decodeHex() private val COMMAND = "80CA000050".decodeHex() -- 2.45.3 From b8cceabdca66ee00832546201cddb3a714f0bac8 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 21:42:57 +0800 Subject: [PATCH 8/9] simplify parse scid --- .../src/main/java/im/angry/openeuicc/util/Vendors.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 81d3415..5b0d2b9 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -110,15 +110,16 @@ private class Eastcompeace : EuiccVendor { override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? { if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null return try { - channel.apduInterface.withLogicalChannel(PRODUCT_AID, ::parseSCID) + channel.apduInterface.withLogicalChannel(PRODUCT_AID) { transmit -> + decodeResponse(transmit(COMMAND))?.let(::parseSCID) + } } catch (e: Exception) { Log.d(TAG, "Failed to get EastcompeaceInfo", e) null } } - fun parseSCID(transmit: (ByteArray) -> ByteArray): EuiccVendorInfo? { - val scid = decodeResponse(transmit(COMMAND)) ?: return null + fun parseSCID(scid: ByteArray): EuiccVendorInfo { // TODO: Some data needs to be accumulated to distinguish SKUs Log.i(TAG, "Eastcompeace SCID: ${scid.encodeHex()}") return EuiccVendorInfo( -- 2.45.3 From f22375027dbd946621e9536d7b56cac1e08dcf48 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 24 Mar 2025 22:00:50 +0800 Subject: [PATCH 9/9] fix: typo --- app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt index 5b0d2b9..a1ff0a6 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Vendors.kt @@ -104,14 +104,14 @@ private class Eastcompeace : EuiccVendor { companion object { private const val EID_PREFIX = "89086030" private val PRODUCT_AID = "A000000533C000FF860000000427".decodeHex() - private val COMMAND = "80CA000050".decodeHex() + private val GET_SCID_COMMAND = "80CA000050".decodeHex() } override fun tryParseEuiccVendorInfo(channel: EuiccChannel): EuiccVendorInfo? { if (!channel.lpa.eID.startsWith(EID_PREFIX)) return null return try { channel.apduInterface.withLogicalChannel(PRODUCT_AID) { transmit -> - decodeResponse(transmit(COMMAND))?.let(::parseSCID) + decodeResponse(transmit(GET_SCID_COMMAND))?.let(::parseSCID) } } catch (e: Exception) { Log.d(TAG, "Failed to get EastcompeaceInfo", e) -- 2.45.3