diff --git a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt index b715ca0..6f8479c 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/LocalProfileAssistantWrapper.kt @@ -1,9 +1,11 @@ package im.angry.openeuicc.core +import net.typeblog.lpac_jni.EuiccConfiguredAddresses import net.typeblog.lpac_jni.EuiccInfo2 import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileNotification +import net.typeblog.lpac_jni.ProfileDiscoveryCallback import net.typeblog.lpac_jni.ProfileDownloadCallback class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : @@ -29,6 +31,8 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : get() = lpa.eID override val euiccInfo2: EuiccInfo2? get() = lpa.euiccInfo2 + override val euiccConfiguredAddresses: EuiccConfiguredAddresses + get() = lpa.euiccConfiguredAddresses override fun setEs10xMss(mss: Byte) = lpa.setEs10xMss(mss) @@ -48,6 +52,9 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) : callback: ProfileDownloadCallback ) = 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 handleNotification(seqNumber: Long): Boolean = lpa.handleNotification(seqNumber) diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt index 9957f30..013fe29 100644 --- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt +++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt @@ -36,6 +36,7 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.yield import net.typeblog.lpac_jni.ProfileDownloadCallback +import net.typeblog.lpac_jni.ProfileMetadata /** * An Android Service wrapper for EuiccChannelManager. @@ -380,6 +381,18 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { getString(R.string.task_profile_download_failure), 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: ProfileMetadata) { + Log.d(TAG, "Downloaded profile metadata: $metadata") + } + } euiccChannelManager.beginTrackedOperation(slotId, portId) { euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> channel.lpa.downloadProfile( @@ -387,13 +400,8 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { matchingId, imei, confirmationCode, - object : ProfileDownloadCallback { - override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) { - if (state.progress == 0) return - foregroundTaskState.value = - ForegroundTaskState.InProgress(state.progress) - } - }) + callback + ) } preferenceRepository.notificationDownloadFlow.first() diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index 12995ff..1af7796 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -43,6 +43,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext +import net.typeblog.lpac_jni.ProfileClass open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, EuiccChannelFragmentMarker { @@ -387,9 +388,10 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, profileClass.isVisible = unfilteredProfileListFlow.value profileClass.setText( when (profile.profileClass) { - LocalProfileInfo.Clazz.Testing -> R.string.profile_class_testing - LocalProfileInfo.Clazz.Provisioning -> R.string.profile_class_provisioning - LocalProfileInfo.Clazz.Operational -> R.string.profile_class_operational + ProfileClass.Testing -> R.string.profile_class_testing + ProfileClass.Provisioning -> R.string.profile_class_provisioning + ProfileClass.Operational -> R.string.profile_class_operational + else -> throw IllegalStateException("profile class should not be null") } ) iccid.text = profile.iccid diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt index 21a2d40..099c627 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt @@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import net.typeblog.lpac_jni.LocalProfileNotification +import net.typeblog.lpac_jni.ProfileManagementOperation class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker { private lateinit var swipeRefresh: SwipeRefreshLayout @@ -163,14 +164,14 @@ class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker { } - private fun operationToLocalizedText(operation: LocalProfileNotification.Operation) = - root.context.getText( - when (operation) { - LocalProfileNotification.Operation.Install -> R.string.profile_notification_operation_download - LocalProfileNotification.Operation.Delete -> R.string.profile_notification_operation_delete - LocalProfileNotification.Operation.Enable -> R.string.profile_notification_operation_enable - LocalProfileNotification.Operation.Disable -> R.string.profile_notification_operation_disable - }) + private fun operationToLocalizedText(operation: ProfileManagementOperation?) = + root.context.getString(when (operation) { + ProfileManagementOperation.Install -> R.string.profile_notification_operation_download + ProfileManagementOperation.Delete -> R.string.profile_notification_operation_delete + ProfileManagementOperation.Enable -> R.string.profile_notification_operation_enable + ProfileManagementOperation.Disable -> R.string.profile_notification_operation_disable + else -> throw IllegalStateException("Unknown operation: $operation") + }) fun updateNotification(value: LocalProfileNotificationWrapper) { notification = value diff --git a/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt index 9f95412..cc1001b 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt @@ -5,6 +5,8 @@ import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileInfo +import net.typeblog.lpac_jni.ProfileClass +import net.typeblog.lpac_jni.ProfileState const val TAG = "LPAUtils" @@ -13,10 +15,10 @@ val LocalProfileInfo.displayName: String val LocalProfileInfo.isEnabled: Boolean - get() = state == LocalProfileInfo.State.Enabled + get() = state == ProfileState.Enabled val List.operational: List - get() = filter { it.profileClass == LocalProfileInfo.Clazz.Operational } + get() = filter { it.profileClass == ProfileClass.Operational } val List.enabled: LocalProfileInfo? get() = find { it.isEnabled } diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 02b3baf..8ce7958 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -15,6 +15,8 @@ import im.angry.openeuicc.util.* import kotlinx.coroutines.delay import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +import net.typeblog.lpac_jni.ProfileClass +import net.typeblog.lpac_jni.ProfileState import kotlin.IllegalStateException class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { @@ -200,15 +202,15 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { setServiceProviderName(it.providerName) setState( when (it.state) { - LocalProfileInfo.State.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED - LocalProfileInfo.State.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED + ProfileState.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED + ProfileState.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED } ) setProfileClass( when (it.profileClass) { - LocalProfileInfo.Clazz.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING - LocalProfileInfo.Clazz.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING - LocalProfileInfo.Clazz.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL + ProfileClass.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING + ProfileClass.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING + ProfileClass.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL } ) }.build() diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt index 75a6905..fe98b1a 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ApduInterface.kt @@ -17,10 +17,10 @@ interface ApduInterface { */ val valid: Boolean - fun withLogicalChannel(aid: ByteArray, cb: ((ByteArray) -> ByteArray) -> T): T { + fun withLogicalChannel(aid: ByteArray, callback: ((ByteArray) -> ByteArray) -> T): T { val handle = logicalChannelOpen(aid) return try { - cb { transmit(handle, it) } + callback { transmit(handle, it) } } finally { logicalChannelClose(handle) } diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccConfiguredAddresses.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccConfiguredAddresses.kt new file mode 100644 index 0000000..ba9c324 --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccConfiguredAddresses.kt @@ -0,0 +1,21 @@ +package net.typeblog.lpac_jni + +import android.util.Patterns + +data class EuiccConfiguredAddresses( + val defaultDPAddress: String, + val rootDSAddress: String +) { + val isValidDefaultDPAddress: Boolean + get() = isValid(defaultDPAddress) + + val isValidRootDSAddress: Boolean + get() = isValid(rootDSAddress) || rootDSAddress == "lpa.ds.gsma.com" +} + +private fun isValid(address: String): Boolean { + if (address.isBlank()) return false + if (address.endsWith(".gsma.com")) return false + if (address.endsWith(".example.com")) return false + return Patterns.DOMAIN_NAME.matcher(address).matches() +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt index 49d6cc0..51f2653 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/HttpInterface.kt @@ -1,32 +1,13 @@ package net.typeblog.lpac_jni -import javax.net.ssl.TrustManager - /* * Should reflect euicc_http_interface in lpac/euicc/interface.h */ interface HttpInterface { - data class HttpResponse(val rcode: Int, val data: ByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false + @Suppress("ArrayInDataClass") + data class HttpResponse(val rcode: Int, val data: ByteArray) - other as HttpResponse - - if (rcode != other.rcode) return false - if (!data.contentEquals(other.data)) return false - - return true - } - - override fun hashCode(): Int { - var result = rcode - result = 31 * result + data.contentHashCode() - return result - } - } - - fun transmit(url: String, tx: ByteArray, headers: Array): HttpResponse + fun transmit(url: String, tx: ByteArray, headers: List): HttpResponse // The LPA is supposed to pass in a list of pkIds supported by the eUICC. // HttpInterface is responsible for providing TrustManager implementations that // validate based on certificates corresponding to these pkIds diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt index 48ab1c5..df9f714 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt @@ -12,9 +12,18 @@ interface LocalProfileAssistant { val lastApduException: Exception?, ) : Exception("Failed to download profile") - class ProfileRenameException() : Exception("Failed to rename profile") - class ProfileNameTooLongException() : Exception("Profile name too long") - class ProfileNameIsInvalidUTF8Exception() : Exception("Profile name is invalid UTF-8") + @Suppress("ArrayInDataClass") + data class ProfileDiscoveryException( + 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 profiles: List @@ -22,6 +31,7 @@ interface LocalProfileAssistant { val eID: String // Extended EuiccInfo for use with LUIs, containing information such as firmware version val euiccInfo2: EuiccInfo2? + val euiccConfiguredAddresses: EuiccConfiguredAddresses /** * 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?, confirmationCode: String?, callback: ProfileDownloadCallback) + fun discoveryProfile(smds: String, imei: String?, callback: ProfileDiscoveryCallback) + fun deleteNotification(seqNumber: Long): Boolean fun handleNotification(seqNumber: Long): Boolean diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt index a7e296f..3807448 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileInfo.kt @@ -1,43 +1,28 @@ package net.typeblog.lpac_jni +data class ProfileMetadata( + val iccid: String, + val name: String, + val providerName: String, + val profileClass: ProfileClass?, + val iconType: IconType?, + val icon: String, +) + data class LocalProfileInfo( val iccid: String, - val state: State, + val state: ProfileState?, val name: String, val nickName: String, val providerName: String, val isdpAID: String, - val profileClass: Clazz -) { - enum class State { - Enabled, - Disabled; + val profileClass: ProfileClass?, + val iconType: IconType?, + val icon: String, +) - companion object { - @JvmStatic - fun fromString(str: String?) = - when (str?.lowercase()) { - "enabled" -> Enabled - "disabled" -> Disabled - else -> Disabled - } - } - } +enum class ProfileState { Enabled, Disabled } - enum class Clazz { - Testing, - Provisioning, - Operational; +enum class ProfileClass { Testing, Provisioning, Operational } - companion object { - @JvmStatic - fun fromString(str: String?) = - when (str?.lowercase()) { - "test" -> Testing - "provisioning" -> Provisioning - "operational" -> Operational - else -> Operational - } - } - } -} \ No newline at end of file +enum class IconType { JPEG, PNG } diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileNotification.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileNotification.kt index 57cc8cf..2261f6d 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileNotification.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileNotification.kt @@ -2,26 +2,11 @@ package net.typeblog.lpac_jni data class LocalProfileNotification( val seqNumber: Long, - val profileManagementOperation: Operation, + val profileManagementOperation: ProfileManagementOperation?, val notificationAddress: String, val iccid: String, -) { - enum class Operation { - Install, - Enable, - Disable, - Delete; +) - companion object { - @JvmStatic - fun fromString(str: String?) = - when (str?.lowercase()) { - "install" -> Install - "enable" -> Enable - "disable" -> Disable - "delete" -> Delete - else -> throw IllegalArgumentException("Unknown operation $str") - } - } - } +enum class ProfileManagementOperation { + Install, Enable, Disable, Delete } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt index fa9474f..e744f91 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LpacJni.kt @@ -19,16 +19,22 @@ internal object LpacJni { // es10c // null returns signify errors external fun es10cGetEid(handle: Long): String? - external fun es10cGetProfilesInfo(handle: Long): Long + external fun es10cGetProfilesInfo(handle: Long): List external fun es10cEnableProfile(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 es10cSetNickname(handle: Long, iccid: String, nickNullTerminated: ByteArray): Int // 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): List 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 // We do not expose all of the functions because of tediousness :) external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?, @@ -41,40 +47,5 @@ internal object LpacJni { // ES10c external fun es10cEuiccMemoryReset(handle: Long): Int // es10cex (actually part of es10b) - external fun es10cexGetEuiccInfo2(handle: Long): Long - - // 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 + external fun es10cexGetEuiccInfo2(handle: Long): EuiccInfo2? } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDiscoveryCallback.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDiscoveryCallback.kt new file mode 100644 index 0000000..b70e0d8 --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDiscoveryCallback.kt @@ -0,0 +1,5 @@ +package net.typeblog.lpac_jni + +interface ProfileDiscoveryCallback { + fun onDiscovered(servers: List) +} diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDownloadCallback.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDownloadCallback.kt index 289ddf6..b7099c5 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDownloadCallback.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/ProfileDownloadCallback.kt @@ -2,15 +2,14 @@ package net.typeblog.lpac_jni interface ProfileDownloadCallback { companion object { - fun lookupStateFromProgress(progress: Int): DownloadState = - when (progress) { - 0 -> DownloadState.Preparing - 20 -> DownloadState.Connecting - 40 -> DownloadState.Authenticating - 60 -> DownloadState.Downloading - 80 -> DownloadState.Finalizing - else -> throw IllegalArgumentException("Unknown state") - } + fun lookupStateFromProgress(progress: Int): DownloadState = when (progress) { + 0 -> DownloadState.Preparing + 20 -> DownloadState.Connecting + 40 -> DownloadState.Authenticating + 60 -> DownloadState.Downloading + 80 -> DownloadState.Finalizing + else -> throw IllegalArgumentException("Unknown state") + } } enum class DownloadState(val progress: Int) { @@ -21,5 +20,9 @@ interface ProfileDownloadCallback { Finalizing(80), // load bpp } + var isCancelled: Boolean + fun onStateUpdate(state: DownloadState) + + fun onProfileMetadata(metadata: ProfileMetadata) } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt index 55b28fe..9ac0ba4 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt @@ -4,19 +4,11 @@ import android.annotation.SuppressLint import java.security.cert.X509Certificate import javax.net.ssl.X509TrustManager -@SuppressLint("CustomX509TrustManager") +@SuppressLint("CustomX509TrustManager,TrustAllX509TrustManager") class AllowAllTrustManager : X509TrustManager { - @SuppressLint("TrustAllX509TrustManager") - override fun checkClientTrusted(p0: Array?, p1: String?) { - return - } + override fun checkClientTrusted(p0: Array?, p1: String?) {} - @SuppressLint("TrustAllX509TrustManager") - override fun checkServerTrusted(p0: Array?, p1: String?) { - return - } + override fun checkServerTrusted(p0: Array?, p1: String?) {} - override fun getAcceptedIssuers(): Array { - return emptyArray() - } + override fun getAcceptedIssuers() = emptyArray() } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt index c1b15f7..5350d79 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt @@ -26,7 +26,7 @@ class HttpInterfaceImpl( override fun transmit( url: String, tx: ByteArray, - headers: Array + headers: List ): HttpInterface.HttpResponse { Log.d(TAG, "transmit(url = $url)") @@ -85,7 +85,7 @@ class HttpInterfaceImpl( if (runBlocking { ignoreTLSCertificateFlow.first() }) { arrayOf(AllowAllTrustManager()) } else { - this.trustManagers + trustManagers } val sslContext = SSLContext.getInstance("TLS") sslContext.init(null, trustManagers, SecureRandom()) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt index 3674f4f..1c1af83 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt @@ -1,16 +1,17 @@ package net.typeblog.lpac_jni.impl import android.util.Log -import net.typeblog.lpac_jni.LpacJni import net.typeblog.lpac_jni.ApduInterface +import net.typeblog.lpac_jni.EuiccConfiguredAddresses import net.typeblog.lpac_jni.EuiccInfo2 import net.typeblog.lpac_jni.HttpInterface import net.typeblog.lpac_jni.HttpInterface.HttpResponse import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileInfo import net.typeblog.lpac_jni.LocalProfileNotification +import net.typeblog.lpac_jni.LpacJni +import net.typeblog.lpac_jni.ProfileDiscoveryCallback import net.typeblog.lpac_jni.ProfileDownloadCallback -import net.typeblog.lpac_jni.Version class LocalProfileAssistantImpl( isdrAid: ByteArray, @@ -61,7 +62,7 @@ class LocalProfileAssistantImpl( */ var lastHttpException: Exception? = null - override fun transmit(url: String, tx: ByteArray, headers: Array): HttpResponse = + override fun transmit(url: String, tx: ByteArray, headers: List): HttpResponse = try { httpInterface.transmit(url, tx, headers).also { lastHttpException = null @@ -106,47 +107,11 @@ class LocalProfileAssistantImpl( override val profiles: List @Synchronized - get() { - val head = LpacJni.es10cGetProfilesInfo(contextHandle) - var curr = head - val ret = mutableListOf() - while (curr != 0L) { - 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 - } + get() = LpacJni.es10cGetProfilesInfo(contextHandle) override val notifications: List @Synchronized - get() { - val head = LpacJni.es10bListNotification(contextHandle) - var curr = head - val ret = mutableListOf() - while (curr != 0L) { - 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() - } + get() = LpacJni.es10bListNotification(contextHandle).sortedBy { it.seqNumber }.reversed() override val eID: String @Synchronized @@ -154,39 +119,9 @@ class LocalProfileAssistantImpl( override val euiccInfo2: EuiccInfo2? @Synchronized - get() { - val cInfo = LpacJni.es10cexGetEuiccInfo2(contextHandle) - if (cInfo == 0L) return null - - 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 - } + get() = LpacJni.es10cexGetEuiccInfo2(contextHandle) + override val euiccConfiguredAddresses: EuiccConfiguredAddresses + get() = LpacJni.es10aGetEuiccConfiguredAddresses(contextHandle) @Synchronized override fun enableProfile(iccid: String, refresh: Boolean): Boolean = @@ -212,21 +147,31 @@ class LocalProfileAssistantImpl( callback ) - if (res != 0) { - // Construct the error now to store any error information we _can_ access - val err = LocalProfileAssistant.ProfileDownloadException( - lpaErrorReason = LpacJni.downloadErrCodeToString(-res), - httpInterface.lastHttpResponse, - httpInterface.lastHttpException, - apduInterface.lastApduResponse, - apduInterface.lastApduException, - ) + if (res == 0) return + // Construct the error now to store any error information we _can_ access + val err = LocalProfileAssistant.ProfileDownloadException( + lpaErrorReason = LpacJni.downloadErrCodeToString(-res), + httpInterface.lastHttpResponse, + httpInterface.lastHttpException, + apduInterface.lastApduResponse, + apduInterface.lastApduException, + ) + // Cancel sessions if possible. This will overwrite recorded errors from HTTP and APDU interfaces. + LpacJni.cancelSessions(contextHandle) + throw err + } - // Cancel sessions if possible. This will overwrite recorded errors from HTTP and APDU interfaces. - LpacJni.cancelSessions(contextHandle) - - 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 diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c index a61fc96..6b41a3a 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.c @@ -1,34 +1,43 @@ #include #include #include "interface-wrapper.h" +#include "utils.h" -jmethodID method_apdu_connect; -jmethodID method_apdu_disconnect; -jmethodID method_apdu_logical_channel_open; -jmethodID method_apdu_logical_channel_close; -jmethodID method_apdu_transmit; +static jmethodID method_apdu_connect; +static jmethodID method_apdu_disconnect; +static jmethodID method_apdu_logical_channel_open; +static jmethodID method_apdu_logical_channel_close; +static jmethodID method_apdu_transmit; -jmethodID method_http_transmit; +static jmethodID method_http_transmit; -jfieldID field_resp_rcode; -jfieldID field_resp_data; +static jfieldID field_resp_rcode; +static jfieldID field_resp_data; -void interface_wrapper_init() { - LPAC_JNI_SETUP_ENV; - jclass apdu_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/ApduInterface"); +#define APDU_INTERFACE_CLASS PACKAGE_NAME "/ApduInterface" +#define HTTP_INTERFACE_CLASS PACKAGE_NAME "/HttpInterface" +#define HTTP_RESPONSE_CLASS HTTP_INTERFACE_CLASS "$HttpResponse" + +void interface_wrapper_init(JNIEnv *env) { + jclass apdu_class = (*env)->FindClass(env, APDU_INTERFACE_CLASS); method_apdu_connect = (*env)->GetMethodID(env, apdu_class, "connect", "()V"); method_apdu_disconnect = (*env)->GetMethodID(env, apdu_class, "disconnect", "()V"); - method_apdu_logical_channel_open = (*env)->GetMethodID(env, apdu_class, "logicalChannelOpen", - "([B)I"); - method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose", - "(I)V"); + method_apdu_logical_channel_open = (*env)->GetMethodID(env, apdu_class, "logicalChannelOpen", "([B)I"); + method_apdu_logical_channel_close = (*env)->GetMethodID(env, apdu_class, "logicalChannelClose", "(I)V"); method_apdu_transmit = (*env)->GetMethodID(env, apdu_class, "transmit", "(I[B)[B"); - jclass http_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface"); - method_http_transmit = (*env)->GetMethodID(env, http_class, "transmit", - "(Ljava/lang/String;[B[Ljava/lang/String;)Lnet/typeblog/lpac_jni/HttpInterface$HttpResponse;"); + jclass http_class = (*env)->FindClass(env, HTTP_INTERFACE_CLASS); + method_http_transmit = (*env)->GetMethodID( + env, http_class, "transmit", + "(" + "Ljava/lang/String;" // url + "[B" // byte array + "Ljava/util/List;" // headers + ")" + "L" HTTP_RESPONSE_CLASS ";" + ); - jclass resp_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/HttpInterface$HttpResponse"); + jclass resp_class = (*env)->FindClass(env, HTTP_RESPONSE_CLASS); field_resp_rcode = (*env)->GetFieldID(env, resp_class, "rcode", "I"); field_resp_data = (*env)->GetFieldID(env, resp_class, "data", "[B"); } @@ -36,7 +45,7 @@ void interface_wrapper_init() { static int apdu_interface_connect(struct euicc_ctx *ctx) { LPAC_JNI_SETUP_ENV; (*env)->CallVoidMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface, method_apdu_connect); - LPAC_JNI_EXCEPTION_RETURN; + LPAC_JNI_EXCEPTION_RETURN return 0; } @@ -52,7 +61,7 @@ apdu_interface_logical_channel_open(struct euicc_ctx *ctx, const uint8_t *aid, u (*env)->SetByteArrayRegion(env, jbarr, 0, aid_len, (const jbyte *) aid); jint ret = (*env)->CallIntMethod(env, LPAC_JNI_CTX(ctx)->apdu_interface, method_apdu_logical_channel_open, jbarr); - LPAC_JNI_EXCEPTION_RETURN; + LPAC_JNI_EXCEPTION_RETURN LPAC_JNI_CTX(ctx)->logical_channel_id = ret; return ret; } @@ -71,16 +80,16 @@ apdu_interface_transmit(struct euicc_ctx *ctx, uint8_t **rx, uint32_t *rx_len, c uint32_t tx_len) { const int logic_channel = LPAC_JNI_CTX(ctx)->logical_channel_id; LPAC_JNI_SETUP_ENV; - jbyteArray txArr = (*env)->NewByteArray(env, tx_len); - (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); + jbyteArray txArr = (*env)->NewByteArray(env, (jsize) tx_len); + (*env)->SetByteArrayRegion(env, txArr, 0, (jsize) tx_len, (const jbyte *) tx); jbyteArray ret = (jbyteArray) (*env)->CallObjectMethod( env, LPAC_JNI_CTX(ctx)->apdu_interface, method_apdu_transmit, logic_channel, txArr ); - LPAC_JNI_EXCEPTION_RETURN; + LPAC_JNI_EXCEPTION_RETURN *rx_len = (*env)->GetArrayLength(env, ret); *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, ret); return 0; @@ -92,31 +101,22 @@ http_interface_transmit(struct euicc_ctx *ctx, const char *url, uint32_t *rcode, const char **headers) { LPAC_JNI_SETUP_ENV; jstring jurl = toJString(env, url); - jbyteArray txArr = (*env)->NewByteArray(env, tx_len); - (*env)->SetByteArrayRegion(env, txArr, 0, tx_len, (const jbyte *) tx); + jbyteArray txArr = (*env)->NewByteArray(env, (jsize) tx_len); + (*env)->SetByteArrayRegion(env, txArr, 0, (jsize) tx_len, (const jbyte *) tx); - int num_headers = 0; - while (headers[num_headers] != NULL) { - num_headers++; - } - jobjectArray headersArr = (*env)->NewObjectArray(env, num_headers, string_class, NULL); - for (int i = 0; i < num_headers; i++) { - jstring header = toJString(env, headers[i]); - (*env)->SetObjectArrayElement(env, headersArr, i, header); - (*env)->DeleteLocalRef(env, header); - } + jobject header_list = to_string_list(env, (char **) headers); jobject ret = (*env)->CallObjectMethod(env, LPAC_JNI_CTX(ctx)->http_interface, - method_http_transmit, jurl, txArr, headersArr); - LPAC_JNI_EXCEPTION_RETURN; + method_http_transmit, jurl, txArr, header_list); + LPAC_JNI_EXCEPTION_RETURN *rcode = (*env)->GetIntField(env, ret, field_resp_rcode); jbyteArray rxArr = (jbyteArray) (*env)->GetObjectField(env, ret, field_resp_data); *rx_len = (*env)->GetArrayLength(env, rxArr); *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, rxArr); - (*env)->DeleteLocalRef(env, headersArr); + (*env)->DeleteLocalRef(env, header_list); (*env)->DeleteLocalRef(env, ret); return 0; } diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h index e22837b..fec3869 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h +++ b/libs/lpac-jni/src/main/jni/lpac-jni/interface-wrapper.h @@ -5,7 +5,7 @@ #include #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_http_interface lpac_jni_http_interface; diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.c new file mode 100644 index 0000000..cc11b86 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" +#include "lpac-discovery.h" + +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; + } + jclass configured_addresses_class = (*env)->FindClass(env, EUICC_CONFIGURED_ADDRESSES_CLASS); + jmethodID configured_addresses_constructor = (*env)->GetMethodID( + env, configured_addresses_class, "", + "(" + "Ljava/lang/String;" // default dp address + "Ljava/lang/String;" // root ds address + ")" + "V" // (returns) void + ); + ret = (*env)->NewObject( + env, configured_addresses_class, + configured_addresses_constructor, + toJString(env, addresses.defaultDpAddress), + toJString(env, addresses.rootDsAddress) + ); + 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; + + jclass callback_class = (*env)->GetObjectClass(env, callback); + jmethodID on_discovered = (*env)->GetMethodID(env, callback_class, "onDiscovered", + "(Ljava/util/List;)V"); + + 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; + + jobject servers = NULL; + char **smdp_list = NULL; + int ret; + +#define CHECK_INVOKE_RESULT(COND) if (COND) { ret = -ES10B_ERROR_REASON_UNDEFINED; goto out; } + + // region preparing + ret = es10b_get_euicc_challenge_and_info(ctx); + syslog(LOG_INFO, "es10b_get_euicc_challenge_and_info %d", ret); + CHECK_INVOKE_RESULT(ret < 0) + // endregion + + // region connecting + ret = es9p_initiate_authentication(ctx); + syslog(LOG_INFO, "es9p_initiate_authentication %d", ret); + CHECK_INVOKE_RESULT(ret < 0) + // endregion + + // region authenticating + ret = es10b_authenticate_server(ctx, NULL, _imei); + syslog(LOG_INFO, "es10b_authenticate_server %d", ret); + CHECK_INVOKE_RESULT(ret < 0) + + ret = es11_authenticate_client(ctx, &smdp_list); + syslog(LOG_INFO, "es11_authenticate_client %d", ret); + CHECK_INVOKE_RESULT(ret < 0) + // endregion + + servers = to_string_list(env, smdp_list); + + (*env)->CallVoidMethod(env, callback, on_discovered, servers); + CHECK_INVOKE_RESULT((*env)->ExceptionCheck(env) == JNI_TRUE) + +#undef CHECK_INVOKE_RESULT + + out: + if (servers != NULL) (*env)->DeleteLocalRef(env, servers); + if (_imei != NULL) (*env)->ReleaseStringUTFChars(env, imei, _imei); + (*env)->ReleaseStringUTFChars(env, address, _address); + es11_smdp_list_free_all(smdp_list); + return ret; +} \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.h b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.h new file mode 100644 index 0000000..511de2f --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-discovery.h @@ -0,0 +1,4 @@ +#pragma once + +#include +#include "lpac-jni.h" diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c index bae2ee8..e2eac0e 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c @@ -3,72 +3,59 @@ #include #include #include +#include "utils.h" #include "lpac-download.h" -jobject download_state_preparing; -jobject download_state_connecting; -jobject download_state_authenticating; -jobject download_state_downloading; -jobject download_state_finalizing; +static jobject download_state_preparing; +static jobject download_state_connecting; +static jobject download_state_authenticating; +static jobject download_state_downloading; +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() { - LPAC_JNI_SETUP_ENV; +void lpac_download_init(JNIEnv *env) { + jclass download_state_class = (*env)->FindClass(env, DOWNLOAD_STATE_CLASS); - jclass download_state_class = (*env)->FindClass(env, - "net/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState"); - jfieldID download_state_preparing_field = (*env)->GetStaticFieldID(env, download_state_class, - "Preparing", - "Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;"); - 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"); + BIND_DOWNLOAD_STATE_STATIC_FIELD(preparing, "Preparing"); + BIND_DOWNLOAD_STATE_STATIC_FIELD(connecting, "Connecting"); + BIND_DOWNLOAD_STATE_STATIC_FIELD(authenticating, "Authenticating"); + BIND_DOWNLOAD_STATE_STATIC_FIELD(downloading, "Downloading"); + BIND_DOWNLOAD_STATE_STATIC_FIELD(finalizing, "Finalizing"); } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, jlong handle, - jstring smdp, jstring matching_id, - jstring imei, jstring confirmation_code, - jobject callback) { +Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jstring smdp, + jstring matching_id, + jstring imei, + jstring confirmation_code, + jobject callback +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; 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 *_matching_id = NULL; const char *_smdp = NULL; const char *_imei = NULL; + jobject bound_profile_metadata = NULL; 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) +#define CHECK_INVOKE_RESULT(COND) if (COND) { ret = -ES10B_ERROR_REASON_UNDEFINED; goto out; } +#define EMIT_STATE_UPDATE(STATE) (*env)->CallVoidMethod(env, callback, on_state_update, STATE) + if (confirmation_code != NULL) _confirmation_code = (*env)->GetStringUTFChars(env, confirmation_code, NULL); if (matching_id != NULL) @@ -79,59 +66,88 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j ctx->http.server_address = _smdp; - (*env)->CallVoidMethod(env, callback, on_state_update, download_state_preparing); + // region preparing + CHECK_INVOKE_RESULT(IS_CANCELLED()) + EMIT_STATE_UPDATE(download_state_preparing); 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; - } + CHECK_INVOKE_RESULT(ret < 0) + // endregion - (*env)->CallVoidMethod(env, callback, on_state_update, download_state_connecting); + // region connecting + CHECK_INVOKE_RESULT(IS_CANCELLED()) + EMIT_STATE_UPDATE(download_state_connecting); ret = es9p_initiate_authentication(ctx); syslog(LOG_INFO, "es9p_initiate_authentication %d", ret); - if (ret < 0) { - ret = -ES10B_ERROR_REASON_UNDEFINED; - goto out; - } + CHECK_INVOKE_RESULT(ret < 0) + // endregion - (*env)->CallVoidMethod(env, callback, on_state_update, download_state_authenticating); + // region authenticating + CHECK_INVOKE_RESULT(IS_CANCELLED()) + EMIT_STATE_UPDATE(download_state_authenticating); ret = es10b_authenticate_server(ctx, _matching_id, _imei); syslog(LOG_INFO, "es10b_authenticate_server %d", ret); - if (ret < 0) { - ret = -ES10B_ERROR_REASON_UNDEFINED; - goto out; - } + CHECK_INVOKE_RESULT(ret < 0) + CHECK_INVOKE_RESULT(IS_CANCELLED()) ret = es9p_authenticate_client(ctx); - if (ret < 0) { - ret = -ES10B_ERROR_REASON_UNDEFINED; - goto out; - } + syslog(LOG_INFO, "es9p_authenticate_client %d", ret); + CHECK_INVOKE_RESULT(ret < 0) + // endregion - (*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading); + // region emit profile metadata + 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; + } + bound_profile_metadata = build_profile_metadata(env, profile_metadata); + (*env)->CallVoidMethod(env, callback, on_profile_metadata, bound_profile_metadata); + CHECK_INVOKE_RESULT((*env)->ExceptionCheck(env) == JNI_TRUE) + } + // endregion + + // region downloading + CHECK_INVOKE_RESULT(IS_CANCELLED()) + EMIT_STATE_UPDATE(download_state_downloading); ret = es10b_prepare_download(ctx, _confirmation_code); syslog(LOG_INFO, "es10b_prepare_download %d", ret); - if (ret < 0) { - ret = -ES10B_ERROR_REASON_UNDEFINED; - goto out; - } + CHECK_INVOKE_RESULT(ret < 0) + CHECK_INVOKE_RESULT(IS_CANCELLED()) ret = es9p_get_bound_profile_package(ctx); - if (ret < 0) - goto out; + syslog(LOG_INFO, "es9p_get_bound_profile_package %d", ret); + if (ret < 0) goto out; + // endregion - (*env)->CallVoidMethod(env, callback, on_state_update, download_state_finalizing); + // region finalizing + CHECK_INVOKE_RESULT(IS_CANCELLED()) + EMIT_STATE_UPDATE(download_state_finalizing); 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) { - ret = - (int) es10b_load_bound_profile_package_result.errorReason; + ret = -(int) es10b_load_bound_profile_package_result.errorReason; goto out; } + // endregion euicc_http_cleanup(ctx); out: + // isCancelled() == false, but ret is an error, the set to cancelled + 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 +#undef CHECK_INVOKE_RESULT +#undef EMIT_STATE_UPDATE + if (bound_profile_metadata != NULL) + (*env)->DeleteLocalRef(env, bound_profile_metadata); // 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. if (_confirmation_code != NULL) @@ -144,20 +160,26 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j return ret; } - JNIEXPORT void JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_cancelSessions(JNIEnv *env, jobject thiz, jlong handle) { +Java_net_typeblog_lpac_1jni_LpacJni_cancelSessions( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; es9p_cancel_session(ctx); es10b_cancel_session(ctx, ES10B_CANCEL_SESSION_REASON_UNDEFINED); euicc_http_cleanup(ctx); } -#define QUOTE(S) #S -#define ERRCODE_ENUM_TO_STRING(VARIANT) case VARIANT: return toJString(env, QUOTE(VARIANT)) +#define ERRCODE_ENUM_TO_STRING(VARIANT) case VARIANT: return toJString(env, #VARIANT) JNIEXPORT jstring JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject thiz, jint code) { +Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jint code +) { switch (code) { ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INCORRECT_INPUT_VALUES); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INVALID_SIGNATURE); @@ -167,15 +189,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_SCP03T_STRUCTURE_ERROR); ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR); - ERRCODE_ENUM_TO_STRING( - 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_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_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_ICCID_MISMATCH); - ERRCODE_ENUM_TO_STRING( - ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY); + ERRCODE_ENUM_TO_STRING(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_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR); default: diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.h b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.h index 7130f52..b68bc99 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.h +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.h @@ -3,4 +3,4 @@ #include #include "lpac-jni.h" -void lpac_download_init(); \ No newline at end of file +void lpac_download_init(JNIEnv *env); \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c index ca319db..647c872 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c @@ -8,38 +8,31 @@ #include "lpac-jni.h" #include "lpac-download.h" #include "lpac-notifications.h" +#include "lpac-discovery.h" +#include "utils.h" #include "interface-wrapper.h" JavaVM *jvm = NULL; -jstring empty_string; - -jclass string_class; -jmethodID string_constructor; - -jint JNI_OnLoad(JavaVM *vm, void *reserved) { +jint JNI_OnLoad(JavaVM *vm, __attribute__((unused)) void *reserved) { jvm = vm; - interface_wrapper_init(); - lpac_download_init(); - 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, "", - "([BLjava/lang/String;)V"); - const jchar _unused[1]; - empty_string = (*env)->NewString(env, _unused, 0); - empty_string = (*env)->NewGlobalRef(env, empty_string); + interface_wrapper_init(env); + lpac_convertor_init(env); + lpac_download_init(env); return JNI_VERSION_1_6; } JNIEXPORT jlong JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_createContext(JNIEnv *env, jobject thiz, - jbyteArray isdr_aid, - jobject apdu_interface, - jobject http_interface) { +Java_net_typeblog_lpac_1jni_LpacJni_createContext( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jbyteArray isdr_aid, + jobject apdu_interface, + jobject http_interface +) { struct lpac_jni_ctx *jni_ctx = NULL; struct euicc_ctx *ctx = NULL; jbyte *isdr_java = NULL; @@ -66,7 +59,11 @@ Java_net_typeblog_lpac_1jni_LpacJni_createContext(JNIEnv *env, jobject thiz, } JNIEXPORT void JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_destroyContext(JNIEnv *env, jobject thiz, jlong handle) { +Java_net_typeblog_lpac_1jni_LpacJni_destroyContext( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct lpac_jni_ctx *jni_ctx = LPAC_JNI_CTX(ctx); @@ -78,46 +75,42 @@ Java_net_typeblog_lpac_1jni_LpacJni_destroyContext(JNIEnv *env, jobject thiz, jl } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_euiccInit(JNIEnv *env, jobject thiz, jlong handle) { +Java_net_typeblog_lpac_1jni_LpacJni_euiccInit( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; return euicc_init(ctx); } JNIEXPORT void JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_euiccFini(JNIEnv *env, jobject thiz, jlong handle) { +Java_net_typeblog_lpac_1jni_LpacJni_euiccFini( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; euicc_fini(ctx); } JNIEXPORT void JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_euiccSetMss(JNIEnv *env, jobject thiz, jlong handle, - jbyte mss) { +Java_net_typeblog_lpac_1jni_LpacJni_euiccSetMss( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jbyte mss +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; 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 -Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong handle) { +Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; char *buf = NULL; @@ -129,71 +122,69 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cGetEid(JNIEnv *env, jobject thiz, jlong return ret; } -JNIEXPORT jlong JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo(JNIEnv *env, jobject thiz, jlong handle) { +JNIEXPORT jobject JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct es10c_profile_info_list *info = NULL; + jobject profile_list = new_array_list(env); + int ret = es10c_get_profiles_info(ctx, &info); + if (ret < 0) goto out; - if (es10c_get_profiles_info(ctx, &info) < 0) { - return 0; + jclass profile_info_class = (*env)->FindClass(env, LOCAL_PROFILE_INFO_CLASS); + + jmethodID profile_info_class_constructor = (*env)->GetMethodID( + env, profile_info_class, "", + "(" + "Ljava/lang/String;" // iccid + "L" PROFILE_STATE_CLASS ";" + "Ljava/lang/String;" // name + "Ljava/lang/String;" // nickname + "Ljava/lang/String;" // provider name + "Ljava/lang/String;" // ISD-P AID + "L" PROFILE_CLASS_CLASS ";" + "L" ICON_TYPE_CLASS ";" + "Ljava/lang/String;" // icon (base64-encoded) + ")" + "V" + ); + + jclass profile_list_class = (*env)->GetObjectClass(env, profile_list); + jmethodID add_profile = (*env)->GetMethodID(env, profile_list_class, "add", "(Ljava/lang/Object;)Z"); + + 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), + to_icon_type(info->iconType), + toJString(env, info->icon) + ); + (*env)->CallBooleanMethod(env, profile_list, add_profile, element); + info = info->next; } - - return (jlong) info; + out: + es10c_profile_info_list_free_all(info); + return profile_list; } -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 -Java_net_typeblog_lpac_1jni_LpacJni_es10cEnableProfile(JNIEnv *env, jobject thiz, jlong handle, - jstring iccid, jboolean refresh) { +Java_net_typeblog_lpac_1jni_LpacJni_es10cEnableProfile( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jstring iccid, + jboolean refresh +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; const char *_iccid = NULL; int ret; @@ -205,8 +196,13 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cEnableProfile(JNIEnv *env, jobject thiz } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cDisableProfile(JNIEnv *env, jobject thiz, jlong handle, - jstring iccid, jboolean refresh) { +Java_net_typeblog_lpac_1jni_LpacJni_es10cDisableProfile( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jstring iccid, + jboolean refresh +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; const char *_iccid = NULL; int ret; @@ -218,8 +214,13 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cDisableProfile(JNIEnv *env, jobject thi } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cSetNickname(JNIEnv *env, jobject thiz, jlong handle, - jstring iccid, jbyteArray nick) { +Java_net_typeblog_lpac_1jni_LpacJni_es10cSetNickname( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jstring iccid, + jbyteArray nick +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; const char *_iccid = NULL; jbyte *_nick = NULL; @@ -234,8 +235,12 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cSetNickname(JNIEnv *env, jobject thiz, } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cDeleteProfile(JNIEnv *env, jobject thiz, jlong handle, - jstring iccid) { +Java_net_typeblog_lpac_1jni_LpacJni_es10cDeleteProfile( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jstring iccid +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; const char *_iccid = NULL; int ret; @@ -246,48 +251,59 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cDeleteProfile(JNIEnv *env, jobject thiz return ret; } -JNIEXPORT jlong JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cexGetEuiccInfo2(JNIEnv *env, jobject thiz, jlong handle) { +JNIEXPORT jobject JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_es10cexGetEuiccInfo2( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; 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, "", + "(" + "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) { - free(info); - return 0; - } + ret = (*env)->NewObject( + env, euicc_info_class, euicc_info_constructor, + 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) + ); - return (jlong) info; -} - - -JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10cEuiccMemoryReset(JNIEnv *env, jobject thiz, jlong handle) { - struct euicc_ctx *ctx = (struct euicc_ctx *) handle; - int ret; - ret = es10c_euicc_memory_reset(ctx); + out: + es10c_ex_euiccinfo2_free(info); return ret; } -JNIEXPORT jstring JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_stringDeref(JNIEnv *env, jobject thiz, jlong curr) { - return toJString(env, *((char **) curr)); +JNIEXPORT jint JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_es10cEuiccMemoryReset( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { + struct euicc_ctx *ctx = (struct euicc_ctx *) handle; + return es10c_euicc_memory_reset(ctx); } - -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) diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h index c2300be..e062344 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.h @@ -19,41 +19,4 @@ struct lpac_jni_ctx { (*jvm)->AttachCurrentThread(jvm, &env, NULL) 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); \ - } \ No newline at end of file diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-notifications.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-notifications.c index cf402cf..b231dd5 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-notifications.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-notifications.c @@ -1,23 +1,63 @@ -#include "lpac-notifications.h" #include #include #include #include +#include "utils.h" +#include "lpac-notifications.h" -JNIEXPORT jlong JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10bListNotification(JNIEnv *env, jobject thiz, jlong handle) { +JNIEXPORT jobject JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_es10bListNotification( + JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; - struct es10b_notification_metadata_list *info = NULL; + struct es10b_notification_metadata_list *metadata = NULL; + jobject notification_list = new_array_list(env); + int ret = es10b_list_notification(ctx, &metadata); + if (ret < 0) goto out; - if (es10b_list_notification(ctx, &info) < 0) - return 0; + jclass local_profile_notification_class = (*env)->FindClass( + env, LOCAL_PROFILE_NOTIFICATION_CLASS); + jmethodID local_profile_notification_constructor = (*env)->GetMethodID( + env, local_profile_notification_class, "", + "(" + "J" // seqNumber + "L" PROFILE_MANAGEMENT_OPERATION_CLASS ";" + "Ljava/lang/String;" // notificationAddress + "Ljava/lang/String;" // iccid + ")" + "V" // (returns) void + ); - return (jlong) info; + jclass notification_list_class = (*env)->GetObjectClass(env, notification_list); + jmethodID add_notification = (*env)->GetMethodID(env, notification_list_class, "add", "(Ljava/lang/Object;)Z"); + + jobject element = NULL; + while (metadata) { + element = (*env)->NewObject( + env, local_profile_notification_class, local_profile_notification_constructor, + (jlong) metadata->seqNumber, + to_profile_management_operation(metadata->profileManagementOperation), + toJString(env, metadata->notificationAddress), + toJString(env, metadata->iccid) + ); + (*env)->CallBooleanMethod(env, notification_list, add_notification, element); + metadata = metadata->next; + } + + out: + es10b_notification_metadata_list_free_all(metadata); + return notification_list; } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_handleNotification(JNIEnv *env, jobject thiz, jlong handle, - jlong seq_number) { +Java_net_typeblog_lpac_1jni_LpacJni_handleNotification( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jlong seq_number +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct es10b_pending_notification notification; int res; @@ -40,40 +80,12 @@ Java_net_typeblog_lpac_1jni_LpacJni_handleNotification(JNIEnv *env, jobject thiz } JNIEXPORT jint JNICALL -Java_net_typeblog_lpac_1jni_LpacJni_es10bDeleteNotification(JNIEnv *env, jobject thiz, jlong handle, - jlong seq_number) { +Java_net_typeblog_lpac_1jni_LpacJni_es10bDeleteNotification( + __attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jobject thiz, + jlong handle, + jlong seq_number +) { struct euicc_ctx *ctx = (struct euicc_ctx *) handle; 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) diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/utils.c b/libs/lpac-jni/src/main/jni/lpac-jni/utils.c new file mode 100644 index 0000000..a5ddec1 --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/utils.c @@ -0,0 +1,220 @@ +#include "utils.h" +#include +#include +#include + +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 jobject icon_type_jpeg; +static jobject icon_type_png; + +static jclass version_class; +static jmethodID version_constructor; + +static jstring empty_string; +static 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 ";") + +#define BIND_ICON_TYPE_FIELD(NAME, FIELD) \ + icon_type_##NAME = bind_static_field(env, icon_type_class, FIELD, "L" ICON_TYPE_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, "", + "([BLjava/lang/String;)V"); + const jchar _unused[1]; + empty_string = (*env)->NewString(env, _unused, 0); + empty_string = (*env)->NewGlobalRef(env, empty_string); +} + +static void init_version_class(JNIEnv *env) { + version_class = (*env)->FindClass(env, VERSION_CLASS); + version_class = (*env)->NewGlobalRef(env, version_class); + version_constructor = (*env)->GetMethodID(env, version_class, "", "(Ljava/lang/String;)V"); +} + +void lpac_convertor_init(JNIEnv *env) { + init_string_class(env); + init_version_class(env); + + 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"); + + jclass icon_type_class = (*env)->FindClass(env, ICON_TYPE_CLASS); + BIND_ICON_TYPE_FIELD(jpeg, "JPEG"); + BIND_ICON_TYPE_FIELD(png, "PNG"); +} + +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; + } +} + +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; + } +} + +jstring to_icon_type(enum es10c_icon_type icon_type) { + switch (icon_type) { + case ES10C_ICON_TYPE_JPEG: + return icon_type_jpeg; + case ES10C_ICON_TYPE_PNG: + return icon_type_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_set(JNIEnv *env, char **values) { + jobject elements = new_set_list(env); + jclass list_class = (*env)->GetObjectClass(env, elements); + jmethodID add_element = (*env)->GetMethodID(env, list_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_list(JNIEnv *env, char **values) { + jobject elements = new_array_list(env); + jclass list_class = (*env)->GetObjectClass(env, elements); + jmethodID add_element = (*env)->GetMethodID(env, list_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, "", + "(" + "Ljava/lang/String;" // iccid + "Ljava/lang/String;" // name + "Ljava/lang/String;" // provider name + "L" PROFILE_CLASS_CLASS ";" + "L" ICON_TYPE_CLASS ";" + "Ljava/lang/String;" // icon (base64-encoded) + ")" + "V" // (returns) void + ); + + return (*env)->NewObject( + env, profile_metadata_class, profile_metadata_constructor, + toJString(env, metadata->iccid), + toJString(env, metadata->profileName), + toJString(env, metadata->serviceProviderName), + to_profile_class(metadata->profileClass), + to_icon_type(metadata->iconType), + toJString(env, metadata->icon) + ); +} + +jobject new_set_list(JNIEnv *env) { + jclass set_class = (*env)->FindClass(env, SET_CLASS); + jmethodID set_constructor = (*env)->GetMethodID(env, set_class, "", "()V"); + return (*env)->NewObject(env, set_class, set_constructor); +} + +jobject new_array_list(JNIEnv *env) { + jclass list_class = (*env)->FindClass(env, LIST_CLASS); + jmethodID list_constructor = (*env)->GetMethodID(env, list_class, "", "()V"); + return (*env)->NewObject(env, list_class, list_constructor); +} diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/utils.h b/libs/lpac-jni/src/main/jni/lpac-jni/utils.h new file mode 100644 index 0000000..4487e0b --- /dev/null +++ b/libs/lpac-jni/src/main/jni/lpac-jni/utils.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +#define PACKAGE_NAME "net/typeblog/lpac_jni" +#define PROFILE_STATE_CLASS PACKAGE_NAME "/ProfileState" +#define PROFILE_CLASS_CLASS PACKAGE_NAME "/ProfileClass" +#define PROFILE_METADATA_CLASS PACKAGE_NAME "/ProfileMetadata" +#define PROFILE_MANAGEMENT_OPERATION_CLASS PACKAGE_NAME "/ProfileManagementOperation" +#define ICON_TYPE_CLASS PACKAGE_NAME "/IconType" +#define DOWNLOAD_CALLBACK_CLASS PACKAGE_NAME "/ProfileDownloadCallback" +#define DOWNLOAD_STATE_CLASS DOWNLOAD_CALLBACK_CLASS "$DownloadState" +#define LOCAL_PROFILE_INFO_CLASS PACKAGE_NAME "/LocalProfileInfo" +#define LOCAL_PROFILE_NOTIFICATION_CLASS PACKAGE_NAME "/LocalProfileNotification" +#define EUICC_CONFIGURED_ADDRESSES_CLASS PACKAGE_NAME "/EuiccConfiguredAddresses" +#define VERSION_CLASS PACKAGE_NAME "/Version" +#define SET_CLASS "java/util/HashSet" +#define LIST_CLASS "java/util/ArrayList" + +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_profile_management_operation(enum es10b_profile_management_operation operation); + +jstring to_icon_type(enum es10c_icon_type icon_type); + +jobject to_version(JNIEnv *env, const char *version); + +jobject to_string_set(JNIEnv *env, char **values); + +jobject to_string_list(JNIEnv *env, char **values); + +jobject build_profile_metadata(JNIEnv *env, struct es8p_metadata *metadata); + +jobject new_set_list(JNIEnv *env); + +jobject new_array_list(JNIEnv *env);