# QSJNIC MCU OTA 升级完整操作流程

> 更新时间：2026-07-02
> 适用版本：Service 1.0.0-beta11 及之后（AIDL surface version >= 2）
> 旧版 `/data/system/mcu_app.bin` 方式已废弃，请勿使用。

---

## 前置条件

- 车机已连接 ADB：`adb connect <IP>:5555`
- 已安装最新版 OTA Demo：`com.qsjnic.demo.ota`（含 `pushUpgradeImage` / `startUpgradeFromLocal` 接口）
- 已安装最新版 Service：`com.qsjnic.mcu.service`（含 `pushUpgradeImage` / `startUpgradeFromLocal` 实现）
- QSJNIC Service 正常运行在 User 0，且只有一个进程

---

## 第一步：确认 Service 状态

```bash
adb shell ps -ef | grep com.qsjnic.mcu.service
```

预期只有 1 个进程，用户为 `system`：

```text
system  ...  com.qsjnic.mcu.service
```

如果看到多个 service 进程，先卸载旧版 Demo 或重启车机。

---

## 第二步：确认 OTA Demo 版本

```bash
adb shell dumpsys package com.qsjnic.demo.ota | grep -E 'versionName|versionCode'
```

应包含新接口的版本。若未安装或版本过旧，请先安装最新版 APK：

```bash
adb install -r demos/ota-demo/app/build/outputs/apk/debug/qsmw-ota-demo-debug.apk
```

---

## 第三步：准备 MCU 固件（二选一）

OTA Demo 运行在 **User 10**，bin 最终必须落到 OTA Demo 的私有 files 目录，
再通过 `ParcelFileDescriptor` 传给 Service，避免 `/data/system/` 的 SELinux 问题。

当前 Demo 默认支持**从 URL 下载固件并自动触发升级**，无需 ADB 推送文件，也无需额外文件读写权限。

### 方式一：通过 OTA Demo 内置 URL 下载（推荐）

1. 打开 OTA Demo。
2. URL 输入框默认已填入：

   ```
   http://wtb.anzhitek.com/ota/0701/mcu_app.bin
   ```

   可按实际情况修改。
3. 点击 **Download & Start OTA**。
4. Demo 内部会：
   - 下载 bin 到 `getFilesDir()/mcu_app.bin`
   - 用 `ParcelFileDescriptor.open()` 打开文件
   - 调用 `McuClient.getInstance().upgrade().pushUpgradeImage(pfd, size)`
   - 调用 `McuClient.getInstance().upgrade().startUpgradeFromLocal()`

> 该方式只需要 `android.permission.INTERNET` 权限，已在 `AndroidManifest.xml` 中声明。

### 方式二：通过 ADB 推送到 OTA Demo 私有目录

若车机无法访问外网，可手动把 bin 推到 Demo 的私有目录。

#### 3.1 创建目录并推送

```bash
adb shell mkdir -p /data/user/10/com.qsjnic.demo.ota/files
adb push /Users/admin/workspace/jyq/MCU/0701/mcu_app.bin /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
```

#### 3.2 设置属主为 OTA Demo 的 User 10 用户

先查看 OTA Demo 在 User 10 的实际 UID/GID：

```bash
adb shell ls -ld /data/user/10/com.qsjnic.demo.ota
# 或
adb shell ps -ef | grep com.qsjnic.demo.ota
```

然后执行：

```bash
# 目录也必须属主为 Demo 用户，否则应用无法创建/删除文件
adb shell chown u10_aXXX:u10_aXXX /data/user/10/com.qsjnic.demo.ota/files
adb shell chmod 700 /data/user/10/com.qsjnic.demo.ota/files

adb shell chown u10_aXXX:u10_aXXX /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
adb shell chmod 644 /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
```

> `u10_aXXX` 请替换为实际查到的 UID/GID。
> 如果 `files` 目录被 `adb shell mkdir` 创建成了 `root:root`，应用即使拥有文件也删不掉它，会导致 `EACCES / cannot clear existing bin`。

#### 3.3 验证文件

```bash
adb shell ls -la /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
```

预期：

```text
-rw-r--r-- 1 u10_aXXX u10_aXXX 209232  ...  /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
```

> 注意：当前 Demo UI 默认通过 URL 下载按钮触发升级。若使用 ADB 推送，需要自行调用升级接口或在 Demo 中添加触发逻辑。

---

## 第四步：打开 OTA Demo 并开始升级

```bash
adb shell am start -n com.qsjnic.demo.ota/.MainActivity
```

- 若使用**方式一（URL 下载）**，直接点击 **Download & Start OTA** 即可。
- 若使用**方式二（ADB 推送）**，需要触发 `pushUpgradeImage` → `startUpgradeFromLocal` 流程。

OTA Demo 内部流程：

1. 从 `getFilesDir()` 读取 `mcu_app.bin`
2. 用 `ParcelFileDescriptor.open()` 打开文件
3. 调用 `McuClient.getInstance().upgrade().pushUpgradeImage(pfd, size)`
4. 调用 `McuClient.getInstance().upgrade().startUpgradeFromLocal()`

---

## 第五步：观察升级日志

```bash
adb shell logcat -s QsMcuSystemService:* McuClient:* McuUpgrade:* | grep -iE 'upgrade|ota|update|push|project|magic'
```

正常应看到：

```text
UpgradeManager: pushed upgrade image to ... (209232 bytes)
UpgradeManager: starting upgrade ... project=A1A2A1A0A6
McuUpgrade: [UPGRADE] upgrade.req acked
McuUpgrade: [UPGRADE] upgrade.begin acked
McuUpgrade: [UPGRADE] chunk=1/205 ...
McuUpgrade: [UPGRADE] chunk=2/205 ...
...
McuUpgrade: [UPGRADE] upgrade.end acked
```

---

## 第六步：升级完成后验证版本

1. 等待 MCU 自动重启（约几秒到几十秒）。
2. 打开 Vehicle Demo：

```bash
adb shell am start -n com.qsjnic.demo.vehicle/.MainActivity
```

3. 查看 **HW Version** 字段是否为新版本号，例如：

```text
12106_260630_0001
```

---

## 常见问题

| 现象 | 可能原因 | 处理 |
|---|---|---|
| 点击升级无反应 | bin 路径错误或文件不存在 | 检查 `/data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin`；若使用 URL 下载，检查网络与 URL |
| 网络下载失败 | 车机无外网或 URL 不可达 | 切换到方式二 ADB 推送 |
| Permission denied | 属主不是 OTA Demo 的 User 10 | 执行 `chown u10_aXXX:u10_aXXX` |
| cannot clear existing bin | `files` 目录属主是 root，应用无法删除旧 bin | 对 `files` 目录执行 `chown u10_aXXX:u10_aXXX && chmod 700` |
| magic 校验失败 | bin 文件不是目标 MCU 固件 | 确认 `0x2418` magic 是否为 `D4 D3 D5 DE EA D9 E8` |
| project 不匹配 | bin 项目号与当前 MCU 不一致 | 确认 `0x2430` project bytes 是否正确 |
| 两个 service 进程 | 旧版 Demo 导致 | 先卸载旧 Demo，重装新版 |
| OTA 启动后 MCU 无响应 | MCU 当前固件不支持新 OTA 协议 | 先刷入可支持 OTA 的 MCU 基线版本 |

---

## 完整命令汇总

```bash
# 1. 连接车机
adb connect 192.168.0.102:5555

# 2. 确认状态
adb shell ps -ef | grep com.qsjnic.mcu.service
adb shell dumpsys package com.qsjnic.demo.ota | grep -E 'versionName|versionCode'

# 3. 方式一（推荐）：打开 Demo 后从 URL 下载并升级
adb shell am start -n com.qsjnic.demo.ota/.MainActivity
# 然后在车机屏幕上点击 "Download & Start OTA"

# 3. 方式二（离线）：推送 bin 到 OTA Demo 私有目录
adb shell mkdir -p /data/user/10/com.qsjnic.demo.ota/files
adb push /Users/admin/workspace/jyq/MCU/0701/mcu_app.bin /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
adb shell chown u10_aXXX:u10_aXXX /data/user/10/com.qsjnic.demo.ota/files
adb shell chmod 700 /data/user/10/com.qsjnic.demo.ota/files
adb shell chown u10_aXXX:u10_aXXX /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
adb shell chmod 644 /data/user/10/com.qsjnic.demo.ota/files/mcu_app.bin
```

方式二推送后，需要在 Demo 中自行触发升级逻辑。
