Files
Obsidian/项目文档/重汽/PX3 升级流程说明.md
T
2026-11-05 14:35:04 +08:00

422 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PX3 升级流程说明
> 范围:仅梳理 PX3 本地 MPU 升级和 MCU 升级
> 代码:`D:\zdCode\zhongqi_px3`
> 分支:`zhongqi_th7_px3_plus_security`,HEAD `8497c2da8`
## 1. 总体说明
PX3 当前有两条升级链路:
| 类型 | 触发方式 | 新版包 | 旧版兼容 |
| -------- | ------------ | ---------------- | ---------------------------- |
| MPU/系统升级 | 开机或介质挂载后自动扫描 | `update_mpu.zip` | 受属性控制的 `update.img` |
| MCU 升级 | 工程菜单手动触发 | `update_mcu.zip` | ZIP 不存在时兼容 `m3/cortex-*.bin` |
如果 `update_mpu.zip` 就走验签流程,如果有 `update.img` 就走老的升级流程,
PX3 计划保留的 MPU 新旧兼容与 RK3568 不同:
- RK3568:外层都叫 `update.zip`,根据包内是否存在 `ValidationFile.ini` 判断新旧格式。
- PX3 业务目标:按外层文件名分流,优先级是 `update_mpu.zip → update.img`。
- 当前 `1224` 源码尚未完全符合该目标:扫描函数仍主动检查 `update.zip`,独立裸 `update.img` 默认还会被属性开关拦截。
---
## 2. MPU 升级流程
### 2.1 触发和扫描
`RKUpdateReceiver` 在以下场景启动本地升级扫描:
| 场景 | 延时 |
|---|---:|
| 系统启动完成 `BOOT_COMPLETED` | 20 秒 |
| 存储介质挂载 `MEDIA_MOUNTED` | 7 秒 |
| 特定 USB 状态变化 | 5 秒 |
扫描目录:
```text
/data/media/0/
Environment.getExternalStorageDirectory()/
/mnt/external_sd0/
/mnt/usb_storage/
```
如果根目录没有命中,还会扫描 `/mnt/usb_storage` 的一级子目录。
### 2.2 MPU 业务目标优先级
| 优先级 | 文件 | 处理方式 |
|---:|---|---|
| 1 | `update_mpu.zip` | 解压、验签内层 `update.img`、产品检查、RK image 升级 |
| 2 | `update.img` | 属性允许时执行产品检查和 RK image 升级 |
| 3 | 其他文件 | 不处理 |
服务发现第一个候选文件后立即返回。
### 2.3 当前源码与目标的差异
当前 `getValidFirmwareImageFile()` 实际扫描顺序为:
```text
update_mpu.zip → OTA_PACKAGE_FILE(默认 update.zip)→ update.img
```
另外,`1224` 版本处理独立裸 `update.img` 时会读取:
```text
persist.sys.update.show
```
- 属性为 `false`:直接忽略裸 `update.img`。
- 属性为 `true`:才允许继续产品检查和升级。
- 当前仓库中,唯一把该属性设置为 `true` 的位置在 `update_mpu.zip` 处理分支。
- `1009`、`1010` 等早期压缩版本没有该属性拦截。
因此,当前源码不能直接表述为“已经实现 `update_mpu.zip → update.img` 两级兼容”。要达到业务目标,还需要停止扫描 `update.zip`,并明确恢复裸 `update.img` 的允许条件。
### 2.4 新版 `update_mpu.zip`
包结构:
```text
update_mpu.zip
├── update.img
└── ValidationFile.ini
```
`ValidationFile.ini` 当前读取:
```ini
sign=<十六进制 RSA 签名>
```
流程:
```text
检测到 update_mpu.zip
↓
设置 persist.sys.update.show=true
↓
解压到 update_mpu.zip 所在目录
↓
读取 update.img 和 ValidationFile.ini
↓
计算 update.img 的 SHA-256
↓
使用硬编码 RSA-2048 公钥和 SHA256withRSA 验签
↓
JNI 读取 update.img 产品名
↓
与 ro.product.model 比较
↓
弹出升级确认页
↓
用户确认
↓
RecoverySystem.installRKimage()
↓
写入 /cache/recovery/last_flag 和 command
↓
执行 sync,等待 2 秒
↓
重启 Recovery 安装 update.img
```
Recovery 命令:
```text
--update_rkimage=<update.img完整路径>
--locale=<当前语言>
```
当前验签失败分支缺少 `return`,仍会打开确认页,并把外层 `update_mpu.zip` 当成标准 OTA 包再次校验。这是现有缺陷,不是兼容流程。
### 2.5 旧版裸 `update.img`
```text
检测到 update.img
↓
读取 persist.sys.update.show
├── false:忽略,不弹确认页
└── true:继续
↓
检查镜像产品名是否匹配 ro.product.model
↓
用户确认
↓
installRKimage()
↓
重启 Recovery 安装
```
裸 `update.img` 没有新增 RSA 验签,只做产品名检查。
### 2.6 MPU 结果处理
服务下次启动时读取:
```text
/cache/recovery/last_flag
```
| 标记 | 含义 |
|---|---|
| `success$path=...` | Recovery 报告升级成功 |
| `updating$path=...` | 上次升级未形成成功状态 |
当前结果流程尚未闭环:
- 成功/失败提示 Activity 的启动代码被注释。
- 没有统一结果广播。
- 升级包删除没有稳定执行。
- 新版包验签后用户取消,升级锁没有释放。
- Recovery 写回 `last_flag` 的实现不在当前项目中。
---
## 3. MCU 升级流程
### 3.1 页面入口
```text
Pro 版本设置页
↓
在硬件版本位置 3 秒内连续点击 10 次
↓
工程菜单 ProjectMenuActivity
↓
升级菜单 UpgradeMainActivity
↓
选择“M3程序升级”
↓
ZhongQiM3UpdateActivity
↓
用户确认
↓
IM0ManagerService.sendUpdateRequest(type, flashType)
↓
ImplM0ManagerService
↓
MonitorM0Update.sendUpdateRequest()
```
升级通过 Binder 发起。广播只用于 MCU 返回最终结果后通知页面。
### 3.2 介质和文件
| 介质 | `flashType` | 文件路径 |
|---|---:|---|
| U 盘 | `0` | `/mnt/usb_storage/update_mcu.zip` |
| TF 卡 | `1` | `/mnt/external_sd0/update_mcu.zip` |
当前正常 UI 没有传入 `FLASHTYPE=1`,所以实际默认走 U 盘。
代码保留 TF 卡分支,但页面状态检测历史上使用 `/mnt/external_sd`,通讯服务读取 `/mnt/external_sd0`,两边路径不一致。
`update_mcu.zip` 只在介质根目录查找,不递归搜索子目录。
### 3.3 包结构
```text
update_mcu.zip
├── cortex-gdm4.bin
├── cortex-m3.bin
└── ValidationFile.ini
```
根据设备类型,实际刷写文件是 `cortex-gdm4.bin` 或 `cortex-m3.bin`。
### 3.4 解压和验签
```text
开始 sendUpdateRequest()
↓
删除 /sdcard/zhongqi_mcu_update/
↓
解压 update_mcu.zip 到该目录
↓
根据 MCU 版本选择实际目标固件
↓
计算 SHA-256
↓
读取 ValidationFile.ini 中的 sign
↓
使用硬编码 RSA-2048 公钥和 SHA256withRSA 验签
├── 失败:返回 -2
└── 成功:选择实际固件并进入协议传输
```
当前算法先计算文件 SHA-256,再把摘要传给 `SHA256withRSA`,实际相当于:
```text
RSA-Sign(SHA256(SHA256(payload)))
```
制包工具必须使用相同的双重摘要方式。
### 3.5 固件选择
页面选择升级类型:
| MCU 版本 | 升级类型 |
|---|---:|
| `110px02m3`、`110rv01m3` | `0x01` |
| `ae6501` | `0x0F` |
| 其他版本 | `0x1F` |
通讯服务选择实际文件:
| MCU 版本 | 实际传输文件 |
|---|---|
| `110px02m3`、`110rv01m3` | `cortex-m3.bin` |
| 其他情况 | `cortex-gdm4.bin` |
当前工作区已改为使用同一个目标文件名完成验签和传输:M3 设备验签并传输 `cortex-m3.bin`,其他设备验签并传输 `cortex-gdm4.bin`。页面按 `-` 分割版本字符串后判断,通讯服务直接比较原始版本字符串,这个差异仍需后续统一。
### 3.6 MCU 协议
| 消息号 | 名称 | 作用 |
|---:|---|---|
| `0xE1` | `MSG_REQUEST_UPDATE` | MCU 主动请求升级 |
| `0xE2` | `MSG_SEND_UPDATE_REQUEST` | 主机发送固件总长度 |
| `0xE3` | `MSG_REQUEST_UPDATE_DATA` | MCU 按偏移请求固件数据 |
| `0xE4` | `MSG_RECV_UPDATE_RESULT` | MCU 返回最终结果 |
主时序:
```text
Android 完成解压和验签
↓
E2:主机发送固件总长度
↓
MCU 返回状态和最大分包长度
↓
E3:MCU 发送 offset(4B) + length(2B)
↓
主机按偏移读取并返回固件数据
↓
重复 E3 直到传输完成
↓
MCU 校验并执行升级
↓
E4:MCU 返回最终结果
↓
主机回复 ACK
↓
通讯服务发送 ACTION_M0_UPDATE_FILE 广播
↓
升级页面显示结果
```
收到 MCU 主动 E1 后,当前代码会再次删除临时目录、解压、验签并发送 E2,可能与已经开始的 Binder 升级会话冲突。
### 3.7 状态码
E2 阶段:
| 返回值 | 含义 |
|---:|---|
| `-2` | ZIP 不存在、验签失败或目标固件不可读 |
| `-1` | 发送失败或等待响应超时 |
| `0` | MCU 不支持升级 |
| `1` | MCU 已准备好,开始传输 |
| `2` | 固件长度超限 |
E4 最终结果:
| 返回值 | 含义 |
|---:|---|
| `1` | 升级成功 |
| `2` | 固件校验和错误 |
| `3` | 版本错误或固件非法 |
| `4` | 文件或数据获取失败 |
E2 成功响应当前没有检查至少包含 3 字节,截断响应可能导致数组越界。
### 3.8 MCU 新旧兼容
历史裸固件路径:
```text
<介质根目录>/m3/cortex-m3.bin
<介质根目录>/m3/cortex-gdm4.bin
```
当前工作区已恢复历史裸固件路径。
| 输入 | 当前行为 |
|---|---|
| 合法 `update_mcu.zip` | 解压、验签、传输 |
| 非法 `update_mcu.zip` | 返回失败,不回退裸 bin |
| ZIP 不存在,存在 `m3/cortex-m3.bin` | M3 设备使用旧版文件 |
| ZIP 不存在,存在 `m3/cortex-gdm4.bin` | GDM4 设备使用旧版文件 |
| ZIP 与裸 bin 同时存在 | 只处理 ZIP |
PX3 当前分支不包含 RK3568 的 `446193a73` 新旧包兼容提交。
---
## 4. 当前待完善点
### MPU
1. 将未跟踪的 `ZQ_TH7_RKUpdateService` 纳入正式源码和构建基线。
2. 从业务扫描流程中移除 Rockchip 遗留 `update.zip` 分支。
3. 明确恢复裸 `update.img` 时对 `persist.sys.update.show` 的处理方式。
4. 验签失败后立即结束,不得回退裸 `update.img`。
5. 使用安全解压和私有暂存目录,保证验签文件与安装文件一致。
6. 补齐取消、异常、Recovery 结果和升级包清理流程。
### MCU
1. 统一页面和通讯服务的 MCU 版本解析。
2. 增加升级会话互斥,避免 E1 重复初始化。
3. 修复 ZIP 路径校验、共享目录替换和文件流关闭问题。
4. 明确裸 bin 兼容允许使用的工程授权场景。
---
## 5. 关键代码
### MPU
| 文件 | 行号 | 说明 |
|---|---:|---|
| `ZQ_TH7_RKUpdateService/src/android/rockchip/update/service/RKUpdateReceiver.java` | 53 | 本地升级触发 |
| `ZQ_TH7_RKUpdateService/src/android/rockchip/update/service/RKUpdateService.java` | 328 | MPU 本地升级主流程 |
| 同上 | 498 | 文件扫描和优先级 |
| `ZQ_TH7_RKUpdateService/src/android/rockchip/update/service/ZipFileVerifier.java` | 86 | MPU 解压和验签 |
| `ZQ_TH7_RKUpdateService/src/android/rockchip/update/service/UpdateAndRebootActivity.java` | 159 | 选择 RK image 或 OTA 模式 |
| `ZQ_TH7_RKUpdateService/src/android/rockchip/update/service/RecoverySystem.java` | 354 | 安装 `update.img` |
### MCU
| 文件 | 行号 | 说明 |
|---|---:|---|
| `YX_MainInterface_ZhongQi/src/main/java/com/yaxon/base/system/UpgradeMainActivity.java` | 111 | MCU 升级入口 |
| `YX_MainInterface_ZhongQi/src/main/java/com/yaxon/base/system/ZhongQiM3UpdateActivity.java` | 321 | 发起 Binder 调用 |
| `YX_CommunicatePro_ZhongQi/src/main/java/com/yaxon/telematics/service/ImplM0ManagerService.java` | 30 | AIDL 转发 |
| `YX_CommunicatePro_ZhongQi/src/main/java/com/yaxon/telematics/service/protocol/monitor/m0/MonitorM0Update.java` | 75 | E1~E4 协议处理 |
| 同上 | 203 | 查包、验签和 E2 请求 |
| 同上 | 264 | 实际固件选择 |
| `YX_CommunicatePro_ZhongQi/src/main/java/com/yaxon/utils/ZipFileVerifier.java` | 80 | MCU 解压和验签 |
## 6. 代码状态说明
- MPU 升级目录 `ZQ_TH7_RKUpdateService` 当前未纳入 Git。
- 当前源码实际扫描三种文件;业务目标只保留 `update_mpu.zip` 和 `update.img`。
- MCU 签名升级由 `aeb1d9c9e`、`897dc0607` 两笔提交引入。
- 2026-08-19 当前工作区恢复 `m3/cortex-m3.bin`、`m3/cortex-gdm4.bin` 旧版兼容,并改为验签实际传输的 ZIP 内固件。
- 本文只描述 MPU 和 MCU 本地升级,不包含其他升级类型。
不签名: 新版本 - >旧版本