feat: profile metadata on download (lpac-jni) #171
3 changed files with 110 additions and 17 deletions
|
@ -380,6 +380,16 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
|||
getString(R.string.task_profile_download_failure),
|
||||
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.withEuiccChannel(slotId, portId) { channel ->
|
||||
channel.lpa.downloadProfile(
|
||||
|
@ -387,13 +397,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()
|
||||
|
|
|
@ -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,16 @@ interface ProfileDownloadCallback {
|
|||
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 onProfileMetadata(metadata: ProfileMetadata)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
#include <euicc/es8p.h>
|
||||
#include <euicc/es9p.h>
|
||||
#include <euicc/es10b.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -12,6 +13,9 @@ jobject download_state_downloading;
|
|||
jobject download_state_finalizing;
|
||||
|
||||
jmethodID on_state_update;
|
||||
jmethodID on_profile_metadata;
|
||||
|
||||
#define PROFILE_METADATA_CLASS "net/typeblog/lpac_jni/ProfileDownloadCallback$ProfileMetadata"
|
||||
|
||||
void lpac_download_init() {
|
||||
LPAC_JNI_SETUP_ENV;
|
||||
|
@ -54,6 +58,66 @@ void lpac_download_init() {
|
|||
"net/typeblog/lpac_jni/ProfileDownloadCallback");
|
||||
on_state_update = (*env)->GetMethodID(env, download_callback_class, "onStateUpdate",
|
||||
"(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-encoded)
|
||||
"Ljava/lang/String;" // profileClass
|
||||
")"
|
||||
"V" // (returns) void
|
||||
);
|
||||
|
||||
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
|
||||
|
@ -63,6 +127,7 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
|
|||
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;
|
||||
|
@ -109,6 +174,17 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
|
|||
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);
|
||||
ret = es10b_prepare_download(ctx, _confirmation_code);
|
||||
syslog(LOG_INFO, "es10b_prepare_download %d", ret);
|
||||
|
@ -134,6 +210,8 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
|
|||
out:
|
||||
// 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 (profile_metadata != NULL)
|
||||
es8p_metadata_free(&profile_metadata);
|
||||
if (_confirmation_code != NULL)
|
||||
(*env)->ReleaseStringUTFChars(env, confirmation_code, _confirmation_code);
|
||||
if (_matching_id != NULL)
|
||||
|
@ -181,4 +259,4 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject
|
|||
default:
|
||||
return toJString(env, "ES10B_ERROR_REASON_UNDEFINED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue