# B-204 Android 语音通话迁移与构建验证

分支：`agent/agent/3a58b06e`
施工基线 HEAD：`2a817e77d899d7af66ebfecef382b55d872c1285`
越界对比基线：`e26abad`
本卡只做 Android 一对一语音通话迁移与可构建验证，不改 PC 通话、不部署、不发布、不换标。

**结论：Android debug/release 构建通过，状态机替身测试通过。缺有效账号、ARM64 真机和 LiveKit 实测，不得写成通话运行验收通过。**

## 配置入口

| 项 | 位置 | 值 |
|---|---|---|
| 开关 | `mobile-next/lib/company/feature_flags.dart` `FeatureFlags.livekitCall` | 跟随 `LiveKitCallConfig.enabled` |
| 配置门 | `mobile-next/lib/company/rtc/livekit_call_config.dart` | `shipped && endpointsReady` |
| LiveKit | `AppEndpoints.livekitUrl` | `ws://192.168.200.11:17880` |
| 换票 | `POST {authApiBase}/api/rtc_token` | Bearer `imToken`，body `{room, identity}` |
| 信令 | 官方 `customType` 200–204 仅在线自定义消息 | 未自建第二套信令，未改 SDK 核心 |

官方 `Apis.getTokenForRTC`（`/user/rtc/get_token` + `token` 头 + `serverUrl`）与账号服务不兼容。本卡用公司薄适配 `RtcTokenApi` 覆盖 `getRtcCertificate`，缺 `liveURL` 时回落到 `AppEndpoints.livekitUrl`。未改 `account-service/` 与现网端口。

`LiveKitCallConfig.shipped = true` 仅在本卡构建通过后保持开启。若关掉 `endpointsReady`（地址不是 `ws://` / `http`），入口自动隐藏。

## 提交主题（可逐项 `git revert`）

从新到旧回退：先本验收记录，再 Token 映射，再通话适配。

| 主题 | SHA | 文件 |
|---|---|---|
| 本验收记录 | 本文件当次提交 | `docs/acceptance/android-livekit-call.md` |
| 通话页 Token 对齐 | `d7f7a6aed213ad7250e7e0da28608d0e312859fb` | `apply_official_styles.dart` |
| 语音通话薄适配与入口 | `3832e080c4cba2ec9a76e1cb5b988bc06f0fd4a3` | `company/rtc/*`、`im_controller.dart`、聊天/资料入口、`openim_live` 超时/销毁/重连、测试 |

回退示例（新到旧）：

```
git revert <本验收提交>
git revert d7f7a6aed213ad7250e7e0da28608d0e312859fb
git revert 3832e080c4cba2ec9a76e1cb5b988bc06f0fd4a3
```

回退不影响 `mobile/`、`account-service/`、`config/`、`scripts/`、现网服务、PC 通话死代码与已发布安装包。

## 环境

- Flutter 3.24.5 / Dart 3.5.4（`C:\flutter324`）
- Temurin JDK 17.0.20+8（`toolchain\jdk17`）
- Android SDK：既有 B-95 工具链（build-tools 含 36.0.0，compileSdk 34）
- `org.gradle.jvmargs=-Xmx4096M`

## 检查与构建

### 状态机替身

```
cd mobile-next
flutter test test/company/rtc_token_mapper_test.dart test/company/call_session_guard_test.dart
```

11 项通过：换票解析（缺 liveURL 回落、`serverUrl`/`liveURL`、失败码）、占线、重复信令、超时取消、销毁释放计时器。

### Dart 定点

- `lib/company/rtc/**`、`feature_flags.dart`、`im_controller.dart`：**0 error**
- 全量 `flutter analyze`：**529 issues**。error 仍为存量（`local_plugin/flutter_download_manager/example` 等），本卡新增文件无 error。

### Android（真实构建）

```
flutter pub get
flutter build apk --debug
flutter build apk --release
```

| 构建 | 结果 | 大小 | SHA256 | 产物 |
|---|---|---|---|---|
| debug | 成功 | 109,405,761 B | `21487F23963DC60189C9181F3B985495ED6EFF7F74C619FCB114E5DB26B259F3` | `mobile-next/build/app/outputs/flutter-apk/app-debug.apk` |
| release | 成功 | 53,284,571 B | `AADFF25F62CC98D60D5EF0C94112C4388027107669A15743D2C1BB16F2245C78` | `mobile-next/build/app/outputs/flutter-apk/app-release.apk` |

元数据（aapt / apksigner，release）：包名 `io.openim.flutter.demo`，versionName `3.8.3` / versionCode `180`，minSdk 24 / targetSdk 33 / compileSdk 34，仅 `arm64-v8a`，label `F-DEMO`。签名 CN=openim，证书 SHA-256 `1f38a82fb3c33be7951fb5d964ef5bd9c6c9bfc084ae1e2a8a40a0d1f44ae1fe`。

构建期告警：Flutter Gradle apply-script 弃用；部分依赖 Kotlin metadata 1.8.0 vs 工程预期 1.6.0。均属上游，debug/release 均通过。APK 内嵌时间戳，哈希不可位级复现。

## 越界比对

相对 `2a817e77`：仅上表主题涉及的 `mobile-next/` 与本验收文档。

相对 `e26abad`：

- `mobile/`、`account-service/`、`config/`、`scripts/`：**零改动**
- `pc-client/src/store/`、`pc-client/src/layout/MainContentWrap.tsx`、发送/历史链路：**零改动**
- PC 未新增任何通话入口；RTC 死代码按 B-90 保留

## 已测 / 未测

已测（替身或构建）：

- 换票响应映射与失败文案
- 占线拒绝第二路、重复信令去重、30s 超时、页面销毁取消计时器
- Android debug/release 可构建
- 入口：单聊顶栏/工具箱/资料页随开关显示；仅音频；语音要麦克风权限，不要摄像头

未测（缺外部条件，禁止写成通过）：

- 测试账号：无有效工号+密码，未登录
- Android ARM64 真机：未安装、未点验呼叫/来电/接听/拒绝/取消/挂断
- LiveKit 进房、听筒/扬声器、静音、后台/锁屏返回、异常断开重连的真机表现
- 与旧 `mobile/` 或 PC 的互通（PC 本阶段不建设通话）

最小所需：管理员给两个测试工号；两台 ARM64 真机装 `app-release.apk`；确认 LiveKit `:17880` 与账号服务 `:10010/api/rtc_token` 可用后做一轮双向语音。

## 风险清单

- OpenIM 许可书面结论未落；未换标，label 仍 `F-DEMO`
- 官方 chat 换票协议与现网账号服务不兼容，依赖本卡薄适配；关掉开关即无入口
- LiveKit 使用官方 `livekit_client` 2.2.5 默认重连，真机未测
- APK 仍用上游 openim keystore
- 全量 analyze 存量错误仍在，不作为本卡运行通过依据

功能质检与视觉审核须另排。本卡不宣布语音通话实测通过。
