diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt index 8e475df..bb1d5b6 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccInfoActivity.kt @@ -15,12 +15,18 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ViewHolder import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import im.angry.openeuicc.common.R +import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.util.* import kotlinx.coroutines.launch import net.typeblog.lpac_jni.impl.DEFAULT_PKID_GSMA_RSP2_ROOT_CI1 import net.typeblog.lpac_jni.impl.PKID_GSMA_TEST_CI class EuiccInfoActivity : BaseEuiccAccessActivity() { + companion object { + private val YES_NO = Pair(R.string.yes, R.string.no) + private val SUPPORTED_UNSUPPORTED = Pair(R.string.supported, R.string.unsupported) + } + private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var infoList: RecyclerView @@ -71,104 +77,59 @@ class EuiccInfoActivity : BaseEuiccAccessActivity() { swipeRefresh.isRefreshing = true lifecycleScope.launch { - val unknownStr = getString(R.string.unknown) - - val newItems = mutableListOf>() - - newItems.add( - Pair( - getString(R.string.euicc_info_access_mode), - euiccChannelManager.withEuiccChannel(logicalSlotId) { channel -> channel.type } - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_removable), - if (euiccChannelManager.withEuiccChannel(logicalSlotId) { channel -> channel.port.card.isRemovable }) { - getString(R.string.yes) - } else { - getString(R.string.no) - } - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_eid), - euiccChannelManager.withEuiccChannel(logicalSlotId) { channel -> channel.lpa.eID } - ) - ) - - val euiccInfo2 = euiccChannelManager.withEuiccChannel(logicalSlotId) { channel -> - channel.lpa.euiccInfo2 - } - - newItems.add( - Pair( - getString(R.string.euicc_info_firmware_version), - euiccInfo2?.euiccFirmwareVersion ?: unknownStr - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_globalplatform_version), - euiccInfo2?.globalPlatformVersion ?: unknownStr - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_pp_version), - euiccInfo2?.ppVersion ?: unknownStr - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_sas_accreditation_number), - euiccInfo2?.sasAccreditationNumber ?: unknownStr - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_free_nvram), - euiccInfo2?.freeNvram?.let { formatFreeSpace(it) } ?: unknownStr - )) - - newItems.add( - Pair( - getString(R.string.euicc_info_gsma_prod), - if (euiccInfo2?.euiccCiPKIdListForSigning?.contains( - DEFAULT_PKID_GSMA_RSP2_ROOT_CI1 - ) == true - ) { - getString(R.string.supported) - } else { - getString(R.string.unsupported) - } - ) - ) - - newItems.add( - Pair( - getString(R.string.euicc_info_gsma_test), - if (PKID_GSMA_TEST_CI.any { euiccInfo2?.euiccCiPKIdListForSigning?.contains(it) == true }) { - getString(R.string.supported) - } else { - getString(R.string.unsupported) - } - ) - ) - - (infoList.adapter!! as EuiccInfoAdapter).euiccInfoItems = newItems + (infoList.adapter!! as EuiccInfoAdapter).euiccInfoItems = + euiccChannelManager.withEuiccChannel(logicalSlotId, ::buildPairs).map { + Pair(getString(it.first), it.second ?: getString(R.string.unknown)) + } swipeRefresh.isRefreshing = false } } + private fun buildPairs(channel: EuiccChannel) = buildList { + add(Pair(R.string.euicc_info_access_mode, channel.type)) + add( + Pair( + R.string.euicc_info_removable, + formatByBoolean(channel.port.card.isRemovable, YES_NO) + ) + ) + add(Pair(R.string.euicc_info_eid, channel.lpa.eID)) + channel.lpa.euiccInfo2.let { info -> + add(Pair(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion)) + add(Pair(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion)) + add(Pair(R.string.euicc_info_pp_version, info?.ppVersion)) + add(Pair(R.string.euicc_info_sas_accreditation_number, info?.sasAccreditationNumber)) + add(Pair(R.string.euicc_info_free_nvram, info?.freeNvram?.let(::formatFreeSpace))) + } + channel.lpa.euiccInfo2?.euiccCiPKIdListForSigning.orEmpty().let { signers -> + add( + Pair( + R.string.euicc_info_gsma_prod, + formatByBoolean( + signers.contains(DEFAULT_PKID_GSMA_RSP2_ROOT_CI1), + SUPPORTED_UNSUPPORTED + ) + ) + ) + add( + Pair( + R.string.euicc_info_gsma_test, + formatByBoolean(PKID_GSMA_TEST_CI.any(signers::contains), SUPPORTED_UNSUPPORTED) + ) + ) + } + } + + private fun formatByBoolean(b: Boolean, res: Pair): String = + getString( + if (b) { + res.first + } else { + res.second + } + ) + inner class EuiccInfoViewHolder(root: View) : ViewHolder(root) { private val title: TextView = root.requireViewById(R.id.euicc_info_title) private val content: TextView = root.requireViewById(R.id.euicc_info_content) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt index f10f134..6ad74c4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt @@ -47,10 +47,6 @@ class SettingsFragment: PreferenceFragmentCompat() { setOnPreferenceClickListener(::onAppVersionClicked) } - findPreference("pref_info_source_code")?.apply { - intent = Intent(Intent.ACTION_VIEW, Uri.parse(summary.toString())) - } - findPreference("pref_language")?.apply { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return@apply isVisible = true diff --git a/app-common/src/main/res/layout/euicc_profile.xml b/app-common/src/main/res/layout/euicc_profile.xml index 7971017..58d55ab 100644 --- a/app-common/src/main/res/layout/euicc_profile.xml +++ b/app-common/src/main/res/layout/euicc_profile.xml @@ -28,7 +28,8 @@ app:layout_constraintRight_toLeftOf="@+id/profile_menu" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@+id/state" - app:layout_constraintHorizontal_bias="0" /> + app:layout_constraintHorizontal_bias="0" + app:layout_constrainedWidth="true" /> アプリの言語を選択 すべてのプロファイルを表示 プロダクション以外のプロファイルも表示する + タイプ: + テスティング + 準備中 + 動作中 diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index 107e0b4..52815b9 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -1,5 +1,6 @@ - + + app:key="pref_info_source_code"> + + \ No newline at end of file diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt index 433f9c3..39a360f 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt @@ -1,6 +1,8 @@ package im.angry.openeuicc.ui +import android.annotation.SuppressLint import android.os.Bundle +import android.text.Html import android.view.MenuItem import android.view.View import android.view.ViewGroup @@ -8,6 +10,7 @@ import android.widget.TextView import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.children +import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -29,15 +32,16 @@ class CompatibilityCheckActivity: AppCompatActivity() { setupToolbarInsets() supportActionBar!!.setDisplayHomeAsUpEnabled(true) - compatibilityCheckList = requireViewById(R.id.recycler_view) - compatibilityCheckList.layoutManager = - LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - compatibilityCheckList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) - compatibilityCheckList.adapter = adapter + compatibilityCheckList = requireViewById(R.id.recycler_view).also { + it.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + it.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) + it.adapter = adapter + } setupRootViewInsets(compatibilityCheckList) } + @SuppressLint("NotifyDataSetChanged") override fun onStart() { super.onStart() lifecycleScope.launch { @@ -61,10 +65,10 @@ class CompatibilityCheckActivity: AppCompatActivity() { fun bindItem(item: CompatibilityCheck) { titleView.text = item.title - descView.text = item.description + descView.text = Html.fromHtml(item.description, Html.FROM_HTML_MODE_COMPACT) statusContainer.children.forEach { - it.visibility = View.GONE + it.isVisible = false } val viewId = when (item.state) { @@ -73,7 +77,7 @@ class CompatibilityCheckActivity: AppCompatActivity() { CompatibilityCheck.State.FAILURE_UNKNOWN -> R.id.compatibility_check_unknown else -> R.id.compatibility_check_progress_bar } - root.requireViewById(viewId).visibility = View.VISIBLE + root.requireViewById(viewId).isVisible = true } } diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index a754848..3cf7347 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -12,11 +12,11 @@ OMAPI Connectivity Does your device allow access to Secure Elements on SIM cards via OMAPI? Unable to detect Secure Element readers for SIM cards via OMAPI. If you have not inserted a SIM in this device, try inserting one and retry this check. - Successfully detected Secure Element access, but only for the following SIM slots: SIM%s. + Successfully detected Secure Element access, but only for the following SIM slots: <b>SIM%s</b>. ISD-R Channel Access Does your device support opening an ISD-R (management) channel to eSIMs via OMAPI? Cannot determine whether ISD-R access through OMAPI is supported. You might want to retry with SIM cards inserted (any SIM card will do) if not already. - OMAPI access to ISD-R is only possible on the following SIM slots: SIM%s. + OMAPI access to ISD-R is only possible on the following SIM slots: <b>SIM%s</b>. Not on the Known Broken List Making sure your device is not known to have bugs associated with removable eSIMs. Oops, your device is known to have bugs when accessing removable eSIMs. This does not necessarily mean that it will not work at all, but you will have to proceed with caution. 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 58f25aa..4ff65fa 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 @@ -5,6 +5,7 @@ import net.typeblog.lpac_jni.HttpInterface.HttpResponse interface LocalProfileAssistant { @Suppress("ArrayInDataClass") data class ProfileDownloadException( + val lpaErrorReason: String, val lastHttpResponse: HttpResponse?, val lastHttpException: Exception?, val lastApduResponse: ByteArray?, 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 09c0117..8e3f53a 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 @@ -29,6 +29,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 + external fun downloadErrCodeToString(code: Int): String external fun handleNotification(handle: Long, seqNumber: Long): Int // Cancel any ongoing es9p and/or es10b sessions external fun cancelSessions(handle: Long) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/IgnoreTLSCertificate.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt similarity index 92% rename from libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/IgnoreTLSCertificate.kt rename to libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt index 7b13282..55b28fe 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/IgnoreTLSCertificate.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/AllowAllTrustManager.kt @@ -5,7 +5,7 @@ import java.security.cert.X509Certificate import javax.net.ssl.X509TrustManager @SuppressLint("CustomX509TrustManager") -class IgnoreTLSCertificate : X509TrustManager { +class AllowAllTrustManager : X509TrustManager { @SuppressLint("TrustAllX509TrustManager") override fun checkClientTrusted(p0: Array?, p1: String?) { return diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt index 77227f8..c1b15f7 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/HttpInterfaceImpl.kt @@ -83,7 +83,7 @@ class HttpInterfaceImpl( private fun getSocketFactory(): SSLSocketFactory { val trustManagers = if (runBlocking { ignoreTLSCertificateFlow.first() }) { - arrayOf(IgnoreTLSCertificate()) + arrayOf(AllowAllTrustManager()) } else { this.trustManagers } 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 0cbebf0..b617f2b 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 @@ -214,6 +214,7 @@ class LocalProfileAssistantImpl( if (res != 0) { // Construct the error now to store any error information we _can_ access val err = LocalProfileAssistant.ProfileDownloadException( + lpaErrorReason = LpacJni.downloadErrCodeToString(-res), httpInterface.lastHttpResponse, httpInterface.lastHttpException, apduInterface.lastApduResponse, 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 f63585a..028e30d 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 @@ -148,4 +148,34 @@ Java_net_typeblog_lpac_1jni_LpacJni_cancelSessions(JNIEnv *env, jobject thiz, jl es9p_cancel_session(ctx); es10b_cancel_session(ctx, ES10B_CANCEL_SESSION_REASON_UNDEFINED); euicc_http_cleanup(ctx); +} + +#define QUOTE(S) #S +#define ERRCODE_ENUM_TO_STRING(VARIANT) case VARIANT: return toJString(env, QUOTE(VARIANT)) + +JNIEXPORT jstring JNICALL +Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject thiz, jint code) { + switch (code) { + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INCORRECT_INPUT_VALUES); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INVALID_SIGNATURE); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INVALID_TRANSACTION_ID); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_UNSUPPORTED_CRT_VALUES); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_UNSUPPORTED_REMOTE_OPERATION_TYPE); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_UNSUPPORTED_PROFILE_CLASS); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_STRUCTURE_ERROR); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_SCP03T_SECURITY_ERROR); + ERRCODE_ENUM_TO_STRING( + ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_ALREADY_EXISTS_ON_EUICC); + ERRCODE_ENUM_TO_STRING( + ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INSUFFICIENT_MEMORY_FOR_PROFILE); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_INTERRUPTION); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_PE_PROCESSING_ERROR); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_ICCID_MISMATCH); + ERRCODE_ENUM_TO_STRING( + ES10B_ERROR_REASON_TEST_PROFILE_INSTALL_FAILED_DUE_TO_INVALID_NAA_KEY); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_PPR_NOT_ALLOWED); + ERRCODE_ENUM_TO_STRING(ES10B_ERROR_REASON_INSTALL_FAILED_DUE_TO_UNKNOWN_ERROR); + default: + return toJString(env, "ES10B_ERROR_REASON_UNDEFINED"); + } } \ No newline at end of file