feat: supports for multi logical channel #148
12 changed files with 117 additions and 88 deletions
|
@ -1,6 +1,7 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
|
|
||||||
interface EuiccChannel {
|
interface EuiccChannel {
|
||||||
|
@ -28,5 +29,10 @@ interface EuiccChannel {
|
||||||
*/
|
*/
|
||||||
val intrinsicChannelName: String?
|
val intrinsicChannelName: String?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The underlying APDU interface for this channel
|
||||||
|
*/
|
||||||
|
val apduInterface: ApduInterface
|
||||||
|
|
||||||
fun close()
|
fun close()
|
||||||
}
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package im.angry.openeuicc.core
|
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 kotlinx.coroutines.flow.Flow
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
|
@ -11,7 +12,7 @@ class EuiccChannelImpl(
|
||||||
override val type: String,
|
override val type: String,
|
||||||
override val port: UiccPortInfoCompat,
|
override val port: UiccPortInfoCompat,
|
||||||
override val intrinsicChannelName: String?,
|
override val intrinsicChannelName: String?,
|
||||||
private val apduInterface: ApduInterface,
|
override val apduInterface: ApduInterface,
|
||||||
verboseLoggingFlow: Flow<Boolean>,
|
verboseLoggingFlow: Flow<Boolean>,
|
||||||
ignoreTLSCertificateFlow: Flow<Boolean>
|
ignoreTLSCertificateFlow: Flow<Boolean>
|
||||||
) : EuiccChannel {
|
) : EuiccChannel {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
|
|
||||||
class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel {
|
class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel {
|
||||||
|
@ -33,6 +34,8 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel {
|
||||||
get() = channel.valid
|
get() = channel.valid
|
||||||
override val intrinsicChannelName: String?
|
override val intrinsicChannelName: String?
|
||||||
get() = channel.intrinsicChannelName
|
get() = channel.intrinsicChannelName
|
||||||
|
override val apduInterface: ApduInterface
|
||||||
|
get() = channel.apduInterface
|
||||||
override val atr: ByteArray?
|
override val atr: ByteArray?
|
||||||
get() = channel.atr
|
get() = channel.atr
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.util.Log
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.single
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
|
|
||||||
|
@ -21,7 +20,12 @@ class OmapiApduInterface(
|
||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var session: Session
|
private lateinit var session: Session
|
||||||
private lateinit var lastChannel: Channel
|
private val channels = arrayOf<Channel?>(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
|
||||||
override val valid: Boolean
|
override val valid: Boolean
|
||||||
get() = service.isConnected && (this::session.isInitialized && !session.isClosed)
|
get() = service.isConnected && (this::session.isInitialized && !session.isClosed)
|
||||||
|
@ -38,24 +42,24 @@ class OmapiApduInterface(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
check(!this::lastChannel.isInitialized) {
|
val channel = session.openLogicalChannel(aid)
|
||||||
"Can only open one channel"
|
check(channel != null) { "Failed to open logical channel (${aid.encodeHex()})" }
|
||||||
}
|
val index = channels.indexOf(null)
|
||||||
lastChannel = session.openLogicalChannel(aid)!!
|
check(index != -1) { "No free logical channel slots" }
|
||||||
return 1
|
synchronized(channels) { channels[index] = channel }
|
||||||
|
return index
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelClose(handle: Int) {
|
override fun logicalChannelClose(handle: Int) {
|
||||||
check(handle == 1 && !this::lastChannel.isInitialized) {
|
val channel = channels.getOrNull(handle)
|
||||||
"Unknown channel"
|
check(channel != null) { "Invalid logical channel handle $handle" }
|
||||||
}
|
if (channel.isOpen) channel.close()
|
||||||
lastChannel.close()
|
synchronized(channels) { channels[handle] = null }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(handle: Int, tx: ByteArray): ByteArray {
|
||||||
check(this::lastChannel.isInitialized) {
|
val channel = channels.getOrNull(handle)
|
||||||
"Unknown channel"
|
check(channel != null) { "Invalid logical channel handle $handle" }
|
||||||
}
|
|
||||||
|
|
||||||
if (runBlocking { verboseLoggingFlow.first() }) {
|
if (runBlocking { verboseLoggingFlow.first() }) {
|
||||||
Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}")
|
Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}")
|
||||||
|
@ -63,7 +67,7 @@ class OmapiApduInterface(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (i in 0..10) {
|
for (i in 0..10) {
|
||||||
val res = lastChannel.transmit(tx)
|
val res = channel.transmit(tx)
|
||||||
if (runBlocking { verboseLoggingFlow.first() }) {
|
if (runBlocking { verboseLoggingFlow.first() }) {
|
||||||
Log.d(TAG, "OMAPI APDU response: ${res.encodeHex()}")
|
Log.d(TAG, "OMAPI APDU response: ${res.encodeHex()}")
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,13 @@ class UsbApduInterface(
|
||||||
private lateinit var ccidDescription: UsbCcidDescription
|
private lateinit var ccidDescription: UsbCcidDescription
|
||||||
private lateinit var transceiver: UsbCcidTransceiver
|
private lateinit var transceiver: UsbCcidTransceiver
|
||||||
|
|
||||||
private var channelId = -1
|
|
||||||
|
|
||||||
override var atr: ByteArray? = null
|
override var atr: ByteArray? = null
|
||||||
|
|
||||||
|
override val valid: Boolean
|
||||||
|
get() = channels.isNotEmpty()
|
||||||
|
|
||||||
|
private var channels = mutableSetOf<Int>()
|
||||||
|
|
||||||
override fun connect() {
|
override fun connect() {
|
||||||
ccidDescription = UsbCcidDescription.fromRawDescriptors(conn.rawDescriptors)!!
|
ccidDescription = UsbCcidDescription.fromRawDescriptors(conn.rawDescriptors)!!
|
||||||
|
|
||||||
|
@ -46,11 +49,11 @@ class UsbApduInterface(
|
||||||
|
|
||||||
override fun disconnect() {
|
override fun disconnect() {
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
atr = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
check(channelId == -1) { "Logical channel already opened" }
|
|
||||||
|
|
||||||
// OPEN LOGICAL CHANNEL
|
// OPEN LOGICAL CHANNEL
|
||||||
val req = manageChannelCmd(true, 0)
|
val req = manageChannelCmd(true, 0)
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ class UsbApduInterface(
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
channelId = resp[0].toInt()
|
val channelId = resp[0].toInt()
|
||||||
Log.d(TAG, "channelId = $channelId")
|
Log.d(TAG, "channelId = $channelId")
|
||||||
|
|
||||||
// Then, select AID
|
// Then, select AID
|
||||||
|
@ -78,31 +81,31 @@ class UsbApduInterface(
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channels.add(channelId)
|
||||||
|
|
||||||
return channelId
|
return channelId
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelClose(handle: Int) {
|
override fun logicalChannelClose(handle: Int) {
|
||||||
check(handle == channelId) { "Logical channel ID mismatch" }
|
check(channels.contains(handle)) {
|
||||||
check(channelId != -1) { "Logical channel is not opened" }
|
"Invalid logical channel handle $handle"
|
||||||
|
}
|
||||||
// CLOSE LOGICAL CHANNEL
|
// CLOSE LOGICAL CHANNEL
|
||||||
val req = manageChannelCmd(false, channelId.toByte())
|
val req = manageChannelCmd(false, handle.toByte())
|
||||||
val resp = transmitApduByChannel(req, channelId.toByte())
|
val resp = transmitApduByChannel(req, handle.toByte())
|
||||||
|
|
||||||
if (!isSuccessResponse(resp)) {
|
if (!isSuccessResponse(resp)) {
|
||||||
Log.d(TAG, "CLOSE LOGICAL CHANNEL failed: ${resp.encodeHex()}")
|
Log.d(TAG, "CLOSE LOGICAL CHANNEL failed: ${resp.encodeHex()}")
|
||||||
}
|
}
|
||||||
|
channels.remove(handle)
|
||||||
channelId = -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(handle: Int, tx: ByteArray): ByteArray {
|
||||||
check(channelId != -1) { "Logical channel is not opened" }
|
check(channels.contains(handle)) {
|
||||||
return transmitApduByChannel(tx, channelId.toByte())
|
"Invalid logical channel handle $handle"
|
||||||
|
}
|
||||||
|
return transmitApduByChannel(tx, handle.toByte())
|
||||||
}
|
}
|
||||||
|
|
||||||
override val valid: Boolean
|
|
||||||
get() = channelId != -1
|
|
||||||
|
|
||||||
private fun isSuccessResponse(resp: ByteArray): Boolean =
|
private fun isSuccessResponse(resp: ByteArray): Boolean =
|
||||||
resp.size >= 2 && resp[resp.size - 2] == 0x90.toByte() && resp[resp.size - 1] == 0x00.toByte()
|
resp.size >= 2 && resp[resp.size - 2] == 0x90.toByte() && resp[resp.size - 1] == 0x00.toByte()
|
||||||
|
|
|
@ -18,12 +18,10 @@ class TelephonyManagerApduInterface(
|
||||||
const val TAG = "TelephonyManagerApduInterface"
|
const val TAG = "TelephonyManagerApduInterface"
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lastChannel: Int = -1
|
|
||||||
|
|
||||||
override val valid: Boolean
|
override val valid: Boolean
|
||||||
// TelephonyManager channels will never become truly "invalid",
|
get() = channels.isNotEmpty()
|
||||||
// just that transactions might return errors or nonsense
|
|
||||||
get() = lastChannel != -1
|
private var channels = mutableSetOf<Int>()
|
||||||
|
|
||||||
override fun connect() {
|
override fun connect() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -31,52 +29,39 @@ class TelephonyManagerApduInterface(
|
||||||
|
|
||||||
override fun disconnect() {
|
override fun disconnect() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
lastChannel = -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
check(lastChannel == -1) { "Already initialized" }
|
|
||||||
val hex = aid.encodeHex()
|
val hex = aid.encodeHex()
|
||||||
val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0)
|
val channel = tm.iccOpenLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, hex, 0)
|
||||||
if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
|
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
|
channels.add(channel.channel)
|
||||||
return lastChannel
|
return channel.channel
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelClose(handle: Int) {
|
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)
|
tm.iccCloseLogicalChannelByPortCompat(port.card.physicalSlotIndex, port.portIndex, handle)
|
||||||
lastChannel = -1
|
channels.remove(handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(handle: Int, tx: ByteArray): ByteArray {
|
||||||
check(lastChannel != -1) { "Uninitialized" }
|
check(channels.contains(handle)) {
|
||||||
|
"Invalid logical channel handle $handle"
|
||||||
|
}
|
||||||
if (runBlocking { verboseLoggingFlow.first() }) {
|
if (runBlocking { verboseLoggingFlow.first() }) {
|
||||||
Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}")
|
Log.d(TAG, "TelephonyManager APDU: ${tx.encodeHex()}")
|
||||||
}
|
}
|
||||||
|
val result = tm.iccTransmitApduLogicalChannelByPortCompat(
|
||||||
val cla = tx[0].toUByte().toInt()
|
port.card.physicalSlotIndex, port.portIndex, handle,
|
||||||
val instruction = tx[1].toUByte().toInt()
|
tx,
|
||||||
val p1 = tx[2].toUByte().toInt()
|
)
|
||||||
val p2 = tx[3].toUByte().toInt()
|
if (runBlocking { verboseLoggingFlow.first() })
|
||||||
val p3 = tx[4].toUByte().toInt()
|
Log.d(TAG, "TelephonyManager APDU response: $result")
|
||||||
val p4 = tx.drop(5).toByteArray().encodeHex()
|
return result?.decodeHex() ?: byteArrayOf()
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -111,15 +111,26 @@ fun TelephonyManager.iccCloseLogicalChannelByPortCompat(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat(
|
fun TelephonyManager.iccTransmitApduLogicalChannelByPortCompat(
|
||||||
slotIndex: Int, portIndex: Int, channel: Int,
|
slotIndex: Int,
|
||||||
cla: Int, inst: Int, p1: Int, p2: Int, p3: Int, data: String?
|
portIndex: Int,
|
||||||
): String? =
|
channel: Int,
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
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(
|
iccTransmitApduLogicalChannelByPort(
|
||||||
slotIndex, portIndex, channel, cla, inst, p1, p2, p3, data
|
slotIndex, portIndex, channel,
|
||||||
|
cla, ins, p1, p2, p3, p4
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
iccTransmitApduLogicalChannelBySlot(
|
iccTransmitApduLogicalChannelBySlot(
|
||||||
slotIndex, channel, cla, inst, p1, p2, p3, data
|
slotIndex, channel,
|
||||||
|
cla, ins, p1, p2, p3, p4
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ interface ApduInterface {
|
||||||
fun disconnect()
|
fun disconnect()
|
||||||
fun logicalChannelOpen(aid: ByteArray): Int
|
fun logicalChannelOpen(aid: ByteArray): Int
|
||||||
fun logicalChannelClose(handle: Int)
|
fun logicalChannelClose(handle: Int)
|
||||||
fun transmit(tx: ByteArray): ByteArray
|
fun transmit(handle: Int, tx: ByteArray): ByteArray
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this APDU connection still valid?
|
* Is this APDU connection still valid?
|
||||||
|
@ -16,4 +16,13 @@ interface ApduInterface {
|
||||||
* callers should further check with the LPA to fully determine the validity of a channel
|
* callers should further check with the LPA to fully determine the validity of a channel
|
||||||
*/
|
*/
|
||||||
val valid: Boolean
|
val valid: Boolean
|
||||||
|
|
||||||
|
fun <T> withLogicalChannel(aid: ByteArray, cb: ((ByteArray) -> ByteArray) -> T): T {
|
||||||
|
val handle = logicalChannelOpen(aid)
|
||||||
|
return try {
|
||||||
|
cb { transmit(handle, it) }
|
||||||
|
} finally {
|
||||||
|
logicalChannelClose(handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -28,9 +28,9 @@ class LocalProfileAssistantImpl(
|
||||||
var lastApduResponse: ByteArray? = null
|
var lastApduResponse: ByteArray? = null
|
||||||
var lastApduException: Exception? = null
|
var lastApduException: Exception? = null
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray =
|
override fun transmit(handle: Int, tx: ByteArray): ByteArray =
|
||||||
try {
|
try {
|
||||||
apduInterface.transmit(tx).also {
|
apduInterface.transmit(handle, tx).also {
|
||||||
lastApduException = null
|
lastApduException = null
|
||||||
lastApduResponse = it
|
lastApduResponse = it
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ void interface_wrapper_init() {
|
||||||
"([B)I");
|
"([B)I");
|
||||||
method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose",
|
method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose",
|
||||||
"(I)V");
|
"(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");
|
jclass http_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface");
|
||||||
method_http_transmit = (*env)->GetMethodID(env, http_class, "transmit",
|
method_http_transmit = (*env)->GetMethodID(env, http_class, "transmit",
|
||||||
|
@ -53,24 +53,30 @@ 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,
|
jint ret = (*env)->CallIntMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface,
|
||||||
method_apdu_logical_channel_open, jbarr);
|
method_apdu_logical_channel_open, jbarr);
|
||||||
LPAC_JNI_EXCEPTION_RETURN;
|
LPAC_JNI_EXCEPTION_RETURN;
|
||||||
|
LPAC_JNI_CTX(ctx)->logical_channel_id = ret;
|
||||||
return 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;
|
LPAC_JNI_SETUP_ENV;
|
||||||
|
jint logical_channel_id = LPAC_JNI_CTX(ctx)->logical_channel_id;
|
||||||
(*env)->CallVoidMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface,
|
(*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);
|
(*env)->ExceptionClear(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx,
|
apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, const uint8_t *tx,
|
||||||
uint32_t tx_len) {
|
uint32_t tx_len) {
|
||||||
|
const int logic_channel = LPAC_JNI_CTX(ctx)->logical_channel_id;
|
||||||
LPAC_JNI_SETUP_ENV;
|
LPAC_JNI_SETUP_ENV;
|
||||||
jbyteArray txArr = (*env)->NewByteArray(env, tx_len);
|
jbyteArray txArr = (*env)->NewByteArray(env, tx_len);
|
||||||
(*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx);
|
(*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx);
|
||||||
jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface,
|
jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod(
|
||||||
method_apdu_transmit, txArr);
|
env, LPAC_JNI_CTX(ctx)->apdu_interface,
|
||||||
|
method_apdu_transmit, logic_channel, txArr
|
||||||
|
);
|
||||||
LPAC_JNI_EXCEPTION_RETURN;
|
LPAC_JNI_EXCEPTION_RETURN;
|
||||||
*rx_len = (*env)->GetArrayLength(env, ret);
|
*rx_len = (*env)->GetArrayLength(env, ret);
|
||||||
*rx = calloc(*rx_len, sizeof(uint8_t));
|
*rx = calloc(*rx_len, sizeof(uint8_t));
|
||||||
|
|
|
@ -28,7 +28,7 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||||
string_constructor = (*env)->GetMethodID(env, string_class, "<init>",
|
string_constructor = (*env)->GetMethodID(env, string_class, "<init>",
|
||||||
"([BLjava/lang/String;)V");
|
"([BLjava/lang/String;)V");
|
||||||
|
|
||||||
const char _unused[1];
|
const jchar _unused[1];
|
||||||
empty_string = (*env)->NewString(env, _unused, 0);
|
empty_string = (*env)->NewString(env, _unused, 0);
|
||||||
empty_string = (*env)->NewGlobalRef(env, empty_string);
|
empty_string = (*env)->NewGlobalRef(env, empty_string);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ _Static_assert(sizeof(void *) <= sizeof(jlong),
|
||||||
"jlong must be big enough to hold a platform raw pointer");
|
"jlong must be big enough to hold a platform raw pointer");
|
||||||
|
|
||||||
struct lpac_jni_ctx {
|
struct lpac_jni_ctx {
|
||||||
|
jint logical_channel_id;
|
||||||
jobject apdu_interface;
|
jobject apdu_interface;
|
||||||
jobject http_interface;
|
jobject http_interface;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue