From c954cd73dadfc4fe1e4fad7a4a25ff700c919c6a Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 19:30:36 +0800 Subject: [PATCH 1/6] feat: supports for multi logical channel --- .../im/angry/openeuicc/core/EuiccChannel.kt | 6 ++ .../angry/openeuicc/core/EuiccChannelImpl.kt | 5 +- .../openeuicc/core/EuiccChannelWrapper.kt | 22 +------ .../openeuicc/core/OmapiApduInterface.kt | 32 ++++++----- .../openeuicc/core/usb/UsbApduInterface.kt | 39 +++++++------ .../core/TelephonyManagerApduInterface.kt | 57 +++++++------------ .../util/PrivilegedTelephonyCompat.kt | 23 ++++++-- .../net/typeblog/lpac_jni/ApduInterface.kt | 17 +++++- .../impl/LocalProfileAssistantImpl.kt | 4 +- .../src/main/jni/lpac-jni/interface-wrapper.c | 9 ++- 10 files changed, 111 insertions(+), 103 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt index 5f399ea..597a70d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannel.kt @@ -1,6 +1,7 @@ package im.angry.openeuicc.core import im.angry.openeuicc.util.* +import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant interface EuiccChannel { @@ -28,5 +29,10 @@ interface EuiccChannel { */ val intrinsicChannelName: String? + /** + * The underlying APDU interface for this channel + */ + val apduInterface: ApduInterface + fun close() } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt index 3da829a..a56b1cc 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelImpl.kt @@ -1,6 +1,7 @@ package im.angry.openeuicc.core -import im.angry.openeuicc.util.* +import im.angry.openeuicc.util.UiccPortInfoCompat +import im.angry.openeuicc.util.decodeHex import kotlinx.coroutines.flow.Flow import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant @@ -11,7 +12,7 @@ class EuiccChannelImpl( override val type: String, override val port: UiccPortInfoCompat, override val intrinsicChannelName: String?, - private val apduInterface: ApduInterface, + override val apduInterface: ApduInterface, verboseLoggingFlow: Flow, ignoreTLSCertificateFlow: Flow ) : EuiccChannel { diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt index 4204e82..1a108e5 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt @@ -1,9 +1,8 @@ package im.angry.openeuicc.core -import im.angry.openeuicc.util.* import net.typeblog.lpac_jni.LocalProfileAssistant -class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { +class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel by orig { private var _inner: EuiccChannel? = orig private val channel: EuiccChannel @@ -15,28 +14,11 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { return _inner!! } - override val type: String - get() = channel.type - override val port: UiccPortInfoCompat - get() = channel.port - override val slotId: Int - get() = channel.slotId - override val logicalSlotId: Int - get() = channel.logicalSlotId - override val portId: Int - get() = channel.portId private val lpaDelegate = lazy { LocalProfileAssistantWrapper(channel.lpa) } - override val lpa: LocalProfileAssistant by lpaDelegate - override val valid: Boolean - get() = channel.valid - override val intrinsicChannelName: String? - get() = channel.intrinsicChannelName - override val atr: ByteArray? - get() = channel.atr - override fun close() = channel.close() + override val lpa: LocalProfileAssistant by lpaDelegate fun invalidateWrapper() { _inner = null diff --git a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt index c70669d..bbf8fdd 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt @@ -7,9 +7,9 @@ import android.util.Log import im.angry.openeuicc.util.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.single import kotlinx.coroutines.runBlocking import net.typeblog.lpac_jni.ApduInterface +import java.util.concurrent.atomic.AtomicInteger class OmapiApduInterface( private val service: SEService, @@ -21,7 +21,8 @@ class OmapiApduInterface( } private lateinit var session: Session - private lateinit var lastChannel: Channel + private val channels = mutableMapOf() + private val index = AtomicInteger(0) override val valid: Boolean get() = service.isConnected && (this::session.isInitialized && !session.isClosed) @@ -38,23 +39,24 @@ class OmapiApduInterface( } override fun logicalChannelOpen(aid: ByteArray): Int { - check(!this::lastChannel.isInitialized) { - "Can only open one channel" - } - lastChannel = session.openLogicalChannel(aid)!! - return 1 + val channel = session.openLogicalChannel(aid) + check(channel != null) { "Failed to open logical channel (${aid.encodeHex()})" } + val id = index.addAndGet(1) + channels[id] = channel + return id } override fun logicalChannelClose(handle: Int) { - check(handle == 1 && !this::lastChannel.isInitialized) { - "Unknown channel" - } - lastChannel.close() + val channel = channels[handle] + check(channel != null) { "Invalid logical channel handle $handle" } + channels.remove(handle) + if (channel.isOpen) channel.close() } - override fun transmit(tx: ByteArray): ByteArray { - check(this::lastChannel.isInitialized) { - "Unknown channel" + override fun transmit(handle: Int, tx: ByteArray): ByteArray { + val channel = channels[handle] + check(channel != null) { + "Invalid logical channel handle $handle" } if (runBlocking { verboseLoggingFlow.first() }) { @@ -63,7 +65,7 @@ class OmapiApduInterface( try { for (i in 0..10) { - val res = lastChannel.transmit(tx) + val res = channel.transmit(tx) if (runBlocking { verboseLoggingFlow.first() }) { Log.d(TAG, "OMAPI APDU response: ${res.encodeHex()}") } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbApduInterface.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbApduInterface.kt index 624ef89..f9e764b 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbApduInterface.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbApduInterface.kt @@ -21,10 +21,13 @@ class UsbApduInterface( private lateinit var ccidDescription: UsbCcidDescription private lateinit var transceiver: UsbCcidTransceiver - private var channelId = -1 - override var atr: ByteArray? = null + override val valid: Boolean + get() = channels.isNotEmpty() + + private var channels = mutableSetOf() + override fun connect() { ccidDescription = UsbCcidDescription.fromRawDescriptors(conn.rawDescriptors)!! @@ -46,11 +49,11 @@ class UsbApduInterface( override fun disconnect() { conn.close() + + atr = null } override fun logicalChannelOpen(aid: ByteArray): Int { - check(channelId == -1) { "Logical channel already opened" } - // OPEN LOGICAL CHANNEL val req = manageChannelCmd(true, 0) @@ -66,7 +69,7 @@ class UsbApduInterface( return -1 } - channelId = resp[0].toInt() + val channelId = resp[0].toInt() Log.d(TAG, "channelId = $channelId") // Then, select AID @@ -78,32 +81,32 @@ class UsbApduInterface( return -1 } + channels.add(channelId) + return channelId } override fun logicalChannelClose(handle: Int) { - check(handle == channelId) { "Logical channel ID mismatch" } - check(channelId != -1) { "Logical channel is not opened" } - + check(channels.contains(handle)) { + "Invalid logical channel handle $handle" + } // CLOSE LOGICAL CHANNEL - val req = manageChannelCmd(false, channelId.toByte()) - val resp = transmitApduByChannel(req, channelId.toByte()) + val req = manageChannelCmd(false, handle.toByte()) + val resp = transmitApduByChannel(req, handle.toByte()) if (!isSuccessResponse(resp)) { Log.d(TAG, "CLOSE LOGICAL CHANNEL failed: ${resp.encodeHex()}") } - - channelId = -1 + channels.remove(handle) } - override fun transmit(tx: ByteArray): ByteArray { - check(channelId != -1) { "Logical channel is not opened" } - return transmitApduByChannel(tx, channelId.toByte()) + override fun transmit(handle: Int, tx: ByteArray): ByteArray { + check(channels.contains(handle)) { + "Invalid logical channel handle $handle" + } + return transmitApduByChannel(tx, handle.toByte()) } - override val valid: Boolean - get() = channelId != -1 - private fun isSuccessResponse(resp: ByteArray): Boolean = resp.size >= 2 && resp[resp.size - 2] == 0x90.toByte() && resp[resp.size - 1] == 0x00.toByte() diff --git a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt index 6b09368..f0b1909 100644 --- a/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt +++ b/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduInterface.kt @@ -18,12 +18,10 @@ class TelephonyManagerApduInterface( const val TAG = "TelephonyManagerApduInterface" } - private var lastChannel: Int = -1 - override val valid: Boolean - // TelephonyManager channels will never become truly "invalid", - // just that transactions might return errors or nonsense - get() = lastChannel != -1 + get() = channels.isNotEmpty() + + private var channels = mutableSetOf() override fun connect() { // Do nothing @@ -31,52 +29,39 @@ class TelephonyManagerApduInterface( override fun disconnect() { // Do nothing - lastChannel = -1 } override fun logicalChannelOpen(aid: ByteArray): Int { - check(lastChannel == -1) { "Already initialized" } val hex = aid.encodeHex() val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0) if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) { - throw IllegalArgumentException("Cannot open logical channel $hex via TelephonManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}") + throw IllegalArgumentException("Cannot open logical channel $hex via TelephonyManager on slot ${port.card.physicalSlotIndex} port ${port.portIndex}") } - lastChannel = channel.channel - return lastChannel + channels.add(channel.channel) + return channel.channel } override fun logicalChannelClose(handle: Int) { - check(handle == lastChannel) { "Invalid channel handle " } + check(channels.contains(handle)) { + "Invalid logical channel handle $handle" + } tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle) - lastChannel = -1 + channels.remove(handle) } - override fun transmit(tx: ByteArray): ByteArray { - check(lastChannel != -1) { "Uninitialized" } - + override fun transmit(handle: Int, tx: ByteArray): ByteArray { + check(channels.contains(handle)) { + "Invalid logical channel handle $handle" + } if (runBlocking { verboseLoggingFlow.first() }) { Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}") } - - val cla = tx[0].toUByte().toInt() - val instruction = tx[1].toUByte().toInt() - val p1 = tx[2].toUByte().toInt() - val p2 = tx[3].toUByte().toInt() - val p3 = tx[4].toUByte().toInt() - val p4 = tx.drop(5).toByteArray().encodeHex() - - return tm.iccTransmitApduLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, lastChannel, - cla, - instruction, - p1, - p2, - p3, - p4 - ).also { - if (runBlocking { verboseLoggingFlow.first() }) { - Log.d(TAG, "TelephonyManager APDU response: $it") - } - }?.decodeHex() ?: byteArrayOf() + val result = tm.iccTransmitApduLogicalChannelByPortCompat( + port.card.physicalSlotIndex, port.portIndex, handle, + tx, + ) + if (runBlocking { verboseLoggingFlow.first() }) + Log.d(TAG, "TelephonyManager APDU response: $result") + return result?.decodeHex() ?: byteArrayOf() } - } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt index dbd39f2..a9df18e 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyCompat.kt @@ -111,15 +111,26 @@ fun TelephonyManager.iccCloseLogicalChannelByPortCompat( } fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat( - slotIndex: Int, portIndex: Int, channel: Int, - cla: Int, inst: Int, p1: Int, p2: Int, p3: Int, data: String? -): String? = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + slotIndex: Int, + portIndex: Int, + channel: Int, + tx: ByteArray +): String? { + val cla = tx[0].toUByte().toInt() + val ins = tx[1].toUByte().toInt() + val p1 = tx[2].toUByte().toInt() + val p2 = tx[3].toUByte().toInt() + val p3 = tx[4].toUByte().toInt() + val p4 = tx.drop(5).toByteArray().encodeHex() + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { iccTransmitApduLogicalChannelByPort( - slotIndex, portIndex, channel, cla, inst, p1, p2, p3, data + slotIndex, portIndex, channel, + cla, ins, p1, p2, p3, p4 ) } else { iccTransmitApduLogicalChannelBySlot( - slotIndex, channel, cla, inst, p1, p2, p3, data + slotIndex, channel, + cla, ins, p1, p2, p3, p4 ) } +} diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt index dfa92df..ced7ff1 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt @@ -8,7 +8,7 @@ interface ApduInterface { fun disconnect() fun logicalChannelOpen(aid: ByteArray): Int fun logicalChannelClose(handle: Int) - fun transmit(tx: ByteArray): ByteArray + fun transmit(handle: Int, tx: ByteArray): ByteArray /** * Is this APDU connection still valid? @@ -16,4 +16,19 @@ interface ApduInterface { * callers should further check with the LPA to fully determine the validity of a channel */ val valid: Boolean + + fun openChannel(aid: ByteArray, callback: (TransmitProvider) -> T): T { + val handle = logicalChannelOpen(aid) + return try { + callback(object : TransmitProvider { + override fun transmit(tx: ByteArray) = transmit(handle, tx) + }) + } finally { + logicalChannelClose(handle) + } + } +} + +interface TransmitProvider { + fun transmit(tx: ByteArray): ByteArray } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt index 8aafe94..faf5312 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt @@ -28,9 +28,9 @@ class LocalProfileAssistantImpl( var lastApduResponse: ByteArray? = null var lastApduException: Exception? = null - override fun transmit(tx: ByteArray): ByteArray = + override fun transmit(handle: Int, tx: ByteArray): ByteArray = try { - apduInterface.transmit(tx).also { + apduInterface.transmit(handle, tx).also { lastApduException = null lastApduResponse = it } diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c index 9059171..b51f6a8 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c @@ -22,7 +22,7 @@ void interface_wrapper_init() { "([B)I"); method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose", "(I)V"); - method_apdu_transmit = (*env)->GetMethodID(env, apdu_class, "transmit", "([B)[B"); + method_apdu_transmit = (*env)->GetMethodID(env, apdu_class, "transmit", "(I[B)[B"); jclass http_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface"); method_http_transmit = (*env)->GetMethodID(env, http_class, "transmit", @@ -66,11 +66,14 @@ static void apdu_interface_logical_channel_close(struct euicc_ctx *ctx, uint8_t static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { + const int logic_channel = ctx->apdu._internal.logic_channel; LPAC_JNI_SETUP_ENV; jbyteArray txArr = (*env)->NewByteArray(env, tx_len); (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); - jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface, - method_apdu_transmit, txArr); + jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod( + env, LPAC_JNI_CTX(ctx)->apdu_interface, + method_apdu_transmit, logic_channel, txArr + ); LPAC_JNI_EXCEPTION_RETURN; *rx_len = (*env)->GetArrayLength(env, ret); *rx = calloc(*rx_len, sizeof(uint8_t)); -- 2.45.3 From 989e43bba6445c8c6ed36940b0fb25e0eda9b37c Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 19:50:55 +0800 Subject: [PATCH 2/6] revert: EuiccChannelWrapper --- .../openeuicc/core/EuiccChannelWrapper.kt | 25 +++++++++++++++++-- .../core/LocalProfileAssistantWrapper.kt | 4 +-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt index 1a108e5..09004d3 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelWrapper.kt @@ -1,8 +1,10 @@ package im.angry.openeuicc.core +import im.angry.openeuicc.util.* +import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.LocalProfileAssistant -class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel by orig { +class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { private var _inner: EuiccChannel? = orig private val channel: EuiccChannel @@ -14,11 +16,30 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel by orig { return _inner!! } + override val type: String + get() = channel.type + override val port: UiccPortInfoCompat + get() = channel.port + override val slotId: Int + get() = channel.slotId + override val logicalSlotId: Int + get() = channel.logicalSlotId + override val portId: Int + get() = channel.portId private val lpaDelegate = lazy { LocalProfileAssistantWrapper(channel.lpa) } - override val lpa: LocalProfileAssistant by lpaDelegate + override val valid: Boolean + get() = channel.valid + override val intrinsicChannelName: String? + get() = channel.intrinsicChannelName + override val apduInterface: ApduInterface + get() = channel.apduInterface + override val atr: ByteArray? + get() = channel.atr + + override fun close() = channel.close() fun invalidateWrapper() { _inner = null diff --git a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt index b715ca0..145b5f7 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt @@ -6,10 +6,8 @@ import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.ProfileDownloadCallback -class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : +class LocalProfileAssistantWrapper(private var _inner: LocalProfileAssistant?) : LocalProfileAssistant { - private var _inner: LocalProfileAssistant? = orig - private val lpa: LocalProfileAssistant get() { if (_inner == null) { -- 2.45.3 From 1f6ec17ab506c4d3c6edf4db03f44b54b79d8cff Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 20:39:45 +0800 Subject: [PATCH 3/6] revert: partial code --- .../im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt index 145b5f7..b715ca0 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt @@ -6,8 +6,10 @@ import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.ProfileDownloadCallback -class LocalProfileAssistantWrapper(private var _inner: LocalProfileAssistant?) : +class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : LocalProfileAssistant { + private var _inner: LocalProfileAssistant? = orig + private val lpa: LocalProfileAssistant get() { if (_inner == null) { -- 2.45.3 From 2768bb14f822da9601e27937052803feee801bb1 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 21:01:04 +0800 Subject: [PATCH 4/6] chore: accept reviews --- libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c | 9 ++++++--- libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c | 2 +- libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c index b51f6a8..a61fc96 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c @@ -53,20 +53,23 @@ apdu_interface_logical_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, u jint ret = (*env)->CallIntMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface, method_apdu_logical_channel_open, jbarr); LPAC_JNI_EXCEPTION_RETURN; + LPAC_JNI_CTX(ctx)->logical_channel_id = ret; return ret; } -static void apdu_interface_logical_channel_close(struct euicc_ctx *ctx, uint8_t channel) { +static void apdu_interface_logical_channel_close(struct euicc_ctx *ctx, + __attribute__((unused)) uint8_t channel) { LPAC_JNI_SETUP_ENV; + jint logical_channel_id = LPAC_JNI_CTX(ctx)->logical_channel_id; (*env)->CallVoidMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface, - method_apdu_logical_channel_close, channel); + method_apdu_logical_channel_close, logical_channel_id); (*env)->ExceptionClear(env); } static int apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx, uint32_t tx_len) { - const int logic_channel = ctx->apdu._internal.logic_channel; + const int logic_channel = LPAC_JNI_CTX(ctx)->logical_channel_id; LPAC_JNI_SETUP_ENV; jbyteArray txArr = (*env)->NewByteArray(env, tx_len); (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c index 6ea9d3e..ca319db 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c @@ -28,7 +28,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { string_constructor = (*env)->GetMethodID(env, string_class, "", "([BLjava/lang/String;)V"); - const char _unused[1]; + const jchar _unused[1]; empty_string = (*env)->NewString(env, _unused, 0); empty_string = (*env)->NewGlobalRef(env, empty_string); diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h index a5d6262..c2300be 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h @@ -8,6 +8,7 @@ _Static_assert(sizeof(void *) <= sizeof(jlong), "jlong must be big enough to hold a platform raw pointer"); struct lpac_jni_ctx { + jint logical_channel_id; jobject apdu_interface; jobject http_interface; }; -- 2.45.3 From 25013c2f0809b7cfab790dfefd140b0d62f84183 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 21:25:36 +0800 Subject: [PATCH 5/6] chore: accept reviews --- .../openeuicc/core/OmapiApduInterface.kt | 26 ++++++++-------- .../net/typeblog/lpac_jni/ApduInterface.kt | 30 ++++++++++++------- 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt index bbf8fdd..b3f42b5 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt @@ -9,7 +9,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import net.typeblog.lpac_jni.ApduInterface -import java.util.concurrent.atomic.AtomicInteger class OmapiApduInterface( private val service: SEService, @@ -21,8 +20,12 @@ class OmapiApduInterface( } private lateinit var session: Session - private val channels = mutableMapOf() - private val index = AtomicInteger(0) + private val channels = arrayOf( + null, + null, + null, + null, + ) override val valid: Boolean get() = service.isConnected && (this::session.isInitialized && !session.isClosed) @@ -41,23 +44,22 @@ class OmapiApduInterface( override fun logicalChannelOpen(aid: ByteArray): Int { val channel = session.openLogicalChannel(aid) check(channel != null) { "Failed to open logical channel (${aid.encodeHex()})" } - val id = index.addAndGet(1) - channels[id] = channel - return id + val index = channels.indexOf(null) + check(index != -1) { "No free logical channel slots" } + synchronized(channels) { channels[index] = channel } + return index } override fun logicalChannelClose(handle: Int) { - val channel = channels[handle] + val channel = channels.getOrNull(handle) check(channel != null) { "Invalid logical channel handle $handle" } - channels.remove(handle) if (channel.isOpen) channel.close() + synchronized(channels) { channels[handle] = null } } override fun transmit(handle: Int, tx: ByteArray): ByteArray { - val channel = channels[handle] - check(channel != null) { - "Invalid logical channel handle $handle" - } + val channel = channels.getOrNull(handle) + check(channel != null) { "Invalid logical channel handle $handle" } if (runBlocking { verboseLoggingFlow.first() }) { Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}") diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt index ced7ff1..30787a2 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt @@ -1,5 +1,7 @@ package net.typeblog.lpac_jni +import java.io.Closeable + /* * Should reflect euicc_apdu_interface in lpac/euicc/interface.h */ @@ -17,18 +19,26 @@ interface ApduInterface { */ val valid: Boolean - fun openChannel(aid: ByteArray, callback: (TransmitProvider) -> T): T { + fun withLogicalChannel(aid: ByteArray, callback: (ApduLogicalChannelHandle) -> T): T { val handle = logicalChannelOpen(aid) - return try { - callback(object : TransmitProvider { - override fun transmit(tx: ByteArray) = transmit(handle, tx) - }) - } finally { - logicalChannelClose(handle) - } + return ApduLogicalChannelHandle(handle, this).use(callback) } } -interface TransmitProvider { - fun transmit(tx: ByteArray): ByteArray +data class ApduLogicalChannelHandle( + private val handle: Int, + private val apduInterface: ApduInterface, +) : Closeable { + private var closed: Boolean = false + + fun transmit(tx: ByteArray) { + check(closed) { "Logical channel is already closed" } + apduInterface.transmit(handle, tx) + } + + override fun close() { + if (closed) return + apduInterface.logicalChannelClose(handle) + closed = true + } } \ No newline at end of file -- 2.45.3 From 3df506985c5a4715f44ff1d2f1dc6a06b4387f91 Mon Sep 17 00:00:00 2001 From: septs Date: Wed, 26 Feb 2025 21:32:04 +0800 Subject: [PATCH 6/6] chore: accept reviews --- .../net/typeblog/lpac_jni/ApduInterface.kt | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt index 30787a2..75a6905 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt @@ -1,7 +1,5 @@ package net.typeblog.lpac_jni -import java.io.Closeable - /* * Should reflect euicc_apdu_interface in lpac/euicc/interface.h */ @@ -19,26 +17,12 @@ interface ApduInterface { */ val valid: Boolean - fun withLogicalChannel(aid: ByteArray, callback: (ApduLogicalChannelHandle) -> T): T { + fun withLogicalChannel(aid: ByteArray, cb: ((ByteArray) -> ByteArray) -> T): T { val handle = logicalChannelOpen(aid) - return ApduLogicalChannelHandle(handle, this).use(callback) + return try { + cb { transmit(handle, it) } + } finally { + logicalChannelClose(handle) + } } } - -data class ApduLogicalChannelHandle( - private val handle: Int, - private val apduInterface: ApduInterface, -) : Closeable { - private var closed: Boolean = false - - fun transmit(tx: ByteArray) { - check(closed) { "Logical channel is already closed" } - apduInterface.transmit(handle, tx) - } - - override fun close() { - if (closed) return - apduInterface.logicalChannelClose(handle) - closed = true - } -} \ No newline at end of file -- 2.45.3