Files
Obsidian/项目文档/徐工/蓝牙/蓝牙模块开发熟悉手册.md
2026-11-05 14:35:04 +08:00

30 KiB
Raw Permalink Blame History

蓝牙模块开发熟悉手册

整理日期:2026-07-06
整理范围:F:\zdCode\xugong 当前代码、agent.md 历史线索、蓝牙目录现有 Markdown、docx、xlsx、PDF 可抽取文本。
目的:帮助新接手开发者快速理解徐工项目蓝牙模块的架构、主流程、代码入口、日志排查路径和历史高风险点。

一句话结论

徐工蓝牙不是单一 APK 功能,而是一条跨 YX_BlueTooth、YX_BluetoothSDK、YX_Music、YX_Voice、YX_Launcher、YX_SystemUI、Android Frameworks、系统 Bluetooth 应用、顾凯 SDK/芯片的链路。开发时优先从 YX_BluetoothSDK 看协议封装,再看上层应用如何订阅 SDK 回调和 DataManager 数据。

1. 模块地图

模块 定位 重点入口
YX_BluetoothSDK 蓝牙核心 SDK,封装 HFP/A2DP/AVRCP/PBAP/MAP、数据库、状态推送、语音同步 manager、modules、platform、audio、database
YX_BlueTooth 蓝牙电话主业务应用,联系人、通话记录、拨号、通话页、常驻服务、widget MainActivity、ContactsFragment、CallLogsFragment、BluetoothPhoneActivity、BluetoothService
YX_Music 蓝牙音乐页面所在应用,负责蓝牙音乐页面 UI、播放控制、封面显示 BluetoothMusicFragment、BluetoothMusicViewModel
YX_Voice 语音控制蓝牙开关、电话、音乐、通讯录/通话记录状态同步 VoiceProtocol、BlueToothHelper、BluetoothMonitorReceiver、VoiceEventHelper
YX_Launcher 首页音乐卡片、蓝牙音乐信息展示和媒体按键入口 WidgetsMedia、DisplayShareViewModel、Id3Info
YX_SystemUI 状态栏/导航媒体区、通话状态显示、系统级入口 MediaControlView、VirtualManager、SystemUI 蓝牙封装类
YX_CarControl 配对弹窗、自动同步开关等外围联动 注意不要重复初始化 PBAP 联系人同步
Frameworks / Bluetooth Process Android 蓝牙 API、Binder、系统 Bluetooth app、JNI、协议栈 android.bluetooth.*、packages/apps/Bluetooth
顾凯 SDK/芯片 蓝牙协议栈供应商实现和 HCI 交互 底层联调、btsnoop/顾凯日志

2. 总体分层

PDF《蓝牙框架原理以及通讯数据交互流程》给出的系统层级可落到项目代码中:

flowchart TD
    A["上层应用<br/>YX_BlueTooth / YX_Music / YX_Voice / Launcher / SystemUI"] --> B["YX_BluetoothSDK<br/>ControlManager / Manager / Platform Client"]
    B --> C["Frameworks API<br/>android.bluetooth.*"]
    C --> D["Bluetooth Process<br/>packages/apps/Bluetooth"]
    D --> E["JNI<br/>packages/apps/Bluetooth/jni"]
    E --> F["AOSP 蓝牙协议栈<br/>system/bt"]
    F --> G["顾凯 SDK / HAL / HIDL"]
    G --> H["蓝牙芯片"]

反向回调通常是:芯片/协议栈事件 -> JNI -> 系统 Bluetooth 服务 -> Frameworks 广播/API -> YX_BluetoothSDK platform 接收 -> modules 整理状态 -> 上层 UI、语音、Launcher/SystemUI。

3. SDK 内部分层

YX_BluetoothSDK 是最值得先读的模块,建议按下面顺序看:

层 包路径 职责
对外控制层 com.yaxon.bluetoothsdk.manager 单例入口,给 App/语音/音乐模块调用,例如 BtPhoneControlManager、BtContactsControlManager、BtMusicControlManager
业务模块层 com.yaxon.bluetoothsdk.modules 真正组织业务状态机、监听、数据库、语音/投屏同步,例如 BtContactsManager、BtPhoneManager、BtMusicManager、BtInfoPushManager
平台适配层 com.yaxon.bluetoothsdk.platform 封装 Android 蓝牙 API 和广播,例如 BluetoothHfpClient、BluetoothPbapClient、BluetoothAvrcpClient、BluetoothConnectListenerClient
音频层 com.yaxon.bluetoothsdk.audio 蓝牙音乐音频焦点、A2DP mute/unmute、MediaSession 媒体键
数据层 com.yaxon.bluetoothsdk.database 联系人、通话记录、配对设备、收藏等本地 SQLite 数据
工具层 utils 系统联系人/通话记录读取、号码处理、日志、权限等

开发时一般从 ControlManager 找入口,然后下钻到 modules,再看 platform 是调用系统 API 还是接收广播。

4. 启动与初始化

蓝牙电话应用开机常驻入口:

flowchart LR
    BootReceiver["YX_BlueTooth BootReceiver"] --> Service["BluetoothService.onCreate"]
    Service --> SDK["初始化 BtPhone/BtContacts/BtCallRecords 等控制器"]
    Main["MainActivity.onCreate/initData"] --> VM["SettingViewModel / Fragment ViewModel"]
    VM --> Managers["BtSettingControlManager / BtContactsControlManager / BtPhoneControlManager"]

关键文件:

  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/receiver/BootReceiver.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/service/BluetoothService.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/main/MainActivity.java
  • YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/modules/BtSettingManager.java

BtSettingManager.init() 会初始化 BluetoothHfpClient、BluetoothA2dpSinkClient、BluetoothDeviceClient、BluetoothMapConnectClient、BluetoothConnectListenerClient,并在蓝牙已打开时初始化已配对设备列表。它还会延迟检查已连接设备,并把当前活跃蓝牙 MAC 同步给语音模块。

5. 蓝牙开关、扫描、配对、连接

5.1 状态来源

蓝牙开关状态来自 BluetoothAdapter.ACTION_STATE_CHANGED,SDK 侧由 BluetoothConnectListenerClient.setBluetoothSwitchState() 处理:

状态值 Android 常量 含义
10 BluetoothAdapter.STATE_OFF 蓝牙已关闭
11 BluetoothAdapter.STATE_TURNING_ON 正在打开
12 BluetoothAdapter.STATE_ON 蓝牙已打开
13 BluetoothAdapter.STATE_TURNING_OFF 正在关闭

注意:BluetoothAdapter.enable() / disable() 返回 true 只代表请求被接受,不代表最终状态已切到 ON/OFF。语音场景必须看后续 blueState -> 12 或 blueState -> 10。

5.2 连接状态

连接状态由 BluetoothConnectListenerClient 聚合 HFP、A2DP、A2DP Sink 等 profile 广播,再通知各模块:

  • notifyBluetoothConnected():写入连接 MAC、连接名、连接状态,并回调 onConnect
  • notifyBluetoothDisconnect():清理蓝牙音乐信息,写入断开状态,并回调 onDisconnect
  • setBondState():处理配对状态
  • setPairingRequest():处理配对请求

5.3 HFP 与 A2DP 的关系

交接说明里特别强调:蓝牙连接包含 hfp(电话)和 a2dp(音乐/音频连接);通讯录同步需要再连 pbap,同步完成后需要断开 pbap。

6. 通讯录与通话记录同步(PBAP)

PBAP 是 Phone Book Access Profile,用于从手机拉取通讯录和通话记录。本项目中通讯录和通话记录共享一次 PBAP 同步窗口:联系人完成、通话记录完成后,才认为本轮电话本下载整体完成,并主动断开 PBAP,避免阻塞和卡死。

6.1 主流程

sequenceDiagram
    participant UI as ContactsFragment/CallLogsFragment
    participant CM as BtContactsControlManager
    participant M as BtContactsManager
    participant P as BluetoothPbapClient
    participant SYS as Android PBAP/System Provider
    participant DB as DataContactsHelper/DataCallRecordHelper
    participant V as ViewModel/Voice

    UI->>CM: phoneBookPullRequest(context)
    CM->>M: phoneBookPullRequest(context)
    M->>P: connectPbap 或 phoneBookPullRequest
    P->>SYS: 删除系统 Contacts/CallLog 缓存
    P->>SYS: 发送 Intent.ACTION_USER_UNLOCKED
    SYS-->>P: contacts.download.complete
    P-->>M: onContactsPullCompleted()
    M->>SYS: ContactUtils.getAllContacts()
    M->>DB: deleteContacts + saveContactsList
    M-->>V: onContactsPhoneBook(list)
    SYS-->>P: calllogs.download.complete
    P-->>CallRecords: onCallLogsPullCompleted()
    CallRecords->>SYS: CallLogUtils.getCallLogList()
    CallRecords->>DB: clearCallRecord + saveCallRecordList
    CallRecords-->>V: onCallRecords(list)
    M->>P: contacts + calllogs 都完成后 disconnectPbap()

6.2 关键代码入口

场景 文件/方法
UI 点击同步联系人 ContactsFragment.phoneBookPullRequest()
UI 点击/通话页触发同步 CallLogsFragment -> CallHistoryViewModel.phoneBookPullRequest()
SDK 对外入口 BtContactsControlManager.phoneBookPullRequest()
同步状态、权限、自动同步 BtContactsManager.phoneBookPullRequest()、isAutoSync()、checkContactsPermissionGranted()
PBAP 连接和系统广播 BluetoothPbapClient.connectPbap()、phoneBookPullRequest()、PbapClientReceiver
联系人读取与保存 ContactUtils.getAllContacts()、DataContactsHelper.savePhoneBookList()
通话记录读取与保存 CallLogUtils.getCallLogList()、BtCallRecordsManager.loadCallsLog()、DataCallRecordHelper.saveCallRecordList()
UI 状态判断 ContactsFragment.syncPhoneBookSynchronized()、CallLogsFragment.notifyPhoneRecordList()

6.3 关键状态位

状态 说明 风险
isPhoneBookPulling PBAP 本轮同步是否正在进行 UI loading 和重复点击判断要优先看它
isContactsPullCompeted / getContactLoadWonType() SDK 本轮联系人读取、写库、回调是否完成 比 isPhoneBookSynchronized() 更能代表本轮同步完成
isPhoneBookSynchronized() 本地 DB 当前设备是否已有至少一条联系人 只能代表“本地有数据”,不代表本轮 PBAP 完成
isCallRecordUpdated() 本地 DB 当前设备是否已有通话记录 通话记录页显示和 loading 判断会用到

6.4 已验证历史结论

  • 清空蓝牙本地 SQLite 表不是通讯录同步失败的直接原因;正常清空后 PBAP 会重新写入。
  • isPhoneBookSynchronized() 只表示 DB 有联系人,不能作为“本轮同步完成”的唯一条件。
  • 曾出现 YX_CarControl 和 YX_BlueTooth 双进程同时初始化 PBAP,导致重复 phoneBookPullRequest、重复删写联系人表;当前应统一由 com.yaxon.bluetooth 负责 PBAP 同步。
  • 联系人同步中应优先显示 loading,避免在旧库数据或第一条联系人写入后提前显示“0 个联系人”或旧列表。
  • 通话记录 Adapter 曾因传入同一个 list 导致 clear() 自清空,已通过传 new ArrayList<>(mCallRecordList) 规避。

6.5 排查日志关键字

阶段 关键字
请求开始 BluetoothPbapClient: phoneBookPullRequest
PBAP 连接变化 PbapClientReceiver device name、prevState、state
联系人下载完成 BtContactsManager: onContactsPullCompleted
联系人读取完成 contacts read completed size =
联系人 UI 收到 ContactListViewModel: onContactsPhoneBook =
通话记录读取 mCallRecordBeanList size =
通话记录保存 saveCallRecordList size =
通话记录 UI CallLogsFragment: notifyPhoneRecordList list ==
失败 onContactsPullFailed、contacts synchronization failed、通讯录同步失败

7. 蓝牙电话(HFP)

HFP 是 Hands-Free Profile,用于拨号、接听、拒接、挂断、通话音频状态等。

7.1 电话状态

状态值 常量 说明
0 CALL_STATE_ACTIVE 通话中
1 CALL_STATE_HELD 保持中
2 CALL_STATE_DIALING 正在拨号
3 CALL_STATE_ALERTING 正在响铃,项目中通常合并到拨号处理
4 CALL_STATE_INCOMING 来电
5 CALL_STATE_WAITING 第三方等待
6 CALL_STATE_HELD_BY_RESPONSE_AND_HOLD 响应并保持,项目中通常合并到保持处理
7 CALL_STATE_TERMINATED 挂断

7.2 拨号调用链

PDF《蓝牙电话交互流程解析》给出的拨号链路,对应当前项目可理解为:

flowchart TD
    A["YX_BlueTooth UI / BluetoothService / Voice"] --> B["BtPhoneControlManager.dialing"]
    B --> C["BtPhoneManager.dialing"]
    C --> D["BluetoothHfpClient.dial"]
    D --> E["BluetoothHeadsetClient.dial"]
    E --> F["BluetoothHeadsetClientBinder.dial"]
    F --> G["HeadsetClientService.dial"]
    G --> H["HeadsetClientStateMachine.Connected.dial"]
    H --> I["NativeInterface.dial"]
    I --> J["com_android_bluetooth_hfpclient.dialNative"]
    J --> K["顾凯 SDK / 蓝牙芯片"]

接听、拒接、挂断与拨号类似,只是 Frameworks API 分别换成:

  • BluetoothHeadsetClient.acceptCall()
  • BluetoothHeadsetClient.rejectCall()
  • BluetoothHeadsetClient.terminateCall()

7.3 状态回调链

sequenceDiagram
    participant FW as BluetoothHeadsetClient 广播
    participant HFP as BluetoothHfpClient
    participant M as BtPhoneManager
    participant UI as BluetoothPhoneViewModel/UI/Window
    participant Push as BtInfoPushManager
    participant Voice as BtVoiceControlManager

    FW-->>HFP: ACTION_CALL_CHANGED
    HFP->>HFP: getCurrentCalls()
    HFP-->>M: OnCallPhoneStateListener.callState(list)
    M->>M: 转成 CallingState
    M-->>UI: onHfpCallChanged(list)
    M->>Push: setPhoneState / setPhoneCallState / setPhoneGroups
    M->>Voice: notifyBtInComing / notifyBtCall / notifyBtAccept / notifyBtHangup

7.4 关键文件

  • YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/manager/BtPhoneControlManager.java
  • YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/modules/BtPhoneManager.java
  • YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/platform/BluetoothHfpClient.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/viewmodel/BluetoothPhoneViewModel.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/main/BluetoothPhoneActivity.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/window/TelephoneWindowControl.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/window/ThirdCallingControl.java
  • YX_BlueTooth/src/main/java/com/yaxon/bluetooth/service/BluetoothService.java

7.5 排查日志关键字

问题 关键字
拨号没有发出 BluetoothHfpClient: dial phoneNumber、start dial phoneNumber
HFP 未连接 hfp_phone_protocol_not_connected、isHfpConnect disconnect
来电/去电 UI 不刷新 ACTION_CALL_CHANGED、clientCallList、onHfpCallChanged
挂断/拒接失败 hangupCall、rejectCall、acceptCall flag
通话音频异常 ACTION_AUDIO_STATE_CHANGED、audio connection state、connectAudio、disconnectAudio

8. 蓝牙音乐(A2DP/AVRCP/MediaSession)

蓝牙音乐涉及两条线:

  • A2DP:音频流链路,决定声音是否从手机到车机。
  • AVRCP/MediaBrowser/MediaSession:播放控制、播放状态、歌曲元数据、媒体按键。

8.1 主流程

sequenceDiagram
    participant UI as YX_Music/BluetoothMusicFragment
    participant CM as BtMusicControlManager
    participant M as BtMusicManager
    participant Audio as BtMusicAudioManager
    participant AVRCP as BluetoothAvrcpClient/MediaBrowserClient
    participant Push as BtInfoPushManager/DataManager
    participant L as Launcher/SystemUI/Voice

    UI->>CM: requestAudioFocus/playBtMusic
    CM->>M: requestAudioFocus/playBtMusic
    M->>Audio: requestAudioFocus()
    Audio->>M: setA2dpUnMute / informAudioFocusStateNative=1
    M->>AVRCP: play()
    AVRCP-->>M: onPlaybackStateChanged / onMetadataChanged
    M-->>UI: onPlayMusicState/onPlayMusicInfo
    M->>Push: sendMusicInfoToLauncher(BT_MUSIC_INFO)
    Push-->>L: ContentObserver/DataManager 读取 BT_MUSIC_INFO

8.2 关键代码入口

场景 文件/方法
蓝牙音乐页面 YX_Music/src/music/java/com/yaxon/musicplay/view/music/BluetoothMusicFragment.java
音乐 ViewModel YX_Music/src/main/java/com/yaxon/musicplay/viewmodel/BluetoothMusicViewModel.java
SDK 控制入口 BtMusicControlManager.playBtMusic()、pauseBtMusic()、requestAudioFocus()
音乐业务状态 BtMusicManager.onPlaybackStateChanged()、onMetadataChanged()
音频焦点和媒体键 BtMusicAudioManager.requestAudioFocus()、onMediaButtonEvent()
AVRCP 控制 BluetoothAvrcpClient.play/pause/previous/next()
A2DP 音频状态 BluetoothA2dpSinkClient
Launcher/SystemUI 显示 BtInfoPushManager.sendMusicInfoToLauncher()、DataUtils.BT_MUSIC_INFO

8.3 焦点与保护逻辑

BtMusicManager.onPlaybackStateChanged() 中有一条关键保护:如果蓝牙音乐开始播放,但当前蓝牙音乐不在前台且没有蓝牙音乐焦点,就会 mute A2DP 并 pauseBtMusic()。这是为了防止手机端或后台异常播放抢声道,但也会影响 Launcher 首页音乐卡片冷启动首次播放场景。

历史上已经通过 SDK 侧 BtMusicAudioManager 的 MediaSession 兜底处理过 Launcher 首次点击:

  • MediaSession 在无焦点但蓝牙音乐是当前音源时也可接媒体键。
  • 收到 KEYCODE_MEDIA_PLAY_PAUSE 后先申请焦点,再 playBtMusic()。
  • A2DP 连接、元数据、播放状态变化后刷新 MediaSession,避免第一次点击只激活 session、第二次才播放。

8.4 封面高风险点

蓝牙音乐封面风险集中在固定路径:

/sdcard/ID3/id3cover.JPEG

问题模式:

  • 系统/底层复用同一个封面路径。
  • 歌曲元数据先到,封面路径后到。
  • 文件可能短时间为 0 字节或半写入。
  • 空 albumUrl 或无效文件可能覆盖当前有效封面。

排查关键字:

  • albumUrl = /sdcard/ID3/id3cover.JPEG
  • length = 0
  • Failed to create image decoder
  • 获取bitmap为null
  • skip invalid album file
  • loadFileImageToView success

9. 语音与蓝牙交互

9.1 语音开关蓝牙

flowchart TD
    A["TXZ / VoiceService 请求<br/>openBluetooth / closeBluetooth"] --> B["VoiceProtocol.handleBluetooth"]
    B --> C["BlueToothHelper.openBlueToothByVoice / closeBlueToothByVoice"]
    C --> D["BluetoothAdapter.enable / disable"]
    D --> E["BluetoothMonitorReceiver.ACTION_STATE_CHANGED"]
    E --> F["BlueToothHelper.syncBluetoothStatusToTxzByStateChanged"]
    F --> G["VoiceEventHelper.notifyBluetoothOpenStaus / notifyBluetoothConnectStaus"]

关键文件:

  • YX_Voice/src/main/java/com/yaxon/voice/protocol/VoiceProtocol.java
  • YX_Voice/src/main/java/com/yaxon/voice/helper/BlueToothHelper.java
  • YX_Voice/src/main/java/com/yaxon/voice/receiver/BluetoothMonitorReceiver.java
  • YX_Voice/src/main/java/com/yaxon/voice/helper/VoiceEventHelper.java

重点:

  • openBlueToothByVoice() / closeBlueToothByVoice() 做了同目标去抖、相反目标保护、有限重试、延迟状态回填。
  • BluetoothMonitorReceiver 收到 STATE_ON/OFF 后立即同步 TXZ,并延迟补发状态。
  • 不能以 enable()/disable() 返回值作为最终成功依据。

9.2 语音电话与通讯录

语音电话/通讯录状态同步主要由 SDK 侧 BtVoiceManager 和 BtVoiceControlManager 承接:

  • 电话来电:BtPhoneManager -> BtVoiceControlManager.notifyBtInComing
  • 去电:notifyBtCall
  • 接通:notifyBtAccept
  • 挂断:notifyBtHangup
  • 联系人同步完成:BtVoiceManager.syncBtActiveDeviceMac、BtVoiceControlManager.notifyBtContactReload
  • 通话记录变化:BtVoiceManager.notifyCallRecordChange

10. Launcher/SystemUI/投屏联动

10.1 数据通道

SDK 通过 BtInfoPushManager 写入公共数据,外围模块通过 ContentObserver 或 DataManager.getObject() 读取。

数据 写入方 读取方
DataUtils.BT_MUSIC_INFO BtInfoPushManager YX_Launcher、YX_SystemUI、YX_Voice
DataUtils.BT_INFO BtInfoPushManager Launcher/SystemUI/投屏相关
电话状态、联系人、号码、通话时长 BtPhoneManager -> BtInfoPushManager 仪表、Launcher、SystemUI、语音

10.2 常见现象

  • Launcher 卡片能显示蓝牙音乐元数据,不代表按钮控制一定会走蓝牙音乐控制链路;按钮可能只是全局媒体键。
  • SystemUI/Launcher 读取封面路径时要防空路径、0 字节、解码失败、同一首歌空封面覆盖已有封面。
  • 电话投屏状态依赖 BtInfoPushManager.setPhoneState()、setPhoneCallState()、setPhoneGroups()。

11. 数据存储

数据 类 说明
联系人 DataContactsHelper 按蓝牙设备 MAC 存储联系人
通话记录 DataCallRecordHelper 按蓝牙设备 MAC 存储通话记录,过滤网络电话等异常号码
已配对设备 DataPairedHelper 配对设备列表
收藏 DataCollectionHelper 收藏联系人/号码
系统 Provider ContactUtils、CallLogUtils PBAP 下载完成后从系统联系人/通话记录 Provider 读取
跨模块状态 DataManager、YxIPC 音乐、电话、语音、Launcher/SystemUI 状态同步

开发时要分清:

  • 系统联系人/通话记录 Provider 是 PBAP 下载中间数据。
  • SDK 自己的 SQLite 是业务最终缓存。
  • DataManager 是跨应用状态和展示数据的同步通道。

12. 常见问题排查索引

问题 先看模块 关键日志
蓝牙打不开/关不掉,语音已回复成功 YX_Voice、BluetoothConnectListenerClient openBlueTooth、closeBlueTooth、blueState -> 10/11/12/13、syncBluetoothStatusToTxz
手机能连但电话不可用 HFP hfp onServiceConnected、updateHfpConnectedState、isHfpConnect
手机能播但车机无声 A2DP/焦点 bluetooth music requestAudioFocus、setA2dpUnMute、informAudioFocusStateNative
Launcher 首页点播放无效 MediaSession/焦点 WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE、MediaSessionService: Sending KeyEvent、BtMusicAudioManager: onMediaButtonEvent
通讯录不同步 PBAP phoneBookPullRequest、onContactsPullCompleted、contacts read completed size、onContactsPhoneBook
通话记录为空 PBAP/Adapter/UI mCallRecordBeanList size、saveCallRecordList size、notifyPhoneRecordList list ==
封面闪烁/消失/上一首封面 蓝牙音乐封面 albumUrl、length = 0、decodeFile、skip invalid album file
电话 UI 不弹/不消失 HFP/UI/悬浮窗 ACTION_CALL_CHANGED、onHfpCallChanged、CallingState、TelephoneWindowControl
断电上电状态错 ACC/POWER/SOURCE_ID POWER_MODE、ACC_STATE、SOURCE_ID、restoreBtMusicIfNeeded

13. 开发路线图

建议按这个顺序熟悉:

  1. 先读现有文档:蓝牙模块流程概览.md、蓝牙框架原理以及数据通讯交互流程.pdf、蓝牙电话交互流程解析.pdf、徐工项目蓝牙模块交接说明.docx。
  2. 看 YX_BluetoothSDK 分层:先 manager,再 modules,最后 platform/audio/database。
  3. 跟一遍 PBAP:从 ContactsFragment.phoneBookPullRequest() 跟到 BluetoothPbapClient.phoneBookPullRequest(),再跟回 onContactsPullCompleted()。
  4. 跟一遍 HFP:从 BluetoothPhoneViewModel.acceptCall/rejectCall 或 BtPhoneControlManager.dialing() 跟到 BluetoothHfpClient。
  5. 跟一遍蓝牙音乐:从 BluetoothMusicFragment 跟到 BtMusicControlManager、BtMusicManager、BtMusicAudioManager、BluetoothAvrcpClient。
  6. 跟语音开关蓝牙:从 VoiceProtocol.handleBluetooth() 跟到 BlueToothHelper 和 BluetoothMonitorReceiver。
  7. 看外围联动:BtInfoPushManager 写 BT_MUSIC_INFO/BT_INFO,再看 Launcher/SystemUI 的读取和显示。
  8. 最后看历史问题:agent.md 中的 PBAP 双进程、联系人 loading、通话记录自清空、封面固定路径、Launcher 首次点击播放。

14. 修改代码时的注意事项

  • PBAP 同步不要在多个进程重复初始化监听,联系人/通话记录同步应以 YX_BlueTooth 进程为主。
  • UI 判断“同步完成”不要只看 isPhoneBookSynchronized(),本轮同步期间还要看 isPhoneBookPulling 和 getContactLoadWonType()。
  • PBAP 完成后要断开 PBAP,否则历史上有“阻塞、软件卡死”的风险。
  • 蓝牙开关语音反馈要等最终 STATE_ON/OFF,不能只看 enable/disable result=true。
  • 蓝牙音乐播放控制必须先处理音频焦点和 A2DP unmute,再发 AVRCP play。
  • 封面路径下发/展示前要校验文件存在、非 0 字节、可解码,且要处理固定路径重写竞态。
  • 通话记录/联系人传给 Adapter 或回调前尽量使用快照,避免共享 list 被清空或并发修改。
  • destroy() 类清理要注意真正 shutdown() 线程池,历史代码里存在只调用 isShutdown() 的可疑写法。

15. 已知潜在风险

风险 说明
BtMusicControlManager.isA2dpSinkConnected() 历史排查中怀疑可能固定返回 false,需要结合当前代码确认
多处线程池销毁 部分 destroy() 里可能只调用 isShutdown() 而不是 shutdown()
BluetoothMapConnectClient.isMapConnect() 历史排查中提到潜在 NPE
PBAP 同步耗时 现场 1973 联系人约 85 秒级,日志时间还可能因设备校时跳变
固定封面路径 /sdcard/ID3/id3cover.JPEG 会复用、清零、半写入,跨 Music/Launcher/SystemUI 都要防
全局媒体键路由 Launcher 卡片点击可能先被系统媒体 session 路由影响,需看 MediaSessionService 日志

16. 构建、部署、底层日志

常用构建:

.\gradlew :YX_BluetoothSDK:assembleDebug --console plain
.\gradlew :YX_BlueTooth:assembleDebug --console plain
.\gradlew :YX_Music:assembleDebug --console plain
.\gradlew :YX_Voice:assembleDebug --console plain

交接说明里的部署注意点:

  • 蓝牙音乐修改后,需手动打包更新到 third_apps 目录下。
  • 配合顾凯 SDK 修改时,可以打开顾凯 SDK 日志:
adb shell setprop persist.bluetooth.btsnoopenable true

日志生成位置:

/data/misc/bluetooth/logs

烧录文档补充:

  • pac 格式通过 SDToolBox -> SDFactoryTool 烧录。
  • zip 格式为 U 盘升级,将 totalUpdate.zip 放 U 盘根目录,系统弹窗升级。

17. 参考资料

  • F:\AI\Obsidian\项目文档\徐工\蓝牙\蓝牙模块流程概览.md
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\bluetooth_git_summary.md
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\徐工项目蓝牙模块交接说明.docx
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\徐工项目蓝牙模块主流程.xlsx
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\蓝牙框架原理以及数据通讯交互流程.pdf
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\蓝牙电话交互流程解析.pdf
  • F:\AI\Obsidian\项目文档\徐工\蓝牙\徐工项目软件烧录文档.pdf

18. 快速入口清单

SDK

  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtSettingManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtPhoneManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtContactsManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtCallRecordsManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtMusicManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\modules\BtInfoPushManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\audio\BtMusicAudioManager.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\platform\BluetoothConnectListenerClient.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\platform\BluetoothHfpClient.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\platform\BluetoothPbapClient.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\platform\BluetoothAvrcpClient.java
  • F:\zdCode\xugong\YX_BluetoothSDK\src\main\java\com\yaxon\bluetoothsdk\platform\MediaBrowserClient.java

蓝牙电话应用

  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\main\MainActivity.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\main\BluetoothPhoneActivity.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\fragment\ContactsFragment.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\fragment\CallLogsFragment.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\viewmodel\ContactListViewModel.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\viewmodel\CallHistoryViewModel.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\viewmodel\BluetoothPhoneViewModel.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\service\BluetoothService.java
  • F:\zdCode\xugong\YX_BlueTooth\src\main\java\com\yaxon\bluetooth\provider\BluetoothContactsProvider.java

音乐、语音和外围

  • F:\zdCode\xugong\YX_Music\src\music\java\com\yaxon\musicplay\view\music\BluetoothMusicFragment.java
  • F:\zdCode\xugong\YX_Music\src\main\java\com\yaxon\musicplay\viewmodel\BluetoothMusicViewModel.java
  • F:\zdCode\xugong\YX_Voice\src\main\java\com\yaxon\voice\protocol\VoiceProtocol.java
  • F:\zdCode\xugong\YX_Voice\src\main\java\com\yaxon\voice\helper\BlueToothHelper.java
  • F:\zdCode\xugong\YX_Voice\src\main\java\com\yaxon\voice\receiver\BluetoothMonitorReceiver.java
  • F:\zdCode\xugong\YX_Voice\src\main\java\com\yaxon\voice\helper\VoiceEventHelper.java
  • F:\zdCode\xugong\YX_Launcher\src\main\java\com\yaxon\launcher\view\WidgetsMedia.java
  • F:\zdCode\xugong\YX_SystemUI\src\main\java\com\android\systemui\sdk\view\navigation\MediaControlView.java