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..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,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 class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { @@ -33,6 +34,8 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel { get() = channel.valid override val intrinsicChannelName: String? get() = channel.intrinsicChannelName + override val apduInterface: ApduInterface + get() = channel.apduInterface override val atr: ByteArray? get() = channel.atr 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..a6a5cf0 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,8 +21,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 +45,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..3689363 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 @@ -17,18 +17,12 @@ interface ApduInterface { */ val valid: Boolean - fun openChannel(aid: ByteArray, callback: (TransmitProvider) -> T): T { + fun withLogicalChannel(aid: ByteArray, callback: ((ByteArray) -> ByteArray) -> T): T { val handle = logicalChannelOpen(aid) return try { - callback(object : TransmitProvider { - override fun transmit(tx: ByteArray) = transmit(handle, tx) - }) + callback { tx -> 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/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; };