From 61e9348366f44b1ff08e4f2c69ac23612c84711f Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 10 Mar 2025 20:27:35 +0800 Subject: [PATCH] feat: cancellable download --- .../service/EuiccChannelManagerService.kt | 17 +++--- .../lpac_jni/ProfileDownloadCallback.kt | 2 + .../src/main/jni/lpac-jni/lpac-download.c | 58 +++++++++++++++++-- 3 files changed, 66 insertions(+), 11 deletions(-) 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..d9025c6 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 @@ -380,6 +380,14 @@ 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) + } + } euiccChannelManager.beginTrackedOperation(slotId, portId) { euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> channel.lpa.downloadProfile( @@ -387,13 +395,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/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..9af49c3 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,7 @@ interface ProfileDownloadCallback { Finalizing(80), // load bpp } + var isCancelled: Boolean + fun onStateUpdate(state: DownloadState) } \ No newline at end of file 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..e348aab 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 @@ -12,6 +12,8 @@ jobject download_state_downloading; jobject download_state_finalizing; jmethodID on_state_update; +jmethodID is_cancelled; +jmethodID set_cancelled; void lpac_download_init() { LPAC_JNI_SETUP_ENV; @@ -54,13 +56,23 @@ 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"); + is_cancelled = (*env)->GetMethodID(env, download_callback_class, "isCancelled", "()Z"); + set_cancelled = (*env)->GetMethodID(env, download_callback_class, "setCancelled", "(Z)V"); } +#define IS_CANCELLED (*env)->CallBooleanMethod(env, callback, is_cancelled) + 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; const char *_confirmation_code = NULL; @@ -79,6 +91,11 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j ctx->http.server_address = _smdp; + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } + // callback.onStateUpdate(DownloadState.Preparing) (*env)->CallVoidMethod(env, callback, on_state_update, download_state_preparing); ret = es10b_get_euicc_challenge_and_info(ctx); syslog(LOG_INFO, "es10b_get_euicc_challenge_and_info %d", ret); @@ -87,6 +104,11 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j goto out; } + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } + // callback.onStateUpdate(DownloadState.Connecting) (*env)->CallVoidMethod(env, callback, on_state_update, download_state_connecting); ret = es9p_initiate_authentication(ctx); syslog(LOG_INFO, "es9p_initiate_authentication %d", ret); @@ -95,6 +117,11 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j goto out; } + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } + // callback.onStateUpdate(DownloadState.Authenticating) (*env)->CallVoidMethod(env, callback, on_state_update, download_state_authenticating); ret = es10b_authenticate_server(ctx, _matching_id, _imei); syslog(LOG_INFO, "es10b_authenticate_server %d", ret); @@ -103,12 +130,22 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j goto out; } + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } ret = es9p_authenticate_client(ctx); + syslog(LOG_INFO, "es9p_authenticate_client %d", ret); if (ret < 0) { ret = -ES10B_ERROR_REASON_UNDEFINED; goto out; } + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } + // callback.onStateUpdate(DownloadState.Downloading) (*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); @@ -117,10 +154,20 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j goto out; } + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } ret = es9p_get_bound_profile_package(ctx); + syslog(LOG_INFO, "es9p_get_bound_profile_package %d", ret); if (ret < 0) goto out; + if (IS_CANCELLED) { // callback.isCancelled() + ret = -ES10B_ERROR_REASON_UNDEFINED; + goto out; + } + // callback.onStateUpdate(DownloadState.Finalizing) (*env)->CallVoidMethod(env, callback, on_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); @@ -132,6 +179,9 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j euicc_http_cleanup(ctx); out: + if (IS_CANCELLED == 0 && ret == -ES10B_ERROR_REASON_UNDEFINED) { + (*env)->CallVoidMethod(env, callback, set_cancelled, JNI_TRUE); + } // 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) -- 2.45.3