Compare commits

..

No commits in common. "c62e8bcecd80146022a7197cad2a983af5dc803b" and "bea5aa0dcfc978bcf43807f8226788c1c93b5cd7" have entirely different histories.

5 changed files with 68 additions and 57 deletions

View file

@ -67,11 +67,6 @@ android {
signingConfig signingConfigs.config
}
}
applicationVariants.all { variant ->
if (variant.name == "debug") {
variant.outputs.each { o -> o.versionCodeOverride = System.currentTimeSeconds() }
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8

View file

@ -21,16 +21,7 @@ abstract class EuiccChannel(
val removable = info.removable
abstract val lpa: LocalProfileAssistant
val valid: Boolean
get() {
try {
// Try to ping the eUICC card by reading the EID
lpa.eID
} catch (e: Exception) {
return false
}
return true
}
abstract val valid: Boolean
abstract fun close()
}

View file

@ -7,6 +7,7 @@ import net.typeblog.lpac_jni.ApduInterface
import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
import java.lang.IllegalStateException
class OmapiApduInterface(
private val service: SEService,
@ -24,23 +25,23 @@ class OmapiApduInterface(
}
override fun logicalChannelOpen(aid: ByteArray): Int {
check(!this::lastChannel.isInitialized) {
"Can only open one channel"
if (this::lastChannel.isInitialized) {
throw IllegalStateException("Can only open one channel")
}
lastChannel = session.openLogicalChannel(aid)!!;
return 0;
}
override fun logicalChannelClose(handle: Int) {
check(handle == 0 && !this::lastChannel.isInitialized) {
"Unknown channel"
if (handle != 0 || !this::lastChannel.isInitialized) {
throw IllegalStateException("Unknown channel")
}
lastChannel.close()
}
override fun transmit(tx: ByteArray): ByteArray {
check(this::lastChannel.isInitialized) {
"Unknown channel"
if (!this::lastChannel.isInitialized) {
throw IllegalStateException("Unknown channel")
}
return lastChannel.transmit(tx)
@ -52,9 +53,30 @@ class OmapiChannel(
service: SEService,
info: EuiccChannelInfo,
) : EuiccChannel(info) {
companion object {
private const val TAG = "OmapiChannel"
private val APPLET_ID = byteArrayOf(-96, 0, 0, 5, 89, 16, 16, -1, -1, -1, -1, -119, 0, 0, 1, 0)
/*fun tryConnect(service: SEService, info: EuiccChannelInfo): OmapiChannel? {
try {
val reader = service.getUiccReader(info.slotId + 1) // slotId from telephony starts from 0
val session = reader.openSession()
val channel = session.openLogicalChannel(APPLET_ID) ?: return null
return OmapiChannel(info, channel)
} catch (e: Exception) {
Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId}, skipping")
Log.e(TAG, Log.getStackTraceString(e))
return null
}
}*/
}
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
OmapiApduInterface(service, info),
HttpInterfaceImpl())
override val valid: Boolean
get() = true // TODO: This has to be implemented properly
override fun close() = lpa.close()
}

View file

@ -1,8 +1,6 @@
package im.angry.openeuicc.core
import android.telephony.IccOpenLogicalChannelResponse
import android.telephony.TelephonyManager
import im.angry.openeuicc.util.*
import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.ApduInterface
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
@ -12,42 +10,24 @@ class TelephonyManagerApduInterface(
private val info: EuiccChannelInfo,
private val tm: TelephonyManager
): ApduInterface {
private var lastChannel: Int = -1
override fun connect() {
// Do nothing
TODO("Not yet implemented")
}
override fun disconnect() {
// Do nothing
TODO("Not yet implemented")
}
override fun logicalChannelOpen(aid: ByteArray): Int {
check(lastChannel == -1) { "Already initialized" }
val hex = aid.encodeHex()
val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, 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 " + info.slotId);
}
return channel.channel
TODO("Not yet implemented")
}
override fun logicalChannelClose(handle: Int) {
tm.iccCloseLogicalChannelBySlot(info.slotId, handle)
TODO("Not yet implemented")
}
override fun transmit(tx: ByteArray): ByteArray {
check(lastChannel != -1) { "Uninitialized" }
val cla = tx[0].toInt()
val instruction = tx[1].toInt()
val p1 = tx[2].toInt()
val p2 = tx[3].toInt()
val p3 = tx[4].toInt()
val p4 = tx.drop(5).toByteArray().encodeHex()
return tm.iccTransmitApduLogicalChannelBySlot(info.slotId, lastChannel,
cla, instruction, p1, p2, p3, p4)?.decodeHex() ?: byteArrayOf()
TODO("Not yet implemented")
}
}
@ -56,10 +36,36 @@ class TelephonyManagerChannel(
info: EuiccChannelInfo,
private val tm: TelephonyManager
) : EuiccChannel(info) {
companion object {
private const val TAG = "TelephonyManagerApduChannel"
private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
// TODO: On Tiramisu, we need to specify the portId also if we want MEP support
/*fun tryConnect(tm: TelephonyManager, info: EuiccChannelInfo): TelephonyManagerChannel? {
try {
val channel = tm.iccOpenLogicalChannelBySlot(info.slotId, EUICC_APP_ID, 0)
if (channel.status != IccOpenLogicalChannelResponse.STATUS_NO_ERROR || channel.channel == IccOpenLogicalChannelResponse.INVALID_CHANNEL) {
Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager: ${channel.status}")
return null
}
Log.d(TAG, "channel: ${channel.channel}")
return TelephonyManagerChannel(info, tm, channel.channel)
} catch (e: Exception) {
Log.e(TAG, "Unable to open eUICC channel for slot ${info.slotId} via TelephonyManager")
Log.e(TAG, Log.getStackTraceString(e))
return null
}
}*/
}
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
TelephonyManagerApduInterface(info, tm),
HttpInterfaceImpl()
)
override val valid: Boolean
get() = true // TODO: Fix this
override fun close() = lpa.close()
}

View file

@ -1,22 +1,19 @@
package im.angry.openeuicc.util
fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" }
val decodedLength = length / 2
val out = ByteArray(decodedLength)
for (i in 0 until decodedLength) {
fun hexStringToByteArray(str: String): ByteArray {
val length = str.length / 2
val out = ByteArray(length)
for (i in 0 until length) {
val i2 = i * 2
out[i] = substring(i2, i2 + 2).toInt(16).toByte()
out[i] = str.substring(i2, i2 + 2).toInt(16).toByte()
}
return out
}
fun ByteArray.encodeHex(): String {
fun byteArrayToHex(arr: ByteArray): String {
val sb = StringBuilder()
val length = size
val length = arr.size
for (i in 0 until length) {
sb.append(String.format("%02X", this[i]))
sb.append(String.format("%02X", arr[i]))
}
return sb.toString()
}