eSIM LPA 下载链路:120 字节分片与第 257 片序号回绕

作者:

在

结论:eSIM 的 LPA 下载不是一次独立的串口调用,而是一段横跨 AT 锁、eUICC 逻辑通道与 HTTPS 的长交易:AT+CCHO/CGLA/CCHC 打开 ISD-R 通道,profile 数据按 120 字节分片经 STORE DATA APDU 下发,整笔 ES9+ 下载在持有同一把 AT 锁期间完成。因此网络慢或安装耗时会持续占用共享串口,阻塞同样抢锁的 modem 管理。本文为固定源码快照的静态结构分析,不是可直接在车端照做的配置步骤。

AT 锁的作用域比单条指令长

平台接口 LPABase 定义了 profile 列举、下载、删除、命名、切换与 eUICC 探测;comma 平台实现是 LPA。CellularManager._run_operation() 在后台线程串行化 UI 操作,再把结果回调排到 UI 线程,CLI 也走同一接口。源码在这里明确:UI 可见性不是底层授权。

LPA._acquire_channel() 先经 _acquire_lock() 对 /dev/shm/modem.lock 做排他 flock,再打开 ISD-R 逻辑通道,退出时尝试关闭。AtClient.query() 以 AT+CCHO/CGLA/CCHC 访问 eUICC,发生 SerialException 会重连并重试一次;TimeoutError 不进入该重试。open_isdr() 至多十次,第六次失败之后执行一次模组重置,第十次失败后仍先睡眠再抛最终异常。send_apdu() 在协议结果异常时可重试最多三次。

关键在锁的作用域:LPA.download_profile() 在 _acquire_channel() 内部调用完整的 download_profile(),其中串行执行多次 HTTPS ES9+ 请求与 BPP 安装;process_notifications() 同样在锁内访问 HTTPS。而每个 HTTP 请求的 HTTP_TIMEOUT=30 秒只是单请求超时,不是整笔交易的总 deadline。共享串口的 Serial.write()/flush() 在本层也没有写超时。插件化后应分离「短 AT 交易租约」与「长网络等待」。

TLV 与 APDU 的数据边界

iter_tlv() 按 tag、短/长形式 length、value 迭代,encode_tlv() 执行反向编码;string_to_tbcd() 把 ICCID 十进制数字编码为 TBCD,奇数位填 F;decode_struct() 按字段映射转 profile 属性。解析器遇截断 tag、length 或 value 时只是 break,不会抛出「数据损坏」;size=0x80 被当作长格式且后续长度字节数为 0,结果是零长度值,而不是明确拒绝 BER indefinite length。

es10x_command() 把请求按 120 字节分片,构造 STORE DATA APDU;遇 SW1=0x61 继续 GET RESPONSE,并把 SW1 高半字节为 0x9 的状态视为该片完成,其他状态抛异常。序号是 sequence & 0xFF,本层未限制超过 256 个分片的请求;也未为 APDU 响应累计设置全局大小或循环次数。

构造输入 原函数观察结果 边界
合法 5A 02 31 32 产出 (0x5A, b'12') 正常 TLV 解出。
截断 5A 03 01 产出空列表 截断静默停止,保留前面已解析条目。
5A 80 产出零长度 5A 值 被当作长格式,非拒绝。
121 字节请求 拆为 120+1 两片并拼接响应 分片边界为 120 字节。
257 个满长片 第 257 片序号回到 0 序号按 8 位回绕,无全局上限。
SW1=0x91xx / 0x6A82 前者视为成功,后者抛异常 仅高半字节 0x9 计入成功。

某些调用方的 require_tag() 会把缺失的必需 tag 转为异常,但可选字段、列表和尾部损坏仍可能被静默忽略。这些结论来自八项 TLV/APDU 原函数隔离探针,探针没有模拟无限 0x61 响应或真实 eUICC。

BPP 分块安装的早停与通知队列

BPP 安装由 _split_bpp() 将结构化包拆成 eUICC 接受的片段,load_bpp() 逐片发送并解释安装结果:A1 容器拆为容器头和各子 TLV,找不到 BF36 根时抛错。若根 TLV 之前还有无关 TLV,首个 BF23 命令块会从整份输入起点切片,连前缀一起发送。安装结果解析可从通知元数据取得序号、从 A2/A0 判成功、从 A2/A1 取得命令与错误码。

load_bpp() 一旦收到首个可解析安装结果就停止发送后续块,不论其成功与否;无结果时报错。这意味着畸形包、超大包和重试后的非幂等行为需要离线协议测试,不能以小 profile 安装成功推断边界正确。

通知侧,process_notifications() 逐条取 eUICC 通知,上传 SM-DP+ 后再从本地删除;进入单条 try 后的失败只打印并继续。第一条上传失败时不会发送本地删除 APDU,但会继续第二条并在其成功后删除;而 list_notifications() 抛错,或某条通知缺少 seqNumber/notificationAddress,均发生在逐条 try 之外,会终止整批。因此 UI 操作「完成」不等于远端通知可靠送达。

下载交易与会话取消

parse_lpa_activation_code() 只要求 LPA: 前缀和 $ 分隔后的三个非空字段,返回 SM-DP+ 地址与 matching ID;没有在此处校验首字段版本或限定服务器域名。download_profile() 先要求系统时间有效,再依次获取 eUICC challenge/info、向 SM-DP+ 发起认证、让 eUICC 验证服务器、让服务器验证客户端、解析 metadata、准备下载并获取/安装 BPP。HTTPS 封装 es9p_request() 使用固定 GSMA CI 证书包、单次 30 秒超时和服务端状态码映射。

若取得 transaction ID 后发生异常,代码调用 _cancel_session_safe(),尽力在 eUICC 和 SM-DP+ 取消,取消过程异常会被吞掉;会话正常关闭时原错误继续上抛。需要注意:若 session.close() 自身抛错,finally 中的新异常会覆盖原始请求错误,日志只留最外层异常时可能误诊根因。

switch_profile() 遇 PROFILE_CAT_BUSY 会重置模组并重试一次,PROFILE_NOT_IN_DISABLED_STATE 被当作可接受结果;它没有在该函数中确认蜂窝网已用新 profile 注册。get_active_profile() 始终返回 None,UI 实际依据列表中的 enabled 字段推断当前 profile。本地保护方面,Profile.is_comma 同时要求运营商字符串精确等于 Webbing 和 ICCID 前缀 8985235,大小写或运营商字段变化都会绕开它。

环节 捕获的错误 重试预算
AtClient.query() SerialException 重建串口重发一次;TimeoutError 不重试。
open_isdr() 打开通道失败 至多十次;第六次失败后重置模组;第十次失败先睡眠再抛。
send_apdu() RuntimeError/ValueError 最多三次;TimeoutError 直接传播。
ES9+ 请求 HTTP 状态映射 单次 30 秒超时,非整笔交易 deadline。

CLI 侧 resolve_iccid() 对全数字输入只按 1-based 序号解析并重新列举,因此正常全数字 ICCID 无法作为字面值传给切换/删除/命名,越界会报「无此序号」。若用户查看列表后 profile 集合变化,下一次按旧序号执行可能指向不同 profile。

常见问题

eSIM 下载时为什么整台设备的 modem AT 都会被占住?

因为整笔 ES9+ 下载与 BPP 安装在持有 /dev/shm/modem.lock 期间完成,锁的作用域比单条 AT 指令长得多。网络慢、服务器超时或安装耗时都会持续占用该锁,阻塞使用同一把锁的 modem 管理与 qcomgpsd AT 操作。

TLV 解析遇到截断数据会报错吗?

不会。解析器遇截断 tag、length 或 value 时只是 break,保留前面已解析的条目;size=0x80 会被当作长格式并产生零长度值。只有调用方通过 require_tag() 检查必需 tag 时,缺失才会转为异常。

一段 profile 数据是怎么分片发给 eUICC 的?

经 es10x_command() 按 120 字节分片,构造 STORE DATA APDU;遇 SW1=0x61 继续 GET RESPONSE,SW1 高半字节为 0x9 视为该片完成。序号按 8 位回绕,源文档未给出超过 256 片时的全局上限。

为什么「操作完成」不等于运营商已注册?

switch_profile() 不确认蜂窝网已用新 profile 注册,get_active_profile() 始终返回 None,而通知上传失败只打印并继续。UI 成功只表示对应 LPA 方法没有抛异常,不等于 profile 已完成运营商注册或 PPP 数据链恢复。

延伸阅读

参考资料

  • 项目 02 —— openpilot-research/ESIM_LPA_TLV_APDU_DOWNLOAD_DEEP_DIVE.md(TLV/APDU 分片、BPP 安装早停、ES9+ 交易与共享 AT 锁结构)。
  • 项目 02 —— 同目录探针记录:八项 TLV/APDU、八项 BPP、八项 AT/通道、十项 ES9+、八项通知/profile 隔离探针结论。

说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。

更新记录

首次发布 2026-10-11 最近核对:2026-10-11

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注