Android Kotlin实现eSIM遇EMBEDDED_SUBSCRIPTION_RESULT_ERROR求助
eSIM安装应用问题解决方案
问题排查与修复
1. 卡在EMBEDDED_SUBSCRIPTION_RESULT_ERROR的问题
- 核心问题:你在
EMBEDDED_SUBSCRIPTION_RESULT_ERROR分支错误调用了handleResolvableError——该方法仅适用于可恢复错误(RESOLVABLE_ERROR),而ERROR属于不可恢复错误,应优先通过详细错误码定位根因。 - 修复步骤:
- 在
onReceive中打印detailedCode,根据官方定义的详细错误码排查具体原因(如激活码无效、设备eSIM硬件不兼容、运营商配置缺失等)。 - 移除
ERROR分支中的handleResolvableError调用,改为输出详细日志辅助排查。 - 确认激活码格式合规,且对应运营商支持当前设备的eSIM功能。
- 在
2. 自定义权限下无法触发onReceive的问题
- 核心问题:动态注册广播时使用自定义权限,需确保权限在Manifest中正确声明,且权限校验逻辑符合系统要求。
- 修复步骤:
- 在
AndroidManifest.xml中添加权限声明:<permission android:name="com.example.testesimsdk.lpa.permission.BROADCAST" android:protectionLevel="signature" /> - 动态注册广播时,确保权限参数与Manifest一致,并适配Android 12+的
PendingIntent标志:this.registerReceiver( broadcastReceiver, IntentFilter(ACTION_DOWNLOAD_SUBSCRIPTION), LPA_DECLARED_PERMISSION, null ) - 确认应用签名一致(
signature级别权限仅允许同签名应用发送对应广播)。
- 在
3. 安装eSIM时传入自定义确认码的实现
- 核心逻辑:当激活码需要确认码时,系统会返回
EMBEDDED_SUBSCRIPTION_DETAILED_CODE_CONFIRMATION_CODE_REQUIRED详细错误码,此时需弹出输入框获取用户输入,重新构造带确认码的激活码后发起下载。 - 实现步骤:
- 在
onReceive中判断详细错误码,触发确认码输入流程。 - 获取确认码后,按格式拼接至原激活码(格式:
原激活码,确认码),重新调用下载接口。
- 在
修正后的完整代码
package com.example.testesimsdk import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.Bundle import android.telephony.euicc.DownloadableSubscription import android.telephony.euicc.EuiccManager import android.util.Log import android.view.View import android.widget.Button import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AlertDialog class MainActivity : AppCompatActivity(), View.OnClickListener { private val ACTIVATION_CODE = "LPA:1\$tl2.prod.ondemandconnectivity.com\$XWSAGDHOHV2VBGLN" private val Success = "Success" private val ACTION_DOWNLOAD_SUBSCRIPTION = "download_subscription" private val ACTION_DELETE_SUBSCRIPTION = "delete_subscription" private val LPA_DECLARED_PERMISSION = "com.example.testesimsdk.lpa.permission.BROADCAST" private var mgr: EuiccManager? = null private var broadcastReceiver: BroadcastReceiver? = null private lateinit var btn: Button override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Toast.makeText(applicationContext, ACTIVATION_CODE, Toast.LENGTH_LONG).show() implementEsim() btn = findViewById(R.id.button) btn.setOnClickListener(this) } private fun implementEsim() { setupEsim() } private fun initMgr() { if (mgr == null) { mgr = getSystemService(EUICC_SERVICE) as EuiccManager } } private fun isEsimSupported(): Boolean { initMgr() return mgr?.isEnabled ?: false } private fun setupEsim() { initMgr() if (isEsimSupported()) { broadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (ACTION_DOWNLOAD_SUBSCRIPTION != intent.action) { Log.d("[MainActivity]", "Invalid intent action: ${intent.action}") return } val resultCode = resultCode val detailedCode = intent.getIntExtra( EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0 ) when (resultCode) { EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR -> { Toast.makeText(context, "可恢复错误,详情码:$detailedCode", Toast.LENGTH_LONG).show() Log.d("[MainActivity]", "RESOLVABLE_ERROR, detailedCode: $detailedCode") handleResolvableError(intent, detailedCode) } EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK -> { Toast.makeText(context, Success, Toast.LENGTH_LONG).show() Log.d("[MainActivity]", "下载成功") } EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR -> { Toast.makeText(context, "不可恢复错误,详情码:$detailedCode", Toast.LENGTH_LONG).show() Log.d("[MainActivity]", "ERROR, detailedCode: $detailedCode") } else -> { Log.d("[MainActivity]", "未知错误,resultCode: $resultCode") } } } } // 使用自定义权限注册广播 this.registerReceiver( broadcastReceiver, IntentFilter(ACTION_DOWNLOAD_SUBSCRIPTION), LPA_DECLARED_PERMISSION, null ) startDownloadSubscription(ACTIVATION_CODE) } else { Toast.makeText(this, "设备不支持eSIM", Toast.LENGTH_LONG).show() } } private fun startDownloadSubscription(activationCode: String) { val sub = DownloadableSubscription.forActivationCode(activationCode) val intent = Intent(ACTION_DOWNLOAD_SUBSCRIPTION) val callbackIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) mgr?.downloadSubscription(sub, true, callbackIntent) } private fun handleResolvableError(intent: Intent, detailedCode: Int) { when (detailedCode) { EuiccManager.EMBEDDED_SUBSCRIPTION_DETAILED_CODE_CONFIRMATION_CODE_REQUIRED -> { // 弹出对话框获取确认码 showConfirmationCodeDialog() } else -> { // 其他可恢复错误,调用系统默认处理 try { val callbackIntent = PendingIntent.getBroadcast( this, detailedCode, Intent(ACTION_DOWNLOAD_SUBSCRIPTION), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) mgr?.startResolutionActivity( this, detailedCode, intent, callbackIntent ) } catch (e: Exception) { Log.d("[MainActivity]", "处理可恢复错误失败:${e.localizedMessage}") } } } } private fun showConfirmationCodeDialog() { val builder = AlertDialog.Builder(this) builder.setTitle("请输入确认码") val input = android.widget.EditText(this) builder.setView(input) builder.setPositiveButton("确认") { _, _ -> val confirmationCode = input.text.toString().trim() if (confirmationCode.isNotEmpty()) { // 构造包含确认码的激活码 val newActivationCode = "$ACTIVATION_CODE,$confirmationCode" startDownloadSubscription(newActivationCode) } else { Toast.makeText(this, "确认码不能为空", Toast.LENGTH_SHORT).show() } } builder.setNegativeButton("取消", null) builder.show() } private fun deleteSubs() { // 动态获取当前激活的eSIM订阅ID,避免硬编码 mgr?.activeSubscriptionInfoList?.firstOrNull()?.let { subInfo -> val intent = Intent(ACTION_DELETE_SUBSCRIPTION) val callbackIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) mgr?.deleteSubscription(subInfo.subscriptionId, callbackIntent) Toast.makeText(this, "开始删除订阅", Toast.LENGTH_SHORT).show() } ?: Toast.makeText(this, "无可用eSIM订阅", Toast.LENGTH_SHORT).show() } override fun onClick(p0: View) { when (p0.id) { R.id.button -> deleteSubs() } } override fun onDestroy() { broadcastReceiver?.let { unregisterReceiver(it) } super.onDestroy() } }
额外注意事项
- 权限配置:需在Manifest中添加
android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS权限(普通应用需通过系统授权流程获取,系统签名应用可直接声明)。 - 激活码有效性:确保激活码未过期,且对应运营商支持当前设备所在地区的eSIM服务。
- 版本适配:eSIM API仅在Android 8.0(API 26)及以上可用,需在Manifest中声明
android:minSdkVersion="26"。
内容的提问来源于stack exchange,提问作者Tamir Yakov
相关产品推荐
相关产品推荐

