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 cf8b6d0..0ecca21 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 @@ -30,7 +30,10 @@ interface EuiccChannel { */ val intrinsicChannelName: String? - fun forkApduInterface(): ApduInterface + /** + * 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 f49ac6f..e5b3ce7 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 @@ -13,7 +13,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 { @@ -36,8 +36,6 @@ class EuiccChannelImpl( override val atr: ByteArray? get() = (apduInterface as? ApduInterfaceAtrProvider)?.atr - override fun forkApduInterface() = apduInterface.clone() - override val valid: Boolean get() = lpa.valid 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 7fd7188..c7fed90 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,24 +39,21 @@ 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)!! + val id = index.getAndAdd(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) { "Channel $handle does not exist" } + 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) { "Channel $handle does not exist" } if (runBlocking { verboseLoggingFlow.first() }) { Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}") @@ -63,7 +61,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()}") } @@ -83,10 +81,4 @@ class OmapiApduInterface( throw e } } - - override fun clone() = OmapiApduInterface( - service, - port, - verboseLoggingFlow, - ) } \ No newline at end of file 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 8bb6470..2a01c9e 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,8 +21,6 @@ class UsbApduInterface( private lateinit var ccidDescription: UsbCcidDescription private lateinit var transceiver: UsbCcidTransceiver - private var channelId = -1 - override var atr: ByteArray? = null override fun connect() { @@ -46,11 +44,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 +64,7 @@ class UsbApduInterface( return -1 } - channelId = resp[0].toInt() + val channelId = resp[0].toInt() Log.d(TAG, "channelId = $channelId") // Then, select AID @@ -82,27 +80,21 @@ class UsbApduInterface( } override fun logicalChannelClose(handle: Int) { - check(handle == channelId) { "Logical channel ID mismatch" } - check(channelId != -1) { "Logical channel is not opened" } - // 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 } - 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 { + return transmitApduByChannel(tx, handle.toByte()) } override val valid: Boolean - get() = channelId != -1 + get() = atr != null private fun isSuccessResponse(resp: ByteArray): Boolean = resp.size >= 2 && resp[resp.size - 2] == 0x90.toByte() && resp[resp.size - 1] == 0x00.toByte() @@ -169,11 +161,4 @@ class UsbApduInterface( return resp } - - override fun clone() = UsbApduInterface( - conn, - bulkIn, - bulkOut, - verboseLoggingFlow, - ) } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt index 5123d53..fb68c01 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidDescription.kt @@ -23,9 +23,9 @@ data class UsbCcidDescription( private const val FEATURE_EXCHAGE_LEVEL_EXTENDED_APDU = 0x40000 // bVoltageSupport Masks - private const val VOLTAGE_5V: Byte = 1 - private const val VOLTAGE_3V: Byte = 2 - private const val VOLTAGE_1_8V: Byte = 4 + private const val VOLTAGE_5V0: Byte = 1 + private const val VOLTAGE_3V0: Byte = 2 + private const val VOLTAGE_1V8: Byte = 4 private const val SLOT_OFFSET = 4 private const val FEATURES_OFFSET = 40 @@ -71,10 +71,12 @@ data class UsbCcidDescription( } enum class Voltage(powerOnValue: Int, mask: Int) { - AUTO(0, 0), _5V(1, VOLTAGE_5V.toInt()), _3V(2, VOLTAGE_3V.toInt()), _1_8V( - 3, - VOLTAGE_1_8V.toInt() - ); + // @formatter:off + AUTO(0, 0), + V50(1, VOLTAGE_5V0.toInt()), + V30(2, VOLTAGE_3V0.toInt()), + V18(3, VOLTAGE_1V8.toInt()); + // @formatter:on val mask = powerOnValue.toByte() val powerOnValue = mask.toByte() @@ -84,18 +86,14 @@ data class UsbCcidDescription( (dwFeatures and feature) != 0 val voltages: Array - get() = + get() { if (hasFeature(FEATURE_AUTOMATIC_VOLTAGE)) { - arrayOf(Voltage.AUTO) - } else { - Voltage.values().mapNotNull { - if ((it.mask.toInt() and bVoltageSupport.toInt()) != 0) { - it - } else { - null - } - }.toTypedArray() + return arrayOf(Voltage.AUTO) } + return Voltage.entries + .mapNotNull { if ((it.mask.toInt() and bVoltageSupport.toInt()) != 0) it else null } + .toTypedArray() + } val hasAutomaticPps: Boolean get() = hasFeature(FEATURE_AUTOMATIC_PPS) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt index 5ef35af..5d86a68 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidTransceiver.kt @@ -183,31 +183,26 @@ class UsbCcidTransceiver( usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS ) if (runBlocking { verboseLoggingFlow.first() }) { - Log.d(TAG, "Received " + readBytes + " bytes: " + inputBuffer.encodeHex()) + Log.d(TAG, "Received $readBytes bytes: ${inputBuffer.encodeHex()}") } } while (readBytes <= 0 && attempts-- > 0) if (readBytes < CCID_HEADER_LENGTH) { throw UsbTransportException("USB-CCID error - failed to receive CCID header") } if (inputBuffer[0] != MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK.toByte()) { - if (expectedSequenceNumber != inputBuffer[6]) { - throw UsbTransportException( - ((("USB-CCID error - bad CCID header, type " + inputBuffer[0]) + " (expected " + - MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK) + "), sequence number " + inputBuffer[6] - ) + " (expected " + - expectedSequenceNumber + ")" - ) - } - throw UsbTransportException( - "USB-CCID error - bad CCID header type " + inputBuffer[0] - ) + throw UsbTransportException(buildString { + append("USB-CCID error - bad CCID header") + append(", type ") + append("%d (expected %d)".format(inputBuffer[0], MESSAGE_TYPE_RDR_TO_PC_DATA_BLOCK)) + if (expectedSequenceNumber != inputBuffer[6]) { + append(", sequence number ") + append("%d (expected %d)".format(inputBuffer[6], expectedSequenceNumber)) + } + }) } var result = CcidDataBlock.parseHeaderFromBytes(inputBuffer) if (expectedSequenceNumber != result.bSeq) { - throw UsbTransportException( - ("USB-CCID error - expected sequence number " + - expectedSequenceNumber + ", got " + result) - ) + throw UsbTransportException("USB-CCID error - expected sequence number $expectedSequenceNumber, got $result") } val dataBuffer = ByteArray(result.dwLength) @@ -218,9 +213,7 @@ class UsbCcidTransceiver( usbBulkIn, inputBuffer, inputBuffer.size, DEVICE_COMMUNICATE_TIMEOUT_MILLIS ) if (readBytes < 0) { - throw UsbTransportException( - "USB error - failed reading response data! Header: $result" - ) + throw UsbTransportException("USB error - failed reading response data! Header: $result") } System.arraycopy(inputBuffer, 0, dataBuffer, bufferedBytes, readBytes) bufferedBytes += readBytes @@ -285,7 +278,7 @@ class UsbCcidTransceiver( } val ccidDataBlock = receiveDataBlock(sequenceNumber) val elapsedTime = SystemClock.elapsedRealtime() - startTime - Log.d(TAG, "USB XferBlock call took " + elapsedTime + "ms") + Log.d(TAG, "USB XferBlock call took ${elapsedTime}ms") return ccidDataBlock } @@ -314,8 +307,11 @@ class UsbCcidTransceiver( val elapsedTime = SystemClock.elapsedRealtime() - startTime Log.d( TAG, - "Usb transport connected, took " + elapsedTime + "ms, ATR=" + - response.data?.encodeHex() + buildString { + append("Usb transport connected") + append(", took ", elapsedTime, "ms") + append(", ATR=", response.data?.encodeHex()) + } ) return response } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt index edca7a0..65c7d67 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/usb/UsbCcidUtils.kt @@ -6,31 +6,22 @@ import android.hardware.usb.UsbDevice import android.hardware.usb.UsbEndpoint import android.hardware.usb.UsbInterface -class UsbTransportException(msg: String) : Exception(msg) +class UsbTransportException(message: String) : Exception(message) + +val UsbDevice.interfaces: Iterable + get() = (0 until interfaceCount).map(::getInterface) + +val UsbInterface.endpoints: Iterable + get() = (0 until endpointCount).map(::getEndpoint) fun UsbInterface.getIoEndpoints(): Pair { - var bulkIn: UsbEndpoint? = null - var bulkOut: UsbEndpoint? = null - for (i in 0 until endpointCount) { - val endpoint = getEndpoint(i) - if (endpoint.type != UsbConstants.USB_ENDPOINT_XFER_BULK) { - continue - } - if (endpoint.direction == UsbConstants.USB_DIR_IN) { - bulkIn = endpoint - } else if (endpoint.direction == UsbConstants.USB_DIR_OUT) { - bulkOut = endpoint - } - } - return Pair(bulkIn, bulkOut) + val endpoints = endpoints + .filter { it.type == UsbConstants.USB_ENDPOINT_XFER_BULK } + return Pair( + endpoints.first { it.direction == UsbConstants.USB_DIR_IN }, + endpoints.first { it.direction == UsbConstants.USB_DIR_OUT }, + ) } -fun UsbDevice.getSmartCardInterface(): UsbInterface? { - for (i in 0 until interfaceCount) { - val anInterface = getInterface(i) - if (anInterface.interfaceClass == UsbConstants.USB_CLASS_CSCID) { - return anInterface - } - } - return null -} \ No newline at end of file +fun UsbDevice.getSmartCardInterface() = + interfaces.first { it.interfaceClass == UsbConstants.USB_CLASS_CSCID } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt index 124bcdd..528b232 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt @@ -104,7 +104,7 @@ 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.forkApduInterface())?.let { + 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)) diff --git a/app-common/src/main/java/im/angry/openeuicc/vendored/estkme.kt b/app-common/src/main/java/im/angry/openeuicc/vendored/estkme.kt index b805b89..4d32fb8 100644 --- a/app-common/src/main/java/im/angry/openeuicc/vendored/estkme.kt +++ b/app-common/src/main/java/im/angry/openeuicc/vendored/estkme.kt @@ -1,6 +1,7 @@ package im.angry.openeuicc.vendored import android.util.Log +import im.angry.openeuicc.util.TAG import net.typeblog.lpac_jni.ApduInterface data class ESTKmeInfo( @@ -19,18 +20,18 @@ fun getESTKmeInfo(iface: ApduInterface): ESTKmeInfo? { ) // @formatter:on return try { - iface.connect() - iface.logicalChannelOpen(aid) - fun transmit(p1: Byte) = - decode(iface.transmit(byteArrayOf(0x00, 0x00, p1, 0x00, 0x00)).clone()) - return ESTKmeInfo( - transmit(0x00), // serial number - transmit(0x01), // bootloader version - transmit(0x02), // firmware version - transmit(0x03), // sku name - ) + iface.openChannel(aid) { 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("ESTKme", "Failed to get ESTKme info", e) + Log.d(TAG, "Failed to get ESTKmeInfo", e) null } } 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 265bd6a..b7a9482 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,7 @@ 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 + override var valid: Boolean = false override fun connect() { // Do nothing @@ -31,29 +26,24 @@ 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}") } - lastChannel = channel.channel - return lastChannel + valid = true + return channel.channel } override fun logicalChannelClose(handle: Int) { - check(handle == lastChannel) { "Invalid channel handle " } tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle) - lastChannel = -1 + valid = false } - override fun transmit(tx: ByteArray): ByteArray { - check(lastChannel != -1) { "Uninitialized" } - + override fun transmit(handle: Int, tx: ByteArray): ByteArray { if (runBlocking { verboseLoggingFlow.first() }) { Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}") } @@ -67,7 +57,7 @@ class TelephonyManagerApduInterface( // @formatter:off val result = tm.iccTransmitApduLogicalChannelByPortCompat( - port.card.physicalSlotIndex, port.portIndex, lastChannel, + port.card.physicalSlotIndex, port.portIndex, handle, cla, ins, p1, p2, p3, p4 ) // @formatter:on @@ -75,10 +65,4 @@ class TelephonyManagerApduInterface( Log.d(TAG, "TelephonyManager APDU response: $result") return result?.decodeHex() ?: byteArrayOf() } - - override fun clone() = TelephonyManagerApduInterface( - port, - tm, - verboseLoggingFlow - ) } \ No newline at end of file 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 8075779..983e898 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 @@ -3,13 +3,12 @@ package net.typeblog.lpac_jni /* * Should reflect euicc_apdu_interface in lpac/euicc/interface.h */ -interface ApduInterface : Cloneable { +interface ApduInterface { fun connect() fun disconnect() fun logicalChannelOpen(aid: ByteArray): Int fun logicalChannelClose(handle: Int) - fun transmit(tx: ByteArray): ByteArray - public override fun clone(): ApduInterface + fun transmit(handle: Int, tx: ByteArray): ByteArray /** * Is this APDU connection still valid? @@ -17,4 +16,16 @@ interface ApduInterface : Cloneable { * callers should further check with the LPA to fully determine the validity of a channel */ val valid: Boolean -} \ No newline at end of file + + fun openChannel( + aid: ByteArray, + callback: (transmit: (ByteArray) -> ByteArray) -> T + ): T { + val handle = logicalChannelOpen(aid) + return try { + callback { transmit(handle, it) } + } finally { + logicalChannelClose(handle) + } + } +} 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 827a3b5..12b5d7e 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 @@ -29,9 +29,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));