Compare commits

..

2 commits

Author SHA1 Message Date
0771c7539a
refactor: lpac-jni bridge 2025-03-11 03:03:14 +08:00
93d1c3fd38
refactor: profile list 2025-03-11 01:10:24 +08:00
25 changed files with 765 additions and 482 deletions

View file

@ -1,9 +1,11 @@
package im.angry.openeuicc.core package im.angry.openeuicc.core
import net.typeblog.lpac_jni.EuiccConfiguredAddresses
import net.typeblog.lpac_jni.EuiccInfo2 import net.typeblog.lpac_jni.EuiccInfo2
import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileInfo
import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.LocalProfileNotification
import net.typeblog.lpac_jni.ProfileDiscoveryCallback
import net.typeblog.lpac_jni.ProfileDownloadCallback import net.typeblog.lpac_jni.ProfileDownloadCallback
class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
@ -29,6 +31,8 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
get() = lpa.eID get() = lpa.eID
override val euiccInfo2: EuiccInfo2? override val euiccInfo2: EuiccInfo2?
get() = lpa.euiccInfo2 get() = lpa.euiccInfo2
override val euiccConfiguredAddresses: EuiccConfiguredAddresses
get() = lpa.euiccConfiguredAddresses
override fun setEs10xMss(mss: Byte) = lpa.setEs10xMss(mss) override fun setEs10xMss(mss: Byte) = lpa.setEs10xMss(mss)
@ -48,6 +52,9 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
callback: ProfileDownloadCallback callback: ProfileDownloadCallback
) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback) ) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback)
override fun discoveryProfile(smds: String, imei: String?, callback: ProfileDiscoveryCallback) =
lpa.discoveryProfile(smds, imei, callback)
override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber) override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber)
override fun handleNotification(seqNumber: Long): Boolean = lpa.handleNotification(seqNumber) override fun handleNotification(seqNumber: Long): Boolean = lpa.handleNotification(seqNumber)

View file

@ -380,6 +380,18 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
getString(R.string.task_profile_download_failure), getString(R.string.task_profile_download_failure),
R.drawable.ic_task_sim_card_download R.drawable.ic_task_sim_card_download
) { ) {
val callback = object : ProfileDownloadCallback {
override var isCancelled: Boolean = false
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
if (state.progress == 0) return
foregroundTaskState.value = ForegroundTaskState.InProgress(state.progress)
}
override fun onProfileMetadata(metadata: ProfileDownloadCallback.ProfileMetadata) {
Log.d(TAG, "Downloaded profile metadata: $metadata")
}
}
euiccChannelManager.beginTrackedOperation(slotId, portId) { euiccChannelManager.beginTrackedOperation(slotId, portId) {
euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
channel.lpa.downloadProfile( channel.lpa.downloadProfile(
@ -387,13 +399,8 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
matchingId, matchingId,
imei, imei,
confirmationCode, confirmationCode,
object : ProfileDownloadCallback { callback
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) { )
if (state.progress == 0) return
foregroundTaskState.value =
ForegroundTaskState.InProgress(state.progress)
}
})
} }
preferenceRepository.notificationDownloadFlow.first() preferenceRepository.notificationDownloadFlow.first()

View file

@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.ProfileClass
open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
EuiccChannelFragmentMarker { EuiccChannelFragmentMarker {
@ -387,9 +388,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
profileClass.isVisible = unfilteredProfileListFlow.value profileClass.isVisible = unfilteredProfileListFlow.value
profileClass.setText( profileClass.setText(
when (profile.profileClass) { when (profile.profileClass) {
LocalProfileInfo.Clazz.Testing -> R.string.profile_class_testing ProfileClass.Testing -> R.string.profile_class_testing
LocalProfileInfo.Clazz.Provisioning -> R.string.profile_class_provisioning ProfileClass.Provisioning -> R.string.profile_class_provisioning
LocalProfileInfo.Clazz.Operational -> R.string.profile_class_operational ProfileClass.Operational -> R.string.profile_class_operational
} }
) )
iccid.text = profile.iccid iccid.text = profile.iccid

View file

@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.LocalProfileNotification
import net.typeblog.lpac_jni.ProfileManagementOperation
class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker { class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker {
private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var swipeRefresh: SwipeRefreshLayout
@ -163,13 +164,13 @@ class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker {
} }
private fun operationToLocalizedText(operation: LocalProfileNotification.Operation) = private fun operationToLocalizedText(operation: ProfileManagementOperation) =
root.context.getText( root.context.getText(
when (operation) { when (operation) {
LocalProfileNotification.Operation.Install -> R.string.profile_notification_operation_download ProfileManagementOperation.Install -> R.string.profile_notification_operation_download
LocalProfileNotification.Operation.Delete -> R.string.profile_notification_operation_delete ProfileManagementOperation.Delete -> R.string.profile_notification_operation_delete
LocalProfileNotification.Operation.Enable -> R.string.profile_notification_operation_enable ProfileManagementOperation.Enable -> R.string.profile_notification_operation_enable
LocalProfileNotification.Operation.Disable -> R.string.profile_notification_operation_disable ProfileManagementOperation.Disable -> R.string.profile_notification_operation_disable
}) })
fun updateNotification(value: LocalProfileNotificationWrapper) { fun updateNotification(value: LocalProfileNotificationWrapper) {

View file

@ -5,6 +5,8 @@ import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.core.EuiccChannelManager
import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileInfo
import net.typeblog.lpac_jni.ProfileClass
import net.typeblog.lpac_jni.ProfileState
const val TAG = "LPAUtils" const val TAG = "LPAUtils"
@ -13,10 +15,10 @@ val LocalProfileInfo.displayName: String
val LocalProfileInfo.isEnabled: Boolean val LocalProfileInfo.isEnabled: Boolean
get() = state == LocalProfileInfo.State.Enabled get() = state == ProfileState.Enabled
val List<LocalProfileInfo>.operational: List<LocalProfileInfo> val List<LocalProfileInfo>.operational: List<LocalProfileInfo>
get() = filter { it.profileClass == LocalProfileInfo.Clazz.Operational } get() = filter { it.profileClass == ProfileClass.Operational }
val List<LocalProfileInfo>.enabled: LocalProfileInfo? val List<LocalProfileInfo>.enabled: LocalProfileInfo?
get() = find { it.isEnabled } get() = find { it.isEnabled }

View file

@ -15,6 +15,8 @@ import im.angry.openeuicc.util.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import net.typeblog.lpac_jni.ProfileClass
import net.typeblog.lpac_jni.ProfileState
import kotlin.IllegalStateException import kotlin.IllegalStateException
class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
@ -200,15 +202,15 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
setServiceProviderName(it.providerName) setServiceProviderName(it.providerName)
setState( setState(
when (it.state) { when (it.state) {
LocalProfileInfo.State.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED ProfileState.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED
LocalProfileInfo.State.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED ProfileState.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED
} }
) )
setProfileClass( setProfileClass(
when (it.profileClass) { when (it.profileClass) {
LocalProfileInfo.Clazz.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING ProfileClass.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING
LocalProfileInfo.Clazz.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING ProfileClass.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING
LocalProfileInfo.Clazz.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL ProfileClass.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL
} }
) )
}.build() }.build()

View file

@ -0,0 +1,29 @@
package net.typeblog.lpac_jni
import android.util.Patterns
// example address in GSMA SGP.26, some chips use addresses like this
@Suppress("SpellCheckingInspection")
private val invalidDPAddresses = setOf(
"testrootsmds.gsma.com",
"testrootsmds.example.com",
)
class EuiccConfiguredAddresses(defaultDPAddress: String?, rootDSAddress: String?) {
val defaultDPAddress: String? = defaultDPAddress.takeUnless(::isInvalidDPAddress)
val rootDSAddress = rootDSAddress.takeUnless(::isInvalidDSAddress)
val discoverable: Boolean
get() = !defaultDPAddress.isNullOrBlank() || !rootDSAddress.isNullOrBlank()
}
private fun isInvalidDPAddress(address: String?): Boolean {
if (address.isNullOrBlank()) return true
return !Patterns.DOMAIN_NAME.matcher(address).matches()
}
private fun isInvalidDSAddress(address: String?): Boolean {
if (address.isNullOrBlank()) return true
if (address in invalidDPAddresses) return true
return !Patterns.DOMAIN_NAME.matcher(address).matches()
}

View file

@ -12,9 +12,18 @@ interface LocalProfileAssistant {
val lastApduException: Exception?, val lastApduException: Exception?,
) : Exception("Failed to download profile") ) : Exception("Failed to download profile")
class ProfileRenameException() : Exception("Failed to rename profile") @Suppress("ArrayInDataClass")
class ProfileNameTooLongException() : Exception("Profile name too long") data class ProfileDiscoveryException(
class ProfileNameIsInvalidUTF8Exception() : Exception("Profile name is invalid UTF-8") val lpaErrorReason: String,
val lastHttpResponse: HttpResponse?,
val lastHttpException: Exception?,
val lastApduResponse: ByteArray?,
val lastApduException: Exception?,
) : Exception("Failed to discover profile")
class ProfileRenameException : Exception("Failed to rename profile")
class ProfileNameTooLongException : Exception("Profile name too long")
class ProfileNameIsInvalidUTF8Exception : Exception("Profile name is invalid UTF-8")
val valid: Boolean val valid: Boolean
val profiles: List<LocalProfileInfo> val profiles: List<LocalProfileInfo>
@ -22,6 +31,7 @@ interface LocalProfileAssistant {
val eID: String val eID: String
// Extended EuiccInfo for use with LUIs, containing information such as firmware version // Extended EuiccInfo for use with LUIs, containing information such as firmware version
val euiccInfo2: EuiccInfo2? val euiccInfo2: EuiccInfo2?
val euiccConfiguredAddresses: EuiccConfiguredAddresses
/** /**
* Set the max segment size (mss) for all es10x commands. This can help with removable * Set the max segment size (mss) for all es10x commands. This can help with removable
@ -39,6 +49,8 @@ interface LocalProfileAssistant {
fun downloadProfile(smdp: String, matchingId: String?, imei: String?, fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
confirmationCode: String?, callback: ProfileDownloadCallback) confirmationCode: String?, callback: ProfileDownloadCallback)
fun discoveryProfile(smds: String, imei: String?, callback: ProfileDiscoveryCallback)
fun deleteNotification(seqNumber: Long): Boolean fun deleteNotification(seqNumber: Long): Boolean
fun handleNotification(seqNumber: Long): Boolean fun handleNotification(seqNumber: Long): Boolean

View file

@ -2,42 +2,14 @@ package net.typeblog.lpac_jni
data class LocalProfileInfo( data class LocalProfileInfo(
val iccid: String, val iccid: String,
val state: State, val state: ProfileState,
val name: String, val name: String,
val nickName: String, val nickName: String,
val providerName: String, val providerName: String,
val isdpAID: String, val isdpAID: String,
val profileClass: Clazz val profileClass: ProfileClass
) { )
enum class State {
Enabled,
Disabled;
companion object { enum class ProfileState { Enabled, Disabled }
@JvmStatic
fun fromString(str: String?) =
when (str?.lowercase()) {
"enabled" -> Enabled
"disabled" -> Disabled
else -> Disabled
}
}
}
enum class Clazz { enum class ProfileClass { Testing, Provisioning, Operational }
Testing,
Provisioning,
Operational;
companion object {
@JvmStatic
fun fromString(str: String?) =
when (str?.lowercase()) {
"test" -> Testing
"provisioning" -> Provisioning
"operational" -> Operational
else -> Operational
}
}
}
}

View file

@ -2,26 +2,11 @@ package net.typeblog.lpac_jni
data class LocalProfileNotification( data class LocalProfileNotification(
val seqNumber: Long, val seqNumber: Long,
val profileManagementOperation: Operation, val profileManagementOperation: ProfileManagementOperation,
val notificationAddress: String, val notificationAddress: String,
val iccid: String, val iccid: String,
) { )
enum class Operation {
Install,
Enable,
Disable,
Delete;
companion object { enum class ProfileManagementOperation {
@JvmStatic Install, Enable, Disable, Delete
fun fromString(str: String?) =
when (str?.lowercase()) {
"install" -> Install
"enable" -> Enable
"disable" -> Disable
"delete" -> Delete
else -> throw IllegalArgumentException("Unknown operation $str")
}
}
}
} }

View file

@ -19,16 +19,22 @@ internal object LpacJni {
// es10c // es10c
// null returns signify errors // null returns signify errors
external fun es10cGetEid(handle: Long): String? external fun es10cGetEid(handle: Long): String?
external fun es10cGetProfilesInfo(handle: Long): Long external fun es10cGetProfilesInfo(handle: Long, profiles: List<LocalProfileInfo>): Long
external fun es10cEnableProfile(handle: Long, iccid: String, refresh: Boolean): Int external fun es10cEnableProfile(handle: Long, iccid: String, refresh: Boolean): Int
external fun es10cDisableProfile(handle: Long, iccid: String, refresh: Boolean): Int external fun es10cDisableProfile(handle: Long, iccid: String, refresh: Boolean): Int
external fun es10cDeleteProfile(handle: Long, iccid: String): Int external fun es10cDeleteProfile(handle: Long, iccid: String): Int
external fun es10cSetNickname(handle: Long, iccid: String, nickNullTerminated: ByteArray): Int external fun es10cSetNickname(handle: Long, iccid: String, nickNullTerminated: ByteArray): Int
// es10b // es10b
external fun es10bListNotification(handle: Long): Long // A native pointer to a linked list. Handle with linked list-related methods below. May be 0 (null) external fun es10bListNotification(handle: Long, elements: List<LocalProfileNotification>): Int
external fun es10bDeleteNotification(handle: Long, seqNumber: Long): Int external fun es10bDeleteNotification(handle: Long, seqNumber: Long): Int
// es10a
external fun es10aGetEuiccConfiguredAddresses(handle: Long): EuiccConfiguredAddresses
// es9p + es11
external fun discoveryProfile(handle: Long, address: String, imei: String?, callback: ProfileDiscoveryCallback): Int
// es9p + es10b // es9p + es10b
// We do not expose all of the functions because of tediousness :) // We do not expose all of the functions because of tediousness :)
external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?, external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?,
@ -41,40 +47,5 @@ internal object LpacJni {
// ES10c // ES10c
external fun es10cEuiccMemoryReset(handle: Long): Int external fun es10cEuiccMemoryReset(handle: Long): Int
// es10cex (actually part of es10b) // es10cex (actually part of es10b)
external fun es10cexGetEuiccInfo2(handle: Long): Long external fun es10cexGetEuiccInfo2(handle: Long): EuiccInfo2?
// C <-> Java struct / linked list handling
// C String arrays
external fun stringArrNext(curr: Long): Long
external fun stringDeref(curr: Long): String
// Profiles
external fun profilesNext(curr: Long): Long
external fun profilesFree(head: Long): Long
external fun profileGetIccid(curr: Long): String
external fun profileGetIsdpAid(curr: Long): String
external fun profileGetName(curr: Long): String
external fun profileGetNickname(curr: Long): String
external fun profileGetServiceProvider(curr: Long): String
external fun profileGetStateString(curr: Long): String
external fun profileGetClassString(curr: Long): String
// Notifications
external fun notificationsNext(curr: Long): Long
external fun notificationGetSeq(curr: Long): Long
external fun notificationGetOperationString(curr: Long): String
external fun notificationGetAddress(curr: Long): String
external fun notificationGetIccid(curr: Long): String
external fun notificationsFree(head: Long)
// EuiccInfo2
external fun euiccInfo2Free(info: Long)
external fun euiccInfo2GetSGP22Version(info: Long): String
external fun euiccInfo2GetProfileVersion(info: Long): String
external fun euiccInfo2GetEuiccFirmwareVersion(info: Long): String
external fun euiccInfo2GetGlobalPlatformVersion(info: Long): String
external fun euiccInfo2GetSasAcreditationNumber(info: Long): String
external fun euiccInfo2GetPpVersion(info: Long): String
external fun euiccInfo2GetFreeNonVolatileMemory(info: Long): Long
external fun euiccInfo2GetFreeVolatileMemory(info: Long): Long
// C String Arrays
external fun euiccInfo2GetEuiccCiPKIdListForSigning(info: Long): Long
external fun euiccInfo2GetEuiccCiPKIdListForVerification(info: Long): Long
} }

View file

@ -0,0 +1,7 @@
package net.typeblog.lpac_jni
import java.util.ArrayList
interface ProfileDiscoveryCallback {
fun onDiscovered(servers: ArrayList<String>)
}

View file

@ -2,8 +2,7 @@ package net.typeblog.lpac_jni
interface ProfileDownloadCallback { interface ProfileDownloadCallback {
companion object { companion object {
fun lookupStateFromProgress(progress: Int): DownloadState = fun lookupStateFromProgress(progress: Int): DownloadState = when (progress) {
when (progress) {
0 -> DownloadState.Preparing 0 -> DownloadState.Preparing
20 -> DownloadState.Connecting 20 -> DownloadState.Connecting
40 -> DownloadState.Authenticating 40 -> DownloadState.Authenticating
@ -21,5 +20,18 @@ interface ProfileDownloadCallback {
Finalizing(80), // load bpp Finalizing(80), // load bpp
} }
data class ProfileMetadata(
val iccid: String,
val serviceProviderName: String,
val profileName: String,
val iconType: String,
val icon: String,
val profileClass: ProfileClass,
)
var isCancelled: Boolean
fun onStateUpdate(state: DownloadState) fun onStateUpdate(state: DownloadState)
fun onProfileMetadata(metadata: ProfileMetadata)
} }

View file

@ -3,12 +3,14 @@ package net.typeblog.lpac_jni.impl
import android.util.Log import android.util.Log
import net.typeblog.lpac_jni.LpacJni import net.typeblog.lpac_jni.LpacJni
import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.ApduInterface
import net.typeblog.lpac_jni.EuiccConfiguredAddresses
import net.typeblog.lpac_jni.EuiccInfo2 import net.typeblog.lpac_jni.EuiccInfo2
import net.typeblog.lpac_jni.HttpInterface import net.typeblog.lpac_jni.HttpInterface
import net.typeblog.lpac_jni.HttpInterface.HttpResponse import net.typeblog.lpac_jni.HttpInterface.HttpResponse
import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileAssistant
import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileInfo
import net.typeblog.lpac_jni.LocalProfileNotification import net.typeblog.lpac_jni.LocalProfileNotification
import net.typeblog.lpac_jni.ProfileDiscoveryCallback
import net.typeblog.lpac_jni.ProfileDownloadCallback import net.typeblog.lpac_jni.ProfileDownloadCallback
import net.typeblog.lpac_jni.Version import net.typeblog.lpac_jni.Version
@ -107,45 +109,19 @@ class LocalProfileAssistantImpl(
override val profiles: List<LocalProfileInfo> override val profiles: List<LocalProfileInfo>
@Synchronized @Synchronized
get() { get() {
val head = LpacJni.es10cGetProfilesInfo(contextHandle) val profiles = mutableListOf<LocalProfileInfo>()
var curr = head val ret = LpacJni.es10cGetProfilesInfo(contextHandle, profiles)
val ret = mutableListOf<LocalProfileInfo>() if (ret < 0) throw IllegalStateException("Failed to get profiles")
while (curr != 0L) { return profiles
val state = LocalProfileInfo.State.fromString(LpacJni.profileGetStateString(curr))
val clazz = LocalProfileInfo.Clazz.fromString(LpacJni.profileGetClassString(curr))
ret.add(LocalProfileInfo(
LpacJni.profileGetIccid(curr),
state,
LpacJni.profileGetName(curr),
LpacJni.profileGetNickname(curr),
LpacJni.profileGetServiceProvider(curr),
LpacJni.profileGetIsdpAid(curr),
clazz
))
curr = LpacJni.profilesNext(curr)
}
LpacJni.profilesFree(curr)
return ret
} }
override val notifications: List<LocalProfileNotification> override val notifications: List<LocalProfileNotification>
@Synchronized @Synchronized
get() { get() {
val head = LpacJni.es10bListNotification(contextHandle) val notifications = mutableListOf<LocalProfileNotification>()
var curr = head val ret = LpacJni.es10bListNotification(contextHandle, notifications)
val ret = mutableListOf<LocalProfileNotification>() check(ret > 0) { "Failed to get notifications" }
while (curr != 0L) { return notifications.sortedBy { it.seqNumber }.reversed()
ret.add(LocalProfileNotification(
LpacJni.notificationGetSeq(curr),
LocalProfileNotification.Operation.fromString(LpacJni.notificationGetOperationString(curr)),
LpacJni.notificationGetAddress(curr),
LpacJni.notificationGetIccid(curr),
))
curr = LpacJni.notificationsNext(curr)
}
LpacJni.notificationsFree(head)
return ret.sortedBy { it.seqNumber }.reversed()
} }
override val eID: String override val eID: String
@ -154,39 +130,9 @@ class LocalProfileAssistantImpl(
override val euiccInfo2: EuiccInfo2? override val euiccInfo2: EuiccInfo2?
@Synchronized @Synchronized
get() { get() = LpacJni.es10cexGetEuiccInfo2(contextHandle)
val cInfo = LpacJni.es10cexGetEuiccInfo2(contextHandle) override val euiccConfiguredAddresses: EuiccConfiguredAddresses
if (cInfo == 0L) return null get() = LpacJni.es10aGetEuiccConfiguredAddresses(contextHandle)
val ret = EuiccInfo2(
Version(LpacJni.euiccInfo2GetSGP22Version(cInfo)),
Version(LpacJni.euiccInfo2GetProfileVersion(cInfo)),
Version(LpacJni.euiccInfo2GetEuiccFirmwareVersion(cInfo)),
Version(LpacJni.euiccInfo2GetGlobalPlatformVersion(cInfo)),
LpacJni.euiccInfo2GetSasAcreditationNumber(cInfo),
Version(LpacJni.euiccInfo2GetPpVersion(cInfo)),
LpacJni.euiccInfo2GetFreeNonVolatileMemory(cInfo).toInt(),
LpacJni.euiccInfo2GetFreeVolatileMemory(cInfo).toInt(),
buildSet {
var cursor = LpacJni.euiccInfo2GetEuiccCiPKIdListForSigning(cInfo)
while (cursor != 0L) {
add(LpacJni.stringDeref(cursor))
cursor = LpacJni.stringArrNext(cursor)
}
},
buildSet {
var cursor = LpacJni.euiccInfo2GetEuiccCiPKIdListForVerification(cInfo)
while (cursor != 0L) {
add(LpacJni.stringDeref(cursor))
cursor = LpacJni.stringArrNext(cursor)
}
},
)
LpacJni.euiccInfo2Free(cInfo)
return ret
}
@Synchronized @Synchronized
override fun enableProfile(iccid: String, refresh: Boolean): Boolean = override fun enableProfile(iccid: String, refresh: Boolean): Boolean =
@ -212,7 +158,7 @@ class LocalProfileAssistantImpl(
callback callback
) )
if (res != 0) { if (res == 0) return
// Construct the error now to store any error information we _can_ access // Construct the error now to store any error information we _can_ access
val err = LocalProfileAssistant.ProfileDownloadException( val err = LocalProfileAssistant.ProfileDownloadException(
lpaErrorReason = LpacJni.downloadErrCodeToString(-res), lpaErrorReason = LpacJni.downloadErrCodeToString(-res),
@ -221,12 +167,22 @@ class LocalProfileAssistantImpl(
apduInterface.lastApduResponse, apduInterface.lastApduResponse,
apduInterface.lastApduException, apduInterface.lastApduException,
) )
// Cancel sessions if possible. This will overwrite recorded errors from HTTP and APDU interfaces. // Cancel sessions if possible. This will overwrite recorded errors from HTTP and APDU interfaces.
LpacJni.cancelSessions(contextHandle) LpacJni.cancelSessions(contextHandle)
throw err throw err
} }
override fun discoveryProfile(smds: String, imei: String?, callback: ProfileDiscoveryCallback) {
val ret = LpacJni.discoveryProfile(contextHandle, smds, imei, callback)
if (ret == 0) return
// Construct the error now to store any error information we _can_ access
throw LocalProfileAssistant.ProfileDiscoveryException(
lpaErrorReason = LpacJni.downloadErrCodeToString(-ret),
httpInterface.lastHttpResponse,
httpInterface.lastHttpException,
apduInterface.lastApduResponse,
apduInterface.lastApduException,
)
} }
@Synchronized @Synchronized

View file

@ -1,20 +1,20 @@
#include <string.h> #include <string.h>
#include <malloc.h> #include <malloc.h>
#include "interface-wrapper.h" #include "interface-wrapper.h"
#include "lpac-convertor.h"
jmethodID method_apdu_connect; static jmethodID method_apdu_connect;
jmethodID method_apdu_disconnect; static jmethodID method_apdu_disconnect;
jmethodID method_apdu_logical_channel_open; static jmethodID method_apdu_logical_channel_open;
jmethodID method_apdu_logical_channel_close; static jmethodID method_apdu_logical_channel_close;
jmethodID method_apdu_transmit; static jmethodID method_apdu_transmit;
jmethodID method_http_transmit; static jmethodID method_http_transmit;
jfieldID field_resp_rcode; static jfieldID field_resp_rcode;
jfieldID field_resp_data; static jfieldID field_resp_data;
void interface_wrapper_init() { void interface_wrapper_init(JNIEnv *env) {
LPAC_JNI_SETUP_ENV;
jclass apdu_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/ApduInterface"); jclass apdu_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/ApduInterface");
method_apdu_connect = (*env)->GetMethodID(env, apdu_class, "connect", "()V"); method_apdu_connect = (*env)->GetMethodID(env, apdu_class, "connect", "()V");
method_apdu_disconnect = (*env)->GetMethodID(env, apdu_class, "disconnect", "()V"); method_apdu_disconnect = (*env)->GetMethodID(env, apdu_class, "disconnect", "()V");
@ -80,7 +80,7 @@ apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, c
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));
(*env)->GetByteArrayRegion(env, ret, 0, *rx_len, *rx); (*env)->GetByteArrayRegion(env, ret, 0, (jsize) *rx_len, (jbyte *) *rx);
(*env)->DeleteLocalRef(env, txArr); (*env)->DeleteLocalRef(env, txArr);
(*env)->DeleteLocalRef(env, ret); (*env)->DeleteLocalRef(env, ret);
return 0; return 0;
@ -92,8 +92,8 @@ http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode,
const char **headers) { const char **headers) {
LPAC_JNI_SETUP_ENV; LPAC_JNI_SETUP_ENV;
jstring jurl = toJString(env, url); jstring jurl = toJString(env, url);
jbyteArray txArr = (*env)->NewByteArray(env, tx_len); jbyteArray txArr = (*env)->NewByteArray(env, (jsize) tx_len);
(*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); (*env)->SetByteArrayRegion(env, txArr, 0, (jsize) tx_len, (const jbyte *) tx);
int num_headers = 0; int num_headers = 0;
while (headers[num_headers] != NULL) { while (headers[num_headers] != NULL) {
@ -113,7 +113,7 @@ http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode,
jbyteArray rxArr = (jbyteArray) (*env)->GetObjectField(env, ret, field_resp_data); jbyteArray rxArr = (jbyteArray) (*env)->GetObjectField(env, ret, field_resp_data);
*rx_len = (*env)->GetArrayLength(env, rxArr); *rx_len = (*env)->GetArrayLength(env, rxArr);
*rx = calloc(*rx_len, sizeof(uint8_t)); *rx = calloc(*rx_len, sizeof(uint8_t));
(*env)->GetByteArrayRegion(env, rxArr, 0, *rx_len, *rx); (*env)->GetByteArrayRegion(env, rxArr, 0, (jsize) *rx_len, (jbyte *) *rx);
(*env)->DeleteLocalRef(env, txArr); (*env)->DeleteLocalRef(env, txArr);
(*env)->DeleteLocalRef(env, rxArr); (*env)->DeleteLocalRef(env, rxArr);
(*env)->DeleteLocalRef(env, headersArr); (*env)->DeleteLocalRef(env, headersArr);

View file

@ -5,7 +5,7 @@
#include <euicc/interface.h> #include <euicc/interface.h>
#include "lpac-jni.h" #include "lpac-jni.h"
void interface_wrapper_init(); void interface_wrapper_init(JNIEnv *env);
extern struct euicc_apdu_interface lpac_jni_apdu_interface; extern struct euicc_apdu_interface lpac_jni_apdu_interface;
extern struct euicc_http_interface lpac_jni_http_interface; extern struct euicc_http_interface lpac_jni_http_interface;

View file

@ -0,0 +1,202 @@
#include "lpac-convertor.h"
#include <malloc.h>
#include <string.h>
#include <syslog.h>
static jobject profile_state_enabled;
static jobject profile_state_disabled;
static jobject profile_class_operational;
static jobject profile_class_provisioning;
static jobject profile_class_testing;
static jobject profile_management_operation_install;
static jobject profile_management_operation_enable;
static jobject profile_management_operation_disable;
static jobject profile_management_operation_delete;
static jclass version_class;
static jmethodID version_constructor;
static jstring empty_string;
jclass string_class;
static jmethodID string_constructor;
jobject bind_static_field(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jfieldID field = (*env)->GetStaticFieldID(env, clazz, name, sig);
jobject bound = (*env)->GetStaticObjectField(env, clazz, field);
return (*env)->NewGlobalRef(env, bound);
}
#define BIND_PROFILE_STATE_STATIC_FIELD(NAME, FIELD) \
profile_state_##NAME = bind_static_field(env, profile_state_class, FIELD, "L" PROFILE_STATE_CLASS ";")
#define BIND_PROFILE_CLASS_STATIC_FIELD(NAME, FIELD) \
profile_class_##NAME = bind_static_field(env, profile_class_class, FIELD, "L" PROFILE_CLASS_CLASS ";")
#define BIND_NOTIFICATION_OPERATION_FIELD(NAME, FIELD) \
profile_management_operation_##NAME = bind_static_field(env, profile_management_operation_class, FIELD, "L" PROFILE_MANAGEMENT_OPERATION_CLASS ";")
static void init_string_class(JNIEnv *env) {
string_class = (*env)->FindClass(env, "java/lang/String");
string_class = (*env)->NewGlobalRef(env, string_class);
string_constructor = (*env)->GetMethodID(env, string_class, "<init>",
"([BLjava/lang/String;)V");
const jchar _unused[1];
empty_string = (*env)->NewString(env, _unused, 0);
empty_string = (*env)->NewGlobalRef(env, empty_string);
}
void lpac_convertor_init(JNIEnv *env) {
init_string_class(env);
version_class = (*env)->FindClass(env, VERSION_CLASS);
version_class = (*env)->NewGlobalRef(env, version_class);
version_constructor = (*env)->GetMethodID(
env, version_class, "<init>", "(Ljava/lang/String;)V");
jclass profile_state_class = (*env)->FindClass(
env, PROFILE_STATE_CLASS);
BIND_PROFILE_STATE_STATIC_FIELD(enabled, "Enabled");
BIND_PROFILE_STATE_STATIC_FIELD(disabled, "Disabled");
jclass profile_class_class = (*env)->FindClass(
env, PROFILE_CLASS_CLASS);
BIND_PROFILE_CLASS_STATIC_FIELD(operational, "Operational");
BIND_PROFILE_CLASS_STATIC_FIELD(provisioning, "Provisioning");
BIND_PROFILE_CLASS_STATIC_FIELD(testing, "Testing");
jclass profile_management_operation_class = (*env)->FindClass(
env, PROFILE_MANAGEMENT_OPERATION_CLASS);
BIND_NOTIFICATION_OPERATION_FIELD(install, "Install");
BIND_NOTIFICATION_OPERATION_FIELD(delete, "Delete");
BIND_NOTIFICATION_OPERATION_FIELD(enable, "Enable");
BIND_NOTIFICATION_OPERATION_FIELD(disable, "Disable");
}
jstring toJString(JNIEnv *env, const char *pat) {
jbyteArray bytes = NULL;
jstring encoding = NULL;
jstring jstr = NULL;
jsize len;
if (pat == NULL)
return (*env)->NewLocalRef(env, empty_string);
len = (jsize) strlen(pat);
bytes = (*env)->NewByteArray(env, len);
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) pat);
encoding = (*env)->NewStringUTF(env, "utf-8");
jstr = (jstring) (*env)->NewObject(env, string_class,
string_constructor, bytes, encoding);
(*env)->DeleteLocalRef(env, encoding);
(*env)->DeleteLocalRef(env, bytes);
return jstr;
}
jobject to_profile_state(enum es10c_profile_state profile_state) {
switch (profile_state) {
case ES10C_PROFILE_STATE_ENABLED:
return profile_state_enabled;
case ES10C_PROFILE_STATE_DISABLED:
return profile_state_disabled;
default:
return NULL;
}
}
jobject to_profile_class(enum es10c_profile_class profile_class) {
switch (profile_class) {
case ES10C_PROFILE_CLASS_OPERATIONAL:
return profile_class_operational;
case ES10C_PROFILE_CLASS_PROVISIONING:
return profile_class_provisioning;
case ES10C_PROFILE_CLASS_TEST:
return profile_class_testing;
default:
return NULL;
}
}
jstring to_icon_type(JNIEnv *env, enum es10c_icon_type icon_type) {
switch (icon_type) {
case ES10C_ICON_TYPE_JPEG:
return toJString(env, "jpeg");
case ES10C_ICON_TYPE_PNG:
return toJString(env, "png");
default:
return NULL;
}
}
jobject to_version(JNIEnv *env, const char *version) {
jstring value = toJString(env, version);
return (*env)->NewObject(env, version_class, version_constructor, value);
}
jobject to_string_list(JNIEnv *env, char **values) {
jclass array_class = (*env)->FindClass(env, ARRAY_LIST_CLASS);
jmethodID array_constructor = (*env)->GetMethodID(env, array_class, "<init>", "()V");
jobject elements = (*env)->NewObject(env, array_class, array_constructor);
jmethodID add_element = (*env)->GetMethodID(env, array_class, "add", "(Ljava/lang/Object;)Z");
jstring element = NULL;
for (jsize index = 0; values[index] != NULL; index++) {
element = toJString(env, values[index]);
(*env)->CallBooleanMethod(env, elements, add_element, element);
}
return elements;
}
jobject to_string_set(JNIEnv *env, char **values) {
jclass set_class = (*env)->FindClass(env, HASH_SET_CLASS);
jmethodID set_constructor = (*env)->GetMethodID(env, set_class, "<init>", "()V");
jobject elements = (*env)->NewObject(env, set_class, set_constructor);
jmethodID add_element = (*env)->GetMethodID(env, set_class, "add", "(Ljava/lang/Object;)Z");
jstring element = NULL;
for (jsize index = 0; values[index] != NULL; index++) {
element = toJString(env, values[index]);
(*env)->CallBooleanMethod(env, elements, add_element, element);
}
return elements;
}
jobject build_profile_metadata(JNIEnv *env, struct es8p_metadata *metadata) {
if (metadata == NULL) return NULL;
jclass profile_metadata_class = (*env)->FindClass(env, PROFILE_METADATA_CLASS);
jmethodID profile_metadata_constructor = (*env)->GetMethodID(
env, profile_metadata_class, "<init>",
"("
"Ljava/lang/String;" // iccid
"Ljava/lang/String;" // serviceProviderName
"Ljava/lang/String;" // profileName
"Ljava/lang/String;" // iconType
"Ljava/lang/String;" // icon (base64-encoded)
"L" PROFILE_CLASS_CLASS ";" // profileClass
")"
"V" // (returns) void
);
return (*env)->NewObject(
env, profile_metadata_class, profile_metadata_constructor,
toJString(env, metadata->iccid),
toJString(env, metadata->serviceProviderName),
toJString(env, metadata->profileName),
to_icon_type(env, metadata->iconType),
toJString(env, metadata->icon),
to_profile_class(metadata->profileClass)
);
}
jobject to_profile_management_operation(enum es10b_profile_management_operation operation) {
switch (operation) {
case ES10B_PROFILE_MANAGEMENT_OPERATION_INSTALL:
return profile_management_operation_install;
case ES10B_PROFILE_MANAGEMENT_OPERATION_DELETE:
return profile_management_operation_delete;
case ES10B_PROFILE_MANAGEMENT_OPERATION_ENABLE:
return profile_management_operation_enable;
case ES10B_PROFILE_MANAGEMENT_OPERATION_DISABLE:
return profile_management_operation_disable;
default:
return NULL;
}
}

View file

@ -0,0 +1,41 @@
#pragma once
#include <jni.h>
#include <euicc/es8p.h>
#include <euicc/es10c.h>
#define PACKAGE_NAME "net/typeblog/lpac_jni"
#define PROFILE_STATE_CLASS PACKAGE_NAME "/ProfileState"
#define PROFILE_CLASS_CLASS PACKAGE_NAME "/ProfileClass"
#define DOWNLOAD_CALLBACK_CLASS PACKAGE_NAME "/ProfileDownloadCallback"
#define DOWNLOAD_STATE_CLASS DOWNLOAD_CALLBACK_CLASS "$DownloadState"
#define PROFILE_METADATA_CLASS DOWNLOAD_CALLBACK_CLASS "$ProfileMetadata"
#define LOCAL_PROFILE_NOTIFICATION_CLASS PACKAGE_NAME "/LocalProfileNotification"
#define PROFILE_MANAGEMENT_OPERATION_CLASS PACKAGE_NAME "/ProfileManagementOperation"
#define EUICC_CONFIGURED_ADDRESSES_CLASS PACKAGE_NAME "/EuiccConfiguredAddresses"
#define DISCOVERY_CALLBACK_CLASS PACKAGE_NAME "/ProfileDiscoveryCallback"
#define VERSION_CLASS PACKAGE_NAME "/Version"
#define HASH_SET_CLASS "java/util/HashSet"
#define ARRAY_LIST_CLASS "java/util/ArrayList"
jclass string_class;
void lpac_convertor_init(JNIEnv *env);
jstring toJString(JNIEnv *env, const char *pat);
jobject bind_static_field(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject to_profile_state(enum es10c_profile_state profile_state);
jobject to_profile_class(enum es10c_profile_class profile_class);
jobject to_version(JNIEnv *env, const char *version);
jobject to_string_list(JNIEnv *env, char **values);
jobject to_string_set(JNIEnv *env, char **values);
jobject build_profile_metadata(JNIEnv *env, struct es8p_metadata *metadata);
jobject to_profile_management_operation(enum es10b_profile_management_operation operation);

View file

@ -0,0 +1,109 @@
#include <euicc/es10a.h>
#include <euicc/es10b.h>
#include <euicc/es9p.h>
#include <string.h>
#include <malloc.h>
#include <syslog.h>
#include "lpac-convertor.h"
#include "lpac-discovery.h"
jclass euicc_configured_addresses_class;
jmethodID euicc_configured_addresses_constructor;
jmethodID on_discovered;
void lpac_discovery_init(JNIEnv *env) {
jclass download_callback_class = (*env)->FindClass(env, DISCOVERY_CALLBACK_CLASS);
on_discovered = (*env)->GetMethodID(env, download_callback_class, "onDiscovered",
"(Ljava/util/ArrayList;)V");
euicc_configured_addresses_class = (*env)->FindClass(env, EUICC_CONFIGURED_ADDRESSES_CLASS);
euicc_configured_addresses_class = (*env)->NewGlobalRef(env, euicc_configured_addresses_class);
euicc_configured_addresses_constructor = (*env)->GetMethodID(
env, euicc_configured_addresses_class, "<init>",
"(Ljava/lang/String;Ljava/lang/String;)V");
}
JNIEXPORT jobject JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10aGetEuiccConfiguredAddresses(
JNIEnv *env,
__attribute__((unused)) jobject thiz,
jlong handle
) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10a_euicc_configured_addresses addresses;
jobject ret = NULL;
if (es10a_get_euicc_configured_addresses(ctx, &addresses) < 0) {
goto out;
}
jstring default_dp_address = toJString(env, addresses.defaultDpAddress);
jstring root_ds_address = toJString(env, addresses.rootDsAddress);
ret = (*env)->NewObject(env, euicc_configured_addresses_class,
euicc_configured_addresses_constructor,
default_dp_address, root_ds_address);
out:
es10a_euicc_configured_addresses_free(&addresses);
return ret;
}
JNIEXPORT jint JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_discoveryProfile(
JNIEnv *env,
__attribute__((unused)) jobject thiz,
jlong handle,
jstring address,
jstring imei,
jobject callback
) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
const char *_address = (*env)->GetStringUTFChars(env, address, NULL);
const char *_imei = NULL;
if (imei != NULL) {
_imei = (*env)->GetStringUTFChars(env, address, NULL);
}
ctx->http.server_address = _address;
char **smdp_list = NULL;
int ret;
ret = es10b_get_euicc_challenge_and_info(ctx);
syslog(LOG_INFO, "es10b_get_euicc_challenge_and_info %d", ret);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
ret = es9p_initiate_authentication(ctx);
syslog(LOG_INFO, "es9p_initiate_authentication %d", ret);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
ret = es10b_authenticate_server(ctx, NULL, _imei);
syslog(LOG_INFO, "es10b_authenticate_server %d", ret);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
ret = es11_authenticate_client(ctx, &smdp_list);
syslog(LOG_INFO, "es11_authenticate_client %d", ret);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_discovered, to_string_list(env, smdp_list));
out:
if (_imei != NULL) (*env)->ReleaseStringUTFChars(env, imei, _imei);
(*env)->ReleaseStringUTFChars(env, address, _address);
es11_smdp_list_free_all(smdp_list);
return ret;
}

View file

@ -0,0 +1,6 @@
#pragma once
#include <jni.h>
#include "lpac-jni.h"
void lpac_discovery_init(JNIEnv *env);

View file

@ -3,72 +3,56 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <syslog.h> #include <syslog.h>
#include "lpac-convertor.h"
#include "lpac-download.h" #include "lpac-download.h"
jobject download_state_preparing; static jobject download_state_preparing;
jobject download_state_connecting; static jobject download_state_connecting;
jobject download_state_authenticating; static jobject download_state_authenticating;
jobject download_state_downloading; static jobject download_state_downloading;
jobject download_state_finalizing; static jobject download_state_finalizing;
jmethodID on_state_update; #define BIND_DOWNLOAD_STATE_STATIC_FIELD(NAME, FIELD) \
download_state_##NAME = bind_static_field(env, download_state_class, FIELD, "L" DOWNLOAD_STATE_CLASS ";")
void lpac_download_init() { void lpac_download_init(JNIEnv *env) {
LPAC_JNI_SETUP_ENV; jclass download_state_class = (*env)->FindClass(env, DOWNLOAD_STATE_CLASS);
jclass download_state_class = (*env)->FindClass(env, BIND_DOWNLOAD_STATE_STATIC_FIELD(preparing, "Preparing");
"net/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState"); BIND_DOWNLOAD_STATE_STATIC_FIELD(connecting, "Connecting");
jfieldID download_state_preparing_field = (*env)->GetStaticFieldID(env, download_state_class, BIND_DOWNLOAD_STATE_STATIC_FIELD(authenticating, "Authenticating");
"Preparing", BIND_DOWNLOAD_STATE_STATIC_FIELD(downloading, "Downloading");
"Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;"); BIND_DOWNLOAD_STATE_STATIC_FIELD(finalizing, "Finalizing");
download_state_preparing = (*env)->GetStaticObjectField(env, download_state_class,
download_state_preparing_field);
download_state_preparing = (*env)->NewGlobalRef(env, download_state_preparing);
jfieldID download_state_connecting_field = (*env)->GetStaticFieldID(env, download_state_class,
"Connecting",
"Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;");
download_state_connecting = (*env)->GetStaticObjectField(env, download_state_class,
download_state_connecting_field);
download_state_connecting = (*env)->NewGlobalRef(env, download_state_connecting);
jfieldID download_state_authenticating_field = (*env)->GetStaticFieldID(env,
download_state_class,
"Authenticating",
"Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;");
download_state_authenticating = (*env)->GetStaticObjectField(env, download_state_class,
download_state_authenticating_field);
download_state_authenticating = (*env)->NewGlobalRef(env, download_state_authenticating);
jfieldID download_state_downloading_field = (*env)->GetStaticFieldID(env, download_state_class,
"Downloading",
"Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;");
download_state_downloading = (*env)->GetStaticObjectField(env, download_state_class,
download_state_downloading_field);
download_state_downloading = (*env)->NewGlobalRef(env, download_state_downloading);
jfieldID download_state_finalizng_field = (*env)->GetStaticFieldID(env, download_state_class,
"Finalizing",
"Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;");
download_state_finalizing = (*env)->GetStaticObjectField(env, download_state_class,
download_state_finalizng_field);
download_state_finalizing = (*env)->NewGlobalRef(env, download_state_finalizing);
jclass download_callback_class = (*env)->FindClass(env,
"net/typeblog/lpac_jni/ProfileDownloadCallback");
on_state_update = (*env)->GetMethodID(env, download_callback_class, "onStateUpdate",
"(Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;)V");
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, jlong handle, Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(
jstring smdp, jstring matching_id, JNIEnv *env,
jstring imei, jstring confirmation_code, __attribute__((unused)) jobject thiz,
jobject callback) { jlong handle,
jstring smdp,
jstring matching_id,
jstring imei,
jstring confirmation_code,
jobject callback
) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result; struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
struct es8p_metadata *profile_metadata = NULL;
const char *_confirmation_code = NULL; const char *_confirmation_code = NULL;
const char *_matching_id = NULL; const char *_matching_id = NULL;
const char *_smdp = NULL; const char *_smdp = NULL;
const char *_imei = NULL; const char *_imei = NULL;
int ret; int ret;
jclass callback_class = (*env)->GetObjectClass(env, callback);
jmethodID on_state_update = (*env)->GetMethodID(env, callback_class, "onStateUpdate", "(L" DOWNLOAD_STATE_CLASS ";)V");
jmethodID on_profile_metadata = (*env)->GetMethodID(env, callback_class, "onProfileMetadata", "(L" PROFILE_METADATA_CLASS ";)V");
jmethodID is_cancelled = (*env)->GetMethodID(env, callback_class, "isCancelled", "()Z");
jmethodID set_cancelled = (*env)->GetMethodID(env, callback_class, "setCancelled", "(Z)V");
#define IS_CANCELLED (*env)->CallBooleanMethod(env, callback, is_cancelled)
if (confirmation_code != NULL) if (confirmation_code != NULL)
_confirmation_code = (*env)->GetStringUTFChars(env, confirmation_code, NULL); _confirmation_code = (*env)->GetStringUTFChars(env, confirmation_code, NULL);
if (matching_id != NULL) if (matching_id != NULL)
@ -79,6 +63,10 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
ctx->http.server_address = _smdp; ctx->http.server_address = _smdp;
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_preparing); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_preparing);
ret = es10b_get_euicc_challenge_and_info(ctx); ret = es10b_get_euicc_challenge_and_info(ctx);
syslog(LOG_INFO, "es10b_get_euicc_challenge_and_info %d", ret); syslog(LOG_INFO, "es10b_get_euicc_challenge_and_info %d", ret);
@ -87,6 +75,10 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out; goto out;
} }
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_connecting); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_connecting);
ret = es9p_initiate_authentication(ctx); ret = es9p_initiate_authentication(ctx);
syslog(LOG_INFO, "es9p_initiate_authentication %d", ret); syslog(LOG_INFO, "es9p_initiate_authentication %d", ret);
@ -95,6 +87,10 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out; goto out;
} }
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_authenticating); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_authenticating);
ret = es10b_authenticate_server(ctx, _matching_id, _imei); ret = es10b_authenticate_server(ctx, _matching_id, _imei);
syslog(LOG_INFO, "es10b_authenticate_server %d", ret); syslog(LOG_INFO, "es10b_authenticate_server %d", ret);
@ -103,12 +99,31 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out; goto out;
} }
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
ret = es9p_authenticate_client(ctx); ret = es9p_authenticate_client(ctx);
if (ret < 0) { if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED; ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out; goto out;
} }
const char *b64_profileMetadata = ctx->http._internal.prepare_download_param->b64_profileMetadata;
if (b64_profileMetadata != NULL) {
ret = es8p_metadata_parse(&profile_metadata, b64_profileMetadata);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_profile_metadata,
build_profile_metadata(env, profile_metadata));
}
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading);
ret = es10b_prepare_download(ctx, _confirmation_code); ret = es10b_prepare_download(ctx, _confirmation_code);
syslog(LOG_INFO, "es10b_prepare_download %d", ret); syslog(LOG_INFO, "es10b_prepare_download %d", ret);
@ -117,13 +132,22 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out; goto out;
} }
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
ret = es9p_get_bound_profile_package(ctx); ret = es9p_get_bound_profile_package(ctx);
if (ret < 0) if (ret < 0)
goto out; goto out;
if (IS_CANCELLED) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_finalizing); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_finalizing);
ret = es10b_load_bound_profile_package(ctx, &es10b_load_bound_profile_package_result); ret = es10b_load_bound_profile_package(ctx, &es10b_load_bound_profile_package_result);
syslog(LOG_INFO, "es10b_load_bound_profile_package %d, reason %d", ret, es10b_load_bound_profile_package_result.errorReason); syslog(LOG_INFO, "es10b_load_bound_profile_package %d, reason %d", ret,
es10b_load_bound_profile_package_result.errorReason);
if (ret < 0) { if (ret < 0) {
ret = -(int) es10b_load_bound_profile_package_result.errorReason; ret = -(int) es10b_load_bound_profile_package_result.errorReason;
goto out; goto out;
@ -132,6 +156,11 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
euicc_http_cleanup(ctx); euicc_http_cleanup(ctx);
out: out:
if (IS_CANCELLED == 0 && ret == -ES10B_ERROR_REASON_UNDEFINED) {
(*env)->CallVoidMethod(env, callback, set_cancelled, JNI_TRUE);
}
es8p_metadata_free(&profile_metadata);
#undef IS_CANCELLED
// We expect Java side to call cancelSessions after any error -- thus, `euicc_http_cleanup` is done there // We expect Java side to call cancelSessions after any error -- thus, `euicc_http_cleanup` is done there
// This is so that Java side can access the last HTTP and/or APDU errors when we return. // This is so that Java side can access the last HTTP and/or APDU errors when we return.
if (_confirmation_code != NULL) if (_confirmation_code != NULL)
@ -144,7 +173,6 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
return ret; return ret;
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_cancelSessions(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_cancelSessions(JNIEnv *env, jobject thiz, jlong handle) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
@ -167,15 +195,12 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR);
ERRCODE_ENUM_TO_STRING( ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC);
ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE);
ERRCODE_ENUM_TO_STRING(
ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_MISMATCH); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_MISMATCH);
ERRCODE_ENUM_TO_STRING( ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY);
ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_PPR_NOT_ALLOWED); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_PPR_NOT_ALLOWED);
ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR);
default: default:

View file

@ -3,4 +3,4 @@
#include <jni.h> #include <jni.h>
#include "lpac-jni.h" #include "lpac-jni.h"
void lpac_download_init(); void lpac_download_init(JNIEnv *env);

View file

@ -8,38 +8,33 @@
#include "lpac-jni.h" #include "lpac-jni.h"
#include "lpac-download.h" #include "lpac-download.h"
#include "lpac-notifications.h" #include "lpac-notifications.h"
#include "lpac-discovery.h"
#include "lpac-convertor.h"
#include "interface-wrapper.h" #include "interface-wrapper.h"
JavaVM *jvm = NULL; JavaVM *jvm = NULL;
jstring empty_string; #define LOCAL_PROFILE_INFO_CLASS "net/typeblog/lpac_jni/LocalProfileInfo"
jclass string_class;
jmethodID string_constructor;
jint JNI_OnLoad(JavaVM *vm, void *reserved) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
jvm = vm; jvm = vm;
interface_wrapper_init();
lpac_download_init();
LPAC_JNI_SETUP_ENV; LPAC_JNI_SETUP_ENV;
string_class = (*env)->FindClass(env, "java/lang/String");
string_class = (*env)->NewGlobalRef(env, string_class);
string_constructor = (*env)->GetMethodID(env, string_class, "<init>",
"([BLjava/lang/String;)V");
const jchar _unused[1]; interface_wrapper_init(env);
empty_string = (*env)->NewString(env, _unused, 0); lpac_convertor_init(env);
empty_string = (*env)->NewGlobalRef(env, empty_string); lpac_discovery_init(env);
lpac_download_init(env);
return JNI_VERSION_1_6; return JNI_VERSION_1_6;
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_createContext(JNIEnv *env, jobject thiz, Java_net_typeblog_lpac_1jni_LpacJni_createContext(
JNIEnv *env, jobject thiz,
jbyteArray isdr_aid, jbyteArray isdr_aid,
jobject apdu_interface, jobject apdu_interface,
jobject http_interface) { jobject http_interface
) {
struct lpac_jni_ctx *jni_ctx = NULL; struct lpac_jni_ctx *jni_ctx = NULL;
struct euicc_ctx *ctx = NULL; struct euicc_ctx *ctx = NULL;
jbyte *isdr_java = NULL; jbyte *isdr_java = NULL;
@ -96,26 +91,6 @@ Java_net_typeblog_lpac_1jni_LpacJni_euiccSetMss(JNIEnv *env, jobject thiz, jlong
ctx->es10x_mss = (uint8_t) mss; ctx->es10x_mss = (uint8_t) mss;
} }
jstring toJString(JNIEnv *env, const char *pat) {
jbyteArray bytes = NULL;
jstring encoding = NULL;
jstring jstr = NULL;
int len;
if (pat == NULL)
return (*env)->NewLocalRef(env, empty_string);
len = strlen(pat);
bytes = (*env)->NewByteArray(env, len);
(*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte *) pat);
encoding = (*env)->NewStringUTF(env, "utf-8");
jstr = (jstring) (*env)->NewObject(env, string_class,
string_constructor, bytes, encoding);
(*env)->DeleteLocalRef(env, encoding);
(*env)->DeleteLocalRef(env, bytes);
return jstr;
}
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong handle) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
@ -130,67 +105,56 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong
} }
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo(
JNIEnv *env,
__attribute__((unused)) jobject thiz,
jlong handle,
jobject profiles
) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10c_profile_info_list *info = NULL; struct es10c_profile_info_list *info = NULL;
int ret = es10c_get_profiles_info(ctx, &info);
if (ret < 0) return ret;
if (es10c_get_profiles_info(ctx, &info) < 0) { jclass profile_list_class = (*env)->GetObjectClass(env, profiles);
return 0; jmethodID add_profile = (*env)->GetMethodID(env, profile_list_class, "add",
"(Ljava/lang/Object;)Z");
jclass profile_info_class = (*env)->FindClass(env, LOCAL_PROFILE_INFO_CLASS);
jmethodID profile_info_class_constructor = (*env)->GetMethodID(
env, profile_info_class, "<init>",
"("
"Ljava/lang/String;" // iccid
"Lnet/typeblog/lpac_jni/ProfileState;"
"Ljava/lang/String;" // name
"Ljava/lang/String;" // nickname
"Ljava/lang/String;" // provider name
"Ljava/lang/String;" // ISD-P AID
"Lnet/typeblog/lpac_jni/ProfileClass;"
")"
"V" // (returns) void
);
jobject element = NULL;
while (info) {
element = (*env)->NewObject(
env, profile_info_class, profile_info_class_constructor,
toJString(env, info->iccid),
to_profile_state(info->profileState),
toJString(env, info->profileName),
toJString(env, info->profileNickname),
toJString(env, info->serviceProviderName),
toJString(env, info->isdpAid),
to_profile_class(info->profileClass)
);
(*env)->CallBooleanMethod(env, profiles, add_profile, element);
info = info->next;
} }
return (jlong) info; es10c_profile_info_list_free_all(info);
return ret;
} }
JNIEXPORT jstring JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_profileGetStateString(JNIEnv *env, jobject thiz, jlong curr) {
struct es10c_profile_info_list *info = (struct es10c_profile_info_list *) curr;
const char *profileStateStr = NULL;
switch (info->profileState) {
case ES10C_PROFILE_STATE_ENABLED:
profileStateStr = "enabled";
break;
case ES10C_PROFILE_STATE_DISABLED:
profileStateStr = "disabled";
break;
default:
profileStateStr = "unknown";
}
return toJString(env, profileStateStr);
}
JNIEXPORT jstring JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_profileGetClassString(JNIEnv *env, jobject thiz, jlong curr) {
struct es10c_profile_info_list *info = (struct es10c_profile_info_list *) curr;
const char *profileClassStr = NULL;
switch (info->profileClass) {
case ES10C_PROFILE_CLASS_TEST:
profileClassStr = "test";
break;
case ES10C_PROFILE_CLASS_PROVISIONING:
profileClassStr = "provisioning";
break;
case ES10C_PROFILE_CLASS_OPERATIONAL:
profileClassStr = "operational";
break;
default:
profileClassStr = "unknown";
break;
}
return toJString(env, profileClassStr);
}
LPAC_JNI_STRUCT_GETTER_LINKED_LIST_NEXT(struct es10c_profile_info_list, profiles)
LPAC_JNI_STRUCT_FREE(struct es10c_profile_info_list, profiles, es10c_profile_info_list_free_all)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_profile_info_list, profile, iccid, Iccid)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_profile_info_list, profile, isdpAid, IsdpAid)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_profile_info_list, profile, profileName, Name)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_profile_info_list, profile, profileNickname, Nickname)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_profile_info_list, profile, serviceProviderName, ServiceProvider)
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10cEnableProfile(JNIEnv *env, jobject thiz, jlong handle, Java_net_typeblog_lpac_1jni_LpacJni_es10cEnableProfile(JNIEnv *env, jobject thiz, jlong handle,
jstring iccid, jboolean refresh) { jstring iccid, jboolean refresh) {
@ -246,48 +210,51 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cDeleteProfile(JNIEnv *env, jobject thiz
return ret; return ret;
} }
JNIEXPORT jlong JNICALL JNIEXPORT jobject JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10cexGetEuiccInfo2(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_es10cexGetEuiccInfo2(JNIEnv *env, jobject thiz, jlong handle) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10c_ex_euiccinfo2 *info = malloc(sizeof(struct es10c_ex_euiccinfo2)); struct es10c_ex_euiccinfo2 *info = malloc(sizeof(struct es10c_ex_euiccinfo2));
jobject ret = NULL;
if (es10c_ex_get_euiccinfo2(ctx, info) < 0) goto out;
jclass euicc_info_class = (*env)->FindClass(env, PACKAGE_NAME "/EuiccInfo2");
jmethodID euicc_info_constructor = (*env)->GetMethodID(
env, euicc_info_class, "<init>",
"("
"Lnet/typeblog/lpac_jni/Version;" // sgp22 version
"Lnet/typeblog/lpac_jni/Version;" // profile version
"Lnet/typeblog/lpac_jni/Version;" // euicc firmware version
"Lnet/typeblog/lpac_jni/Version;" // global platform version
"Ljava/lang/String;" // sas accreditation number
"Lnet/typeblog/lpac_jni/Version;" // protected profile version
"I" // freeNvram
"I" // freeRam
"Ljava/util/Set;" // euicc ci public id list (for signing)
"Ljava/util/Set;" // euicc ci public id list (for verification)
")"
"V" // (returns) void
);
if (es10c_ex_get_euiccinfo2(ctx, info) < 0) { ret = (*env)->NewObject(
free(info); env, euicc_info_class, euicc_info_constructor,
return 0; to_version(env, info->svn),
to_version(env, info->profileVersion),
to_version(env, info->euiccFirmwareVer),
to_version(env, info->globalplatformVersion),
toJString(env, info->sasAcreditationNumber),
to_version(env, info->ppVersion),
(jint) info->extCardResource.freeNonVolatileMemory,
(jint) info->extCardResource.freeVolatileMemory,
to_string_set(env, info->euiccCiPKIdListForSigning),
to_string_set(env, info->euiccCiPKIdListForVerification)
);
out:
es10c_ex_euiccinfo2_free(info);
return ret;
} }
return (jlong) info;
}
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10cEuiccMemoryReset(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_es10cEuiccMemoryReset(JNIEnv *env, jobject thiz, jlong handle) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
int ret; return es10c_euicc_memory_reset(ctx);
ret = es10c_euicc_memory_reset(ctx);
return ret;
} }
JNIEXPORT jstring JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_stringDeref(JNIEnv *env, jobject thiz, jlong curr) {
return toJString(env, *((char **) curr));
}
void lpac_jni_euiccinfo2_free(struct es10c_ex_euiccinfo2 *info) {
es10c_ex_euiccinfo2_free(info);
free(info);
}
LPAC_JNI_STRUCT_GETTER_NULL_TERM_LIST_NEXT(char*, stringArr)
LPAC_JNI_STRUCT_FREE(struct es10c_ex_euiccinfo2, euiccInfo2, lpac_jni_euiccinfo2_free)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, svn, SGP22Version)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, profileVersion, ProfileVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, euiccFirmwareVer, EuiccFirmwareVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, globalplatformVersion, GlobalPlatformVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, sasAcreditationNumber, SasAcreditationNumber)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, ppVersion, PpVersion)
LPAC_JNI_STRUCT_GETTER_LONG(struct es10c_ex_euiccinfo2, euiccInfo2, extCardResource.freeNonVolatileMemory, FreeNonVolatileMemory)
LPAC_JNI_STRUCT_GETTER_LONG(struct es10c_ex_euiccinfo2, euiccInfo2, extCardResource.freeVolatileMemory, FreeVolatileMemory)
LPAC_JNI_STRUCT_GETTER_LONG(struct es10c_ex_euiccinfo2, euiccInfo2, euiccCiPKIdListForSigning, EuiccCiPKIdListForSigning)
LPAC_JNI_STRUCT_GETTER_LONG(struct es10c_ex_euiccinfo2, euiccInfo2, euiccCiPKIdListForVerification, EuiccCiPKIdListForVerification)

View file

@ -19,41 +19,4 @@ struct lpac_jni_ctx {
(*jvm)->AttachCurrentThread(jvm, &env, NULL) (*jvm)->AttachCurrentThread(jvm, &env, NULL)
extern JavaVM *jvm; extern JavaVM *jvm;
extern jclass string_class;
jstring toJString(JNIEnv *env, const char *pat);
#define LPAC_JNI_STRUCT_GETTER_LINKED_LIST_NEXT(st, st_jname) \
JNIEXPORT jlong JNICALL Java_net_typeblog_lpac_1jni_LpacJni_##st_jname##Next(JNIEnv *env, jobject thiz, jlong raw) { \
st *p = (st *) raw; \
if (p == NULL) return 0; \
return (jlong) p->next; \
}
#define LPAC_JNI_STRUCT_GETTER_NULL_TERM_LIST_NEXT(st, st_jname) \
JNIEXPORT jlong JNICALL Java_net_typeblog_lpac_1jni_LpacJni_##st_jname##Next(JNIEnv *env, jobject thiz, jlong raw) { \
st *p = (st *) raw; \
p++; \
if (*p == NULL) return 0; \
return (jlong) p; \
}
#define LPAC_JNI_STRUCT_FREE(st, st_jname, free_func) \
JNIEXPORT void JNICALL Java_net_typeblog_lpac_1jni_LpacJni_##st_jname##Free(JNIEnv *env, jobject thiz, jlong raw) { \
st *p = (st *) raw; \
if (p == NULL) return; \
free_func(p); \
}
#define LPAC_JNI_STRUCT_GETTER_LONG(st, st_name, name, jname) \
JNIEXPORT jlong JNICALL Java_net_typeblog_lpac_1jni_LpacJni_##st_name##Get##jname(JNIEnv *env, jobject thiz, jlong raw) { \
st *p = (st *) raw; \
if (p == NULL) return 0; \
return (jlong) p->name; \
}
#define LPAC_JNI_STRUCT_GETTER_STRING(st, st_name, name, jname) \
JNIEXPORT jstring JNICALL Java_net_typeblog_lpac_1jni_LpacJni_##st_name##Get##jname(JNIEnv *env, jobject thiz, jlong raw) { \
st *p = (st *) raw; \
return toJString(env, p->name); \
}

View file

@ -1,18 +1,56 @@
#include "lpac-notifications.h"
#include <euicc/es9p.h> #include <euicc/es9p.h>
#include <euicc/es10b.h> #include <euicc/es10b.h>
#include <malloc.h> #include <malloc.h>
#include <syslog.h> #include <syslog.h>
#include "lpac-convertor.h"
#include "lpac-notifications.h"
JNIEXPORT jlong JNICALL JNIEXPORT jint JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_es10bListNotification(JNIEnv *env, jobject thiz, jlong handle) { Java_net_typeblog_lpac_1jni_LpacJni_es10bListNotification(
JNIEnv *env,
jobject thiz,
jlong handle,
jobject notifications
) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10b_notification_metadata_list *info = NULL; struct es10b_notification_metadata_list *metadata = NULL;
int ret = es10b_list_notification(ctx, &metadata);
if (ret < 0) goto out;
if (es10b_list_notification(ctx, &info) < 0) jclass local_profile_notification_class = (*env)->FindClass(
return 0; env, LOCAL_PROFILE_NOTIFICATION_CLASS);
jmethodID local_profile_notification_constructor = (*env)->GetMethodID(
env, local_profile_notification_class, "<init>",
"("
"J" // seqNumber
"L" PROFILE_MANAGEMENT_OPERATION_CLASS ";"
"Ljava/lang/String;" // notificationAddress
"Ljava/lang/String;" // iccid
")"
"V" // (returns) void
);
jmethodID add_notification = (*env)->GetMethodID(
env,
(*env)->GetObjectClass(env, notifications),
"add", "(Ljava/lang/Object;)Z"
);
return (jlong) info; jobject element;
while (metadata) {
jlong sequence_number = metadata->seqNumber;
jobject operation = to_profile_management_operation(metadata->profileManagementOperation);
jstring address = toJString(env, metadata->notificationAddress);
jstring iccid = toJString(env, metadata->iccid);
element = (*env)->NewObject(
env, local_profile_notification_class, local_profile_notification_constructor,
sequence_number, operation, address, iccid);
(*env)->CallBooleanMethod(env, notifications, add_notification, element);
metadata = metadata->next;
}
out:
es10b_notification_metadata_list_free_all(metadata);
return ret;
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
@ -45,35 +83,3 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10bDeleteNotification(JNIEnv *env, jobject
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
return es10b_remove_notification_from_list(ctx, (unsigned long) seq_number); return es10b_remove_notification_from_list(ctx, (unsigned long) seq_number);
} }
JNIEXPORT jstring JNICALL
Java_net_typeblog_lpac_1jni_LpacJni_notificationGetOperationString(JNIEnv *env, jobject thiz,
jlong curr) {
struct es10b_notification_metadata_list *info = (struct es10b_notification_metadata_list *) curr;
const char *profileManagementOperationStr = NULL;
switch (info->profileManagementOperation) {
case ES10B_PROFILE_MANAGEMENT_OPERATION_INSTALL:
profileManagementOperationStr = "install";
break;
case ES10B_PROFILE_MANAGEMENT_OPERATION_DELETE:
profileManagementOperationStr = "delete";
break;
case ES10B_PROFILE_MANAGEMENT_OPERATION_ENABLE:
profileManagementOperationStr = "enable";
break;
case ES10B_PROFILE_MANAGEMENT_OPERATION_DISABLE:
profileManagementOperationStr = "disable";
break;
default:
profileManagementOperationStr = "unknown";
break;
}
return toJString(env, profileManagementOperationStr);
}
LPAC_JNI_STRUCT_GETTER_LINKED_LIST_NEXT(struct es10b_notification_metadata_list, notifications)
LPAC_JNI_STRUCT_FREE(struct es10b_notification_metadata_list, notifications, es10b_notification_metadata_list_free_all)
LPAC_JNI_STRUCT_GETTER_LONG(struct es10b_notification_metadata_list, notification, seqNumber, Seq)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10b_notification_metadata_list, notification, notificationAddress, Address)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10b_notification_metadata_list, notification, iccid, Iccid)