结论:车载网络设置分三层:大屏 NetworkUI 只有 Wi-Fi 与高级网络(未接入 eSIM 页),mici 才把 Wi-Fi、eSIM、热点、APN 与计费并置;Wi-Fi 由 WifiManager 通过 NetworkManager D-Bus 管理,后台约每 5 秒扫描一次,并用用户 epoch 防止慢查询覆盖刚选择的网络;eSIM 操作经 CellularManager 后台线程串行化后落到 LPABase。UI 显示/操作只是体验门,不是操作系统级授权。本文为固定源码快照 c8fb906 的静态分析,未连接 NetworkManager、真实 eUICC 或蜂窝模组。
三层界面:大屏、mici 与平台接口
大屏侧 SettingsLayout 创建共享的 WifiManager,在 Wi-Fi 与高级网络两页间切换;大屏代码未接入 CellularManager 或 eSIM 管理页。WifiManagerUI 负责扫描结果、保存网络、连接/忘记、隐藏 SSID 与密码重试;AdvancedNetworkSettings 管理热点、热点密码、IP、APN、漫游及蜂窝与 Wi-Fi 计费设置。mici 侧 NetworkLayoutMici 把 Wi-Fi、eSIM、热点、APN、漫游及计费共置在导航滚动页,EsimUI 提供配置文件列表、切换/删除/命名与扫码添加。
两屏的策略判定目前并不统一:大屏 AdvancedNetworkSettings 使用 PrimeType.NONE/LITE 判断显示项,mici 使用 not ui_state.prime_state.is_full_prime()。两者意图相近,但不是同一个策略函数,插件化时应避免继续复制业务判定。UI 显示隐藏不能保护直接调用 Params、D-Bus 或 LPA 的本地进程。
| 入口 | 组件和重要函数 | 作用 |
|---|---|---|
| 大屏设置 | SettingsLayout、NetworkUI |
创建共享 WifiManager,在 Wi-Fi 与高级网络两页间切换;未接入 eSIM 管理页。 |
| 大屏 Wi-Fi | WifiManagerUI |
扫描结果、保存网络、连接/忘记、隐藏 SSID、密码重试。 |
| 大屏高级网络 | AdvancedNetworkSettings |
热点、APN、漫游及蜂窝/Wi-Fi 计费设置。 |
| mici 设置 | NetworkLayoutMici |
Wi-Fi、eSIM、热点、APN、漫游及计费共置在滚动页。 |
| mici eSIM | EsimUI |
配置文件列表、切换/删除/命名、扫码添加;操作受 UI 门限制。 |
Wi-Fi 状态机与用户 epoch
WifiManager.__init__() 连接系统 D-Bus,维护 ssid -> connection path、WifiState、网络列表和待执行回调。初始化线程等待无线设备,启动扫描与 NetworkManager 信号监听,再同步已有连接、热点连接和当前状态;扫描线程约每 5 秒发起一次扫描,set_active() 在页面重新激活时立刻异步同步 Wi-Fi 状态和列表。
连接流程是:用户选择 SSID 后 _set_connecting() 进入 CONNECTING;已保存网络走 ActivateConnection,新网络先删除同 SSID 旧配置再 AddAndActivateConnection2(volatile);收到 ACTIVATED 说明连接成功,随后调用 NetworkManager Save() 持久化临时连接;NEED_AUTH 或 FAILED/NO_SECRETS 组合触发密码重试回调。用户操作调用 _set_connecting() 递增 _user_epoch;监听线程在慢 D-Bus 查询前后比较该值,避免旧查询覆盖用户刚选择的新网络。这是针对已知竞态的保护,不能推断整个管理器所有共享字段都具有完整同步保证。
connect_to_network() 先删除同 SSID 配置,再以 persist=volatile 建立连接;activate_connection() 使用已有连接配置;forget_connection() 删除配置。process_callbacks() 在 UI 线程执行回调,避免后台线程直接操作控件。get_security_type() 当前只返回 OPEN、WPA 或 UNSUPPORTED,不能根据 WPA2/WPA3 枚举名称推断兼容性。
热点与计费标志
_add_tethering_connection() 创建 AP 模式、共享 IPv4 连接,SSID 为 weedle 加设备 ID 前四位(若有),默认 PSK 是 DEFAULT_TETHERING_PASSWORD;已有热点密码可由用户修改,不能据常量断定任意在用设备仍使用默认值。量产硬件应在首次启用前强制生成唯一强密码,并审计恢复出厂与日志泄漏路径。
set_current_network_metered() 更新 NetworkManager 当前 Wi-Fi connection 的 metered 字段,热点时跳过;mici 页面注释指出计费控件在切换网络后可能短暂显示旧状态,且重新启用按钮依赖扫描更新而非直接设置信号。计费标志最终汇入 NetworkMetered:hardwared 从硬件网络状态计算,经 deviceState 并写 Params,updated 读取它,在计费网络且未超时、非用户主动请求时推迟常规更新拉取;超过三天或用户请求可继续,不能解释为绝对禁止联网。
| 用户设置 | 写入与消费 | 车载含义 |
|---|---|---|
GsmRoaming |
大屏/mici 写 Params;modem 读取 |
蜂窝漫游策略;变更生效时序按 modem 状态机验证。 |
GsmApn |
两种 UI 写/删除 Params;modem 初始化并检查变化 |
蜂窝接入点;空白表示回到自动配置。 |
GsmMetered |
两种 UI 写 Params;HardwareComma.get_network_metered() 读取 |
蜂窝链路是否被视为计费网络。 |
Wi-Fi metered |
WifiManager 写 NetworkManager 配置 |
Wi-Fi 计费标志;来源不是 GsmMetered。 |
NetworkMetered |
hardwared 计算并写 Params;updated 读取 |
常规更新在计费网络推迟,超三天或用户请求可继续。 |
eSIM:从扫码到 LPA
CellularManager.process_callbacks() 由 UI tick 调用,执行后台结果回调,并在空闲时按 5 秒间隔读取 modem 状态和轮询 eUICC。_run_operation() 将下载、删除、命名、切换放入后台线程,以锁串行化 LPA 调用,完成后回到 UI 线程更新列表/错误。切换 profile 先乐观更新 UI,失败时触发重新轮询;_finish_poll() 对偶发 is_euicc=False 再确认一次,避免 AT 忙时立刻清空显示。这些是呈现策略,不代表 modem 已注册到新运营商网络。
平台接口是 LPABase,comma 设备的 HardwareComma.get_sim_lpa() 提供实体实现。LPA._acquire_channel() 先文件锁再打开 ISD-R 通道,结束时关闭;AtClient 负责串口 AT 命令、错误/超时与重连。parse_lpa_activation_code() 检查 LPA:...$...$... 基本格式,download_profile() 通过 ES9+ 依次认证服务端/客户端、获取 Bound Profile Package 并经 APDU 安装。EsimUI 侧非 Full Prime 才允许配置文件操作,添加时还要求设备网络类型非 none,这属于 UI 门、不是后端安全授权。
扫码页 QRScannerDialog 从座舱相机帧复制灰度 Y 平面给解码线程,避免底层视频缓冲被复用;显示时写 DisableDriverCameraIR=True、IsDriverViewEnabled=True,离开时写回 False,并没有保存/恢复进入前的原值。和其他座舱相机使用者并存时可能覆盖其意图,需在新硬件上做互斥/租约设计与测试。
常见问题
大屏设置里为什么找不到 eSIM 页?
因为大屏 NetworkUI 只创建共享 WifiManager,代码没有接入 CellularManager 或 eSIM 管理页;eSIM 配置文件列表与扫码添加位于 mici 的 EsimUI。
切换 Wi-Fi 时为什么新选的网络不会被旧查询覆盖?
因为用户操作会递增 _user_epoch,监听线程在慢 D-Bus 查询前后比较该值,避免旧查询结果覆盖用户刚选择的新网络。这是针对已知竞态的保护,不代表所有共享字段都有完整同步保证。
热点默认密码是固定的吗?
源码确认默认 PSK 来自 DEFAULT_TETHERING_PASSWORD,SSID 为 weedle 加设备 ID 前四位(若有),已有热点密码可由用户修改;因此不能据常量断定任意在用设备仍使用默认值,量产硬件应在首次启用前强制生成唯一强密码。
UI 上隐藏了蜂窝高级项就等于进程无权操作吗?
不等于。大屏用 PrimeType.NONE/LITE、mici 用 is_full_prime() 判定可见性,这属于 UI 门,不是安全授权;UI 显示隐藏不能保护直接调用 Params、D-Bus 或 LPA 的本地进程。
延伸阅读
- eSIM LPA 下载链路:120 字节分片与第 257 片序号回绕 —— 设置页 eSIM 操作最终落到的那条底层交易。
- 软件与开发者设置页:调试开关和权限门应该怎么摆 —— 设置页之间的权限与可见性对照。
- UI 运行时:界面线程与后台线程的边界 —— 为什么回调要排到 UI 线程执行。
- 设备身份注册:device ID 在车载链路里怎么用 —— 热点 SSID 里设备 ID 前四位的来源背景。
参考资料
- 项目 02 ——
openpilot-research/NETWORK_SETTINGS_ESIM_DEEP_DIVE.md(网络设置三层界面、Wi-Fi 状态机、热点与计费、eSIM 到 LPA 的链路)。 - 项目 02 —— 同目录记录:
test_handle_state_change.py的密码错误、忘记网络、自动连接与竞态用例,以及各屏布局未在本次运行验证的说明。
说明:本文为对固定源码快照的静态结构分析,未运行完整测试套件、未连接硬件、未验证目标车型的真实参数组合。文中所有阈值、行号索引与配置均出自源文档,标注为未验证的结论保留其原有边界。
更新记录
首次发布 2026-10-11 最近核对:2026-10-11
发表回复