Compare commits
2 commits
bea5aa0dcf
...
c62e8bcecd
Author | SHA1 | Date | |
---|---|---|---|
c62e8bcecd | |||
1c2ca55d51 |
5 changed files with 57 additions and 68 deletions
|
@ -67,6 +67,11 @@ android {
|
||||||
signingConfig signingConfigs.config
|
signingConfig signingConfigs.config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
applicationVariants.all { variant ->
|
||||||
|
if (variant.name == "debug") {
|
||||||
|
variant.outputs.each { o -> o.versionCodeOverride = System.currentTimeSeconds() }
|
||||||
|
}
|
||||||
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
|
|
@ -21,7 +21,16 @@ abstract class EuiccChannel(
|
||||||
val removable = info.removable
|
val removable = info.removable
|
||||||
|
|
||||||
abstract val lpa: LocalProfileAssistant
|
abstract val lpa: LocalProfileAssistant
|
||||||
abstract val valid: Boolean
|
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 fun close()
|
abstract fun close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
||||||
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
|
import net.typeblog.lpac_jni.impl.LocalProfileAssistantImpl
|
||||||
import java.lang.IllegalStateException
|
|
||||||
|
|
||||||
class OmapiApduInterface(
|
class OmapiApduInterface(
|
||||||
private val service: SEService,
|
private val service: SEService,
|
||||||
|
@ -25,23 +24,23 @@ class OmapiApduInterface(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
if (this::lastChannel.isInitialized) {
|
check(!this::lastChannel.isInitialized) {
|
||||||
throw IllegalStateException("Can only open one channel")
|
"Can only open one channel"
|
||||||
}
|
}
|
||||||
lastChannel = session.openLogicalChannel(aid)!!;
|
lastChannel = session.openLogicalChannel(aid)!!;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelClose(handle: Int) {
|
override fun logicalChannelClose(handle: Int) {
|
||||||
if (handle != 0 || !this::lastChannel.isInitialized) {
|
check(handle == 0 && !this::lastChannel.isInitialized) {
|
||||||
throw IllegalStateException("Unknown channel")
|
"Unknown channel"
|
||||||
}
|
}
|
||||||
lastChannel.close()
|
lastChannel.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(tx: ByteArray): ByteArray {
|
||||||
if (!this::lastChannel.isInitialized) {
|
check(this::lastChannel.isInitialized) {
|
||||||
throw IllegalStateException("Unknown channel")
|
"Unknown channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastChannel.transmit(tx)
|
return lastChannel.transmit(tx)
|
||||||
|
@ -53,30 +52,9 @@ class OmapiChannel(
|
||||||
service: SEService,
|
service: SEService,
|
||||||
info: EuiccChannelInfo,
|
info: EuiccChannelInfo,
|
||||||
) : EuiccChannel(info) {
|
) : 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(
|
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
|
||||||
OmapiApduInterface(service, info),
|
OmapiApduInterface(service, info),
|
||||||
HttpInterfaceImpl())
|
HttpInterfaceImpl())
|
||||||
|
|
||||||
override val valid: Boolean
|
|
||||||
get() = true // TODO: This has to be implemented properly
|
|
||||||
|
|
||||||
override fun close() = lpa.close()
|
override fun close() = lpa.close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package im.angry.openeuicc.core
|
package im.angry.openeuicc.core
|
||||||
|
|
||||||
|
import android.telephony.IccOpenLogicalChannelResponse
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
|
import im.angry.openeuicc.util.*
|
||||||
import net.typeblog.lpac_jni.LocalProfileAssistant
|
import net.typeblog.lpac_jni.LocalProfileAssistant
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
import net.typeblog.lpac_jni.impl.HttpInterfaceImpl
|
||||||
|
@ -10,24 +12,42 @@ class TelephonyManagerApduInterface(
|
||||||
private val info: EuiccChannelInfo,
|
private val info: EuiccChannelInfo,
|
||||||
private val tm: TelephonyManager
|
private val tm: TelephonyManager
|
||||||
): ApduInterface {
|
): ApduInterface {
|
||||||
|
private var lastChannel: Int = -1
|
||||||
|
|
||||||
override fun connect() {
|
override fun connect() {
|
||||||
TODO("Not yet implemented")
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect() {
|
override fun disconnect() {
|
||||||
TODO("Not yet implemented")
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
TODO("Not yet implemented")
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelClose(handle: Int) {
|
override fun logicalChannelClose(handle: Int) {
|
||||||
TODO("Not yet implemented")
|
tm.iccCloseLogicalChannelBySlot(info.slotId, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(tx: ByteArray): ByteArray {
|
||||||
TODO("Not yet implemented")
|
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,36 +56,10 @@ class TelephonyManagerChannel(
|
||||||
info: EuiccChannelInfo,
|
info: EuiccChannelInfo,
|
||||||
private val tm: TelephonyManager
|
private val tm: TelephonyManager
|
||||||
) : EuiccChannel(info) {
|
) : 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(
|
override val lpa: LocalProfileAssistant = LocalProfileAssistantImpl(
|
||||||
TelephonyManagerApduInterface(info, tm),
|
TelephonyManagerApduInterface(info, tm),
|
||||||
HttpInterfaceImpl()
|
HttpInterfaceImpl()
|
||||||
)
|
)
|
||||||
override val valid: Boolean
|
|
||||||
get() = true // TODO: Fix this
|
|
||||||
|
|
||||||
override fun close() = lpa.close()
|
override fun close() = lpa.close()
|
||||||
}
|
}
|
|
@ -1,19 +1,22 @@
|
||||||
package im.angry.openeuicc.util
|
package im.angry.openeuicc.util
|
||||||
|
|
||||||
fun hexStringToByteArray(str: String): ByteArray {
|
fun String.decodeHex(): ByteArray {
|
||||||
val length = str.length / 2
|
check(length % 2 == 0) { "Must have an even length" }
|
||||||
val out = ByteArray(length)
|
|
||||||
for (i in 0 until length) {
|
val decodedLength = length / 2
|
||||||
|
val out = ByteArray(decodedLength)
|
||||||
|
for (i in 0 until decodedLength) {
|
||||||
val i2 = i * 2
|
val i2 = i * 2
|
||||||
out[i] = str.substring(i2, i2 + 2).toInt(16).toByte()
|
out[i] = substring(i2, i2 + 2).toInt(16).toByte()
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
fun byteArrayToHex(arr: ByteArray): String {
|
|
||||||
|
fun ByteArray.encodeHex(): String {
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
val length = arr.size
|
val length = size
|
||||||
for (i in 0 until length) {
|
for (i in 0 until length) {
|
||||||
sb.append(String.format("%02X", arr[i]))
|
sb.append(String.format("%02X", this[i]))
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue