Files

422 lines
12 KiB
Markdown
Raw Permalink Normal View History

2026-11-05 14:35:04 +08:00
# 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 本地升级,不包含其他升级类型。
不签名: 新版本 - >旧版本