Compare commits
No commits in common. "c62e8bcecd80146022a7197cad2a983af5dc803b" and "bea5aa0dcfc978bcf43807f8226788c1c93b5cd7" have entirely different histories.
c62e8bcecd
...
bea5aa0dcf
5 changed files with 68 additions and 57 deletions
|
@ -67,11 +67,6 @@ 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,16 +21,7 @@ abstract class EuiccChannel(
|
||||||
val removable = info.removable
|
val removable = info.removable
|
||||||
|
|
||||||
abstract val lpa: LocalProfileAssistant
|
abstract val lpa: LocalProfileAssistant
|
||||||
val valid: Boolean
|
abstract 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,6 +7,7 @@ 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,
|
||||||
|
@ -24,23 +25,23 @@ class OmapiApduInterface(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
check(!this::lastChannel.isInitialized) {
|
if (this::lastChannel.isInitialized) {
|
||||||
"Can only open one channel"
|
throw IllegalStateException("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) {
|
||||||
check(handle == 0 && !this::lastChannel.isInitialized) {
|
if (handle != 0 || !this::lastChannel.isInitialized) {
|
||||||
"Unknown channel"
|
throw IllegalStateException("Unknown channel")
|
||||||
}
|
}
|
||||||
lastChannel.close()
|
lastChannel.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(tx: ByteArray): ByteArray {
|
||||||
check(this::lastChannel.isInitialized) {
|
if (!this::lastChannel.isInitialized) {
|
||||||
"Unknown channel"
|
throw IllegalStateException("Unknown channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastChannel.transmit(tx)
|
return lastChannel.transmit(tx)
|
||||||
|
@ -52,9 +53,30 @@ 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,8 +1,6 @@
|
||||||
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
|
||||||
|
@ -12,42 +10,24 @@ 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() {
|
||||||
// Do nothing
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun disconnect() {
|
override fun disconnect() {
|
||||||
// Do nothing
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun logicalChannelOpen(aid: ByteArray): Int {
|
override fun logicalChannelOpen(aid: ByteArray): Int {
|
||||||
check(lastChannel == -1) { "Already initialized" }
|
TODO("Not yet implemented")
|
||||||
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) {
|
||||||
tm.iccCloseLogicalChannelBySlot(info.slotId, handle)
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun transmit(tx: ByteArray): ByteArray {
|
override fun transmit(tx: ByteArray): ByteArray {
|
||||||
check(lastChannel != -1) { "Uninitialized" }
|
TODO("Not yet implemented")
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -56,10 +36,36 @@ 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,22 +1,19 @@
|
||||||
package im.angry.openeuicc.util
|
package im.angry.openeuicc.util
|
||||||
|
|
||||||
fun String.decodeHex(): ByteArray {
|
fun hexStringToByteArray(str: String): ByteArray {
|
||||||
check(length % 2 == 0) { "Must have an even length" }
|
val length = str.length / 2
|
||||||
|
val out = ByteArray(length)
|
||||||
val decodedLength = length / 2
|
for (i in 0 until length) {
|
||||||
val out = ByteArray(decodedLength)
|
|
||||||
for (i in 0 until decodedLength) {
|
|
||||||
val i2 = i * 2
|
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
|
return out
|
||||||
}
|
}
|
||||||
|
fun byteArrayToHex(arr: ByteArray): String {
|
||||||
fun ByteArray.encodeHex(): String {
|
|
||||||
val sb = StringBuilder()
|
val sb = StringBuilder()
|
||||||
val length = size
|
val length = arr.size
|
||||||
for (i in 0 until length) {
|
for (i in 0 until length) {
|
||||||
sb.append(String.format("%02X", this[i]))
|
sb.append(String.format("%02X", arr[i]))
|
||||||
}
|
}
|
||||||
return sb.toString()
|
return sb.toString()
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue