fix(control_plane): bound provisioning recovery and prevent reboot loops

This commit is contained in:
chenjintao
2026-10-23 15:37:59 +08:00
parent aede687dc9
commit 415c75b0e6
12 changed files with 947 additions and 57 deletions
@@ -160,6 +160,7 @@ ERROR <reason>
| `set_device_id <value>` | 临时设置设备 ID |
| `set_serial_number <value>` | 临时设置序列号 |
| `save_config` | 提交并保存两个身份字段 |
| `retry_provisioning` | 配网故障修正后请求重新尝试;不重置内存恢复次数 |
| `get_auth_material` | 生成或加载设备密钥,返回公开材料 |
| `auth_sign_challenge <id> <challenge>` | 使用设备私钥签署工厂提供的 Challenge |
| `auth_prepare_rotate <version>` | 生成新密钥并准备轮换授权签名 |
@@ -178,6 +179,10 @@ get_device_info
### 3.2 `set` 和 `save_config` 的区别
身份缺失时设备保持运行并等待写入,不再作为低内存故障重启。`save_config` 成功后
通知 Wi-Fi 协调器重新检查身份并恢复配网;串口处理函数不直接执行 BLE 初始化。
错误分类、有限退避及恢复次数见 [配网失败处理与内存恢复](../配网失败处理与内存恢复.zh-CN.md)。
`set_device_id` 和 `set_serial_number` 只将值暂存在 RAM:
```c
@@ -0,0 +1,92 @@
# 配网失败处理与内存恢复
更新日期:2026-10-23。本文描述源码策略与主机验证,不代表新板实机验收。
## 故障背景
新板启动日志显示内部 RAM 空闲 135,167 B、最大 DMA 连续块 73,728 B,均高于
当前配网门槛 49,152 B / 12,288 B。设备身份缺失返回 `ESP_ERR_NOT_FOUND`,
发生在 BLE 初始化之前。旧控制层将所有重新配网错误送入低内存恢复重启,并将
空快照打印为三个零。恢复启动再次失败后清除恢复模式,又被运行时触发重启。
因此该样本不能证明 BLE 内存不足,也不能证明身份补齐后的 BLE 内存一定充足。
## 当前职责与流程
- `domain.h` 是配网语义及 `domain_wifi_provisioning_start_status_t` 的唯一声明点,
对应 AGENTS 所有权矩阵的“小程序 Wi-Fi 配网语义”一行。未增加新契约家族或改变所有权。
- `domain_runtime_mini_program_provisioning_preflight()` 只检查身份,无 BLE 初始化副作用。
`domain_runtime_mini_program_provisioning_start()` 同步启动,必须传入结果输出指针;
失败返回错误码及类型化原因,domain/platform 不后台重试。
- `control_plane_connectivity_runtime.c` 持有唯一配网协调策略,所有共享字段在
`s_provisioning_lock` 下访问;资源操作、通知和日志在锁外执行。
- 私有 `control_plane_provisioning_policy.h` 只实现等待、退避和恢复次数决策。
Wi-Fi worker 轮询协调器,不新增任务、定时器或上层状态镜像。
```text
尝试被允许 → 身份检查 → 有界暂停语音 → 停止 STA → 内存检查 → BLE 启动 → 配网 worker
↓ ↓ ↓
等待身份 按实际原因处理 仅内存错误允许一次恢复启动
```
| 原因 | 策略 | 如何继续 |
| --- | --- | --- |
| 身份缺失 | 等待身份,不停现有音频,不自动重试、不重启 | 成功 `save_config` 后由 Wi-Fi worker 重新检查 |
| 无效参数、不支持的配置 | 停止自动尝试 | 修正后 `retry_provisioning` |
| 明确临时超时 | 首次失败后依次等待至少 1、3、10 秒,最多三次额外尝试 | 超限后等待显式重试 |
| 未分类启动错误 | 停止自动尝试,不猜测为内存故障 | 根据日志排查后显式重试 |
| 内存门槛不满足或明确分配失败 | 本轮最多一次恢复重启 | 恢复启动仍失败则保持运行,等待排查 |
| Wi-Fi 密码错误或连接超时 | 保持 BLE,通知小程序连接失败 | 小程序重新提交凭据 |
| BLE teardown 失败 | 暂停云语音/HTTPS,保留待释放义务 | 显式重试先有界完成旧实例 teardown,成功后才能重启 BLE |
| 配网被停止或取消 | 有界释放,不自动重新进入配网 | 显式重试 |
退避由 worker 轮询驱动,1/3/10 秒是最短间隔,不是精确唤醒时间。
只有明确 `ESP_ERR_TIMEOUT` 被归为临时失败;未知错误不自动循环。
内存采样 API 本身失败属于诊断失败,不触发内存恢复重启。
## 恢复次数与日志
`recovery_boot` 表示当前采用精简启动;`recovery_attempted` 表示该恢复链已用过重启。
RTC 一次性请求在恢复启动消费后锁存 attempted。恢复失败退出精简启动时,不清除
attempted;`save_config`、显式重试及再次消费请求也不重置它。
恢复配网成功并完成 BLE teardown 后,仍按原流程重启进入完整正常运行时;
这是成功后的模式切换,不是失败重试。普通启动配网成功直接恢复运行时,无需重启。
启动失败日志包含 `stage`、`error`、`reason`、`retry_ms`、`recovery_attempted` 和
`voice_paused`。内存日志使用实际采样;采样失败明确显示 unavailable / unknown,
不能把缺少快照打印为内存零值。
## 等待期间的资源与产测命令
重新配网不再设置全局运行时停止标志,因此 Wi-Fi 协调、屏幕、电池、按键与
产测串口继续工作。身份检查在暂停资源之前完成。若已暂停语音后才失败,设备
明确进入离线等待/退避,云语音保持暂停,避免与 BLE 重试争抢内存;OTA、财运
HTTPS 和语音启动入口遵守此门禁。BLE 成功 teardown 或其他 STA 恢复路径解除门禁。
若旧 BLE 尚未成功释放,STA 即使已连接也不能直接恢复云服务。
```text
get_device_info
set_device_id <后台分配的实际设备ID>
set_serial_number <实际序列号>
save_config
get_device_info
```
`set_*` 只暂存 RAM,`save_config` 成功持久化后才发布配置通知;失败不发布。
CLI 不直接停资源或初始化 BLE。新命令 `retry_provisioning` 只请求协调器重新尝试,
不写入设备身份、不删除 Wi-Fi 凭据,也不重置内存恢复次数。Wi-Fi 已正常连接且
没有待清理 BLE 时继续正常联网,不强制中断连接进入配网。
## 验证
```bash
python3 tools/test_provisioning_recovery.py
```
该主机测试提取并编译实际领域启动、控制层协调、恢复请求消费、配网 worker 和
产测保存函数,使用 RTOS/BLE/存储故障桩及 ASan/UBSan。覆盖身份等待、失败与成功保存、
错误分类、退避与计时回绕、内存门槛边界和分配失败、恢复失败不再重启、未知快照日志、
停止超时、Wi-Fi 失败后继续接收凭据、成功恢复运行时、teardown 失败、取消和启动门超时。
固件使用 ESP-IDF 5.5.4 构建。实机仍需验证空身份持续运行、身份写入后的 BLE 广播、
错误/正确 Wi-Fi 凭据、连接后的 BLE 释放与语音恢复,并记录 BLE 真正初始化前后的
内部 RAM、最大 DMA 连续块及并行负载。主机故障注入不替代这些设备证据。