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..cd62fca 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
@@ -45,8 +45,9 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
matchingId: String?,
imei: String?,
confirmationCode: String?,
+ preview: Boolean,
callback: ProfileDownloadCallback
- ) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback)
+ ) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, preview, callback)
override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(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 760f1af..07c8bd4 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
@@ -373,6 +373,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
smdp: String,
matchingId: String?,
confirmationCode: String?,
+ preview: Boolean,
imei: String?
): ForegroundTaskSubscriberFlow =
launchForegroundTask(
@@ -387,7 +388,10 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
matchingId,
imei,
confirmationCode,
+ preview,
object : ProfileDownloadCallback {
+ override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
+ }
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
if (state.progress == 0) return
foregroundTaskState.value =
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
index e342dee..11503ac 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt
@@ -29,6 +29,7 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
var smdp: String,
var matchingId: String?,
var confirmationCode: String?,
+ var preview: Boolean,
var imei: String?,
var downloadStarted: Boolean,
var downloadTaskID: Long,
@@ -66,6 +67,7 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
"",
null,
null,
+ false,
null,
false,
-1,
diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt
index 1b816d4..9fd7c21 100644
--- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt
+++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt
@@ -160,6 +160,7 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep
state.smdp,
state.matchingId,
state.confirmationCode,
+ state.preview,
state.imei
)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cae19d3..4f5083e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -53,6 +53,18 @@
+
+
+
+
+
+
+
+
+
+ channel.lpa.downloadProfile(
+ smdp = smdpAddress,
+ matchingId = matchingId,
+ imei = telephonyManager.imei,
+ confirmationCode = subscription.confirmationCode,
+ preview = false,
+ callback = object : ProfileDownloadCallback {
+ override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
+ Log.i(TAG, "Profile metadata received: profileName=$profileName iccid=$iccid")
+ profileIccid = iccid
+ }
+
+ override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
+ Log.i(TAG, "Profile download state update: progress=${state.progress}")
+ }
+ }
+ )
+
+ if (switchAfterDownload) {
+ onSwitchToSubscriptionWithPort(slotIndex, portId, profileIccid, false)
+ }
+
+ DownloadSubscriptionResult(
+ RESULT_OK,
+ 0,
+ channel.cardId
+ )
+ }
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Error during profile download: ${e.message}", e)
+ DownloadSubscriptionResult(
+ RESULT_FIRST_USER,
+ 0,
+ -1
+ )
+ }
+ }
+
override fun onGetDownloadableSubscriptionMetadata(
slotId: Int,
subscription: DownloadableSubscription?,
forceDeactivateSim: Boolean
): GetDownloadableSubscriptionMetadataResult {
- // Stub: return as-is and do not fetch anything
- // This is incompatible with carrier eSIM apps; should we make it compatible?
- return GetDownloadableSubscriptionMetadataResult(RESULT_OK, subscription)
+ Log.i(TAG, "onGetDownloadableSubscriptionMetadata slotId=$slotId")
+
+ if (subscription == null) {
+ return GetDownloadableSubscriptionMetadataResult(
+ RESULT_FIRST_USER,
+ null
+ )
+ }
+
+ Log.i(TAG, "onGetDownloadableSubscriptionMetadata getEncodedActivationCode()=${subscription.getEncodedActivationCode()}")
+
+ // Extract SMDP address and matching ID
+ val activationParts = subscription.getEncodedActivationCode()?.split("$")
+ val smdpAddress = activationParts?.getOrNull(1)
+ val matchingId = activationParts?.getOrNull(2)
+
+ if (smdpAddress.isNullOrBlank() || matchingId.isNullOrBlank()) {
+ Log.e(TAG, "SMDP address or activation code is not valid.")
+ return GetDownloadableSubscriptionMetadataResult(
+ RESULT_FIRST_USER,
+ null
+ )
+ }
+
+ Log.i(TAG, "onGetDownloadableSubscriptionMetadata smdpAddress=$smdpAddress")
+ Log.i(TAG, "onGetDownloadableSubscriptionMetadata matchingId=$matchingId")
+
+ val portId = withEuiccChannelManager {
+ euiccChannelManager.findFirstAvailablePort(slotId)
+ }
+
+ if (portId < 0) {
+ Log.e(TAG, "No available port for slotId=$slotId")
+ return GetDownloadableSubscriptionMetadataResult(
+ RESULT_FIRST_USER,
+ null
+ )
+ }
+
+ var updatedSubscription: DownloadableSubscription? = null
+
+ return try {
+ withEuiccChannelManager {
+ euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
+ channel.lpa.downloadProfile(
+ smdp = smdpAddress,
+ matchingId = matchingId,
+ imei = telephonyManager.imei,
+ confirmationCode = subscription.confirmationCode,
+ preview = true,
+ callback = object : ProfileDownloadCallback {
+ override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
+ Log.i(TAG, "App certificates: appCerts=$appCerts")
+ Log.i(TAG, "Profile metadata received: profileName=$profileName")
+
+ val accessRules = appCerts.split(",").mapNotNull { certHex ->
+ try {
+ val certBytes = certHex.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
+ UiccAccessRule(certBytes, null, 0L)
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to parse certificate hash: $certHex", e)
+ null
+ }
+ }
+
+ updatedSubscription = DownloadableSubscription.Builder(subscription)
+ .setCarrierName(profileName)
+ .setAccessRules(accessRules)
+ .build()
+ }
+
+ override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
+ Log.i(TAG, "Profile download state update: progress=${state.progress}")
+ }
+ }
+ )
+ }
+ }
+
+ if (updatedSubscription == null) {
+ throw IllegalStateException("Metadata not received from callback")
+ }
+
+ GetDownloadableSubscriptionMetadataResult(
+ RESULT_OK,
+ updatedSubscription
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Error during profile download: ${e.message}", e)
+ GetDownloadableSubscriptionMetadataResult(
+ RESULT_FIRST_USER,
+ null
+ )
+ }
}
override fun onGetDefaultDownloadableSubscriptionList(
diff --git a/app/src/main/java/im/angry/openeuicc/ui/UserConsentActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/UserConsentActivity.kt
new file mode 100644
index 0000000..d425a3b
--- /dev/null
+++ b/app/src/main/java/im/angry/openeuicc/ui/UserConsentActivity.kt
@@ -0,0 +1,94 @@
+package im.angry.openeuicc.ui
+
+import android.app.Activity
+import android.app.AlertDialog
+import android.content.pm.PackageManager
+import android.os.Bundle
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.euicc.EuiccManager
+import android.text.TextUtils
+import android.util.Log
+
+import im.angry.openeuicc.R
+
+class UserConsentActivity : Activity() {
+
+ private lateinit var euiccManager: EuiccManager
+ private lateinit var subscriptionManager: SubscriptionManager
+ private lateinit var telephonyManager: TelephonyManager
+ private var mCardId: Int = -1
+ private var mUsePortIndex: Boolean = false
+ private var mPortIndex: Int = -1
+ private var mSubscriptionId: Int = -1
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ euiccManager = getSystemService(EuiccManager::class.java) as EuiccManager
+ subscriptionManager = getSystemService(SubscriptionManager::class.java) as SubscriptionManager
+ telephonyManager = getSystemService(TelephonyManager::class.java) as TelephonyManager
+
+ val callingPackage = intent.getStringExtra("android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE")
+ if (TextUtils.isEmpty(callingPackage)) {
+ Log.e("UserConsentActivity", "Unknown calling package in resolution activity")
+ sendResultAndFinish(false)
+ return
+ }
+
+ val extras = intent.extras
+ if (extras != null) {
+ mCardId = extras.getInt("android.service.euicc.extra.RESOLUTION_CARD_ID", -1)
+ mUsePortIndex = extras.getBoolean("android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX", false)
+ mPortIndex = extras.getInt("android.service.euicc.extra.RESOLUTION_PORT_INDEX", -1)
+ mSubscriptionId = extras.getInt("android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID", -1)
+ }
+
+ try {
+ val packageManager = packageManager
+ val appLabel = packageManager.getApplicationLabel(packageManager.getApplicationInfo(callingPackage!!, 0))
+ if (TextUtils.isEmpty(appLabel)) {
+ Log.e("UserConsentActivity", "Calling app doesn't have a label")
+ sendResultAndFinish(false)
+ return
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.e("UserConsentActivity", "Calling package doesn't exist", e)
+ sendResultAndFinish(false)
+ return
+ }
+
+ showUserConsentDialog()
+ }
+
+ fun sendResultAndFinish(consent: Boolean) {
+ val bundle = Bundle()
+
+ bundle.putBoolean("android.service.euicc.extra.RESOLUTION_CONSENT", consent)
+ if (mUsePortIndex) {
+ bundle.putInt("android.service.euicc.extra.RESOLUTION_PORT_INDEX", mPortIndex);
+ }
+
+ euiccManager.createForCardId(mCardId)
+ euiccManager.continueOperation(intent, bundle)
+
+ setResult(if (consent) Activity.RESULT_OK else Activity.RESULT_CANCELED)
+ finish()
+ }
+
+ fun showUserConsentDialog() {
+ AlertDialog.Builder(this)
+ .setTitle(getString(R.string.user_consent_title))
+ .setMessage(getString(R.string.user_consent_message))
+ .setPositiveButton(getString(R.string.user_consent_positive)) { _, _ ->
+ sendResultAndFinish(true)
+ }
+ .setNegativeButton(getString(R.string.user_consent_negative)) { _, _ ->
+ sendResultAndFinish(false)
+ }
+ .setOnCancelListener {
+ sendResultAndFinish(false)
+ }
+ .show()
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 47c88bd..041bf0a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,4 +22,9 @@
Your device supports eSIMs. To connect to mobile network, download your eSIM issued by a carrier, or insert a physical SIM.
Skip
Download eSIM
+
+ Allow your carrier to download SIM?
+ Your SIM will be used immediately after download
+ No thanks
+ Yes
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..54cdad2
--- /dev/null
+++ b/app/src/main/res/values/themes.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/libs/hidden-apis-stub/src/main/java/android/service/euicc/EuiccService.java b/libs/hidden-apis-stub/src/main/java/android/service/euicc/EuiccService.java
index a1cd43b..629b8d8 100644
--- a/libs/hidden-apis-stub/src/main/java/android/service/euicc/EuiccService.java
+++ b/libs/hidden-apis-stub/src/main/java/android/service/euicc/EuiccService.java
@@ -305,12 +305,45 @@ public abstract class EuiccService extends Service {
* defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
* this method does nothing and returns null by default.
* @see android.telephony.euicc.EuiccManager#downloadSubscription
+ * @deprecated prefer {@link #onDownloadSubscription(int, int,
+ * DownloadableSubscription, boolean, boolean, Bundle)}
*/
+ @Deprecated
public DownloadSubscriptionResult onDownloadSubscription(int slotId,
- DownloadableSubscription subscription, boolean switchAfterDownload,
- boolean forceDeactivateSim, Bundle resolvedBundle) {
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
return null;
}
+ /**
+ * Download the given subscription.
+ *
+ * @param slotIndex Index of the SIM slot to use for the operation.
+ * @param portIndex Index of the port from the slot. portIndex is used when
+ * switchAfterDownload is set to {@code true}, otherwise download is port agnostic.
+ * @param subscription The subscription to download.
+ * @param switchAfterDownload If true, the subscription should be enabled upon successful
+ * download.
+ * @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
+ * eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
+ * should be returned to allow the user to consent to this operation first.
+ * @param resolvedBundle The bundle containing information on resolved errors. It can contain
+ * a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
+ * and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
+ * the user allows profile policy rules or not.
+ * @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
+ * bit map, and original the card Id. The result code may be one of the predefined
+ * {@code RESULT_} constants or any implementation-specific code starting with
+ * {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
+ * defined in {@code RESOLVABLE_ERROR_}.
+ * @see android.telephony.euicc.EuiccManager#downloadSubscription
+ */
+ @NonNull
+ public DownloadSubscriptionResult onDownloadSubscription(int slotIndex, int portIndex,
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
+ boolean forceDeactivateSim, @NonNull Bundle resolvedBundle) {
+ // stub implementation, LPA needs to implement this
+ throw new UnsupportedOperationException("LPA must override onDownloadSubscription");
+ }
/**
* Download the given subscription.
*
@@ -329,8 +362,8 @@ public abstract class EuiccService extends Service {
* {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The
* default return value for this one is Integer.MIN_VALUE.
*/
- @Deprecated public int onDownloadSubscription(int slotId,
- DownloadableSubscription subscription, boolean switchAfterDownload,
+ @Deprecated public @Result int onDownloadSubscription(int slotId,
+ @NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
boolean forceDeactivateSim) {
return Integer.MIN_VALUE;
}
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..5056da3 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
@@ -37,7 +37,7 @@ interface LocalProfileAssistant {
fun deleteProfile(iccid: String): Boolean
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
- confirmationCode: String?, callback: ProfileDownloadCallback)
+ confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback)
fun deleteNotification(seqNumber: Long): Boolean
fun handleNotification(seqNumber: Long): Boolean
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 370fcab..956736e 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
@@ -28,7 +28,7 @@ internal object LpacJni {
// es9p + es10b
// We do not expose all of the functions because of tediousness :)
external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?,
- confirmationCode: String?, callback: ProfileDownloadCallback): Int
+ confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback): Int
external fun downloadErrCodeToString(code: Int): String
external fun handleNotification(handle: Long, seqNumber: Long): Int
// Cancel any ongoing es9p and/or es10b sessions
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..aa37540 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
@@ -21,5 +21,6 @@ interface ProfileDownloadCallback {
Finalizing(80), // load bpp
}
+ fun onMetadataReceived(profileName: String, iccid: String, appCerts: String)
fun onStateUpdate(state: DownloadState)
}
\ No newline at end of file
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 7310acd..ff1a9b7 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
@@ -202,13 +202,14 @@ class LocalProfileAssistantImpl(
@Synchronized
override fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
- confirmationCode: String?, callback: ProfileDownloadCallback) {
+ confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback) {
val res = LpacJni.downloadProfile(
contextHandle,
smdp,
matchingId,
imei,
confirmationCode,
+ preview,
callback
)
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 028e30d..04bc331 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
@@ -1,5 +1,7 @@
+#include
#include
#include
+#include
#include
#include
#include
@@ -60,7 +62,7 @@ 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) {
+ jboolean preview, jobject callback) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
const char *_confirmation_code = NULL;
@@ -109,6 +111,63 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out;
}
+ if (ctx->http._internal.prepare_download_param->b64_profileMetadata) {
+ struct es8p_metadata *profile_metadata = NULL;
+ ret = es8p_metadata_parse(&profile_metadata, ctx->http._internal.prepare_download_param->b64_profileMetadata);
+
+ if (ret == 0) {
+ const char *profile_name = profile_metadata->serviceProviderName ? profile_metadata->serviceProviderName : "Unknown";
+ const char *iccid = (profile_metadata->iccid[0] != '\0') ? profile_metadata->iccid : "Unknown";
+
+ size_t totalLength = 0;
+ for (int i = 0; i < profile_metadata->numRefArDo; i++) {
+ if (profile_metadata->refArDoList[i].deviceAppIdRefDo) {
+ totalLength += (profile_metadata->refArDoList[i].deviceAppIdLength * 2) + 1;
+ }
+ }
+
+ char *certSignatures = malloc(totalLength + 1);
+ if (certSignatures) {
+ certSignatures[0] = '\0';
+ for (int i = 0; i < profile_metadata->numRefArDo; i++) {
+ if (profile_metadata->refArDoList[i].deviceAppIdRefDo) {
+ char deviceAppIdHex[profile_metadata->refArDoList[i].deviceAppIdLength * 2 + 1];
+ euicc_hexutil_bin2hex(deviceAppIdHex, sizeof(deviceAppIdHex),
+ profile_metadata->refArDoList[i].deviceAppIdRefDo,
+ profile_metadata->refArDoList[i].deviceAppIdLength);
+ strcat(certSignatures, deviceAppIdHex);
+ strcat(certSignatures, ",");
+ }
+ }
+ if (strlen(certSignatures) > 0 && certSignatures[strlen(certSignatures) - 1] == ',') {
+ certSignatures[strlen(certSignatures) - 1] = '\0';
+ }
+ }
+
+ jclass callbackClass = (*env)->GetObjectClass(env, callback);
+ jmethodID onMetadataReceived = (*env)->GetMethodID(env, callbackClass, "onMetadataReceived",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ if (onMetadataReceived) {
+ jstring jProfileName = (*env)->NewStringUTF(env, profile_name);
+ jstring jIccid = (*env)->NewStringUTF(env, iccid);
+ jstring jCertSignatures = (*env)->NewStringUTF(env, certSignatures ? certSignatures : "");
+
+ (*env)->CallVoidMethod(env, callback, onMetadataReceived, jProfileName, jIccid, jCertSignatures);
+
+ (*env)->DeleteLocalRef(env, jProfileName);
+ (*env)->DeleteLocalRef(env, jIccid);
+ (*env)->DeleteLocalRef(env, jCertSignatures);
+ }
+
+ es8p_metadata_free(&profile_metadata);
+ }
+
+ if (preview) {
+ goto out;
+ }
+ }
+
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading);
ret = es10b_prepare_download(ctx, _confirmation_code);
syslog(LOG_INFO, "es10b_prepare_download %d", ret);
@@ -178,4 +237,4 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject
default:
return toJString(env, "ES10B_ERROR_REASON_UNDEFINED");
}
-}
\ No newline at end of file
+}