diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index 18cdb0b..b4937db 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -3,9 +3,11 @@ package im.angry.openeuicc.ui import android.app.Dialog import android.os.Bundle import android.text.Editable +import android.text.format.Formatter import android.util.Log import android.view.* import android.widget.ProgressBar +import android.widget.TextView import android.widget.Toast import androidx.appcompat.widget.Toolbar import androidx.fragment.app.DialogFragment @@ -34,8 +36,11 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O private lateinit var profileDownloadCode: TextInputLayout private lateinit var profileDownloadConfirmationCode: TextInputLayout private lateinit var profileDownloadIMEI: TextInputLayout + private lateinit var profileDownloadFreeSpace: TextView private lateinit var progress: ProgressBar + private var freeNvram: Int = -1 + private var downloading = false private val barcodeScannerLauncher = registerForActivityResult(ScanContract()) { result -> @@ -60,6 +65,7 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O profileDownloadCode = view.findViewById(R.id.profile_download_code) profileDownloadConfirmationCode = view.findViewById(R.id.profile_download_confirmation_code) profileDownloadIMEI = view.findViewById(R.id.profile_download_imei) + profileDownloadFreeSpace = view.findViewById(R.id.profile_download_free_space) progress = view.findViewById(R.id.progress) toolbar.inflateMenu(R.menu.fragment_profile_download) @@ -102,6 +108,18 @@ class ProfileDownloadFragment : DialogFragment(), EuiccFragmentMarker, Toolbar.O override fun onStart() { super.onStart() profileDownloadIMEI.editText!!.text = Editable.Factory.getInstance().newEditable(channel.imei) + + lifecycleScope.launch(Dispatchers.IO) { + // Fetch remaining NVRAM + val str = channel.lpa.euiccInfo2?.freeNvram?.also { + freeNvram = it + }?.let { Formatter.formatShortFileSize(requireContext(), it.toLong()) } + + withContext(Dispatchers.Main) { + profileDownloadFreeSpace.text = getString(R.string.profile_download_free_space, + str ?: getText(R.string.unknown)) + } + } } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { diff --git a/app-common/src/main/res/layout/fragment_profile_download.xml b/app-common/src/main/res/layout/fragment_profile_download.xml index 868cb9d..57ee8a1 100644 --- a/app-common/src/main/res/layout/fragment_profile_download.xml +++ b/app-common/src/main/res/layout/fragment_profile_download.xml @@ -102,13 +102,14 @@ android:id="@+id/profile_download_imei" android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginVertical="15dp" + android:layout_marginTop="15dp" + android:layout_marginBottom="6dp" android:hint="@string/profile_download_imei" style="@style/Widget.OpenEUICC.Input" app:layout_constraintTop_toBottomOf="@id/profile_download_confirmation_code" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" - app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintBottom_toTopOf="@id/profile_download_free_space" app:layout_constraintWidth_percent=".8" app:passwordToggleEnabled="true"> @@ -120,4 +121,16 @@ + + \ No newline at end of file diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index ae6931f..c6b9c7b 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ No eUICC card on this device is accessible by this app.\nInsert a supported eUICC card, or try out the privileged OpenEUICC app instead. + Unknown Enabled Disabled @@ -21,6 +22,7 @@ Activation Code Confirmation Code (Optional) IMEI (Optional) + Space remaining: %s Scan QR Code Download Failed to download eSIM. Check your activation / QR code. diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccInfo2.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccInfo2.kt new file mode 100644 index 0000000..ecaf40d --- /dev/null +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/EuiccInfo2.kt @@ -0,0 +1,15 @@ +package net.typeblog.lpac_jni + +/* Corresponds to EuiccInfo2 in SGP.22 */ +data class EuiccInfo2( + val profileVersion: String, + val sgp22Version: String, + val euiccFirmwareVersion: String, + val uiccFirmwareVersion: String, + val globalPlatformVersion: String, + val sasAccreditationNumber: String, + val ppVersion: String, + val installedApp: Int, + val freeNvram: Int, + val freeRam: Int, +) \ No newline at end of file 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 07fca0f..9a34920 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 @@ -3,6 +3,8 @@ package net.typeblog.lpac_jni interface LocalProfileAssistant { val profiles: List val eID: String + // Extended EuiccInfo for use with LUIs, containing information such as firmware version + val euiccInfo2: EuiccInfo2? fun enableProfile(iccid: String): Boolean fun disableProfile(iccid: String): 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 607127b..4f18ca9 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 @@ -25,4 +25,7 @@ internal object LpacJni { // 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 + + // es10cex (actually part of es10b) + 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/impl/LocalProfileAssistantImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt index 751b9ec..c62e3af 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 @@ -2,6 +2,7 @@ package net.typeblog.lpac_jni.impl import net.typeblog.lpac_jni.LpacJni import net.typeblog.lpac_jni.ApduInterface +import net.typeblog.lpac_jni.EuiccInfo2 import net.typeblog.lpac_jni.HttpInterface import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileInfo @@ -25,6 +26,9 @@ class LocalProfileAssistantImpl( LpacJni.es10cGetEid(contextHandle)!! } + override val euiccInfo2: EuiccInfo2? + get() = LpacJni.es10cexGetEuiccInfo2(contextHandle) + override fun enableProfile(iccid: String): Boolean { return LpacJni.es10cEnableProfile(contextHandle, iccid) == 0 } 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 14db016..58a84b5 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 @@ -24,6 +24,9 @@ jstring empty_string; jclass string_class; jmethodID string_constructor; +jclass euicc_info2_class; +jmethodID euicc_info2_constructor; + jint JNI_OnLoad(JavaVM *vm, void *reserved) { jvm = vm; interface_wrapper_init(); @@ -58,6 +61,10 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) { local_profile_class_operational = (*env)->GetStaticObjectField(env, local_profile_class_class, field_operational); local_profile_class_operational = (*env)->NewGlobalRef(env, local_profile_class_operational); + euicc_info2_class = (*env)->FindClass(env, "net/typeblog/lpac_jni/EuiccInfo2"); + euicc_info2_class = (*env)->NewGlobalRef(env, euicc_info2_class); + euicc_info2_constructor = (*env)->GetMethodID(env, euicc_info2_class, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;III)V"); + const char _unused[1]; empty_string = (*env)->NewString(env, _unused, 0); empty_string = (*env)->NewGlobalRef(env, empty_string); @@ -229,4 +236,45 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cDeleteProfile(JNIEnv *env, jobject thiz int ret = es10c_delete_profile_iccid(ctx, _iccid); (*env)->ReleaseStringUTFChars(env, iccid, _iccid); return ret; +} + +JNIEXPORT jobject JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_es10cexGetEuiccInfo2(JNIEnv *env, jobject thiz, jlong handle) { + struct euicc_ctx *ctx = (struct euicc_ctx *) handle; + struct es10cex_euiccinfo2 info; + jstring sas_accreditation_number = NULL; + jstring global_platform_version = NULL; + jstring euicc_firmware_version = NULL; + jstring uicc_firmware_version = NULL; + jstring profile_version = NULL; + jstring sgp22_version = NULL; + jstring pp_version = NULL; + jobject ret = NULL; + + if (es10cex_get_euiccinfo2(ctx, &info) < 0) + goto out; + + profile_version = toJString(env, info.profile_version); + sgp22_version = toJString(env, info.sgp22_version); + euicc_firmware_version = toJString(env, info.euicc_firmware_version); + uicc_firmware_version = toJString(env, info.uicc_firmware_version); + global_platform_version = toJString(env, info.global_platform_version); + sas_accreditation_number = toJString(env, info.sas_accreditation_number); + pp_version = toJString(env, info.pp_version); + + ret = (*env)->NewObject(env, euicc_info2_class, euicc_info2_constructor, + profile_version, sgp22_version, euicc_firmware_version, + uicc_firmware_version, global_platform_version, + sas_accreditation_number, pp_version, + info.installed_app, info.free_nvram, info.free_ram); + + out: + (*env)->DeleteLocalRef(env, profile_version); + (*env)->DeleteLocalRef(env, sgp22_version); + (*env)->DeleteLocalRef(env, euicc_firmware_version); + (*env)->DeleteLocalRef(env, uicc_firmware_version); + (*env)->DeleteLocalRef(env, global_platform_version); + (*env)->DeleteLocalRef(env, sas_accreditation_number); + (*env)->DeleteLocalRef(env, pp_version); + return ret; } \ No newline at end of file