Compare commits

...

1 commit

Author SHA1 Message Date
378380fe45
feat: profile metadata (lpac-jni) 2025-03-10 18:43:58 +08:00
3 changed files with 109 additions and 17 deletions

View file

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

View file

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

View file

@ -1,3 +1,4 @@
#include <euicc/es8p.h>
#include <euicc/es9p.h> #include <euicc/es9p.h>
#include <euicc/es10b.h> #include <euicc/es10b.h>
#include <stdlib.h> #include <stdlib.h>
@ -12,6 +13,9 @@ jobject download_state_downloading;
jobject download_state_finalizing; jobject download_state_finalizing;
jmethodID on_state_update; jmethodID on_state_update;
jmethodID on_profile_metadata;
#define PROFILE_METADATA_CLASS "net/typeblog/lpac_jni/ProfileDownloadCallback$ProfileMetadata"
void lpac_download_init() { void lpac_download_init() {
LPAC_JNI_SETUP_ENV; LPAC_JNI_SETUP_ENV;
@ -54,6 +58,65 @@ void lpac_download_init() {
"net/typeblog/lpac_jni/ProfileDownloadCallback"); "net/typeblog/lpac_jni/ProfileDownloadCallback");
on_state_update = (*env)->GetMethodID(env, download_callback_class, "onStateUpdate", on_state_update = (*env)->GetMethodID(env, download_callback_class, "onStateUpdate",
"(Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;)V"); "(Lnet/typeblog/lpac_jni/ProfileDownloadCallback$DownloadState;)V");
on_profile_metadata = (*env)->GetMethodID(env, download_callback_class, "onProfileMetadata",
"(L" PROFILE_METADATA_CLASS ";)V");
}
static jobject build_profile_metadata(JNIEnv *env, struct es8p_metadata *metadata) {
if (metadata == NULL) return NULL;
jclass profile_metadata_class = (*env)->FindClass(env, PROFILE_METADATA_CLASS);
jmethodID profile_metadata_constructor = (*env)->GetMethodID(
env, profile_metadata_class, "<init>",
"("
"Ljava/lang/String;" // iccid
"Ljava/lang/String;" // serviceProviderName
"Ljava/lang/String;" // profileName
"Ljava/lang/String;" // iconType
"Ljava/lang/String;" // icon (base64)
"Ljava/lang/String;" // profileClass
")V"
);
const char *icon_type;
const char *profile_class;
switch (metadata->iconType) {
case ES10C_ICON_TYPE_JPEG:
icon_type = "jpeg";
break;
case ES10C_ICON_TYPE_PNG:
icon_type = "png";
break;
default:
icon_type = "unknown";
break;
}
switch (metadata->profileClass) {
case ES10C_PROFILE_CLASS_TEST:
profile_class = "test";
break;
case ES10C_PROFILE_CLASS_PROVISIONING:
profile_class = "provisioning";
break;
case ES10C_PROFILE_CLASS_OPERATIONAL:
profile_class = "operational";
break;
default:
profile_class = "unknown";
break;
}
return (*env)->NewObject(
env, profile_metadata_class, profile_metadata_constructor,
toJString(env, metadata->iccid),
toJString(env, metadata->serviceProviderName),
toJString(env, metadata->profileName),
toJString(env, icon_type),
toJString(env, metadata->icon),
toJString(env, profile_class)
);
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
@ -63,6 +126,7 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
jobject callback) { jobject callback) {
struct euicc_ctx *ctx = (struct euicc_ctx *) handle; struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result; struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
struct es8p_metadata *profile_metadata = NULL;
const char *_confirmation_code = NULL; const char *_confirmation_code = NULL;
const char *_matching_id = NULL; const char *_matching_id = NULL;
const char *_smdp = NULL; const char *_smdp = NULL;
@ -109,6 +173,17 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
goto out; goto out;
} }
const char *b64_profileMetadata = ctx->http._internal.prepare_download_param->b64_profileMetadata;
if (b64_profileMetadata != NULL) {
ret = es8p_metadata_parse(&profile_metadata, b64_profileMetadata);
if (ret < 0) {
ret = -ES10B_ERROR_REASON_UNDEFINED;
goto out;
}
(*env)->CallVoidMethod(env, callback, on_profile_metadata,
build_profile_metadata(env, profile_metadata));
}
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading); (*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading);
ret = es10b_prepare_download(ctx, _confirmation_code); ret = es10b_prepare_download(ctx, _confirmation_code);
syslog(LOG_INFO, "es10b_prepare_download %d", ret); syslog(LOG_INFO, "es10b_prepare_download %d", ret);
@ -134,6 +209,8 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
out: out:
// We expect Java side to call cancelSessions after any error -- thus, `euicc_http_cleanup` is done there // We expect Java side to call cancelSessions after any error -- thus, `euicc_http_cleanup` is done there
// This is so that Java side can access the last HTTP and/or APDU errors when we return. // This is so that Java side can access the last HTTP and/or APDU errors when we return.
if (profile_metadata != NULL)
es8p_metadata_free(&profile_metadata);
if (_confirmation_code != NULL) if (_confirmation_code != NULL)
(*env)->ReleaseStringUTFChars(env, confirmation_code, _confirmation_code); (*env)->ReleaseStringUTFChars(env, confirmation_code, _confirmation_code);
if (_matching_id != NULL) if (_matching_id != NULL)