Files
Obsidian/项目文档/同力/同力老项目蓝牙/蓝牙模块完整代码分析.md
T

580 lines
23 KiB
Markdown
Raw Normal View History

2026-11-05 14:35:04 +08:00
# 同力老项目蓝牙模块完整代码分析
> 项目路径:`F:\zdCode\tongli_old`
> 核心模块:`YX_BlueTooth`
> 分析日期:2026-07-13
> 分析方式:静态代码分析,未修改项目源码,未执行真机蓝牙测试
## 1. 总体结论
`YX_BlueTooth` 是一个拥有系统 UID 的独立车机蓝牙 APK,核心能力包括设备管理、HFP 蓝牙电话、A2DP/AVRCP 蓝牙音乐、PBAP 通讯录和通话记录同步,以及语音、仪表、音频通道、GPIO MIC、系统休眠唤醒等整车联动。
当前实际底层链路是:
```text
业务 UI / 广播 / AIDL
↓
UseCase / Presenter / Repository
↓
BtAdapterManager(约 4200 行的核心总控)
↓
GocJar(外部 GOC 蓝牙服务客户端)
↓
外部蓝牙系统服务 / 蓝牙芯片
↓
HFP / SCO / A2DP / AVRCP / PBAP 回调
```
旧的 `BtAdapter_NF2311` JNI 适配层仍保留大量 native API 和旧回调代码,但加载 native 库、初始化及主要调用均已注释。当前不能把旧 NF2311 回调中的功能等同于现行 GOC 链路已支持。
### 1.1 功能成熟度判断
| 功能 | 代码状态 | 结论 |
|---|---|---|
| 蓝牙开关、扫描、配对、连接 | 主链路存在 | 可用,但存在回调过早、配对时序和状态一致性风险 |
| 单方 HFP 电话 | 主链路较完整 | 拨号、来电、接听、挂断、拒接、DTMF、MIC、音频切换基本具备 |
| 三方通话 | 有 UI、状态和旧回调残留 | **不能认定为完整支持**,新 GOC 回调链未形成可靠闭环 |
| 蓝牙音乐 | 主链路完整 | A2DP 出声、AVRCP 控制、ID3、进度、音频焦点均具备 |
| 通讯录/通话记录 | 主链路完整 | PBAP 下载、本地缓存、搜索和 Provider 查询均具备 |
| 语音与整车联动 | 多条链路存在 | 功能丰富,但广播、全局状态和服务绑定耦合较重 |
## 2. 工程定位与依赖
- 包名:`com.yaxon.bluetooth`
- 系统身份:`android:sharedUserId="android.uid.system"`
- 最低 Android:API 19(Android 4.4)
- 编译/目标 SDK:27
- 代码规模:约 160 个 Java 文件、23,911 行
- 自动化测试:未发现 `src/test` 或 `src/androidTest`
主要依赖:
- `goc.jar`:当前蓝牙协议栈和外部服务接口
- `EventBus 2.4.0`:模块内事件分发
- `LitePal 1.3.1`:配对设备、通讯录、通话记录持久化
- `classes_44new.jar`:Android framework 隐藏 API
- `yx_skin`:项目 UI/皮肤依赖
主要源码入口:
- `YX_BlueTooth/src/main/java/com/yaxon/bluetooth/YXApplication.java`
- `YX_BlueTooth/src/main/java/com/yaxon/bluetooth/nf2311/BtAdapterManager.java`
- `YX_BlueTooth/src/main/java/com/yaxon/bluetooth/main/MainActivity.java`
- `YX_BlueTooth/src/main/AndroidManifest.xml`
## 3. 启动和初始化
```text
YXApplication.onCreate()
├─ 初始化 SharedPreferences / LitePal
├─ 绑定语音切换服务
├─ 绑定音频通道服务
├─ 绑定 GPIO 服务
├─ 绑定仪表音乐状态服务
├─ BtAdapterManager.moduleInit()
│ ├─ GocJar.init(context)
│ ├─ 注册 Adapter/设置回调
│ ├─ 注册 HFP/SCO 电话回调
│ └─ 注册 A2DP/AVRCP/ID3 回调
└─ 启动 OpenBlueToothService
```
`OpenBlueToothService` 根据持久化的用户期望状态决定是否开蓝牙。这里存在冷启动竞态:如果 GOC AIDL 服务尚未连接,代码重新调用 `GocJar.init()` 后没有可靠的排队与重试,服务很快结束,可能出现“配置为开,实际未开”。
## 4. 模块划分
| 模块 | 主要职责 | 关键类 |
|---|---|---|
| 应用总控 | 初始化、服务绑定、全局状态 | `YXApplication`、`ServiceBindManage` |
| 底层适配 | GOC 命令、Profile 回调、状态汇聚 | `BtAdapterManager`、`GocJar` |
| 蓝牙设置 | 开关、扫描、配对、连接、名称、PIN | `BluetoothSettingPresenter`、Setting Fragments |
| 电话 | 拨号、来电、通话控制和通话 UI | `KeyBoardFragment`、`PhoningActivity`、`PhoningPresenter` |
| 音乐 | AVRCP 控制、ID3、进度和页面 | `MusicFragment`、`AudioFocusManager` |
| 通讯录 | PBAP、搜索、本地缓存、Provider | `PhoneBookRepository`、`BluetoothPhonebookProvider` |
| 通话记录 | PBAP 通话记录、本地缓存、列表 | `CallRecordRepository`、`PhoneRecordFragment` |
| 外部联动 | 语音、ITS、仪表、本地音乐、休眠 | 多个 Receiver、AIDL Service、`music_sdk` |
## 5. 设备管理功能
### 5.1 蓝牙开关
```text
Setting UI
→ BluetoothSettingPresenter.openBluetooth()/closeBluetooth()
→ SwitchBluetooth UseCase
→ BtAdapterManager.setBluetoothPower(on, callback)
→ GocJar.setBtEnable(boolean)
→ onEnableChanged(boolean)
→ handleBluetoothOpened()/handleBluetoothClosed()
→ 设置图标、广播、扫描和 UI 回调
```
问题在于 `setBluetoothPower()` 将 `GocJar.setBtEnable()` 的命令受理结果当作最终状态,立即回调成功并执行 opened/closed 处理,没有等待 `onEnableChanged()`。真实回调到达时还可能再次执行,形成成功回调重复、语音回复成功但底层状态未完成等问题。
### 5.2 扫描
```text
Presenter.inquiryDevice()
→ InquiryDevice
→ BtAdapterManager.inquiry()
→ GocJar.startBtDiscovery()/cancelBtDiscovery()
→ onAdapterDiscoveryStarted/Finished
→ onDeviceFound
→ LinkedInfoRepository
→ EventBus 刷新附近设备列表
```
扫描状态保存在 `YXApplication.mBlueToothInquiryStatus`。设备数据同时存在于 GOC 实际列表、Repository 内存和 LitePal 数据库中。
### 5.3 配对和取消配对
附近设备点击后,Presenter 当前代码倾向于先发起 HFP 连接,再根据状态决定是否显式配对。由于连接结果异步,检查时 `mBlueToothConnect` 通常尚未变化,显式 `requestBtPair()` 可能不会执行。该时序依赖底层“连接自动触发配对”的容错能力。
取消配对先删除本地数据,再异步下发远端 unpair,失败时没有回滚,会造成车机列表与底层配对列表不一致。
### 5.4 连接和断开
```text
ConnectMacDevice
→ 设置 mConnectingMac / mLinkingMac
→ requestHfpConnect(mac)
→ onHfpStateChanged(CONNECT_SUCCESSED)
→ 更新 mLinkedMac、Repository、广播、回调
```
总连接状态主要由 HFP 成功置位,因此 A2DP-only 情况不能可靠代表“已连接”。断开操作依赖 `mLinkedMac`,但该值主要在 HFP 成功后写入;进程恢复、状态丢失或仅媒体连接时可能为空或过期。
### 5.5 名称和 PIN
代码支持读取/设置本机蓝牙名称和 PIN。当前实现中:
- 已有名称偏好时,部分 `getBluetoothName()` 路径返回空串而不是偏好值。
- `setBluetoothPassWord()` 未可靠透传远端执行结果,调用侧可能收到伪成功。
## 6. HFP 蓝牙电话
### 6.1 页面与功能
- `KeyBoardFragment`:号码输入、删除、拨号、通话记录回拨。
- `PhoningActivity`:来电、去电、通话中 UI,计时、接听、拒接、挂断、MIC、手机/车机音频切换、DTMF、小键盘、三方提示。
- `PhoningPresenter`:通过 UseCase 把 UI 命令放入后台执行。
- `RingManager`:铃声音频焦点和系统来电标记;但 `startRing()` 未发现业务调用,本地铃声并未真正启动。
### 6.2 电话调用链
拨号:
```text
KeyBoardFragment
→ Dial UseCase
→ BtAdapterManager.dial(number)
→ GocJar.requestHfpDialCall(mac, number)
```
来电/去电状态:
```text
GOC onHfpCallChanged(mac, call)
→ 保存 Settings.System.BT_CALL_STATE
→ 映射为项目旧 phoneStatus
→ processCallStatus()
→ actionStart()
→ 启动/刷新 PhoningActivity
→ EventBus、广播、仪表状态上报
```
控制操作:
| 功能 | 底层命令 |
|---|---|
| 接听 | HFP accept/pick up |
| 挂断 | HFP hang up |
| 拒接 | HFP reject |
| DTMF | `requestHfpSendDtmf(mac, value)` |
| MIC 静音 | `GocJar.muteHfpMic()` |
| 转手机 | `requestHfpAudioTransferToPhone()` |
| 转车机 | `requestHfpAudioTransferToCarkit()` |
SCO(通话音频链路)状态通过回调更新 `scoState`。通话激活和结束还会调用 GPIO 服务切换物理 MIC 通道。
### 6.3 电话状态模型
新 GOC 回调大致映射为:
```text
IDLE / TERMINATED
├─ DIALING → phoneStatus=4 → 去电
└─ INCOMING → phoneStatus=5 → 来电
DIALING / INCOMING
→ ACTIVE → phoneStatus=6 → 通话中
ACTIVE
→ TERMINATED → phoneStatus=3 → 挂断
```
通话 UI 同时使用另一套 `CurrentCalls` 语义:
| 值 | 含义 |
|---|---|
| 0 | ACTIVE |
| 1 | HELD |
| 2 | DIALING |
| 3 | ALERTING |
| 4 | INCOMING |
| 5 | WAITING |
此外还有 `isPhoneActive`、`isMultiCallBuild`、`hfpState`、`scoState`、系统 Settings、EventBus 消息和外部广播,多套状态并存,没有统一状态机。
## 7. 三方通话专项结论
### 7.1 是否有三方通话功能
**代码中有三方通话意图、UI 和部分控制,但现行 GOC 链路不完整,不能确认产品具备可靠可用的三方通话功能。**
代码证据包括:
- `isMultiCallBuild` 三方状态标志。
- `SECOND_PHONE_IN` 第二路来电事件。
- `CurrentCalls` 中 ACTIVE、HELD、WAITING 等状态。
- `PhoningActivity` 的第二路号码、等待提示、三方计时和处理 UI。
- `enhancedCallCtrl(int action)` 增强通话控制入口。
- `GocJar.setThreePartyCallEnable(true)` 三方开关命令。
### 7.2 为什么不能认定完整支持
1. `enhancedCallCtrl(int action)` 完全忽略 `action`,无论调用方请求释放当前、保持当前、接听等待或合并,最终都只执行 `setThreePartyCallEnable(true)`。
2. `isMultiCallBuild=true` 和 `SECOND_PHONE_IN` 的明确设置点位于旧 `CallWaitingCallback()`;旧 NF2311 适配器初始化已被注释,现行 GOC 回调没有对应闭环。
3. 新 `onHfpCallChanged()` 使用 `prePreStatus == status` 直接去重。第二路电话如果状态值与第一路相同,但号码或 `isMultiParty` 变化,回调可能被丢弃。
4. 三方 ACTIVE 的映射存在被当成旧状态“普通来电”处理的可能,可能重复拉起或错误刷新通话页面。
5. 未找到针对不同增强通话 action 的底层命令映射,也没有三方通话自动化或真机验证证据。
### 7.3 三方通话验收前置条件
必须在支持呼叫等待的手机和 SIM 卡上完成以下真机矩阵:
- 通话 A 中收到 B 来电,显示第二路号码。
- 保持 A 并接听 B。
- 挂断 A 并接听 B。
- 在 A/B 间切换。
- 合并会议。
- 单独挂断其中一路。
- 手机端主动保持、切换、合并后,车机 UI 同步。
- 每一步验证 SCO、MIC、通话计时、仪表、本地音乐暂停恢复。
## 8. 蓝牙音乐
### 8.1 职责边界
应用自身不解码音乐:
- A2DP:手机音乐音频流传输到车机。
- AVRCP:播放、暂停、停止、上一首、下一首、播放状态、进度和 ID3 元数据。
- GOC:提供 A2DP/AVRCP 命令和回调。
- `MusicFragment`:展示和控制 UI。
- `AudioFocusManager`:车机音频焦点、音量和其他音源协调。
### 8.2 控制链
```text
MusicFragment / 语音 / MODE 按键
→ PlayControls / ModePlayControls
→ BtAdapterManager.playControl(command)
→ GocJar AVRCP Play/Pause/Stop/Next/Prev
→ 手机端播放器
→ AVRCP 播放状态回调
→ mPlaying / UI / 仪表 / 音频通道
```
MODE 场景会在首次 Play 后约 1.5 秒重试一次,以适配手机或协议栈尚未就绪的情况。
### 8.3 播放状态
1. A2DP 连接后设置 `a2dpState=CONNECTED`;未取得播放条件时先将 A2DP 本地音量置 0。
2. AVRCP 连接后设置 `avrcpState=CONNECTED`。
3. 蓝牙主 Activity 进入前台时注册 `BTMusic/MusicBluetooth` 音频资源并申请媒体焦点。
4. UI/语音发送 AVRCP 命令。
5. 实际播放状态以 AVRCP 回调确认:`1=播放、2=暂停、0=停止`。
6. 播放且持有焦点时恢复 A2DP 音量、更新 UI 和仪表。
7. 未持有焦点却收到播放时,代码静音并连续发送 Pause。
8. 暂停、停止或 A2DP 断开时,更新 UI 并注销/释放相关音频资源。
### 8.4 ID3 和进度
AVRCP 1.3 元数据写入静态 `ID3Repository`:
- 标题
- 歌手
- 专辑
- 总时长
- 当前进度
更新后通过 EventBus 刷新 `MusicFragment`。SeekBar 只展示进度,不允许用户拖动定位。
### 8.5 音频焦点和本地音乐联动
蓝牙音乐与 `music_sdk` 没有直接 Gradle 依赖,主要通过以下方式协调:
- Android `AudioFocus`
- VoiceSwitcher AIDL 音频资源
- `com.yaxon.ACTION_MEDIA_STATUS_CHANGE` 广播
- `media.type` / `yaxon_media` 等系统状态
- `IMusicStateService` 仪表音乐状态
媒体类型约定:
- 本地音乐:`request_value=1`、`local`
- 蓝牙音乐:`request_value=2`、`blue`
`music_sdk` 还监听蓝牙来电、拨号、挂断和拒接广播,在电话期间暂停本地音乐,通话结束后恢复。
### 8.6 蓝牙音乐主要风险
- `VoiceSwitchReceiver` 中 Pause/Stop 处理被注释,Resume 也不直接下发 AVRCP Play,音源抢占后不保证手机端真正暂停/恢复。
- 旧 ID3 长度解析高字节掩码错误,长度超过 255 时可能解析错误;部分边界检查发生在读取之后,存在越界风险。
- 首次只收到时长/进度回调时,新建的 `ID3Info` 没有放回 Repository,页面可能继续显示 `00:00`。
- 播放状态同时存在于 GOC、`mPlaying`、Fragment、SharedPreferences、AudioFocus 和系统媒体类型,乱序回调可能造成“图标播放但无声”。
- 永久丢失焦点路径可能重复发送 Pause。
- 方向盘音乐广播注释声称支持播放/暂停/快进快退,但实际只处理上一首、下一首。
- 蓝牙音乐语音资源注册标志未正确置为 true,重复注册依赖远端服务容错。
## 9. PBAP 通讯录与通话记录
### 9.1 数据模型
LitePal 数据库名为 `bluetooth`,版本 2,主要表:
- `Linkedinfo`:配对/连接设备
- `PhoneBook`:通讯录
- `CallRecord`:通话记录
数据层采用 Repository 组合:
```text
UI / Presenter
→ Repository
├─ 内存缓存
├─ LitePal 本地数据源
└─ GOC/PBAP 远端数据源
```
### 9.2 通讯录功能
- 手机通讯录下载
- SIM 通讯录下载
- 本地缓存和数据库持久化
- 中文姓名、拼音首字母、全拼和电话号码搜索
- 下载完成后刷新缓存、落库
- 广播 `ACTION_BLUETOOTH_CONTACT` 给语音模块
- 通过 `BluetoothPhonebookProvider` 给外部模块查询联系人
### 9.3 Provider 风险
`BluetoothPhonebookProvider` 在 Manifest 中导出,但未配置访问权限,外部应用可查询完整通讯录。其 `insert/delete/update` 直接抛出 `UnsupportedOperationException`,因此它实际上是未显式声明契约的只读 Provider。
建议后续明确:
- 配置 signature/system 权限。
- 或设置 `exported=false`,由受控 AIDL 提供查询。
- 对所有 query 参数、投影和返回字段做白名单限制。
## 10. 外部系统联动
### 10.1 语音
`VoiceRecognizeReceiver` 支持:
- 打开/关闭蓝牙
- 打开蓝牙音乐
- 上一首、下一首
- 暂停、继续
- 联系人查询与拨号相关命令
语音执行前会检查连接状态和当前媒体类型。蓝牙开关当前存在“命令受理即回复成功”的问题,可能导致语音说成功但实际状态未变化。
### 10.2 ITS AIDL
`ItsAutoBtService` 对外提供蓝牙与电话控制,但存在明显测试/逻辑残留:
- `type == 2` 关闭蓝牙时,把 `BOOLEAN_SETTING_BLUETOOTH_STATE` 写成了 `true`,应为 `false`。
- `type == 5` 不是单纯打开通话界面,而是硬编码拨打 `10000`。
### 10.3 状态栏和仪表
- 主界面 `StatusBarIconReceiver` 监听 `com.yaxon.bluetooth.BLUETOOTH` 更新蓝牙图标。
- `IMusicStateService` 接收蓝牙音乐类型、播放状态和电话状态,用于仪表显示。
- 多条系统 Settings 和广播同时承担状态同步,缺少统一协议版本和来源优先级。
### 10.4 音频和 GPIO
- 音频服务提供 Bluetooth 输入通道和蓝牙音量控制。
- HFP 通话激活/结束时通过 GPIO `SwitchMicChannel()` 切换 MIC。
- 蓝牙音乐使用 `STREAM_MUSIC` 和永久 AudioFocus。
- 电话铃声使用 `STREAM_RING` 焦点,但本地铃声启动链不完整。
### 10.5 EasyConn 和休眠唤醒
外部广播和服务负责系统休眠/唤醒时关闭、恢复蓝牙,也与 EasyConn 等车机互联场景协调。关闭服务会先清理电话、音乐和全局状态,再调用底层关闭,因此异常中断时可能出现“状态已清理、底层未关闭”的短暂不一致。
## 11. 全局状态与并发模型
`YXApplication` 保存大量全局静态状态:
- 蓝牙总开关和扫描状态
- `mBlueToothConnect`
- `hfpState/a2dpState/avrcpState/scoState`
- `mLinkedMac/mConnectingMac/mLinkingMac`
- `mPlaying`
- `isPhoneActive/isMultiCallBuild`
- 通讯录下载状态
同时还有:
- SharedPreferences
- Settings.System
- LitePal/Repository 缓存
- EventBus
- Android 广播
- AIDL 服务状态
- GOC 实际状态
这些状态会被 AIDL 回调线程、HandlerThread、UseCase 线程池、IntentService 和主线程共同读写,大多没有 `volatile`、锁或统一 reducer/state machine。`BtAdapterManager` 又只保存单个 open/connect/disconnect 等结果回调,并发请求时后一个请求可能覆盖前一个请求。
`UseCaseScheduler` 的线程池队列容量只有 2,拒绝策略为 `DiscardPolicy`。高频扫描、连接或控制命令可能无日志静默丢弃。
## 12. 重点风险清单
### 12.1 高风险
| 编号 | 风险 | 影响 |
|---|---|---|
| H1 | 蓝牙开关未等待最终回调,成功回调可能重复 | UI/语音与实际状态不一致 |
| H2 | GOC 冷启动绑定无可靠排队重试 | 开机自动开蓝牙偶发失败 |
| H3 | 未配对设备连接/配对时序反向 | 配对失败或依赖底层隐式行为 |
| H4 | 连接失败不清理旧回调 | 后续请求可能误完成旧操作 |
| H5 | 三方电话主要依赖已停用旧回调 | 第二路来电和增强控制可能失效 |
| H6 | `enhancedCallCtrl()` 忽略 action | 保持、释放、切换、合并语义不成立 |
| H7 | Provider 导出且无访问权限 | 通讯录隐私泄露 |
| H8 | ITS 关闭状态写反、硬编码拨号 10000 | 外部控制状态错误或误拨号 |
| H9 | ID3 长度/边界解析错误 | 元数据错乱、潜在数组越界 |
### 12.2 中风险
| 编号 | 风险 | 影响 |
|---|---|---|
| M1 | 多套全局状态、无统一状态机 | UI、仪表、语音与底层乱序 |
| M2 | UseCase 队列满时静默丢任务 | 用户操作无响应且无日志 |
| M3 | 断开依赖可能为空的 `mLinkedMac` | 断开失败、状态残留 |
| M4 | MAC 有冒号/无冒号混用 | 部分底层命令兼容性不确定 |
| M5 | `Linkedinfo.equals()` 契约错误且无 `hashCode()` | 设备误去重、集合行为异常 |
| M6 | 音源抢占 Pause/Resume 不完整 | 蓝牙音乐与本地音乐互抢或不恢复 |
| M7 | `PhoningActivity singleTask` 无 `onNewIntent()` | 第二次来电号码/方向不刷新 |
| M8 | 挂断/拒接忽略底层结果,6 秒强关 UI | 手机仍通话但车机页面消失 |
| M9 | 外部 Receiver 广播普遍缺权限保护 | 非受信应用可注入控制命令 |
| M10 | `music_sdk` 中蓝牙 Activity 类名错误 | 前台页面判断失败 |
`music_sdk/src/futian/.../ProjectConstant.java` 中写的是:
```text
com.yaxon.bluetooth.activity.PhoningActivity
com.yaxon.bluetooth.activity.MainActivity
```
实际类名是:
```text
com.yaxon.bluetooth.keyboard.PhoningActivity
com.yaxon.bluetooth.main.MainActivity
```
## 13. 建议整改顺序
### P0:先保证真实状态闭环
1. 蓝牙开关只在 `onEnableChanged()` 确认后完成请求;增加超时、幂等和单次回调保护。
2. GOC 服务绑定期间建立命令队列或明确重试状态。
3. 重做连接/配对流程:先判断 bond,未配对先 pair,bonded 后 connect。
4. 连接、断开和开关请求使用 request token,超时后清除回调,拒绝陈旧回调。
5. 修复 ITS 关闭状态与硬编码 `10000`。
### P1:补齐三方电话和隐私安全
1. 根据 GOC 当前 SDK 能力明确 `CHLD=0/1/2/3` 或对应 API 映射。
2. 新 GOC 回调按“号码 + 状态 + multiparty + index”去重,不能只看状态值。
3. 在新链路中明确生成第二路来电、保持、激活、会议状态事件。
4. 为 Provider 和控制广播添加系统签名权限。
5. 修复 `PhoningActivity.onNewIntent()` 和挂断结果确认。
### P2:统一音乐与全局状态
1. 建立单一蓝牙状态模型,区分 Desired、Command、Confirmed、Error。
2. 音乐状态以 AVRCP confirmed state 为准,AudioFocus 只控制是否允许出声。
3. 修复 ID3 解析、首次进度对象未落 Repository、重复 Pause。
4. 完善 VoiceSwitcher Pause/Resume 的真实 AVRCP 操作。
5. 将线程池拒绝策略改为可观测策略,至少记录任务名、队列和调用来源。
## 14. 测试与验收建议
### 14.1 设备管理
- GOC 服务未绑定、延迟绑定、绑定断开、服务重启。
- 连续快速开关蓝牙,验证回调只完成一次。
- 扫描中关蓝牙、休眠、切换页面。
- 新手机首次配对、PIN 错误、用户取消、超时。
- 已配对连接、连接另一设备、HFP-only、A2DP-only。
- 进程被杀后恢复连接并断开。
### 14.2 电话
- 去电、来电、接听、拒接、挂断。
- 手机端与车机端分别操作,验证双向同步。
- SCO 手机/车机切换,MIC 静音,DTMF。
- 空号码、隐藏号码、超长号码和特殊字符。
- 来电时本地音乐、蓝牙音乐、收音机的暂停与恢复。
- 三方通话按第 7.3 节完整矩阵测试。
### 14.3 蓝牙音乐
- Play/Pause/Stop/Prev/Next。
- Android 与 iPhone,多种播放器。
- 无 ID3、长标题、中文、emoji、元数据超过 255 字节。
- 只先到进度回调、回调乱序、A2DP/AVRCP 分别断开。
- 导航播报、语音、电话、收音机抢占与恢复。
- 方向盘、MODE、语音和页面按键连续快速操作。
### 14.4 通讯录
- 0、1、1000、5000 条联系人。
- 同名、多号码、中文、英文、特殊字符。
- PBAP 拒绝授权、中途断开、重复同步、换手机。
- Provider 未授权应用访问验证。
## 15. 可追踪的关键代码位置
| 主题 | 文件/位置 |
|---|---|
| 总初始化 | `YXApplication.java:115` |
| Manager 初始化与回调注册 | `BtAdapterManager.java:174` |
| 蓝牙开关 | `BtAdapterManager.java:1923` |
| 扫描 | `BtAdapterManager.java:1983` |
| 连接 | `BtAdapterManager.java:2023`、`:3479` |
| 三方控制 | `BtAdapterManager.java:619` |
| 旧等待电话回调 | `BtAdapterManager.java:1903` |
| 新 GOC 电话回调 | `BtAdapterManager.java:3801` |
| AVRCP 播放状态 | `BtAdapterManager.java:4090` |
| 音乐页面 | `music/MusicFragment.java:42` |
| 电话页面 | `keyboard/PhoningActivity.java:266` |
| Provider | `phonebook/model/BluetoothPhonebookProvider.java:39` |
| ITS 服务 | `aidl/its/ItsAutoBtService.java:95` |
| 线程池 | `UseCaseScheduler.java:23` |
## 16. 分析边界
本结论来自应用层 Java、Manifest、Gradle 配置及跨模块静态调用分析。以下内容未获得运行时证据:
- `goc.jar` 内部实现和外部 GOC 蓝牙服务源码。
- 蓝牙芯片固件及其协议兼容性。
- 实车音频路由、GPIO MIC 和仪表服务行为。
- Android/iPhone 真机连接、PBAP、SCO、A2DP 和三方通话结果。
因此,“代码入口存在”不等于“硬件环境已验证可用”。三方通话尤其需要按真机矩阵重新确认。