OpenEUICC/app/src/main/java/im/angry/openeuicc/core/TelephonyManagerApduChannel.kt
Peter Cai 0de44f7d03 Initial implementation of OpenEuiccService
Nothing is really implemented properly as of now :)

The hidden EuiccService APIs are included as a stubbed library, which
allows us to build outside of the AOSP source tree and support multiple
AOSP versions.
2022-05-03 16:04:34 -04:00

94 lines
3.9 KiB
Kotlin

package im.angry.openeuicc.core
import android.telephony.IccOpenLogicalChannelResponse
import android.telephony.IccOpenLogicalChannelResponse.INVALID_CHANNEL
import android.telephony.IccOpenLogicalChannelResponse.STATUS_NO_ERROR
import android.telephony.TelephonyManager
import android.util.Log
import com.truphone.lpa.ApduChannel
import com.truphone.lpa.ApduTransmittedListener
import java.lang.Exception
import java.lang.reflect.Method
class TelephonyManagerApduChannel(
private val tm: TelephonyManager,
private val slotId: Int,
private val channelId: Int) : ApduChannel {
companion object {
private const val TAG = "TelephonyManagerApduChannel"
private const val EUICC_APP_ID = "A0000005591010FFFFFFFF8900000100"
private val iccOpenLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccOpenLogicalChannelBySlot",
Int::class.java, String::class.java, Int::class.java)
private val iccCloseLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccCloseLogicalChannelBySlot",
Int::class.java, Int::class.java)
private val iccTransmitApduLogicalChannelBySlot: Method =
TelephonyManager::class.java.getMethod("iccTransmitApduLogicalChannelBySlot",
Int::class.java, Int::class.java, Int::class.java, Int::class.java,
Int::class.java, Int::class.java, Int::class.java, String::class.java)
// TODO: On Tiramisu, we need to specify the portId also if we want MEP support
fun tryConnectUiccSlot(tm: TelephonyManager, slotId: Int): Pair<ApduChannel, EuiccChannelStateManager>? {
try {
// FIXME: Clean up previously opened channels across restarts
iccCloseLogicalChannelBySlot.invoke(tm, slotId, 1)
val channel = iccOpenLogicalChannelBySlot.invoke(tm, slotId, EUICC_APP_ID, 0) as IccOpenLogicalChannelResponse
if (channel.status != STATUS_NO_ERROR || channel.channel == INVALID_CHANNEL) {
Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager: ${channel.status}")
return null
}
Log.d(TAG, "channel: ${channel.channel}")
val stateManager = object : EuiccChannelStateManager {
override val valid: Boolean
get() = true // TODO: Fix this properly
override fun destroy() {
iccCloseLogicalChannelBySlot.invoke(tm, slotId, channel.channel)
}
}
return Pair(TelephonyManagerApduChannel(tm, slotId, channel.channel), stateManager)
} catch (e: Exception) {
Log.e(TAG, "Unable to open eUICC channel for slot ${slotId} via TelephonyManager")
Log.e(TAG, Log.getStackTraceString(e))
return null
}
}
}
override fun transmitAPDU(apdu: String): String {
val cla = Integer.parseInt(apdu.substring(0, 2), 16)
val instruction = Integer.parseInt(apdu.substring(2, 4), 16)
val p1 = Integer.parseInt(apdu.substring(4, 6), 16)
val p2 = Integer.parseInt(apdu.substring(6, 8), 16)
val p3 = Integer.parseInt(apdu.substring(8, 10), 16)
val p4 = apdu.substring(10)
return iccTransmitApduLogicalChannelBySlot.invoke(
tm, slotId, channelId,
cla, instruction, p1, p2, p3, p4) as String
}
override fun transmitAPDUS(apdus: MutableList<String>): String {
var res = ""
for (pdu in apdus) {
res = transmitAPDU(pdu)
}
return res
}
override fun sendStatus() {
}
override fun setApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
}
override fun removeApduTransmittedListener(apduTransmittedListener: ApduTransmittedListener?) {
}
}