Files
2026-11-05 14:35:04 +08:00

504 lines
126 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# agent.md
## 项目目标
新项目 `F:\zdCode\tongli` 的蓝牙业务交互、需求和 UI,需要对齐旧项目 `F:\zdCode\tongli_old` 的蓝牙体验。
用户明确要求:
- 业务逻辑一样。
- UI 和业务交互要一样。
- 代码可以不参考旧项目。
- 当前阶段先梳理,不急着改代码。
## 当前阶段
蓝牙模块主界面迁移开发阶段。
已生成详细文档:
- `蓝牙业务迁移梳理_2026-07-09.md`
- `蓝牙开发实施方案_2026-07-09.md`
## 当前环境
- 当前系统:Windows
- 当前 shell:PowerShell
- 当前工作目录:`F:\zdCode\tongli`
- 旧项目参考路径:`F:\zdCode\tongli_old`
## 核心结论
这不是单纯 UI 换皮,而是要按旧项目蓝牙业务基线,对齐整套车机蓝牙体验。
新项目已有承接点:
- 蓝牙电话:`YX_BlueTooth`
- 蓝牙 SDK:`YX_BluetoothSDK`
- 蓝牙音乐:`YX_BtMusic`
- 桌面卡片:`YX_Launcher`
- 状态栏/快捷蓝牙:`YX_SystemUI`
更新 2026-07-14:`YX_BtMusic` 当前仅保留历史代码,APK 已不再集成;当前运行链路中的蓝牙音乐入口归入 `YX_BlueTooth`,蓝牙能力仍统一由 `YX_BluetoothSDK` 封装。用户已从 `third_apps/preinstall.mk` 删除 `YX_BtMusic` 预装项;根目录 release 打包脚本也已移除主动构建 `:YX_BtMusic:assembleRelease`。本地仍存在部分旧包名常量,先按历史入口残留处理,后续结合入口迁移需求清理。
旧项目核心业务入口:
- 蓝牙拨号
- 联系人
- 蓝牙音乐
- 蓝牙设置
- 通话记录
- 来电/拨号/通话中/三方通话
## 关键决策
- 不搬旧代码,优先抽旧项目业务行为和 UI 状态。
- 新平台保持现有 SDK/ViewModel/Activity 架构,按旧业务补齐交互。
- 后续开发前应先确认页面资产清单、弹窗清单、状态清单。
- 开发策略:旧项目作为需求基线和验收标准,新项目作为实现架构和平台能力。
- 三方通话在老项目中明确存在,应作为新项目高风险专项验收。
- 当前阶段只专注 `YX_BlueTooth` 蓝牙模块,不考虑 SystemUI、Launcher、MCU 或其他模块联动。
- 主界面先按老项目四入口落地:蓝牙拨号、联系人、蓝牙音乐、蓝牙设置。
- 资源优先从老项目复用,不从截图切图;代码继续使用新项目架构。
- 顶部栏“蓝牙”标题先不作为 `YX_BlueTooth` 蓝牙模块功能处理;老项目该标题属于顶部栏/主壳视觉,当前蓝牙模块内先隐藏,避免压住左侧菜单。
- 长期边界:`YX_Setting` 保持无蓝牙;`YX_BlueTooth` 是唯一蓝牙 UI 和业务入口;`YX_BluetoothSDK` 是唯一蓝牙能力封装。
- `YX_BtMusic` 不进入当前 APK 集成和运行链路,只作为历史代码保留;本次不修改其代码本体。
## 蓝牙后续分析基线(2026-07-24)
- 已结构化回顾历史会话 `019f406a-be1b-7722-bb19-96f14e36fa5e`。后续蓝牙分析统一以本节为需求口径,不再沿用会话中已被撤回的临时结论。
- 最终实现原则:保留新项目的协议、数据、PBAP、数据库和同步状态逻辑;旧项目只作为 UI、交互和用户可见状态的基线。该结论覆盖早期“业务逻辑也完全照旧项目”的表述。
- 证据优先级:当前代码与当前 Git 历史 > `agent.md` 中较晚的“更新标记” > 较早记录、交接摘要和临时提交号。运行行为以最新实车复现和日志为准,`BUILD SUCCESSFUL` 只能证明编译通过;若证据冲突,必须重新核对代码、提交 diff 和实车日志。
- 范围继续限定为 `YX_BlueTooth` 与 `YX_BluetoothSDK`。除非用户重新授权,不扩展到 SystemUI、Launcher、MCU 或其他模块;Launcher 临时将音乐入口改为蓝牙的代码不属于蓝牙正式提交。
- 四入口维持“蓝牙拨号 / 联系人 / 蓝牙音乐 / 蓝牙设置”。拨号和联系人依赖 HFP,音乐依赖 A2DP + AVRCP,设置始终可用;未连接时引导到设置页,HFP 重连后尽快进入拨号页。
- 通话记录和联系人未就绪时显示加载/失败状态,不先显示空白或误导性权限结果;同步成功只刷新列表,不增加成功 Toast。
- 手动下载通讯录保留旧项目式确认与进度弹框,文案使用“正在下载通讯录列表,请稍等...”;解析进度封顶 95%,数据库保存成功后显示 100% 并短暂保留。联系人不读取真人头像。
- 后续重点验证:PBAP 连续下载、断线、超时、空通讯录、空通话记录、双设备迟到事件、数据库故障和切页/后台恢复;HFP 扬声器路由、蓝牙音乐系统 APEX 自动暂停、MCU 媒体键抖动和三方通话仍是独立未闭环专项。
- 当前验证以编译、截图、ADB 和实车日志为主,有效单元测试与自动化覆盖不足;提交或宣称修复完成前必须重新审查当前 Git 差异并执行对应异常场景测试。
- 历史会话在 2026-07-24 最后 8 次请求均因上游 HTTP 503 结束且没有生成助手答复;这是服务不可用,不代表会话丢失、线程 ID 无效或代码任务失败。
## 蓝牙深度静态分析基线(会话 019f91d1-340f-72b2-8e3e-c94249a57be6)
- 已读取本机会话归档 `C:\Users\Administrator\.codex\archived_sessions\rollout-2026-07-24T09-50-39-019f91d1-340f-72b2-8e3e-c94249a57be6.jsonl`。会话名称最终为“蓝牙模块 0724”,工作目录为 `F:\zdCode\tongli`,内容覆盖历史修改回顾、会话 `019f406a-be1b-7722-bb19-96f14e36fa5e` 的结构化分析,以及当前蓝牙代码的深度静态检查。
- 当前架构主链路为 `Activity / Fragment -> ViewModel -> ControlManager -> Bt*Manager -> HFP / A2DP / AVRCP / PBAP / MediaBrowser / SQLCipher`。拨号和联系人依赖 HFP,音乐依赖 A2DP + AVRCP,设置始终可进入;正常单设备连接、PBAP 同步和页面门禁主流程基本闭环。
- 安全最高优先级风险:`BluetoothService` 对外导出且缺少权限保护,外部应用可能通过显式 Intent 或广播触发拨号、挂断等系统权限操作。修复方向为取消不必要的导出,或增加 signature 级权限并限制广播来源。
- PBAP 最高优先级风险:有效空通话记录不能稳定表示为“同步成功”,之后产生的第一条本地通话也可能因同步状态判断而无法入库;同步超时后后台数据库事务仍可能提交;同一设备连续同步缺少真正的会话 ID,迟到广播可能串入新一轮;广播缺少设备信息时存在双设备串代风险;已有缓存时同步失败可能使通话记录页面永久停留在 loading。
- 更新标记(修正 2026-07-22“原子终态标志互斥,避免事务提交与超时竞态”的记录):当前原子终态只能互斥 UI 层成功/失败通知,不能保证超时后旧后台事务停止或禁止提交,仍可能出现“UI 已失败,旧任务随后覆盖数据库”。后续须以事务代次校验、提交前终态检查和强制超时测试重新验证。
- 更新标记(细化 2026-07-23“同步代次隔离上一轮后台任务”的记录):应用内 generation 只能隔离已接入该代次检查的任务,不能替代平台 PBAP 会话 ID;同 MAC 上一轮迟到广播以及未携带设备信息的广播仍可能被当前轮接受,须通过连续同步和双设备迟到事件实测。
- 生命周期与交互风险:Activity 重建可能留下重复 Fragment、ViewModel 和监听;`BtMusicViewModel`、`SettingViewModel`、`CallHistoryViewModel` 等存在监听释放缺口,音乐监听移除接口还存在参数类型不匹配风险;隐藏的音乐 Fragment 仍可能申请音频焦点;自动接听只在完整通话 Activity 中执行;匿名或网络电话空号码可能导致挂断失败但页面先关闭;设置流程调用全局 `HandlerUtil.removeAll()` 可能误删电话 pending 超时和 Profile 回调任务。
- 历史归属:`e233a2d1` 主要涉及 PBAP、事务和超时保护,上述空记录、超时提交和迟到事件风险集中在该状态机;`c439bcfd`、`d7bca4ba` 主要负责菜单/Profile 刷新;`8ec79048`、`8cb0e58e` 主要负责蓝牙音乐页面和播放进度。
- 建议验证顺序:外部 Intent/广播安全测试 -> 空通讯录和空通话记录 -> 强制数据库事务超时 -> 同 MAC 连续同步及双设备迟到广播 -> Activity 重建后的 Fragment/监听计数 -> APEX 音频焦点、后台自动接听和匿名电话挂断。
- 本会话最后一轮仅完成 Windows PowerShell 环境下的静态只读分析,没有运行 Gradle、ADB 或实车测试。以上问题属于待复核风险,后续修复前必须重新对照当前代码、Git 差异和最新实车日志,不能直接视为已复现缺陷。
## 蓝牙音乐后续分析基线(会话 019f4080-a188-7aa0-98b6-92d3e5f10fb2)
- 已只读回顾该会话从 2026-07-08 到 2026-07-24 的本地 JSONL 转录,并用当前代码、Git 历史和 `Y:\tongli\android14.0_rk3562` 系统源码复核关键结论。后续蓝牙音乐问题应先以本节为口径,再结合最新实车日志判断。
- 当前产品链路为:`YX_BlueTooth` 提供唯一蓝牙音乐 UI,`YX_BluetoothSDK` 负责 A2DP Sink、AVRCP、歌曲信息和控制能力;`YX_BtMusic/com.yaxon.btmusic` 仅保留历史代码,不进入当前 APK 集成和运行链路。音乐入口门禁依赖 A2DP 与 AVRCP 均连接。
- 蓝牙音乐 UI 已通过提交 `8ec79048 modify(YX_BlueTooth):新增蓝牙音乐功能` 集成:主界面 `BtMusicFragment` 和独立 `BluetoothMusicActivity` 对齐旧项目圆形封面、歌曲信息、进度条和上一首/播放暂停/下一首按钮,控制逻辑继续复用新项目 `BtMusicViewModel`。
- “点击播放后立即暂停”的系统侧根因是 `AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED` 在非活动、焦点错误或无焦点分支主动下发 AVRCP `PAUSE(70)`。正常分支 `Y:\txj\android14.0_rk3562` 的提交 `926b121d63500ff2f7f9c6305d106e45c8fc1f68` 注释了这三处自动暂停;该差异已同步到 `Y:\tongli\android14.0_rk3562` 并提交为 `3d18d82958b fix(Bluetooth):修复蓝牙音乐播放自动暂停`。当前目标文件工作区干净,用户实车确认该修复可解决播放即暂停。
- 7 月 13 日曾尝试恢复完整 A2DP Sink 焦点状态机、让应用通过 AVRCP `prepare()` 委托系统焦点以及增加应用侧失焦防重;这些属于中间试验,用户随后已还原,不是最终实现基线。旧独立 `com.yaxon.btmusic` 也曾在测试固件中造成第二次暂停,但当前已不集成,不能继续作为默认根因。
- “播放进度条不变化”的直接原因是 AVRCP 位置已每秒上报,但 `PlaybackStateCompat.getExtras()` 经常为 null,旧代码因此没有写回 `playPosition`。提交 `8cb0e58e modify(YX_BlueTooth):修改蓝牙音乐进度条不会刷新问题。` 已改为 `playPosition >= 0` 时直接更新;当前代码和 Git 历史均确认该修复仍在。
- `BtMusicManager.onPlaybackStateChanged()` 当前仍保留 `isPlaying && !mBtMusicAudioManager.isFocus()` 时调用 `setA2dpMute()` 和 `pauseBtMusic()` 的旧逻辑。7 月 14 日临时加入的 1200ms 延迟失焦策略已撤回,临时 `pauseBtMusic()` 调用栈日志在当前代码中也不存在。若再次出现自动暂停,必须先按时间线区分 `com.android.bluetooth` 发出的系统 `PAUSE(70)` 与 `BtMusicManager`、UI、MCU、语音或外部控制触发的应用侧暂停,不能直接沿用旧结论。
- 已有学习文档:`F:\AI\Obsidian\项目文档\同力\蓝牙音乐\蓝牙音乐模块功能流程梳理_2026-07-13.md`。后续实车验收重点为 A2DP/AVRCP 连接门禁、播放/暂停、上下首、歌曲元数据、进度连续刷新、音源切换、来电后恢复、断线重连和后台恢复。
## 已完成内容
- `YX_BlueTooth` 主界面从“键盘 + 通话记录/联系人横向页签”改为老项目式“左侧四菜单 + 右侧页面容器”。
- 新增 `DialHomeFragment`:左侧拨号键盘,右侧嵌入现有 `CallLogsFragment`,并保留号码输入后的联系人匹配列表。
- 新增 `BtMusicFragment`:复用现有 `BtMusicViewModel` 和蓝牙音乐控制逻辑,作为主界面内切页面。
- 新增 `BtSettingFragment`:复用现有 `BtSettingViewModel`、已配对设备/可用设备 Adapter 和扫描连接逻辑,将原弹窗式设置迁成页面式入口。
- `ContactsFragment` 继续复用,主界面右上角保留搜索和重新同步入口。
- 已从老项目拷贝主界面视觉资源:`activity_bg.png`、`common_bg_line.png`、`bluetooth_tabvertical.xml`、`text_click_selector.xml`、`icon_tabvertical_selected.png`、`title_head_background.png`。
- 已将 `BtSettingFragment` 主页面改成老项目式设置列表结构:自动接听、蓝牙开关、蓝牙名称、已配对设备、可用设备。
- 已将设置页自动接听开关接到新平台 SDK 既有 `setAutoAcceptCall/isAutoAcceptCall` 能力。
- 已对齐蓝牙主界面标题资源:顶部标题为“蓝牙”,四个左侧入口为“蓝牙拨号 / 联系人 / 蓝牙音乐 / 蓝牙设置”。
- 已将蓝牙设置页两个开关切换为老项目蓝色开关资源 `common_switch_on/off`,尺寸对齐老项目 `72dp x 40dp`。
- 已修正左侧四入口菜单选中态文字颜色:`text_click_selector` 增加 `state_selected=true` 白色,适配 `MenuAdapter` 的 `TextView.setSelected(true)`。
- 已按老项目补齐蓝牙设置页“蓝牙名称”二级编辑页:点击名称行后隐藏设置列表,显示 360x64 输入框与“确定”按钮;确认后调用新平台 SDK 设置本机蓝牙名称,成功后返回设置列表并刷新名称。
- 已补齐蓝牙名称设置结果提示:保存成功/失败不再使用系统 Toast,改为老项目样式居中结果弹层,成功显示 `icon_success` 和“蓝牙设置成功”,失败显示 `ico_failed` 和“蓝牙设置失败”。
- 已按老项目继续对齐蓝牙设置页前三行 UI:行高改为 88dp,左侧标题改为 30sp/#F3F3F3,蓝牙名称右侧值改为 20sp/#E6E6E6,并改用老项目 48x48 右箭头 selector 资源。
- 已按老项目继续对齐蓝牙拨号页 UI:键盘改为 356dp 宽、按键 116x60、4dp 间距,拨号按钮改为 356x68 旧项目整条绿色按钮;号码输入区删除键挪回顶部右侧;通话记录 item 改为图标 + 号码 + 联系人/未知号码两行样式。
- 根据新旧拨号截图对比,确认新项目拨号页主要偏差不在键盘资源,而在 `YX_BlueTooth` 主壳额外标题区导致整体内容下移;已将主壳改回更接近老项目的坐标:隐藏 80dp 大标题、顶部横线使用老项目 916x40 资源高度、左侧菜单从根布局 37dp 起、右侧内容从根布局 4dp 起,菜单选中胶囊改回老项目 172x60。
- 已按老项目拨号盘截图和 `fragment_keyboard_test.xml` 进一步对齐拨号盘本体:数字键只显示居中数字/符号,不显示 ABC/GHI 等副标题;删除键按老项目保持常显;号码文字改为 `#DFDFDF`,号码下划线改为 `#505050`,数字键文字改为 `#F3F3F3`。
- 根据用户新旧拨号截图像素对比,确认新项目绿色拨号按钮纵向范围为 y=511-578,老项目为 y=482-549,新项目拨号内容整体低约 29px;已将 `fragment_dial_home.xml` 中拨号键盘顶部 margin 从 40dp 调整为 11dp,并移除 `fragment_call_logs.xml` 通话记录 RecyclerView 顶部 28dp margin,让右侧记录同步上移。
- 用户确认只改蓝牙模块后,继续按老项目拨号页对齐 UI 坐标:`activity_main.xml` 中左侧菜单调整为 start=36dp/top=30dp,右侧容器 start 改为 0dp;`fragment_dial_home.xml` 中拨号盘 start=24dp/top=0dp;`fragment_call_logs.xml` 中通话记录列表顶部 margin 调整为 16dp。只涉及 `YX_BlueTooth`。
- 根据新旧拨号截图继续修正右侧通话记录:号码不再拼接通话次数 `(callTimes)`,避免比老项目更早省略;号码和联系人 TextView 改为占满右侧剩余宽度,去掉固定 `maxWidth=340dp`;列表右边距按老项目收窄到 5dp;通话记录号码色值改为老项目白色 `#F3F3F3`、联系人灰色 `#B7B9C0`、未接红色 `#FE0404`。
- 根据 2026-07-10 新旧拨号截图复核,确认号码显示已完整,剩余主要偏差是拨号盘偏左/偏下、通话记录偏左/偏上;已将 `fragment_dial_home.xml` 中拨号盘 start 从 24dp 调到 36dp、top 从 0dp 调到 -9dp,右侧通话记录容器 start 从 38dp 调到 50dp;将 `fragment_call_logs.xml` 列表顶部 margin 从 16dp 调到 40dp,使布局更接近老项目截图。
- 根据 2026-07-10 改后截图复核,拨号盘本体已基本对齐,右侧通话记录存在号码省略和列表偏低;已保持拨号盘不动,将 `fragment_dial_home.xml` 右侧通话记录容器 start 从 50dp 收到 42dp 以释放号码宽度,将 `fragment_call_logs.xml` 列表顶部 margin 从 40dp 回调到 24dp。
- 根据 adb 截图复核,右侧通话记录仍存在 `15659813.../18250742...` 省略,判断主界面 `activity_main.xml` 的右侧容器 `container` 仍有 `38dp` 右边距,压缩了通话记录可用宽度;已将 `container` 的 `layout_marginEnd` 从 `@dimen/dip_38` 改为 `0dp`,优先保证 11 位手机号完整显示。
- 根据老项目 `KeyBoardFragment/SeparatorEditText` 和老项目截图 13.png 复核,老项目输入号码时右侧仍保持通话记录列表,不显示“最佳适配”;号码显示使用 `SeparatorEditText` 空格分隔规则,纯数字长度不超过 11 时在第 3 位和第 8 位后插入空格。已在新项目中调整:`DialHomeFragment` 输入号码后不再切换 `isMathCalls=true`,不再触发联系人匹配搜索;`KeyBoardView.setDialNumber()` 按老项目规则格式化显示号码,但内部拨号仍使用原始号码。
- 根据 2026-07-10 通话记录点击后“等很久才弹通话页”的日志分析,确认应用点击和 `ATD` 拨号命令已快速发出,慢点主要在等待 HFP `CALL_CHANGED/CLCC` 回调以及底层音频链路异常。已先做应用层体验兜底:`CallRecordAdapter` 对通话记录点击加 2 秒防连点,`dialing()` 返回成功后立即以本地 pending 拨号状态打开 `BluetoothPhoneActivity`;`BluetoothPhoneActivity` 针对 `EXTRA_PENDING_LOCAL_DIAL` 放行 `isPhoneCalling()==false` 的短窗口,并增加 15 秒超时保护,真实 HFP 回调到达后清除 pending 并刷新状态。
- 根据老项目联系人截图 `联系人4.png` 与旧项目 `fragment_phonebook.xml/listitem_phonebook.xml`,已对齐新项目联系人页:搜索框和下载按钮固定在联系人内容顶部,隐藏新项目 A-Z 索引和分组字母;联系人列表行改为 88dp 高、60dp 联系人图标、左侧姓名 26sp、右侧电话号码 20sp;主界面右上角联系人搜索/刷新入口保持隐藏,改用页面内老项目样式搜索/下载按钮;搜索逻辑保留新项目 ViewModel,但空关键词时恢复正常列表,有关键词时切到搜索结果列表。
- 根据 2026-07-10 联系人实际截图复核,新项目联系人内容整体偏右偏下;已二次微调 `fragment_contacts.xml`:搜索区 marginStart 从 132dp 改为 38dp、marginTop 从 88dp 改为 4dp,列表/搜索结果 marginStart 从 132dp 改为 24dp,并将联系人搜索提示从“搜索”改为老项目“请在联系人中搜索”。
- 根据 2026-07-10 14:17 联系人截图和老项目截图做像素对比:老项目搜索框约为 x=362..878,下载按钮约为 x=896..959;新项目为 x=350..912、x=930..993。已继续调整 `fragment_contacts.xml`:搜索区宽度 652dp -> 614dp,start 38dp -> 50dp,搜索输入 marginEnd 88dp -> 98dp;联系人列表 start 24dp -> 36dp,让搜索框、下载按钮和列表左边界更贴近老项目。
- 根据 2026-07-10 14:23 联系人截图与老项目继续对比:搜索框本体已基本对齐,但下载按钮仍右偏约 8px,联系人分割线右端因 `share_contact_lv_line.xml` 内缩 65dp 明显短于老项目,且顶部“蓝牙”标题被隐藏。已恢复 `activity_main.xml` 顶部标题显示,搜索区宽度 614dp -> 606dp、输入框右边距 98dp -> 88dp,将下载按钮左移;`share_contact_lv_line.xml` 取消左右内缩,让分割线拉到右侧号码区域后方。
- 根据 2026-07-10 14:33 联系人截图复核,恢复 `activity_main.xml` 顶部“蓝牙”标题后会压到左侧菜单“蓝牙拨号”,当前新项目主壳坐标体系不适合显示该全局标题;已将 `tv_main_title` 重新隐藏,保留联系人主体坐标、搜索框、下载按钮和列表分割线调整。
- 根据 2026-07-10 15:05/15:06 截图复核,左侧系统栏/主界面可用宽度变化后,蓝牙主壳左侧菜单和页面容器整体右移,导致联系人页右移、拨号页右侧通话记录宽度被压窄,11 位号码重新显示省略号。已调整 `activity_main.xml`:顶部蓝线 start 从 48dp 改为 20dp,左侧菜单 start 从 36dp 改为 8dp;调整 `fragment_contacts.xml`:搜索区 start 50dp -> 38dp,列表 start 36dp -> 24dp;调整 `fragment_dial_home.xml`:右侧通话记录容器 start 42dp -> 24dp,以恢复联系人坐标并释放通话记录显示宽度。
- 根据蓝牙设置页新旧截图和老项目 `fragment_blueforum.xml/listitem_forum.xml/ForumAdapter.java`,已对齐设置页设备列表 UI:补齐旧项目搜索按钮、列表背景、分割线、下拉箭头资源;已配对/可用设备 item 改为 64dp 行高、左侧 36dp 手机图标、26sp 设备名、24sp 状态文字和右侧箭头;移除列表行内直接显示的“断开”大按钮;可用设备标题固定显示“可用设备”,避免有设备列表时仍显示“请搜索可用设备!”。
- 根据老项目 `蓝牙设置6.png` 继续对齐设置页设备列表细节:设备行左侧图标改用老项目 `drawable-sw600dp/icon_iphone.png` 小矩形手机图标;可用设备标题右侧补旧项目同款 32dp `ProgressBar`,扫描时显示在“可用设备”文字后,最右侧搜索按钮保持常显。
- 根据老项目 `progressbar_bg.xml` 资源链路修正可用设备搜索菊花:旧项目动画引用 `@drawable/progress_loading_bar`,已复制旧项目 `drawable-sw600dp/progress_loading_bar.png` 到新项目并将新项目 `progressbar_bg.xml` 从 `@drawable/icon_loading` 改为 `@drawable/progress_loading_bar`。
- 根据老项目 `fragment_setting.xml` 继续修正设置页前三行分割线:将新项目 `fragment_bt_setting.xml` 中外层主列表容器 `bt_layout` 从 `LinearLayoutCompat` 改回普通 `LinearLayout`,使 `android:showDividers="middle"` 和 `android:divider="@drawable/shape_my_divider"` 行为更贴近老项目。
- 已按用户要求先提交当前蓝牙设置设备列表 UI 对齐版本,提交号 `31f1ac3e`,未推送;随后补齐已配对设备点击后的老项目式下拉 popup:箭头展开/收起、连接/断开、删除、点击外部关闭,并复用老项目 `180dp x 152dp` 深色弹层样式。
- 根据 2026-07-10 用户提供的蓝牙音乐新旧截图,已按旧项目 `fragment_music.xml` 对齐蓝牙音乐 UI:主界面 `BtMusicFragment` 和独立 `BluetoothMusicActivity` 均改为旧项目圆形默认封面、歌名/歌手居中、旧项目图片进度条和底部三颗圆形上一首/播放暂停/下一首按钮;隐藏新项目音效扁按钮;封面行为按旧项目固定显示 `icon_bluetooth_music`,不再使用手机专辑图;播放控制逻辑继续复用新项目 `BtMusicViewModel`。
- 根据 2026-07-10 19:49 蓝牙音乐实机截图与老项目效果图复核,二次修正主壳和音乐页坐标:恢复顶部“蓝牙”标题并按老项目 30sp 显示,顶部蓝线下移 10dp,左侧菜单按老项目 `activity_main.xml` 下移到 50dp,菜单横向回到 0dp;音乐封面/歌名/歌手下压 6dp,同时收窄歌手到进度条距离,保持进度条和底部三颗控制按钮位置贴近老项目。
## 验证结果
- 已使用 Android Studio JBR 临时 Java 17 环境编译通过:
```powershell
$env:JAVA_HOME='C:\Program Files\Android\Android Studio\jbr'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain
```
- 结果:`BUILD SUCCESSFUL`。
- 注意:默认系统 Java 是 11,直接编译会失败;工程当前 AGP 8.8 需要 Java 17。
## 待办事项
1. 上车/模拟器实测四个入口切换是否和老项目 UI 效果一致。
2. 对齐拨号页细节:号码显示、删除键、未连接提示、点击通话记录拨号。
3. 对齐联系人页细节:搜索、重新同步、空状态、未授权状态。
4. 对齐蓝牙音乐页细节:未连接状态、播放/暂停、上一首/下一首、歌曲信息刷新。
5. 对齐蓝牙设置页细节:蓝牙开关、设备扫描、连接/断开/删除、蓝牙名称修改。
6. 后续再专项处理通话页和三方通话验收。
7. 蓝牙设置页下一轮继续补齐:已配对设备点击弹窗、可用设备连接确认弹窗、打开/关闭蓝牙进度提示。
## 更新记录
- 2026-07-27:结合 `tongli_2026_07_27_14_50_12.log` 与旧项目复核 HFP 重连后的通话记录交互。日志确认新平台自动 PBAP 同步正常,但重连后约 20 秒内会多次读取并展示旧数据库记录;旧项目行为是立即清空旧列表、在列表区域显示“正在下载通话记录”,同步完成后再展示最新记录。实现决策:不重复发起 PBAP 请求,仅在 HFP 重连路径等待本轮通话记录同步完成,并过滤完成前的旧记录回调。
- 2026-07-24:按用户要求备份后,读取并记录会话 `019f91d1-340f-72b2-8e3e-c94249a57be6`。新增“蓝牙深度静态分析基线”,固化该会话的架构结论、安全/PBAP/生命周期风险、历史提交归属和建议验证顺序;同时明确这些风险尚未经过本轮 Gradle、ADB 或实车复测。修改前备份为 `F:\zdCode\tongli-backup-20260724-115233\agent-before-session-019f91d1-record.md`,源文件与备份的 SHA-256 一致。本次仅修改 `agent.md`,未修改蓝牙业务源码。
- 2026-07-24:按用户要求且不备份,读取会话 `019f4080-a188-7aa0-98b6-92d3e5f10fb2` 的本地 JSONL 转录,并用当前应用代码、Git 提交和 Android 14 系统源码复核蓝牙音乐结论;新增“蓝牙音乐后续分析基线”,区分已落地修复、已撤回试验和后续日志判定方法。本次仅更新 `agent.md`,未修改蓝牙代码。
- 2026-07-22:根据 `tongli_2026_07_22_15_53_54.log` 确认连续 7 轮通讯录与通话记录同步均成功;重复点击时出现“通话记录正在同步中”,根因是联系人和通话记录共用 PBAP busy 状态,数据保存完成后仍有约 1~2 秒断开收尾。保留底层并发保护和现有 `100% -> 400ms -> 关闭` 交互,不提前清除 pulling 状态;移除 `BtContactsManager` 的固定 Toast,改由 `ContactsFragment` 在显示进度弹框前拦截重复点击并提示“通讯录同步尚未完成,请稍后”。Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-22:根据 10:57~10:58 三轮实车日志复核,258 条联系人每轮均完整成功,PBAP 约 8.6~8.9 秒、本地解析约 7.3 秒、保存约 0.5 秒;保存完成后进度弹框均在约 400ms 后关闭,确认 100% 展示闭环生效。针对等待阶段 48dp 橙色菊花切换到 148dp 蓝色圆环造成的视觉跳变,改为全程复用同一个 148dp 蓝色圆环:PBAP 阶段显示不确定旋转弧,本地解析阶段原位平滑切换到真实百分比;提示语恢复老项目“正在下载通讯录列表,请稍等...”。使用 Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-22:补齐通讯录下载完成与保存失败闭环。`DataContactsHelper.savePhoneBookList()` 改为返回 SQLCipher 事务结果,`BtContactsManager` 在保存失败时进入既有同步失败处理,不再继续上报 100% 和成功列表;成功时 `ContactListViewModel` 保留 100% 进度约 400ms 后再隐藏,`ContactsFragment` 不再因联系人列表先到而提前关闭弹框。使用 Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-22:按老项目需求确认不需要真人联系人头像。旧项目 `PhoneBook` 仅保存姓名、拼音和号码,列表使用固定联系人图标;新项目联系人列表也未消费 `photoData`,通话页虽读取该字段但当前未绑定到头像控件。已从 `ContactUtils.getAllContacts()` 的逐联系人解析循环中移除 `PHOTO_URI/data15` 查询,避免有头像联系人额外执行 Provider 查询和读取 Blob;保留 `Contacts` 模型及数据库头像字段,避免 Schema 和接口变更。姓名、号码、拼音、T9、排序、去重逻辑不变。使用 Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过。该改动减少应用本地解析耗时,但不能修复系统 Bluetooth vCard 阶段的 `Decode error on base64 photo`。
- 2026-07-21:根据 `tongli_2026_07_21_20_00_33.log` 优化联系人手动下载交互。日志确认 PBAP 远端传输约 8.6 秒、联系人本地解析约 7.4 秒、258 条写库约 0.58 秒,功能链路正常;原先 PBAP 无进度阶段显示 `0%` 会造成卡住错觉。现将手动下载与自动同步的 UI 状态分离:手动点击后立即显示弹框并保留已有联系人列表作为背景,不再先闪整页 loading;PBAP 传输阶段显示不确定进度圈,收到真实本地解析进度后才切换百分比圆环;成功、失败、切页和生命周期退出时清理手动弹框。自动连接同步仍使用联系人页内 loading,不弹手动下载弹框。使用 Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-21:分析 `tongli_2026_07_21_19_47_53.log`,确认 6 次手动通讯录下载均完整成功,单轮约 21~27 秒;进度到 100% 后仍等待约 4~5 秒,根因是 `ContactUtils` 在解析结束即上报 100%,随后 `DataContactsHelper.savePhoneBookList()` 对 258 条联系人逐条独立提交 SQLCipher。首轮还因设置 PBAP priority 自动触发连接后再次显式 `connect()`,第二次返回 false 被误判为失败。已修改:联系人解析进度封顶 95%,数据库事务提交完成后上报 100%;联系人批量写库改为单事务;PBAP connect 返回 false 后若实际状态为 CONNECTING/CONNECTED 则继续等待,不再报失败;HFP 手机连接成功后重新安排未同步状态检查。`:YX_BlueTooth:assembleDebug` 编译通过。实车复测关注 `PBAP connect already in progress`、`contacts read completed`、`contacts save completed` 的时间差。
- 2026-07-21:分析 `tongli_2026_07_21_19_28_43.log`,确认联系人页长期停留“正在同步通讯录”属于假加载:日志中 `phoneBookPullRequest/MSG_DOWNLOAD/onContactsPullCompleted/download.failed` 均为 0,且 `isPhoneBookPulling=false`。根因是蓝牙应用进程启动时手机 HFP/A2DP 已连接,PBAP 仅回调 profile proxy 就绪,应用错过此前的 PBAP 连接广播;同时 `ContactsFragment` 将“未同步且未失败”无条件显示为加载态。已增加 PBAP proxy-ready 状态补查:已连接手机未同步且无任务时只恢复一次下载;联系人页仅在真实 pulling 状态显示加载,否则显示“通讯录未同步”。`:YX_BlueTooth:assembleDebug` 编译通过。实车复测应检查 `reconcile PBAP service ready`、`phoneBookPullRequest`、`MSG_DOWNLOAD`、`onContactsPullCompleted` 日志链路。
- 2026-07-09:创建蓝牙业务迁移长期索引,承接昨日截图和项目梳理上下文。
- 2026-07-09:补充蓝牙开发实施方案,明确按“旧项目需求基线 + 新项目架构实现”推进。
- 2026-07-09:本次需求“不备份”后执行;在 `YX_McuService` 启动阶段关闭 Android `show_touches` 与 `pointer_location` 系统开关,用于去掉屏幕触点显示和坐标显示。
- 2026-07-09:用户补充“应该是调试模式,点击有触点”;已将处理方式升级为监听 `show_touches` / `pointer_location` 变化,调试模式重新打开后自动拉回关闭。
- 2026-07-09:用户确认只专注蓝牙模块,不考虑 SystemUI 和其他模块;已完成 `YX_BlueTooth` 主界面四入口骨架并编译通过。
- 2026-07-09:按老项目 `fragment_setting.xml` 结构改造新项目蓝牙设置主界面;`BtSettingViewModel` 补出自动接听接口封装;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户反馈“页面标题不对”,修正 `YX_BlueTooth` 标题资源:`app_name=蓝牙`、`keyboard=蓝牙拨号`、`contacts=联系人`,保留 `music=蓝牙音乐`、`setting=蓝牙设置`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据截图反馈蓝牙设置页开关应参考老项目蓝色样式;从旧项目复制 `common_switch.xml/common_switch_on.png/common_switch_off.png`,设置页自动接听和蓝牙开关改为 `72dp x 40dp` 老项目开关;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户反馈左侧“蓝牙拨号/联系人/蓝牙音乐/蓝牙设置”选中后字体应为白色;确认新项目 Adapter 使用 `TextView.setSelected(true)`,而 `text_click_selector` 缺少 `state_selected`,已补选中态白色;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:参考老项目 `fragment_bluename.xml` 和截图,补齐新项目 `BtSettingFragment` 内部蓝牙名称编辑页;复制旧项目 `edit_bg_selector/edit_confirm_bg_selector` 及 360x64 输入框/按钮图片资源,新增确认保存、空名称提示、未开蓝牙提示、返回键回列表、切换菜单退出编辑页逻辑;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户反馈老项目蓝牙名称保存成功有绿色成功图标弹层;新增 `BluetoothResultToast` 和 `toast_result_layout.xml`,复制旧项目 `common_toast_bg.png/icon_success.png/ico_failed.png`,蓝牙名称保存成功/失败改为老项目样式结果提示;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据 MasterGo 与老项目 `fragment_setting.xml` 对照,蓝牙设置页自动接听、蓝牙开关、蓝牙名称三行改为老项目 88dp 行高和 30sp 标题;蓝牙名称值改为 20sp/#E6E6E6、右侧间距 64dp,并从老项目复制 `btn_arrow_right_selector.xml/arrow_left.png/arrow_left_pressed.png`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据老项目蓝牙拨号页截图和 `fragment_keyboard_test.xml/listitem_phonerecord_new.xml` 对照,复制旧项目 `dial_keyboard/button_diag/keyboard_delete/icon_phone_*` 等资源,调整 `view_keyboard_dial.xml/fragment_dial_home.xml/listitem_phonerecord.xml` 和 `CallRecordAdapter`;保留原拨号/删除/长按 0 输入 +/点击通话记录拨号逻辑;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户提供的新旧拨号截图继续修正拨号主界面偏差;对齐老项目主壳坐标,修改 `activity_main.xml/listitem_main.xml/dimens.xml`,让左侧菜单、键盘、通话记录整体上移并使用老项目 172x60 选中项尺寸;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户明确要求“拨号盘 UI 按老项目”,继续修改 `view_keyboard_dial.xml/KeyBoardView.java/theme.xml/colors.xml`;去除数字键字母副标题、删除键常显、拨号盘文字/线色按老项目色值对齐;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据新旧拨号截图继续微调坐标;将拨号盘整体上移约 29dp,通话记录列表去掉顶部 28dp margin;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:用户确认“只改蓝牙模块”后,继续对齐老项目拨号页相对间距;调整左侧菜单、拨号盘和通话记录列表坐标,只修改 `YX_BlueTooth` 资源布局;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-09:根据用户反馈“通话列表电话号码显示不全”,修正 `listitem_phonerecord.xml/fragment_call_logs.xml/CallRecordAdapter.java/colors.xml`;去掉通话次数后缀、释放号码文字宽度并按老项目色值对齐;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份,直接改”后,根据新旧拨号截图继续微调拨号页坐标;只修改 `fragment_dial_home.xml` 与 `fragment_call_logs.xml`,拨号盘右移并上移,通话记录区域右移且列表下移;首次编译因不存在 `@dimen/dip_42` 失败,改用现有 `@dimen/dip_40` 后 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份,直接改”后,继续按改后截图微调右侧通话记录;`ll_dial_right` start 改为字面量 `42dp`,`lv_record` top 改为 `@dimen/dip_24`;首次编译因项目无 `@dimen/dip_42` 失败,改为字面量后 `:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份,直接改”后,根据 adb 截图继续修正通话记录号码显示宽度;将 `activity_main.xml` 中 `container` 右边距从 `@dimen/dip_38` 改为 `0dp`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份,直接改”后,根据老项目输入号码截图和 `SeparatorEditText` 逻辑修正拨号输入交互;输入号码后右侧保持通话记录,不再显示“最佳适配”,号码显示增加老项目空格分隔;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据用户要求“第一步应用层先做体验兜底”,只改 `YX_BlueTooth`:通话记录点击防连点,拨号成功后立即显示“正在拨号”通话页,保留 HFP 真实回调刷新;`:YX_BlueTooth:assembleDebug` 编译通过。底层 `pcm_open Device or resource busy`、`AudioFlinger/audio-hal` 异常仍需系统/BSP 或蓝牙厂商继续分析。
- 2026-07-10:根据用户提供的老/新联系人截图,继续只改 `YX_BlueTooth` 联系人页;复制旧项目联系人搜索、下载和联系人头像资源,调整 `fragment_contacts.xml/listitem_phonebook.xml/ContactsAdapter.java/ContactsSearchAdapter.java/ContactsFragment.java/MainActivity.java/colors.xml`,让联系人页按老项目固定搜索栏、下载按钮、姓名+号码列表显示;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据联系人页实机截图继续调坐标和提示文案;`fragment_contacts.xml` 搜索区/列表整体上移左移,`values/strings.xml` 与 `values-zh/strings.xml` 的 `contact_search_hint` 改为“请在联系人中搜索”;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据 14:17 联系人截图做像素级横向复核,微调搜索区宽度、下载按钮位置和联系人列表左边界;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据 14:23 联系人截图继续补齐顶部“蓝牙”标题、下载按钮横向偏移和联系人分割线长度;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据 14:33 联系人截图确认全局“蓝牙”标题会与左侧菜单重叠,已再次隐藏 `tv_main_title`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据 15:05/15:06 截图修复 SystemUI/主壳宽度变化后导致的联系人页右移和拨号页通话记录号码省略;只改 `YX_BlueTooth` 的 `activity_main.xml/fragment_contacts.xml/fragment_dial_home.xml`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份”后,对齐蓝牙设置页设备列表 UI;只改 `YX_BlueTooth` 的设置页布局、设备列表 Adapter 和旧项目列表资源;`:YX_BlueTooth:assembleDebug` 编译通过。当前只完成 UI 对齐,已配对设备点击弹出“连接/断开/删除”操作层仍作为下一步。
- 2026-07-10:用户确认“不备份,直接改”后,继续按老项目 `蓝牙设置6.png` 修正设置页设备列表图标和搜索中进度圈;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份”后,修正可用设备搜索菊花资源为老项目橙色 `progress_loading_bar.png`;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认“不备份”后,按老项目方式将设置页主列表容器改回普通 `LinearLayout`,用于恢复自动接听/蓝牙开关/蓝牙名称下方分割线;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:按用户要求先提交当前代码但不推送,提交 `31f1ac3e feat(YX_BlueTooth): 对齐蓝牙设置设备列表UI`;提交后新增已配对设备下拉 popup,参考旧项目 `popup_window.xml/ForumAdapter/MainActivity.popUpWindow`,`:YX_BlueTooth:assembleDebug` 编译通过。popup 改动暂未提交。
- 2026-07-10:用户确认“这个项目都不备份”后,对齐蓝牙音乐页 UI;复制旧项目 `icon_bluetooth_music`、`music_wg_*` 和 `icon_seekbar_horizontal_*` 资源,修改 `fragment_bt_music.xml/activity_bluetooth_music.xml/selector_music_*.xml/bt_music_progress_layer.xml/BtMusicViewModel.java/BtMusicFragment.java/BluetoothMusicActivity.java`;封面按旧项目固定显示默认蓝牙音乐图;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:根据用户 19:49 新截图继续对齐蓝牙音乐与老项目效果;修改 `activity_main.xml/fragment_bt_music.xml/activity_bluetooth_music.xml`,恢复顶部标题、下移菜单和音乐封面文字;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-10:用户确认顶部“蓝牙”标题属于顶部栏/主壳显示,不作为当前蓝牙模块功能处理;已将 `activity_main.xml` 内 `tv_main_title` 重新隐藏,后续若要显示应在主壳/顶部栏范围单独处理。
- 2026-07-13:按当前代码重新梳理蓝牙音乐模块功能、入口、A2DP/AVRCP/音频焦点/状态回调流程,并输出文档到 `F:\AI\Obsidian\项目文档\同力\蓝牙音乐\蓝牙音乐模块功能流程梳理_2026-07-13.md`。
- 2026-07-13:用户确认“不备份”后,重排蓝牙音乐模块文档中的 Mermaid 流程图;将大图拆为 17 个 3-6 节点的小图,并用表格承接完整类名、分支规则和代码定位;已检查 Markdown 围栏成对。
- 2026-07-13:用户确认“不备份”后,结合 `xugong_2026_07_13_11_01_55.log` 分析蓝牙音乐播放后自动暂停。日志确认 App 下发 AVRCP PLAY(68)并成功获得音频焦点后,`com.android.bluetooth` 很快主动下发 AVRCP PAUSE(70);当前工程 `BluetoothA2dpSinkClient.setFluorideStreaming()` 内真正通知 A2DP Sink 焦点状态的调用被注释,导致 App 焦点与蓝牙 Sink 焦点状态脱节。`F:\zdCode\xugong`、`seaz`、`weidu` 同类工程均启用了该调用。另发现无焦点播放回调会在 `BtMusicManager` 中重复触发暂停,建议作为防重保护一并处理。本次仅分析,未修改业务代码。
- 2026-07-13:用户要求修改蓝牙音乐自动暂停问题并沿用“不备份”。已在 `BtMusicManager` 增加失焦暂停防重,同一轮 PLAYING 回调只下发一次暂停。Android 14 编译和运行框架均确认 `BluetoothA2dpSink.setFluorideStreaming(int)` 已不存在,不能恢复旧接口;实机分别验证移除全局 MediaSession、手动写入 `SOURCE_BT_MUSIC` 均无法阻止自动暂停,相关试验已撤回。反编译设备 Bluetooth APK 确认:`AvrcpControllerStateMachine` 在 PLAYING 回调中读取 A2DP Sink 焦点状态,状态无效时主动发送消息 302/AVRCP PAUSE(70);同时当前 Bluetooth APK 的 `A2dpSinkService.requestAudioFocus()` 与 `A2dpSinkStreamHandler.handleMessage()` 已为空实现,且无可运行时开启的 feature flag。根治需要在 Android 14 系统 `com.android.bluetooth` 模块恢复 A2DP Sink 焦点状态处理,或在该能力关闭时取消 AVRCP Controller 的强制暂停,应用层无法完整修复。
- 2026-07-13:用户提供 Android 14 源码 `Y:\tongli\android14.0_rk3562` 并继续沿用“不备份”。已恢复系统 Bluetooth 的 `A2dpSinkService.requestAudioFocus()` 转发、A2DP 音频流开始/停止事件,以及 `A2dpSinkStreamHandler.handleMessage()` 焦点状态机;应用侧将蓝牙音乐焦点请求改为通过 AVRCP `MediaController.prepare()` 委托 `com.android.bluetooth` 获取真实 A2DP Sink 焦点,删除 PLAYING 回调按应用焦点主动暂停手机的逻辑,并停止 `com.yaxon.bluetooth` 直接调用 `AudioManager.requestAudioFocus()`。`:YX_BlueTooth:assembleDebug` 编译成功;系统 Bluetooth 需在 Linux Android 源码构建环境编译并上车验证。
- 2026-07-13:分析 `xugong_2026_07_13_14_08_42.log`,确认新 `com.yaxon.bluetooth` 应用代码已生效并发出 AVRCP `prepare()`,但设备激活的仍是 `/system/apex/com.android.btservices.apex` 出厂 APEX,日志没有 `A2dpSinkStreamHandler` 焦点处理及 `com.android.bluetooth` 的实际 `requestAudioFocus()`,因此系统修复尚未上车。另确认系统预装的独立旧应用 `com.yaxon.btmusic`(`/system/app/YX_BtMusic/YX_BtMusic.apk`)仍在运行,其旧 SDK 在 PLAYING 且应用焦点为 false 时再次调用 `pauseBtMusic()`;同一轮播放先由旧系统 Bluetooth 下发一次 PAUSE(70),再由旧独立应用下发一次 PAUSE(70)。产品当前只有 `YX_BlueTooth` 内一个蓝牙音乐入口,应从 `third_apps/preinstall.mk` 移除 `PRODUCT_PACKAGES += YX_BtMusic`,当前设备可先禁用该包验证,同时必须编译并刷入新的 `com.android.btservices` APEX 后重启。
- 2026-07-13:分析 `xugong_2026_07_13_14_22_51.log`,确认旧 `com.yaxon.btmusic` 已不再运行,第二次应用侧 PAUSE 已消失;当前只剩 `com.android.bluetooth` 在手机回报 PLAYING 后发送 PAUSE(70)。ADB 确认设备系统指纹仍为 `eng.znzc.20260708.173717`、构建日期为 2026-07-08,激活的仍是 `/system/apex/com.android.btservices.apex`,日志仍无 `A2dpSinkStreamHandler`、`com.android.bluetooth requestAudioFocus()` 和 `onAudioFocusStateChanged(1)`,因此 7 月 13 日系统 Bluetooth 修复仍未刷入设备,本轮不能用于判断系统修复是否有效。
- 2026-07-13:分析 `xugong_2026_07_13_14_26_43.log`,确认设备已完整重启,但实时 ADB 仍显示系统指纹 `eng.znzc.20260708.173717`、构建日期 `Wed Jul 8 17:37:08 CST 2026`,`com.android.bluetooth` 仍来自 `/apex/com.android.btservices/app/Bluetooth@UQ1A.240205.004.B1/Bluetooth.apk`。旧独立 `com.yaxon.btmusic` 已无法正常解析/启动,不再参与暂停;本轮仍是 `BtMusicManager.prepareA2dpSinkAudio` 后发 `PLAY(68)`,A2DP Stream started,手机回报 `Playback status changed to 3`,随后 `com.android.bluetooth` 立即发 `PAUSE(70)`。日志仍无 `A2dpSinkStreamHandler`、`callingPack=com.android.bluetooth`、`onAudioFocusStateChanged(1)`,说明系统 Bluetooth A2DP Sink 焦点修复仍未上车。
- 2026-07-14:用户说明正常播放分支为 `Y:\txj\android14.0_rk3562`,疑似修复提交 `926b121d63500ff2f7f9c6305d106e45c8fc1f68`。已按“不备份”同步该提交到 `Y:\tongli\android14.0_rk3562`:仅修改 `packages/modules/Bluetooth/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java`,在 `MESSAGE_PROCESS_PLAY_STATUS_CHANGED` 中三处原本会触发 `PASS_THRU_CMD_ID_PAUSE` 的保护分支改为只打印 `WL...` 日志,不再发送 AVRCP PAUSE。目标文件 diff 已确认与 `txj` 提交一致,待 Linux Android 构建环境编译 `com.android.btservices`/刷机验证。
- 2026-07-14:对比 `Y:\txj\android14.0_rk3562` 与 `Y:\tongli\android14.0_rk3562` 的 `packages/modules/Bluetooth` 提交历史。两边已有三组内容一致的提交:`屏蔽原生蓝牙音乐焦点注册处理`、`添加诚谦libbluetooth_jni so库集成`、`修改通讯录通话记录下载失败问题`。`txj` 额外多两项:`926b121d635` 蓝牙播放自动暂停修复(已同步到 `tongli`,验证可解决播放即暂停)和 `5528a8da056` 蓝牙搜索设备时额外向 `com.yaxon.launcher` 发送 `ACTION_FOUND` 广播(尚未同步,属于桌面设备发现联动,不影响蓝牙音乐播放)。
- 2026-07-14:结合 `xugong_2026_07_14_13_53_10.log` 分析蓝牙音乐进度条不变化。日志显示 AVRCP 播放位置每秒上报并进入 `BtMusicManager`,但 `PlaybackStateCompat.getExtras()` 多数为 null,旧代码只有 `bundle != null` 才写回 `mMusicInfoBean.playPosition`,导致 UI 收到的 `MusicInfo.playPosition` 不更新。已按用户确认“不备份”修改 `YX_BluetoothSDK/modules/BtMusicManager.java`:`playPosition >= 0` 时直接写入;同时屏蔽 PLAYING 回调里因应用音频焦点为 false 而主动 `pauseBtMusic()` 的旧逻辑,只保留日志,避免再次中断远端播放。
- 2026-07-14:用户已提交“进度条变化问题”并还原直接屏蔽失焦暂停逻辑;继续按“不备份”优化 `BtMusicManager` 的失焦暂停策略。新逻辑在 `isPlaying && !isFocus` 时先判断蓝牙音乐页前台、当前音源为 `SOURCE_BT_MUSIC` 或本机 3 秒内刚下发 PLAY,满足则请求/等待焦点不暂停;否则只安排 1200ms 延迟检查,二次确认仍播放且无焦点且非蓝牙音乐场景时才 `setA2dpMute()` + `pauseBtMusic()`。同时 A2DP 断开和模块销毁时取消延迟任务,避免残留回调。
- 2026-07-14:根据最新日志复核,进度条修复已经生效,剩余 `PAUSE(70)` 来自应用主动 `pauseBtMusic()`,且播放期间应用焦点一直为 true,失焦延迟暂停策略本轮未触发。用户担心策略改动过大后确认改为保守排查:已撤回失焦延迟暂停策略,只在 `BtMusicManager.pauseBtMusic()` 增加调用栈日志,用下一份日志定位 UI/按键/语音/外部控制中的具体调用来源。
- 2026-07-14:用户确认“不备份”后,处理 `YX_Setting` 移除 `YX_BluetoothSDK` 依赖后的编译错误,并按“蓝牙模块独立,设置里不体现蓝牙”清理设置模块内蓝牙入口。已移除 `SettingApplication` 蓝牙 SDK 初始化/监听、连接页蓝牙开关和弹窗入口、蓝牙设置 ViewModel/Fragment/Adapter/配对浮层及相关布局、字符串、常量和未引用图标;`rg` 确认 `YX_Setting/src/main` 已无 `com.yaxon.bluetoothsdk`、`BluetoothDialogFragment`、`title_bluetooth` 等残留;使用 Java 17 执行 `.\gradlew.bat :YX_Setting:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-14:用户澄清 `com.yaxon.btmusic` 当前只是保留代码,APK 已不集成。按长期边界继续处理:不恢复 `YX_Setting` 蓝牙依赖,不依赖 `YX_BtMusic` 旧独立应用;已增强 `YX_BlueTooth` 自启动/常驻职责,`application`、`BluetoothService`、`BootReceiver` 支持 direct boot,`BootReceiver` 监听 `LOCKED_BOOT_COMPLETED` 和 `BOOT_COMPLETED` 拉起 `BluetoothService`,`BluetoothService.onStartCommand()` 默认返回 `START_STICKY`。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-14:用户已删除 `third_apps/preinstall.mk` 中的 `PRODUCT_PACKAGES += YX_BtMusic`,本地核对确认预装清单不再集成 `YX_BtMusic`;`settings.gradle` 中 `include ':YX_BtMusic'` 仍为注释。当前仍残留 `YX_Launcher`、`YX_Music`、`YX_Radio`、`YX_Factory`、`YX_BlueTooth/utils/ItsBluetoothApp.java` 等旧入口对 `com.yaxon.btmusic` 的引用,后续若这些入口仍可见,应迁移到 `com.yaxon.bluetooth` 对应入口或隐藏。
- 2026-07-14:继续清理 release 打包脚本中的旧独立 APK 构建项,已从 `build_fd_apk.sh` 和 `build_znzc_apk.sh` 移除主动执行的 `gradle :YX_BtMusic:assembleRelease`;注释掉的历史命令保留作为历史线索,不参与执行。
- 2026-07-14:用户确认“参考之前设置的蓝牙连接功能,挪到蓝牙模块”。对比 `a9c0c3f2` 旧 `YX_Setting` 实现后确认 `YX_BlueTooth` 已承接扫描、设备列表、连接、断开、删除、蓝牙开关和名称设置;本轮补齐旧 `SettingApplication + BluetoothPairedRequestView` 承担的系统配对请求确认能力:新增 `YX_BlueTooth/views/BluetoothPairedRequestView.java`、`view_paired_request_layout.xml` 及配套按钮/背景资源,并在 `BluetoothService.onPairingRequest/onPairingBondStatus/onBtClose` 中显示或关闭 PIN 配对确认层。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-15:用户确认采用保守启动策略,撤回 `YX_BlueTooth` 中新增的 direct boot 相关项:移除 `application/service/receiver` 的 `android:directBootAware="true"`,移除 `LOCKED_BOOT_COMPLETED` 监听和 `BootReceiver` 对应分支。最终保留 `android:persistent="true"`、普通 `BOOT_COMPLETED`、`BluetoothService START_STICKY` 和系统浮层权限,贴近原 `YX_Setting` 的蓝牙宿主行为。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-15:用户确认“不备份”后,按老项目规则给 `YX_BlueTooth` 主界面四入口补菜单门禁:`SettingViewModel` 聚合蓝牙开关/HFP/A2DP 状态,拨号和联系人依赖 HFP,蓝牙音乐先依赖 A2DP,设置始终可用;`MenuAdapter` 支持入口灰显和不可点击;`MainActivity` 对入口点击和外部 `setPositionFragment()` 做二次拦截,协议断开导致当前页不可用时自动跳蓝牙设置。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-15:结合 `tongli_2026_07_15_19_38_11.log` 分析菜单灰显不同步问题:断开时 A2DP 先从 connected 进入 disconnected,HFP 后面才断;连接时 A2DP 先 connected,HFP 再 connected,AVRCP 更晚,因此“蓝牙音乐”和“蓝牙拨号/联系人”先后变灰/恢复是底层 profile 事件时序暴露到 UI。已在 `SettingViewModel` 增加 450ms 菜单状态合并刷新,协议瞬时事件先收敛后统一通知菜单,同时保留拨号/联系人依赖 HFP、音乐依赖 A2DP 的旧项目门禁规则;`:YX_BlueTooth:assembleDebug` 编译通过,待实车复测断开/连接时左侧四入口视觉是否同步。
- 2026-07-15:用户确认最终业务逻辑按老项目。复核旧项目 `MenuListAdapter/MainActivity/BtAdapterManager` 后,菜单门禁基线更新为:蓝牙拨号和联系人依赖 HFP connected,蓝牙音乐依赖 A2DP connected 且 AVRCP connected,蓝牙设置始终可点;蓝牙打开但手机未连接时自动落蓝牙设置页,蓝牙开关保持真实打开状态并保留设备列表/搜索。已在 `YX_BluetoothSDK` 的 `BtSettingManager/BtSettingControlManager` 暴露 `isAvrcpConnected()`,`SettingViewModel` 的音乐入口门禁改为 `isBtOpen && isA2dpConnected() && isAvrcpConnected()`,并继续保留 450ms 合并刷新;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-15:根据老项目断开连接截图继续对齐左侧菜单 UI。旧项目 `MenuListAdapter` 默认将菜单文字置为 `white_10=#1AFFFFFF`,满足协议或设置项时才改为 `gray_low=#DFDFDF`;断开时“蓝牙设置”主要是亮字,不显示明显选中胶囊。新项目已新增 `white_10`,禁用文字改为 `#1AFFFFFF`,`MenuAdapter` 手动按可用状态设置文字颜色;当蓝牙断开且当前落在“蓝牙设置”时,取消该项 selected 背景,仅保留亮字。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-15:结合 `tongli_2026_07_15_20_23_24.log` 复核“打开蓝牙时蓝牙音乐比蓝牙拨号/联系人慢几秒”。日志显示 HFP 在 20:23:37.098 connected,A2DP 在 20:23:37.775 connected,AVRCP 在 20:23:39.577 connected,菜单到 20:23:40.102 才刷新为 `avrcp=true`;旧项目 `MenuListAdapter.isEnabled()` 同样要求音乐入口 `A2DP + AVRCP`,电话/联系人只要求 HFP。因此音乐入口晚亮是旧项目规则和底层协议连接时序导致,不作为新项目回归问题处理。
- 2026-07-16:结合 `tongli_2026_07_16_09_40_45.log` 分析“蓝牙已连接但蓝牙音乐入口置灰”。日志显示 09:33:30 菜单先刷新为 `hfp=true a2dp=true avrcp=false`,09:33:31 `BluetoothAvrcpClient` 才 `avrcp onServiceConnected=12`,09:33:32 后已能收到 AVRCP 音乐元数据/播放状态,但菜单没有再次刷新。根因是 AVRCP profile proxy 接入后没有通知设置模块刷新菜单。已按 A2DP service-ready 设计补齐:`BluetoothAvrcpClient` 新增 AVRCP service connected listener,`BtSettingManager` 转发到 `IBtSettingModuleListener.onAvrcpServiceConnected()`,`SettingViewModel` 收到后 `scheduleRefreshMenuState()`;其他监听实现补空方法。保留老项目音乐入口 `A2DP + AVRCP` 规则,`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-16:结合 `tongli_2026_07_16_10_57_37.log` 分析源切换浮层反复弹出,确认 MCU `0x15/mode` 被映射为 `KEYCODE_ASSIST` 后触发 `SystemUIService.startAssist()`,进而连续发送 `android.intent.action.CHANGE_SW_MUSIC`。按用户要求先临时屏蔽弹框:`YX_Factory` 的 `BootReceiver` 收到 `ACTION_CHANGE_SW_MUSIC` 时只记录 ignored,不再调用 `SwitchAudioManager.switchAudioSource()`;后续正式修复应考虑 MCU 按键防抖和修正收音机/蓝牙音乐目标 Activity。
- 2026-07-17:根据 `tongli_2026_07_16_20_01_29.log` 复核“自动跳转蓝牙拨号页后不显示通话记录”。旧项目 HFP 连接后会 `setPositionFragment(TAB_KEYBOARD, HFP_PROFILE)` 并调用 `mKeyBoradFragment.clear(); mKeyBoradFragment.loadCallrecords()`,其中 `loadCallrecords()` 会 `start(true)` 强制加载通话记录。新项目此前只在 HFP false->true 时跳转拨号页,`CallLogsFragment` 又被 `isCallRecordUpdated=false` 拦在同步提示面板。已在 `YX_BlueTooth` 中补齐 HFP 连接后的通话记录加载链路:`MainActivity.switchToDialOnHfpConnected()` 跳拨号页后通知 `DialHomeFragment`,再转发到 `CallLogsFragment.loadCallRecordsOnHfpConnected()`;有本地记录则读取,没有且未同步中则触发 `phoneBookPullRequest()` 并显示 loading。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-17:根据 `tongli_2026_07_17_11_05_34.log` 复核实车效果“通话记录列表只显示几个号码”。日志显示 `notifyPhoneRecordList list = 3`,第一次 HFP 自动跳转时 `CallLogsFragment` 子页面尚未初始化,因此强制加载入口未执行;第二次入口执行时 `isCallRecordUpdated=true`,旧实现只读本地 3 条并返回,没有像老项目 `start(true)` 那样重新拉取。已修正:`DialHomeFragment` 增加 pending+重试,子 Fragment 未 ready 时最多重试约 2 秒;`CallLogsFragment.loadCallRecordsOnHfpConnected()` 不再被 `isCallRecordUpdated=true` 短路,HFP 连接后始终优先触发 `phoneBookPullRequest()` 重新 PBAP 同步,发起失败才回退读本地或显示同步提示。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17 更新标记:用户明确“通话记录逻辑还是要用新项目,只是交互参考老项目”。因此撤回上一条中的“连接后始终强制 `phoneBookPullRequest()`”方向。最终口径改为:HFP 连接后自动跳拨号页,并在 `DialHomeFragment` 子页面 ready 后延迟触发一次 `CallLogsFragment.loadCallRecordsOnHfpConnected()`;该方法仍遵循新项目规则,`isCallRecordUpdated=true` 时只读本地记录并经过 `handleMergeList()` 合并显示,`false` 时显示新项目原有同步面板/加载态,不主动强制 PBAP 同步。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17 更新标记:用户确认“这显示估计要参考老项目”。继续保留新项目 PBAP/数据库/读取状态逻辑,但通话记录列表展示口径改按老项目:不再经过 `handleMergeList()` 合并连续同号码同类型记录,SDK 返回多少条就交给列表展示;新项目 Adapter 原有最多显示 50 条限制暂时保留。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17:用户确认老项目连接后跳蓝牙拨号页直接显示“正在同步通讯录/通话记录”,没有新项目式“通讯录未同步/一键同步”大按钮;已将拨号页和联系人页未同步态改为 loading,隐藏一键同步按钮,loading 圈按老项目缩小到拨号 35dp、联系人 32dp,文字降到 20sp;`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17:继续收口同步失败体验。拨号页 loading 文案改为“正在同步通话记录”;通话记录同步失败后改为停止 loading 并显示老项目式 `callrecord_get_failed` 文本;联系人同步失败去掉 `同步失败` toast,改为停止 loading 并显示 `contact_get_failed` 提示;`CallHistoryViewModel` 补齐 PBAP 失败回调到通话记录页面;`BluetoothPbapClient.connectPbap()` 将 PBAP `STATE_CONNECTING/STATE_CONNECTED` 视为成功,避免重复连接时误判失败。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-17:对齐老项目拨号/联系人主页面成功提示逻辑:联系人同步成功后只刷新列表,不再弹 `同步成功` toast;拨号页本身也不增加成功 toast。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17:根据 `tongli_2026_07_17_19_44_50.log` 复核,PBAP 同步会出现早到的 `download.failed` 或重复连接失败信号,但 10-60 秒内通话记录仍可能陆续返回。旧项目不会一开始显示 `callrecord_get_failed`,而是 loading 后约 60 秒超时才失败。已将 `CallLogsFragment` 的通话记录失败态改为延迟 60 秒确认,期间继续显示“正在同步通话记录”,一旦记录返回即取消失败提示;同时将最终失败文案改为“通话记录同步失败,请检查手机蓝牙权限或稍后重试”,避免误判为一定未授权。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17:根据 `tongli_2026_07_17_19_56_00.log` 分析“设置页提示已连接后几秒才跳蓝牙拨号页”。日志显示 A2DP/设备列表在 19:57:33 已显示连接,HFP 广播 19:57:36 到达,但原逻辑等 `SettingViewModel` 菜单状态合并刷新和主线程空闲后,19:57:40 才执行 `HFP connected, switch to dial page`。旧项目 HFP connected 事件会直接 `setPositionFragment(TAB_KEYBOARD, HFP_PROFILE)`。已新增 `SettingViewModel.mHfpConnectedLiveData`,在手机 HFP `STATE_CONNECTED` 到达时即时通知 `MainActivity`;主界面收到后直接切拨号页并触发 `loadCallRecordsOnHfpConnected()`,菜单灰显仍保留 450ms 合并刷新,音乐入口仍按 A2DP+AVRCP 判断。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-17:根据 `tongli_2026_07_17_20_18_07.log` 继续复核重连概率延迟跳转。旧项目 HFP 连接事件不受 `lastMainSelectedItem`/菜单刷新影响,直接跳 `TAB_KEYBOARD` 并 `loadCallrecords()`。新项目上一版 `force=true` 仍被 `!mWasHfpConnected` 限制,且 `SettingViewModel.onConnect()` 会立即触发通讯录读取。已修正 `MainActivity.switchToDialOnHfpConnected()`:HFP 事件 force 路径在当前未初始化/仍停蓝牙设置页/此前未连接时立即跳拨号页;已将 `SettingViewModel` 连接后的通讯录读取延后 1200ms 且去重,断开/关闭/释放时取消,避免抢在跳转前触发重复读取。`:YX_BlueTooth:assembleDebug` 编译通过。
- 2026-07-18:根据 `tongli_2026_07_18_14_30_05.log` 继续分析“提示蓝牙已连接后约 3 秒才跳蓝牙拨号页”。日志显示 `SettingViewModel.notifyHfpConnected` 已到达,但 `MainActivity` 仍受 force 条件、通用 `onConnect` 提前隐藏面板/触发读取、以及重复 `SettingViewModel onConnect` 监听回调影响。已改为:`MainActivity.handleHfpConnected()` 收到 HFP connected 后直接强制切到 `TAB_KEYBOARD` 并触发 `DialHomeFragment.loadCallRecordsOnHfpConnected()`,仅保留 1500ms 重复事件防抖;`SettingViewModel.onConnect()` 不再向主界面发送“已连接”和读取通讯录,真正 HFP connected 时才发送 `mConnectedStateLiveData(true)` 并延后 1200ms 读取联系人;`SettingViewModel` 构造时主动移除上一个主界面同类实例的 setting/phone/contacts 三类监听,避免旧实例重复回调放大主线程压力,同时不改变 SDK 全局多监听语义。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-21:按新旧联系人截图像素对比调整 `fragment_contacts.xml`,搜索区、结果提示、联系人列表和搜索结果列表统一右移 `9dp`、下移 `22dp`;联系人 item 的 `88dp` 行高、`60dp` 头像、姓名/号码字号和右侧对齐保持不变。使用 Java 17 执行 `.\gradlew.bat :YX_BlueTooth:assembleDebug --no-daemon --console=plain` 编译通过。
- 2026-07-21:用户确认“不备份”后,补齐联系人页顶部下载按钮的旧项目式二次确认。新增 `ContactDownloadConfirmDialog` 和 `640x420dp` 弹框布局,直接复用旧项目弹框背景及按钮图片;已有联系人时显示“提示信息 / 是否要重新下载联系人? / 确定 / 取消”,确定后继续调用新项目既有 `reLoadContact()`,取消只关闭弹框,无联系人时保持直接同步。弹框按宿主窗口可见区域动态居中,并在页面隐藏、暂停或销毁时关闭。`:YX_BlueTooth:assembleDebug` 编译通过,已安装到 `1024x600/160dpi` 实机验证弹框显示和取消行为。
- 2026-07-21:继续沿用“不备份”口径,补齐联系人同步进度弹框。PBAP 传输阶段显示旧项目式 `640x420dp` 等待弹框和 `0%`,系统联系人数据就绪后,由 `ContactUtils` 按 `Cursor` 解析条数上报真实百分比;进度经 `BtContactsManager -> IBtContactsModuleListener -> ContactListViewModel -> ContactsFragment` 到达 UI,并按百分比变化节流。成功、失败、蓝牙断开、切页和页面销毁时关闭弹框,页面恢复时可继续显示当前进度;SDK 使用同步代次隔离旧解析任务,断开、关闭或失败后旧任务不能重新弹出进度,线程池内部解析异常也会进入失败收口。该百分比仅表示本地联系人解析进度,不代表 PBAP 空中传输进度;未使用刘汽项目的 `SystemProperties` 全局总数方案,也未伪造计时进度。
- 2026-07-21:实车发现联系人进度到 `100%` 后弹框未关闭。复核旧项目确认关闭点不在 `REFRESH_PROGRESS`,而在联系人结果回调 `PhoneBookFragment.showResults()`;新项目已对应调整为 `ContactListViewModel.onContactsPhoneBook()` 收到最终列表后立即隐藏进度弹框,`onContactsLoadCompeted()` 保留为 PBAP 断开完成兜底。
- 2026-07-21:根据 `tongli_2026_07_21_18_52_50.log` 修复第二次下载联系人误报“未授予下载权限”。日志确认第一次已解析并保存 258 条联系人,但 PBAP 断开后系统残留插入任务与应用新请求重叠,产生 `socket closed`、`OBEX request is already being performed` 和共用的 `bluetooth.pbapclient.download.failed`;该广播不等于权限被拒绝。`BtContactsManager` 现会在 PBAP 刚断开时等待约 1.5 秒再重连,新建 PBAP 会话使用当前平台自动下载,不再额外发送一次 `ACTION_USER_UNLOCKED`;等待重连期间忽略上一会话迟到的完成/失败广播,当前联系人完成广播收到后忽略同批通话记录失败和重复失败,解析异常仍按真实失败处理。蓝牙/手机断开时取消待执行重连。使用 Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过,待实车连续点击两次下载验证。
- 2026-07-21:继续修复“通讯录一直重复下载”。根因补充为:当前平台在 PBAP `STATE_CONNECTED` 后自动执行 `MSG_DOWNLOAD`,旧应用的自动同步分支在已有联系人时仍调用 `phoneBookPullRequest()`,导致同一连接再发送 `ACTION_USER_UNLOCKED` 并排队第二轮下载。现已移除所有 PBAP 连接回调中的手动下载请求;非用户发起的新连接只调用 `beginPlatformPhoneBookPull()` 初始化完成标志、同步代次和 UI 进度,不清空既有联系人、不发送广播。仅保留“PBAP 已稳定连接且用户主动重新下载”路径发送一次广播。`:YX_BlueTooth:assembleDebug` 编译通过;实车验收要求每个 PBAP 会话只出现一次 `MSG_DOWNLOAD`。
- 2026-07-22:提交前复核通讯录下载异常闭环后继续按“不备份”修改。联系人数据库更新改为 SQLCipher 单事务执行“按 MAC 删除旧数据 + 批量插入新数据”,任一步失败均回滚;PBAP 下载增加分阶段超时,连接/手机传输阶段 60 秒、收到联系人完成广播后的本地解析与保存阶段 120 秒,超时会作废当前同步代次、复位 pulling/恢复标志并进入既有失败状态;成功、失败和超时通过原子终态标志互斥,避免事务提交与超时竞态导致先失败后成功;`ContactListViewModel` 使用独立完成标志保证 100% 保留约 400ms,不再被快速 PBAP 断开回调提前关闭。Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过,待实车验证正常下载、连续下载、超时和断连场景。
- 2026-07-22 更新标记:用户认为原有提示时机更合理,撤回联系人页提前拦截 PBAP busy 以及新增的“通讯录同步尚未完成,请稍后”提示,恢复 `BtContactsManager.phoneBookPullRequest()` 在同步中收到重复请求时提示原有“通话记录正在同步中”并返回失败;进度弹框、PBAP 超时、解析进度和数据库事务修复均保留。
- 2026-07-23:重新审查通讯录下载代码后按“不备份”修复异常闭环。PBAP 开始同步时只清理系统临时 Contacts/CallLog 数据,不再提前删除应用数据库中最后一次有效联系人和通话记录;同步失败时 ViewModel/Fragment 保留并继续显示旧联系人,首次同步无缓存时仍显示失败态。迟到的联系人完成广播若发现 PBAP 已断开,会取消处理超时、结束当前代次并关闭 loading,避免 120 秒后再次误报失败。`BtContactsManager` 联系人监听集合改为 `CopyOnWriteArrayList`,避免后台完成回调与页面销毁移除监听并发冲突;`BluetoothPbapClient.isPhoneBookPulling` 改为 `volatile`,保证主线程与后台线程状态可见。
- 2026-07-23:继续按“不备份”收口 PBAP 同步一致性。联系人和通话记录均改为按 MAC 单事务替换,保存失败回滚并保留旧数据;联系人解析改用局部列表,页面回读不再清空下载线程数据,过滤无号码/异常联系人并完整关闭 Cursor;有效空通讯录通过按 MAC 同步标记识别,旧数据场景遇到空 Provider 结果会短暂重试。PBAP 完成/失败广播仅在活动同步且设备匹配时分发,联系人/通话记录完成标志改为 `volatile`;通话记录缺少完成广播、保存失败或断线时由 60 秒传输超时/120 秒本地处理超时释放 busy 并断开 PBAP,成功也主动清除 pulling,不再完全依赖断开广播。Java 17 执行 `:YX_BlueTooth:assembleDebug` 编译通过;仍需实车覆盖正常、空通讯录、通话记录失败、断线和连续下载场景。
- 2026-07-23:提交前再次复核后继续修复 PBAP 异常路径。联系人同步终态改为独立于全局 `pulling` 的原子状态,联系人完成广播前遇到 PBAP 断开会立即失败,不再依赖联系人/通话记录监听注册顺序或等待超时;完成广播到达且设备校验通过后,即使通话记录阶段断开 PBAP,本地联系人解析和保存仍继续,避免把通话记录失败扩大为通讯录失败。PBAP 连接状态事件在活动同步期间按目标 MAC 过滤。通话记录增加同步代次、重复完成广播拦截和代次绑定超时,上一轮后台任务不能结束下一轮同步;系统 CallLog Provider 返回空 Cursor 时明确失败并保留旧数据库,联系人和通话记录的有效空结果均短暂重试后再提交。联系人页切页/后台仅暂时关闭手动下载弹框,返回时恢复当前进度;成功、失败、断开或销毁才清除手动状态;ViewModel 使用独立列表快照发布和搜索,避免复用列表并发混合。Java 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 通过,仍需实车覆盖断线、连续下载、空通讯录和空通话记录场景。
- 2026-07-23:根据 `tongli_2026_07_23_15_15_43.log` 修复点击重新下载通讯录时的应用崩溃。根因是 `reLoadContact()` 清空搜索框后触发 `searchPhoneBookByKeyWord("")`,后续高亮循环仍调用 `keyWord.charAt(0)`,抛出 `StringIndexOutOfBoundsException`;现对空关键词发布空搜索结果并立即返回,普通联系人列表继续由 `ContactsFragment.showNormalContactListIfNeeded()` 恢复。仅修改搜索空值保护,未调整 PBAP、数据库和弹框流程;Java 17 执行 `:YX_BlueTooth:assembleDebug` 通过,待实车验证重新下载、关键词搜索和清空搜索框。
- 2026-07-24:按用户要求读取并分析 Codex 会话 `019f406a-be1b-7722-bb19-96f14e36fa5e` 的本地 JSONL 转录,确认最终需求口径、方案回退、提交边界、验证证据和遗留风险,并将其固化为“会话回顾后的后续分析基线”。本次只修改 `agent.md`,修改前已创建独立备份并校验 SHA-256 一致。
- 2026-07-24:处理 `YX_BlueTooth` 安装时报 `Requested internal only, but not enough space`。设备 `/data` 仅 502MB,F2FS 配置 `reserve_root=32768` 固定预留约 128MB,`dumpsys diskstats` 显示普通安装可用空间为 0;主要占用为 `/data/apex/decompressed` 约 205MB 和正在使用的 `/data/gsi/remount/scratch.img` 约 136MB,不能直接删除。当前固件未预装 `com.yaxon.bluetooth`,已按系统应用部署方式将 APK 写入现有 overlay 的 `/system/app/YX_BlueTooth/YX_BlueTooth.apk`,设置权限与 SELinux 标签并重启。验证 `pm path` 正常、包标志包含 `SYSTEM/DEBUGGABLE/PERSISTENT`、主入口解析为 `com.yaxon.bluetooth/.main.MainActivity`,进程已启动。后续 Android Studio 直接 Run 仍会受 `/data` 空间限制,调试更新应继续覆盖系统 APK 后重启,长期方案是调整 userdata 分区容量或系统 F2FS 预留配置。
- 2026-07-24:用户确认“不备份”后恢复 `YX_Setting` 主界面蓝牙入口,在 `IntentUtil` 增加蓝牙包名和主 Activity 常量,并在 `SettingActivity` 的 `home_grid_phone` 点击分支通过显式组件跳转 `com.yaxon.bluetooth/.main.MainActivity`;保留用户已有入口恢复改动。使用 Android Studio JBR 21 执行 `:YX_Setting:assembleDebug` 编译通过。
- 2026-07-24:用户要求蓝牙通话页对齐目标图中的深灰背景、四个横排操作项界面,并确认“不备份”。静态及像素复核确认当前 `activity_bluetooth_phone.xml` 和现有 80x80 mdpi 图标已与目标图一致;`tongli_old` 的 `phoning_layout_futian.xml` 反而是雪山背景、三功能键加独立挂断键的旧界面。本轮仅将音频切换控件初始硬编码“转到手机”改为现有 `@string/textview_switch_phone`(“切换至手机”),保留来电、拨号、DTMF、网络电话和三方通话逻辑;Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 通过。当前 ADB 设备为 Android 4.4.4/API 19 的 `rkpx3`,运行 `/system/app/YXBlueToothFuTian.apk` 和旧 `com.yaxon.bluetooth.keyboard.PhoningActivity`,而当前工程要求 minSdk 29;标准覆盖安装明确失败为 `INSTALL_FAILED_OLDER_SDK`。因此该设备不能作为当前 Android 14 工程的安装/截图验收目标,也不能强刷当前 APK,后续须连接同力 3562 对应 API 29+ 设备验证。
- 2026-07-24 更新标记:用户确认参考项目就是 `F:\zdCode\tongli_old`,因此上一条对两张截图目标方向的判断作废;本次实际目标为老项目雪山背景界面,而不是深灰四按钮界面。已在新项目 `YX_BlueTooth` 中复用老项目原始按钮 PNG:普通通话改为顶部 `60sp` 号码、`26sp` 状态与计时,接通后显示“通话中”;中部横排键盘、音频路由、麦克风三个 `120dp` 图标,拨号中隐藏、接通后显示;底部独立 `140dp` 挂断键;来电改用老项目 `140dp` 接听/拒接资源。通话 Activity 增加沉浸式全屏并取消系统栏 inset,普通通话隐藏返回键;原有 ViewModel、挂断、DTMF、静音、音频路由、网络电话和三方通话逻辑保留。使用 Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 成功;当前仅连接 API 19 旧设备,无法安装 minSdk 29 的新 APK,仍需同力 3562/API 29+ 实车截图及通话状态验收。
- 2026-07-24 更新标记:已连接同力 3562 `rk3562_u`(Android 14/API 34,`1024x600/160dpi`)完成通话页沉浸式实机复测。供应商配置仍报告 `mAppBounds=916x600`,但 `BluetoothPhoneActivity` 窗口 frame 为完整 `[0,0][1024,600]`;Activity 专用 `BluetoothPhoneTheme` 配合 `setDecorFitsSystemWindows(false)` 后,顶部 `54px` 和左侧 `108px` 系统栏 inset 均不再影响通话页。有效截图 `YX_BlueTooth/build/bluetooth_phone_current2.png` 中号码、状态、计时、三功能键和挂断键统一以 `x=512` 居中,纵向无重叠,因此不增加额外 `-54dp` 布局补偿,避免在当前正确窗口上造成左偏。
- 2026-07-24:用户明确第二张截图为老项目通话页基准后,继续按“不备份”做像素和代码双重对比。已将普通通话页顶部 `y=0..199` 从 `#181818` 改为老项目黑色,去掉“通话中”及对应 en/fr 文案的尾部省略号;针对 Android 14 与旧平台 `26sp` 字体度量差异,将状态和计时字形分别上移约 `2px/3px`。修正音频路由按钮的目标动作映射:SCO 在车机时显示电话图标并表示“切换至手机”,SCO 在手机时显示车机图标并表示“切换至车机”,同时动态更新 `contentDescription`。号码、三个功能键、挂断键和雪山背景坐标保持不变。Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 成功,`git diff --check` 通过,最终 APK 已通过 `adb install -r` 覆盖安装成功;安装后设备 HFP 当前未连接,因此最终真实 SCO 图标切换仍需下一次实车通话截图确认。
- 2026-07-25:提交前继续按“不备份”审查老项目式通话页。修复 DTMF 键盘展开时与三个功能键重叠、功能键 selector 的 pressed/selected 状态未透传、来电接听/拒接顺序、手机音频路由下麦克风禁用状态以及 DTMF 长按 `0` 误输入 `+`;selector 补齐 disabled、pressed、focused 状态。`:YX_BlueTooth:assembleDebug` 与 `:YX_BlueTooth:lintDebug` 成功,`:YX_BlueTooth:testDebugUnitTest` 为 `NO-SOURCE`。Lint 报告仍有 24 errors/589 warnings,24 个 error 均位于本次未修改的历史代码行,本次不扩展处理;最终仍需同力 3562 实车验证真实来电、DTMF、三方通话和 SCO 路由切换。
- 2026-07-25:用户要求参考老项目左侧蓝牙通话 DTMF 界面继续修改,并确认“不备份”。`view_keyboard_dtmf.xml` 改为左上 `20dp` 定位的 `3x4` 圆形纯数字键盘,单键 `84x84dp`、间距 `4dp`、文字 `36sp/#E6E6E6`,移除 ABC 等副标题;复用老项目 `button_numsmall` 普通态/按下态原图及 selector,两张界面按钮图放在 `drawable-mdpi`(不是应用图标目录 `mipmap-mdpi`),资源 SHA-256 与老项目一致。键盘展开时继续显示中央号码、通话状态、计时、键盘/音频路由/麦克风三个功能键和底部挂断键;DTMF 输入串在中央号码区回显,关闭键盘后恢复联系人名/原号码,并增加 `500ms` 键盘开关防抖。Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 与 `:YX_BlueTooth:lintDebug` 成功,`:YX_BlueTooth:testDebugUnitTest` 为 `NO-SOURCE`;Lint 仍为历史基线 `24 errors/584 warnings`,本次文件没有新增 error。APK 已通过 `adb install -r` 安装到 Android 14、`1024x600/160dpi` 同力 3562 设备,主界面启动正常;设备当前无 HFP 通话,仍需下一次真实通话截图确认 DTMF 展开态最终像素效果。
- 2026-07-25 更新标记:用户以老项目源码及最新确认截图为最终基准,来电状态文案应为“来电中”,不带省略号;此前按另一张截图保留“来电中...”的判断作废。已同步移除 `YX_BlueTooth` 默认、中文、英文及现有法文资源中 `textview_phone_in` 的末尾省略号,Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 成功。
- 2026-07-25:用户确认拨出电话页以老项目截图和源码为准,状态文案由“拨号...”改为“正在拨号”;`textview_dialing` 的默认/中文资源改为“正在拨号”,英文改为 `Dialing`,法文按老项目改为 `Composition en cours`。通话状态逻辑与布局坐标未调整,Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 成功。
- 2026-07-25:结合 `tongli_2026_07_25_14_59_17.log` 和实机系统 CallLog 查询,确认“每打一次多一笔记录”是双写导致:Android Telecom 已为 HFP 通话写入一条系统 CallLog,`BtPhoneManager.setCallLogDatebase()` 又按挂断时间手工写应用数据库;ContentObserver 随后按系统通话开始时间同步时,原有精确时间戳去重无法识别同一通话,再写第二条。已删除 `BtPhoneManager` 的手工入库链路,只保留 Telecom CallLog 监听作为实时来源,并将 `CallLogUtils` 的记录上限由 `<= 50` 修正为 `< 50`。`:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功;历史重复数据不做模糊删除,安装后需重新同步一次通话记录,再实测去电、来电接听、未接来电各新增一条。
- 2026-07-25 更新标记:重新核对通话记录完整生命周期后,确认上述“双写”根因与删除手工入库方向不变,但原两文件补丁存在 PBAP 同步期间直接丢弃 CallLog Observer 事件、仅按秒级时间去重可能误合并不同号码/类型、Observer 和相关监听销毁不完整三个风险。现已增加 pending 刷新标志并在 PBAP 退出 pulling 时补读一次,去重键改为“标准化号码 + 通话类型 + 系统时间”,销毁时注销 CallLog Observer、PBAP/连接监听并允许控制管理器重新初始化;新增 4 个去重单元测试。使用本机 JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,5 个 SDK 单测全部通过;本轮不备份、不安装、不提交,仍需实车覆盖通话结束与 PBAP 同步重叠场景。
- 2026-07-27 更新标记:提交前再次审查后,撤销“任意 CallLog Observer 事件生成通用 pending”的中间方案,改为仅由真实 HFP 通话结束生成带 MAC、号码、类型、开始/结束时间的刷新请求。HFP 使用稳定的“MAC + callId”跟踪,覆盖列表清空、单条 `TERMINATED`、三方通话中单条消失及手机/蓝牙断连;PBAP pulling 期间保留请求,成功、失败或超时退出后再处理。通话记录任务改为单线程并用独立 generation 隔离 PBAP 全量替换与本地增量刷新;增量刷新只匹配本次 HFP 通话,对连续拨打同一号码实行系统记录一对一占用并排除已保存旧记录,匹配结果与原数据库在单事务内合并、按时间排序并限制 50 条,失败最多重试 5 次后明确丢弃,避免永久 pending。`CallLogUtils` 达到 50 条即停止遍历并支持注销 Observer,PBAP/连接监听和控制管理器生命周期同步收口。JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功;通话记录专项 8 个测试及原示例 1 个测试全部通过,`git diff --check` 通过。本轮按用户要求不备份、不安装、不提交,Launcher 两处用户改动不纳入本次修复;仍需同力 3562 实车覆盖去电、来电接听、未接来电、连续同号码、三方通话及通话结束与 PBAP 成功/失败/超时重叠。
- 2026-07-27:结合 `tongli_2026_07_27_10_53_46.log` 修复“蓝牙断开期间手机产生通话,重连后车机记录不更新”。日志中三轮 HFP 重连后均出现 `reconcile PBAP service ready state = 0 synchronized = true pulling = false`,没有 `MSG_DOWNLOAD`、联系人完成或通话记录完成广播,列表一直为 50 条;根因是 `BtContactsManager` 把“该 MAC 历史上成功保存过联系人”的 `synchronized=true` 错当成本次连接数据仍为最新,跳过了 PBAP。现改为按 HFP 连接会话去重:每次 HFP 断开清除会话标记,每次 HFP 重连在自动同步开关开启时最多发起一次完整 PBAP 同步,同一连接内重复 HFP/PBAP 回调不会重复下载,PBAP busy 和恢复中状态继续拦截。平台源码 `PbapClientConnectionHandler.MSG_DOWNLOAD` 已确认一次请求会固定下载联系人及 MCH/ICH/OCH 三类通话记录,没有应用层 call-log-only 入口。新增 6 个自动同步决策单元测试;JBR 21 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,SDK 共 15/15 测试通过,`git diff --check` 无空白错误。本轮按用户要求不备份、不安装、不提交;仍需同力 3562 实车复现“断开时手机通话 -> 重连”,确认每个 HFP 连接只有一轮 `MSG_DOWNLOAD`,并确认手机新增通话出现在车机列表。
- 2026-07-27:曾尝试将通话记录重连同步改为“保留旧列表并显示不可取消下载弹窗”,同时新增专用弹窗类、布局和下载文案;该方案完成过构建验证,但尚未安装、提交。
- 2026-07-27 更新标记:用户确认上述弹窗方案不合理,也不是老项目的真实交互。已完整撤销本次 `CallLogsFragment` 状态逻辑、专用弹窗类与布局以及四套下载文案,恢复修改前行为;后续不得再把“旧列表 + 下载弹窗”作为老项目基准。
- 2026-07-27:对比老项目联系人首次进入交互并结合 `tongli_2026_07_27_15_11_53.log`,确认新项目约 1.4~1.8 秒空白的根因是把数据库历史同步标记 `isPhoneBookSynchronized()` 当成本次异步读取已完成,并在 Fragment 初始化和连接面板关闭时重复触发 `readPhoneBook()`。现由 `ContactsFragment` 独立记录本次页面数据是否返回:首次读取和自动 PBAP 开始时保留顶部搜索栏及下载按钮,隐藏联系人列表、搜索结果和字母索引,并显示页面中部“正在下载通讯录”;联系人回调后再显示列表或“暂无联系人”。连接面板关闭时只刷新页面状态,不再重复读取数据库,避免重连瞬间露出旧列表;手动重新下载仍保留旧列表和既有进度弹框。默认、中文、英文、法文下载文案同步调整。Android Studio JBR 执行 `:YX_BlueTooth:compileDebugJavaWithJavac --rerun-tasks` 与 `:YX_BlueTooth:assembleDebug` 均成功,未安装、未提交;仍需实车验证首次进入、切页返回、HFP 重连、自动 PBAP 及手动下载失败路径。
- 2026-07-27:结合 `tongli_2026_07_27_16_42_42.log` 修复蓝牙拨号页首次启动时右侧通话记录短暂空白。日志显示 `16:42:50.484` 页面仅凭数据库历史状态 `isCallRecordUpdated=true` 提前显示空适配器并隐藏 loading,直到 `16:42:51.736` 本次 50 条数据回调才显示列表,空白约 1.25 秒;老项目则在 `showCallRecords()` 回调前持续显示右侧加载态。现由 `CallLogsFragment` 独立记录本次页面数据是否返回:初始化、本地读取、自动 PBAP 及 HFP 重连等待期间显示既有“正在同步通话记录”,数据回调后再显示列表或空态;失败状态优先于历史同步标志,连接面板关闭只刷新 UI,不再重复调用 `readCallRecords()`。未修改 PBAP、数据库和 SDK。Android Studio JBR 执行 `:YX_BlueTooth:assembleDebug` 成功,`git diff --check` 通过;本轮不备份、不安装、不提交,仍需实车验证首次启动、空记录、HFP 断开重连、PBAP 成功/失败/超时及切页返回。
- 2026-07-27:用户确认“不备份”后,对齐老项目“返回后再次进入蓝牙模块仍显示最后页面”的交互。`BluetoothApplication.lastMainSelectedItem` 以拨号页为进程内默认值,`MainActivity` 每次切换到最终有效页面时更新该值,Activity 重新创建后仅在对应 Profile 可用时恢复联系人或蓝牙音乐,否则进入设置页;现有 HFP 重连自动切换拨号页逻辑保持不变。未使用 `SharedPreferences`,因此进程重启后仍按老项目恢复默认拨号页。Android Studio JBR 21 执行 `:YX_BlueTooth:assembleDebug` 和 `:YX_BlueTooth:compileDebugJavaWithJavac --rerun-tasks` 均成功,`git diff --check` 通过;未安装、未提交,待同力 3562 实车验证联系人/音乐返回恢复、断开及 HFP 重连场景。
- 2026-07-27:用户确认“不备份”且当前产品无语音功能后,对齐老项目普通进入蓝牙音乐页不自动播放的交互。已从主界面 `BtMusicFragment` 和独立 `BluetoothMusicActivity` 的 `onResume()` 删除 `playBtMusicState()` 生命周期播放链路及仅为该链路服务的音源状态、最后播放状态和控制管理器引用;进入或恢复页面现在只刷新 A2DP 连接、歌曲信息和远端实际播放状态,手机原本播放时仍通过既有状态观察者申请焦点,用户点击播放按钮仍正常发送播放命令。未新增 MODE 或语音特殊入口。Android Studio JBR 21 强制执行 `:YX_BlueTooth:compileDebugJavaWithJavac --rerun-tasks :YX_BlueTooth:assembleDebug` 成功,`git diff --check` 通过;未安装、未提交,待实车验证手机暂停/播放两种状态下首次进入、返回后恢复及播放按钮。
## 媒体无声诊断记录(2026-07-28)
- 分析日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_28_09_08_28.log`。本轮按用户要求不备份,只做日志、源码和 ADB 只读诊断,未修改业务代码。
- 结论:蓝牙 A2DP/AVRCP 播放链路正常,媒体无声的直接原因是系统扬声器主声卡 `rockchip-rk809` 未注册。Audio HAL 因此得到 `card=-1`,无法打开 `AUDIO_DEVICE_OUT_SPEAKER (0x2)` 对应 PCM。
- 蓝牙正常证据:音频焦点申请成功并执行 `setA2dpUnMute`;`handleAvStartInd Stream started`、`a2dp_player_start` 正常;SBC 数据持续到达;AVRCP 播放状态和进度持续更新。媒体音量为 39 且未静音,功放、DSP 和 MCU 音源也不是静音或错误源。
- 内核/Audio HAL 证据:启动时 `rk809-sound` 持续 `Failed to register card: -517`,ALSA 只列出 `#0: rockchip,bt`;播放时 `route_pcm_card_open() card -1 error!` 共出现 12721 次,并伴随 `invalid card num.`。
- `rockchip,audio-routing` 缺失会打印错误,但 `rockchip_multicodecs.c` 调用 `snd_soc_of_parse_audio_routing()` 后没有检查返回值,因此它不是本次 `-517` 的直接来源。首次 `-517` 来自 `rockchip,wait-card-locked=<0>` 等待 0 号声卡;后续 `-517` 来自 ASoC 注册时缺少 DAI 组件。
- ADB 已锁定缺失组件:`rk817-codec` 已绑定,ASoC DAI 中存在 `rk817-hifi/rk817-voice`;`ff800000.sai` 未绑定且 ASoC DAI 中不存在,只有蓝牙 SCO 使用的 `ff820000.sai`。`/sys/kernel/debug/devices_deferred` 中 `rk809-sound` 一直 pending。
- 根因:当前设备实际加载的 Device Tree 中 `/sai@ff800000/status` 为 `disabled`,而 `/rk809-sound/status` 为 `okay`,导致启用的主声卡引用了禁用的 `sai0` CPU DAI。当前源码 `rk3562-evb2-ddr4-v10.dtsi` 中 `&sai0` 已是 `status = "okay"`,且后续 include 未覆盖它,说明烧录固件中的 DTB 与当前源码不一致,或构建/打包/烧录复用了旧 DTB。
- 当前设备:`Rockchip RK3562 EVB2 DDR4 V10 Board`,内核 `6.1.99 #35 Tue Jul 28 09:56:46 CST 2026`;日志中的内核为 `#34 Tue Jul 28 07:56:11 CST 2026`。两版运行时都只有 `rockchip,bt`,主声卡问题未恢复。
- 修复落点:确保最终打包并烧录到设备的 DTB 中 `sai@ff800000` 为 `okay`,重新生成并烧录实际承载 DTB 的镜像/分区,避免只更新 Image 而沿用旧 DTB。修复后先验证 `/proc/device-tree/sai@ff800000/status`、`/sys/kernel/debug/asoc/dais`、`/proc/asound/cards` 和 `/proc/asound/pcm`,再测试蓝牙音乐、U 盘音乐和系统提示音。
- 待确认:实际构建输出目录当前未挂载在 `Y:\tongli\android14.0_rk3562\out`,尚未核对被打包 DTB 的时间戳与内容;需在构建机确认 DTS 编译依赖、镜像打包输入和烧录分区。
## HFP 音频路由切换无声诊断(2026-07-28)
- 分析日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_28_15_59_31.log`。本轮沿用用户“不备份”要求,只修改本诊断记录,未修改系统或业务源码。
- 复现时间线:`16:00:15.147` 主扬声器 PCM 在普通模式下打开;`16:00:18.464` Telecom 将 Audio HAL 设置为 `AUDIO_MODE_IN_CALL(2)`;前两轮切换后,厂商 HFP 均重新创建 `16000 Hz/双声道` AudioTrack。第二轮切到手机后,AudioFlinger 空闲约 3 秒,于 `16:00:30.915` 将主输出流置为 standby 并关闭 PCM。
- 第三轮切回车机时,异常从 `16:00:36.420` 开始:主 Audio HAL 对 `AUDIO_DEVICE_OUT_SPEAKER(0x2)` 以约 90 次/秒反复执行 `do_out_standby/close device`,持续到 `16:01:24` 通话结束;同时 `get_presentation_position` 持续返回 `Operation not permitted`。蓝牙侧在 `16:00:36.907` 和 `16:00:48.417` 仍能重新创建 AudioTrack、持续写入并累计 delivered frames,因此“写入成功”只是 HAL 向上消费了数据,不代表 PCM 真正播放。
- 系统源码根因位于 `Y:\tongli\android14.0_rk3562\hardware\rockchip\audio\tinyalsa_hal\audio_hw.c:1374`:`start_output_stream()` 在 `adev->mode == AUDIO_MODE_IN_CALL` 时无条件 `return 0`,未打开扬声器 PCM。之后 `out_write()` 因没有任何 `out->pcm[i]` 保持 `ret=-1`,向上仍返回 `bytes`,并在错误恢复分支执行 `do_out_standby()`,形成上述循环。
- 该产品的 HFP Client 通话下行由厂商 SCO PCM 读取后写入 Android AudioTrack,Rockchip `voice.in_call` 实际为 false;HAL 仅凭 Audio Mode 禁止打开 PCM,错误地套用了“基带 voice session 独占声卡”的 workaround。`out_write()` 和 `do_out_standby()` 已使用 `AUDIO_MODE_IN_CALL && voice_is_in_call(adev)` 判断,只有 `start_output_stream()` 条件不一致。
- 修复责任优先归属 Android 系统 Audio HAL/BSP 层。建议最小修复为仅在真实 `voice_is_in_call(adev)` 时跳过打开输出流,HFP Client + AudioTrack 场景必须允许重新打开 speaker PCM。修复后验证至少 5 轮“车机 -> 手机 -> 等待超过 3 秒 -> 车机”,确认每轮都有 `start_output_stream device=0x2`、无高频 standby/close、AudioTrack 有声;同时回归系统提示音、蓝牙音乐及真实基带通话(若产品支持)。
- 2026-07-28 实车验证更新:用户确认按上述判断修正后重新编译并测试,通话音频切换已恢复。`tongli_2026_07_28_19_50_02.log` 中完成了多轮手机端/车机端切换;在 `AUDIO_MODE_IN_CALL` 下,`19:51:19.207` AudioTrack 停止、`19:51:22.237` 输出 standby 后,`19:51:22.263` 成功重新执行 `start_output_stream device=0x2`;第二组等待约 9 秒的场景中,`19:52:47.501` standby 后,`19:52:56.608` 同样成功重新打开扬声器输出,随后 HFP AudioTrack 创建并恢复音量。
- 修复前日志共有 `18648` 次 `do_out_standby`、`19645` 次 `get_presentation_position` 错误,仅有 `19` 次完整输出启动;修复后日志仅有 `13` 次正常 standby、`13` 次 presentation 初始查询告警,并有 `13` 次完整 `start_output_stream`,高频约 90 次/秒的失败循环已消失。结合用户确认喇叭有声,可判定该 HAL 修复有效。
- 版本管理风险:当前可见的 `Y:\tongli\android14.0_rk3562\hardware\rockchip\audio\tinyalsa_hal\audio_hw.c` 修改时间仍为 `2026-07-28 16:54:36`,Git 状态干净,且 `start_output_stream()` 仍是旧的无条件 `AUDIO_MODE_IN_CALL` 判断。说明本次验证镜像来自另一份源码/构建目录,或修复尚未同步回 `Y:\tongli`。正式提交或后续重编前必须先将已验证的一行条件修改同步到目标仓库,避免修复丢失。
- 已生成BSP交付文档:`F:\zdCode\tongli\HFP通话音频切换无声_BSP分析与修复验证_2026-07-28.md`,包含问题现象、蓝牙侧证据、异常时间线、HAL根因、最小修复、修复前后日志统计、实车验证结果、责任归属、回归测试和版本管理风险。
## 系统蓝牙开机崩溃一次后恢复诊断(2026-07-29)
- 分析现场:用户开机后通过 ADB 只读采集 `F:\zdCode\tongli\logs\bt_boot_20260729_092224`,设备为 Android 14 `rk3562_u`,构建时间 `Tue Jul 28 19:21:50 CST 2026`,诚谦版本属性为 `persist.blinkbt.version=BLINK_2652_RELEASE0/2026:07:21:18:57:32`。
- 复现结论:本次开机 `dumpsys bluetooth_manager` 显示蓝牙当前已恢复为 `ON`,但 `Bluetooth crashed 1 time`,Enable log 为 `SYSTEM_BOOT -> RESTARTED -> RESTARTED -> CRASH -> RESTARTED`。
- 直接崩溃链路:`logcat_all.txt` 中先出现 `AdapterState: BLE_TURNING_ON : BLE_START_TIMEOUT`,随后 `blink-jni SEND: AT#INIT1`、`blink Cur BT Init Failed(0)`、`blink ----RSP: [IINIT2]----`、`CQ_IND_BTINIT status 2 BT reset so abort.`,最终 `com.android.bluetooth` 发生 `Fatal signal 6 (SIGABRT)`,native 堆栈落在 `/system/lib64/libbluetooth_jni.so` 的 `SockIndHandler::DoHandleAdapterInd/OnDataIn/ClientLocalSerial::SocketDataHander`。
- 启动时序证据:设备属性显示 `ro.boottime.vendor.bluetooth-1-1=6877680253`,而 `ro.boottime.wifi_insmod=48899783939`、`ro.boottime.yx_bt=48927902356`;日志也显示 `vendor.bluetooth-1-1` 约 7 秒启动,Android `BluetoothService` 在 `19:21:52.904` 因 `SYSTEM_BOOT` 启用蓝牙,但 `wifi_insmod/yx_bt` 到 `19:22:24` 才被 `sys.boot_completed=1` 触发。系统蓝牙先启动,诚谦 blink、Unisoc SDIO BT 驱动和 `/dev/ttyBT0` 后准备好,足以解释首轮 BLE start timeout。
- 源码定位:`Y:\tongli\android14.0_rk3562\device\rockchip\rk3562\init.rk3562.rc` 当前在 `on property:sys.boot_completed=1` 中同时 `start wifi_insmod` 和 `start yx_bt`;`wifi.insmod.sh` 负责加载 `umw2652_bsp_sdio.ko/sprdwl_ng.ko/sprdbt_tty_sdio.ko`;`yx_bt.sh` 启动 `/system/bin/blink`。`2927c583eb0 添加诚谦蓝牙blink库` 只替换了 `device/rockchip/rk3562/rk3562_u/firmware/w260/blink/blink` 二进制,未改 init、DTS、device.mk 或权限;`2927` 之后到当前 HEAD 也未再修改蓝牙集成脚本。
- DTS 判断:`rk3562-evb2-ddr4-v10.dtsi` 中 `wireless-bluetooth` 为 `status = "disabled"`,当前主链路是 `sprd_marlin3` + SDIO WCN + `sprdbt_tty_sdio.ko`;`BT_RFKILL: rfkill-bt driver has not Successful initialized` 更像旧 Rockchip rfkill 节点未启用的噪声,不是当前崩溃的第一根因。
- 仍需确认:`libbluetooth_jni.so` 在收到 `IINIT2/status 2` 后主动 abort 是否为诚谦设计;`AT#INIT1` 超时是否应由库内部等待/重试而非杀掉 `com.android.bluetooth`;`hal_util_load_bt_library: libbluetooth.so not found` 是否符合该 Android 14 集成方案;SELinux permissive 下的 `blink` 访问 `/data/blink/bt_conf.ini`、`/dev/socket/zj_bt_socket`、`/dev/ttyBT0`、`/sys/class/rfkill` denials 未来若切 enforcing 需要补规则。
- 建议最小修复方向:系统侧先调整 init 时序,不再等 `sys.boot_completed=1` 才加载 WCN/BT 模块和启动 `blink`;建议在 framework 蓝牙自动启用前完成 `wifi_insmod`,并在 `wifi_insmod` 完成后再启动 `yx_bt`,避免 Android Bluetooth 先进入 BLE start timeout。修改前需确认是否按该方案动 `init.rk3562.rc`。
- 2026-07-29 09:58 复现更新:按用户要求重启当前设备并重新采集 `F:\zdCode\tongli\logs\bt_reboot_20260729_095805`。设备正常完成启动且蓝牙最终为 `ON`,但 `dumpsys bluetooth_manager` 再次记录 `Bluetooth crashed 1 time`;Enable log 为 `SYSTEM_BOOT -> RESTARTED -> RESTARTED -> CRASH -> RESTARTED`,说明问题稳定复现。
- 本轮时序更清晰:`ro.boottime.vendor.bluetooth-1-1=6678120961`(约 6.68 秒),`ro.boottime.wifi_insmod=49532270023`、`ro.boottime.yx_bt=49571142773`(约 49.53/49.57 秒),蓝牙 HAL 比 WCN 模块和 `blink` 早约 42.9 秒。内核日志确认 `init.rk3562.rc:1` 的 `sys.boot_completed=1` 到约 50.57 秒才触发,随后才启动 `wifi_insmod/yx_bt`。
- 本轮异常链仍为两次 `BLE_START_TIMEOUT`(`19:40:12.941`、`19:40:31.617`)后,`blink` 看门狗到 `Cur BT Init Failed(0)`,返回 `IINIT2`;诚谦 `/system/lib64/libbluetooth_jni.so` 在 `SockIndHandler::DoHandleAdapterInd` 记录 `CQ_IND_BTINIT status 2 BT reset so abort.` 并触发 `SIGABRT`。Android 第三次重启蓝牙服务后于 `19:40:42.060` 收到 `CQ_IND_BTINIT, state: 1`,最终恢复。
- ADB 核对设备运行时 `/vendor/etc/init/hw/init.rk3562.rc` 仍是修改前版本:第 13 行 `start wifi_insmod`、第 16 行 `start yx_bt` 均位于 `on property:sys.boot_completed=1`;因此本轮属于修复前基线,不能用于判断源码中移动到 `on boot` 的修改是否有效。待新镜像烧录后按同样流程复测,验收标准为无 `BLE_START_TIMEOUT/IINIT2/SIGABRT`、`Bluetooth crashed 1 time` 消失,且 `wifi_insmod/yx_bt` 启动不再晚于蓝牙自动启用。
- 2026-07-29 10:19 新版本验证更新:设备已刷入构建时间 `Wed Jul 29 10:03:33 CST 2026` 的镜像,运行时 `init.rk3562.rc` 确认将 `exec_start wifi_insmod`、`start yx_bt` 放入 `on boot`。采集目录为 `F:\zdCode\tongli\logs\bt_reboot_fixed_20260729_101905`。本轮开机 `bluetooth_on=0`,框架记录 `Bluetooth persisted state: 0`,因此首次采集蓝牙保持 `OFF`、`Bluetooth crashed 0 times`,不能仅凭零崩溃判定修复通过。
- ADB 将蓝牙正常开启后,`bluetooth_on` 已写为 `1`,但在 90 秒内始终无法到 `ON`;采集目录为 `F:\zdCode\tongli\logs\bt_manual_enable_fixed_20260729_102335`。`dumpsys` 显示 `APPLICATION_REQUEST` 后连续 5 次 `RESTARTED`,每轮均发生 `BLE_START_TIMEOUT/BLE_STOP_TIMEOUT`,蓝牙服务最终未连接;本轮没有 `SIGABRT`,但功能不可用。
- 新回归根因:`on boot` 对 WCN 模块仍然过早且与 Rockchip Wi-Fi HAL 冲突。`vendor.wifi_hal_legacy` 约 7.90 秒启动,因此时看不到真实 SDIO Wi-Fi 芯片而回退加载 `/vendor/lib/modules/bcmdhd.ko`;自定义 `wifi_insmod` 约 8.35 秒同时加载 Unisoc 模块。`bcmdhd` 随后的重试反复操作同一个 `mmc1`,在约 10.69、12.96、15.20、17.44 秒移除已枚举的 WCN SDIO 卡,最终出现 `WCN loopcheck erro`、`mtty_sdio_write sprdwcn_bus_push_list failed: -19`,导致 `blink` 无法完成 `AT#INIT1`。
- 启动阶段依据:本轮 `early-boot` 在约 7.58 至 7.66 秒执行,系统随后约 7.69 秒进入 `boot`,并在约 7.70 至 7.99 秒执行 `class_start hal`;当前自定义 `on boot` 直到约 8.32 秒才运行。设备 `/vendor/bin/wifi.insmod.sh` 仅包含三条 `insmod`,不依赖 `/data`,适合提前执行。
- 更新建议:不要继续使用“`on boot` 内同时 `exec_start wifi_insmod` + `start yx_bt`”。将 `exec_start wifi_insmod` 单独放到 `on early-boot`,确保 Unisoc 驱动加载完成后系统才进入 `boot/class_start hal`,避免 Wi-Fi HAL误加载 `bcmdhd`;`start yx_bt` 保留在 `on boot`,等待驱动节点就绪后启动诚谦程序。`on property:wlan.driver.status=ok` 可作为等待 Broadcom 探测结束的备选触发点,但离蓝牙框架启动仅约 1.3 秒,时序余量小,不作为首选。
## PBAP 大通讯录超时与失败交互(2026-07-29)
- 结合 `C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_29_11_37_01.log` 确认,本次不是手机拒绝通讯录权限:PBAP 于 `11:36:52.450` 成功连接并开始拉取 `2393` 个联系人,日志未出现 OBEX Unauthorized/Forbidden。
- `BtContactsManager` 在约 60 秒后触发固定传输超时并主动断开 PBAP;断开时底层已得到 `2392` 张 vCard,随后出现 `Interrupted durring insert`、`socket closed` 和通话记录 OBEX busy,均为主动断开后的连带结果。
- 已将 `CONTACT_TRANSFER_TIMEOUT_MS` 从 60 秒调整为 5 分钟硬保护,避免大通讯录在仍有传输/落库进展时被误杀;平台当前未向应用暴露逐条传输进度,后续更理想方案是增加进度或无活动超时机制。
- 联系人失败且无旧数据时不再显示不可操作、单行截断的权限文案,改为老项目风格的“请下载联系人”可重试状态;同步按钮和下载图标均可重新下载。已有联系人时继续保留旧列表,并通过短 Toast 提示同步失败。
- 通话记录同步失败时,有旧数据则保留列表;无数据则显示“一键同步”入口,不再只显示不可操作的权限失败文案。收到明确失败回调后立即进入失败态,不再额外等待 60 秒。
- 使用 Android Studio JBR 17 执行 `:YX_BlueTooth:assembleDebug` 成功,`git diff --check` 通过。本轮按用户要求不备份、不安装、不提交;待实车使用约 2393 个联系人的同一手机复测,确认 60 秒后仍继续同步并最终显示联系人及通话记录,同时验证失败页两个重试入口。
- 提交前交互核对更新:联系人和通话记录同步失败提示已由系统 `Toast.makeText()` 改为蓝牙模块现有的 `BluetoothResultToast`,统一使用 `ico_failed` 和 2 秒展示时长;两个 Fragment 均在 `onDestroyView()` 释放 Dialog,避免窗口泄漏。`:YX_BlueTooth:assembleDebug` 再次构建成功。
## PBAP 去头像同步时长验证(2026-07-30)
- 日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_30_11_20_08.log`。本轮为系统 PBAP 请求字段去掉 `PBAP_FILTER_PHOTO` 后的实车结果;只做日志分析和记录,未修改源码、未备份。
- 统一计时口径:PBAP 传输为 `MSG_DOWNLOAD -> onContactsPullCompleted`;本地读取解析为 `onContactsPullCompleted -> contacts read completed`;数据库保存为 `contacts read completed -> contacts save completed`;核心同步总时长为 `MSG_DOWNLOAD -> contacts save completed`;端到端总时长为首次 `Received request to ConnectPBAPPhonebook -> contacts save completed`。
| 258 联系人轮次 | PBAP 传输 | 本地读取解析 | 数据库保存 | 核心同步总时长 | 端到端总时长 |
|---|---:|---:|---:|---:|---:|
| 第 1 轮 | 13.791s | 2.873s | 0.121s | 16.785s | 17.540s |
| 第 2 轮 | 11.343s | 2.901s | 0.159s | 14.403s | 15.801s |
| 第 3 轮 | 10.522s | 2.804s | 0.154s | 13.480s | 14.549s |
| 平均 | 11.885s | 2.859s | 0.145s | 14.889s | 15.963s |
| 中位数 | 11.343s | 2.873s | 0.154s | 14.403s | 15.801s |
- 与 2026-07-22 同为 258 联系人的历史基线相比:历史 PBAP 约 `8.6~8.9s`、本地解析约 `7.3s`、保存约 `0.5s`、核心阶段合计约 `16.4~16.7s`。本轮平均核心同步总时长 `14.889s`,快约 `1.5~1.8s`(约 `9%~11%`);按中位数 `14.403s` 看,快约 `2.0~2.3s`(约 `12%~14%`)。
- 改善主要发生在本地读取解析阶段,约从 `7.3s` 降至 `2.8~2.9s`;PBAP 传输本身反而增加到 `10.5~13.8s`。因此当前只能确认完整同步总时长略有改善,不能凭这一份日志证明“去掉头像直接缩短了 PBAP 无线传输时间”。三轮连续变快,也表明手机端响应、连接状态或缓存存在波动。
- 三轮均保存 258 条联系人,未出现 `Decode error on base64 photo`。每轮 `numberOfContactsRemaining` 依次为 `0 / 260 / 1`,结合平台固定下载顺序,对应 FAV 为空、主通讯录约 260 项、SIM 目录无有效联系人;FAV/SIM 在该手机上主要是额外请求开销,但不计入上述联系人保存完成时间之后的核心总时长。
| 2393 项大通讯录完整轮次 | PBAP 传输 | 本地读取解析 | 数据库保存 | 核心同步总时长 | 端到端总时长 |
|---|---:|---:|---:|---:|---:|
| 第 1 轮 | 101.403s | 26.353s | 0.407s | 128.163s | 128.686s |
| 第 2 轮 | 106.260s | 23.850s | 0.405s | 130.515s | 131.757s |
| 平均 | 103.832s | 25.102s | 0.406s | 129.339s | 130.222s |
- 大通讯录两轮均从 2393 项规模收到 2392 张 vCard,但应用最终读取并保存 2000 条;该数据规模和 258 联系人历史基线不可直接混合比较。日志读取时第 3 轮大通讯录已开始但尚未完成,因此未纳入统计。后续应单独核对 2000 条上限是否为产品预期,并继续按相同五段口径记录至少 3 轮冷连接结果。
- 更新标记:上述日志文件后来继续写入,第 3 轮大通讯录最终完整结束。补齐后“仅去头像”大通讯录基线为 3 轮:PBAP 平均 `104.226s`、应用读取解析 `24.550s`、保存 `0.396s`、核心同步总时长 `129.172s`、PBAP 连接请求到列表显示 `130.199s`。原“两轮平均”保留作为当时日志截点记录,后续比较统一使用本条三轮基线。
## PBAP 精简通讯录字段同步时长验证(2026-07-30)
- 日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_07_30_14_16_40.log`。系统 PBAP 已在上一笔去掉 `PHOTO`,本轮继续从 `PBAP_REQUESTED_FIELDS` 去掉应用未使用的 `ADR / EMAIL / NICKNAME`,仅保留 `VERSION / FN / N / TEL`。本轮只分析日志并记录,未修改源码、未备份。
- 同一手机 `...2D:AD`、同为 258 条应用联系人,共有 8 轮关键时间点完整的同步;日志开头另有 1 轮缺少 PBAP 起点,未纳入统计。计时口径沿用上一节。
| 轮次 | PBAP 传输 | 本地读取解析 | 数据库保存 | 核心同步总时长 | 端到端总时长 |
|---|---:|---:|---:|---:|---:|
| 第 1 轮 | 11.034s | 2.833s | 0.090s | 13.957s | 15.353s |
| 第 2 轮 | 10.042s | 2.598s | 0.087s | 12.727s | 13.547s |
| 第 3 轮 | 10.356s | 2.570s | 0.146s | 13.072s | 13.966s |
| 第 4 轮 | 11.014s | 2.605s | 0.133s | 13.752s | 15.081s |
| 第 5 轮 | 9.476s | 2.772s | 0.101s | 12.349s | 13.099s |
| 第 6 轮 | 10.825s | 2.561s | 0.098s | 13.484s | 15.743s |
| 第 7 轮 | 10.248s | 2.723s | 0.140s | 13.111s | 14.353s |
| 第 8 轮 | 10.977s | 2.567s | 0.094s | 13.638s | 15.782s |
| 平均 | 10.497s | 2.654s | 0.111s | 13.261s | 14.616s |
| 中位数 | 10.591s | 2.602s | 0.100s | 13.298s | 14.717s |
- 用户可见结束点补充:以 `ContactsFragment: notifyPhoneBookList phoneBookBeanList size = 258` 作为列表数据已交给 Adapter 并切换到可见状态的日志点;该方法随后立即执行 `setData()`、`notifyDataSetChanged()` 和 `showNormalContactListIfNeeded()`。7 轮有明确 `ContactsFragment: reLoadContact` 的手动下载如下,另 1 轮为手机重连自动同步,不混入手动点击统计。
| 手动轮次 | 点击下载 -> 列表显示 | PBAP 连接请求 -> 列表显示 | 系统联系人下载完成 -> 列表显示 | 数据库保存完成 -> 列表刷新 |
|---|---:|---:|---:|---:|
| 第 1 轮 | 15.546s | 15.360s | 2.930s | 7ms |
| 第 2 轮 | 13.656s | 13.553s | 2.691s | 6ms |
| 第 3 轮 | 14.078s | 13.973s | 2.723s | 7ms |
| 第 4 轮 | 15.212s | 15.088s | 2.745s | 7ms |
| 第 5 轮 | 15.857s | 15.751s | 2.667s | 8ms |
| 第 6 轮 | 14.470s | 14.362s | 2.872s | 9ms |
| 第 7 轮 | 15.901s | 15.790s | 2.669s | 8ms |
| 平均 | 14.960s | 14.840s | 2.757s | 7.4ms |
| 中位数 | 15.212s | 15.088s | 2.723s | 7ms |
- 因此,当前 258 联系人的真实手动操作体验可以记为:点击下载到列表更新平均约 `14.96s`,典型值(中位数)约 `15.21s`,范围 `13.66~15.90s`。系统发出联系人下载完成广播后,还需平均约 `2.76s` 完成本地读取、数据库保存和界面刷新;保存完成到界面列表刷新仅约 `7ms`,UI 通知不是主要瓶颈。
- 相比“仅去头像”版本的 3 轮基线,本轮 PBAP 平均从 `11.885s` 降到 `10.497s`,缩短 `1.389s`(`11.7%`);核心同步总时长从 `14.889s` 降到 `13.261s`,缩短 `1.628s`(`10.9%`);端到端总时长从 `15.963s` 降到 `14.616s`,缩短 `1.348s`(`8.4%`)。
- 分段证据更支持字段精简有效:`MSG_DOWNLOAD -> 主通讯录大小返回` 的前置阶段平均约 `0.519s`,与仅去头像基线约 `0.491s` 基本相同;真正的主通讯录拉取阶段从约 `11.389s` 降到 `9.974s`,缩短约 `1.415s`(`12.4%`)。因此本次改善主要来自主通讯录负载减少,不是 FAV 空目录请求变快。
- 8 轮均收到 259 张 vCard、读取并保存 258 条联系人,未出现 PBAP 下载失败、超时、OBEX 权限错误或 `Decode error on base64 photo`。从日志看,该手机上的姓名和号码链路没有回归;地址、邮箱、昵称本就不属于当前产品使用字段。
- 日志仍有 10 次 ContactsProvider `SqliteObjectLeakedViolation / LeakedClosableViolation`,但没有导致本轮失败。告警 SQL 中出现 `photo_id / photo_uri / photo_thumb_uri`,是因为 Provider 查询使用 `projection=null` 返回默认全列,不代表 PBAP 仍下载了头像 Blob。当前 `ContactUtils` 已在 `finally` 中关闭主 Cursor 和逐联系人的号码 Cursor,不能仅凭该 Provider 侧终结栈归责给此方法;若继续处理泄漏,需要单独采集完整调用侧证据。
- 后续更有价值的性能方向是应用层读取:`ContactUtils.getAllContacts()` 当前先查询联系人,再对每个联系人单独查询号码,属于 `1 + N` 次 Provider 查询;258 条本轮仍需约 `2.65s`,2393 项大通讯录此前需约 `25.1s`。建议下一笔改为一次批量读取号码并按 `CONTACT_ID` 聚合,保持姓名、多个号码、去重、拼音和进度行为不变。
### 大容量通讯录复测
- 同一日志在 `14:39~14:49` 新增同一台 `...54:6A` 手机的 4 轮完整同步。每轮主目录规模均为 2393、收到 2392 张 vCard、应用读取/保存/列表显示均为 2000 条;没有 PBAP 失败、超时、OBEX 权限错误或头像解码错误。
| 轮次 | PBAP 传输 | 应用读取解析 | 保存 | 核心同步总时长 | 连接请求 -> 列表显示 |
|---|---:|---:|---:|---:|---:|
| 第 1 轮 | 96.869s | 32.238s | 0.403s | 129.510s | 132.133s |
| 第 2 轮 | 95.320s | 27.628s | 0.517s | 123.465s | 125.948s |
| 第 3 轮 | 105.760s | 23.057s | 0.340s | 129.157s | 131.633s |
| 第 4 轮 | 104.192s | 23.323s | 0.363s | 127.878s | 131.410s |
| 平均 | 100.535s | 26.562s | 0.406s | 127.503s | 130.281s |
| 中位数 | 100.531s | 25.476s | 0.383s | 128.518s | 131.522s |
- 对比“仅去头像”三轮大容量基线:PBAP 从 `104.226s` 降到 `100.535s`,缩短 `3.691s`(`3.5%`);核心同步总时长从 `129.172s` 降到 `127.503s`,缩短 `1.670s`(`1.3%`)。说明精简 `ADR / EMAIL / NICKNAME` 对主通讯录传输有小幅收益,但远小于 258 联系人样本中的比例。
- 应用读取解析从基线 `24.550s` 波动到平均 `26.562s`,前两轮分别达到 `32.238s / 27.628s`,后两轮回落到 `23.057s / 23.323s`;该阶段受 ContactsProvider、GC、AppSearch 索引和缓存状态影响明显。日志累计出现大量 `AppSearchIcing Document not found`,连续反复删除/导入联系人并不是稳定的单变量环境。
- 最终用户可见结果没有明显改善:PBAP 连接请求到列表显示由 `130.199s` 变为 `130.281s`,差异仅 `0.082s`,可视为持平。两轮有明确正常起点的手动点击到列表显示为 `131.935s / 131.518s`,平均 `131.727s`,即约 `2 分 11.7 秒`;另一次 `reLoadContact` 发生在上一轮已经下载完成但应用仍在解析期间,不作为新一轮起点。
- 系统下载完成到列表显示平均 `27.003s`,保存完成到 UI 列表通知平均仅 `35.5ms`。大容量场景下一步优化重点仍应转向应用层批量读取,系统字段精简可保留,但不应预期它单独显著缩短最终列表出现时间。
## PBAP 应用层批量读取优化(2026-07-30)
- 已按“不备份、不提交”修改 `YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/utils/ContactUtils.java`:将原先查询一次联系人表、再按每个联系人查询号码的 `1 + N` 次 ContactsProvider 查询,改为一次查询 `CommonDataKinds.Phone.CONTENT_URI`,仅投影 `CONTACT_ID / DISPLAY_NAME / NUMBER`,并使用 `LinkedHashMap` 按 `CONTACT_ID` 聚合多号码。
- 保留既有号码清洗、空姓名回退、多个号码详情、拼音、T9、拼音排序、姓名+主号码去重、解析进度和最多 2000 条联系人等业务行为;超过 2000 条后不再构造联系人及执行拼音/T9计算,但仍遍历聚合结果并完成进度回调。
- 查询显式按 `CONTACT_ID ASC, _ID ASC` 排序,保证同一联系人号码聚合顺序稳定。风险边界:超过 2000 条时按联系人 ID 截取,最终显示仍按拼音排序;旧查询未显式指定排序,本身也不保证超过上限时的入选顺序。需在实车重点核对大容量场景的联系人数量、姓名、主/副号码、搜索和拨号。
- 新增 `YX_BluetoothSDK/src/test/java/com/yaxon/bluetoothsdk/utils/ContactUtilsTest.java`,覆盖多号码聚合及顺序、号码清洗、空 ID/号码过滤、后续记录回填姓名。
- 使用 Android Studio JBR 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug`,构建成功;全部 19 项单元测试通过,其中本次新增 4 项通过。APK 输出为 `YX_BlueTooth/build/outputs/apk/debug/YX_BlueTooth.apk`,`git diff --check` 无错误,仅有工作区 LF/CRLF 提示。
- 实车比较基线:258 联系人应用读取解析平均 `2.654s`;2393 项大通讯录应用读取解析平均 `26.562s`。下一步分别复测小容量与大容量至少 3 轮,统一比较 `onContactsPullCompleted -> contacts read completed`,同时记录“点击下载 -> notifyPhoneBookList”的最终可见总时长。
## PBAP 产品数量上限确认(2026-07-30)
- 产品已确认联系人上限为 `2000` 条。因此手机主通讯录报告约 `2393` 项、系统收到约 `2392` 张 vCard、应用最终读取和显示 `2000` 条属于符合产品约束的结果,不再按截断缺陷处理。
- 产品已确认通话记录为呼入、呼出、未接三类合并后总共显示最新 `50` 条。应用侧 `CallLogUtils.MAX_CALL_LOGS_ROWS` 与 `BtCallRecordsManager.MAX_STORED_CALL_LOGS` 均已按总量 `50` 做时间倒序截取,不需要继续修改应用层数量限制。
- Android 14 PBAP 当前分别拉取 `MCH_PATH / ICH_PATH / OCH_PATH`,且 `BluetoothPbapRequestPullPhoneBook` 的 `maxListCount` 为 `0`,实际会请求全部通话记录。为保证合并后的最新 50 条不遗漏任一类型,系统侧建议三个目录各请求最新 `50` 条,再由应用合并排序取最新 `50` 条;系统侧传输上限因此为 `150` 条,而不是直接按类型平均分配为 `17 / 17 / 16`。
- 不优先改为单次 `CCH_PATH` 拉取总共 50 条:当前 AOSP `CallLogPullRequest` 仅根据 MCH/ICH/OCH 路径确定通话类型,而 CCH 的类型位于 `X-IRMC-CALL-DATETIME;TYPE=...` 参数中,现有 vCard 数据模型未向该处理器保留此参数。采用 CCH 需要扩展解析链并验证手机兼容性,风险和改动范围明显更大。
- 下一步系统源码改动点:在 `PbapClientConnectionHandler.java` 增加通话记录单目录上限 `50`,并将 `downloadCallLog()` 构造 `BluetoothPbapRequestPullPhoneBook` 时的第五个参数 `maxListCount` 从 `0` 改为该常量。应用源码仓不包含该系统文件,按用户要求不通过 Windows 网络映射盘执行 Ubuntu Git 或源码修改。
## 蓝牙同步失败提示 UI 调整(2026-07-31)
- 用户实车截图确认,联系人/通话记录同步失败复用了蓝牙名称设置结果使用的 `680dp x 384dp` 大图标弹层,遮挡范围过大。
- 对照老项目交互,通讯录和通话记录下载失败应使用紧凑的纯文字提示,带图标的大结果弹层仅保留给蓝牙名称设置成功/失败。
- 按用户要求不备份;联系人和通话记录的“同步失败”改用模块现有 `ToastUtils + toast_layout.xml`,以 `484dp x 80dp` 深色居中文字 Toast 展示,不改变同步状态逻辑和蓝牙设置结果弹层。
## 通讯录搜索高亮色统一(2026-07-31)
- 用户实车截图确认,通讯录搜索命中文字使用 `#34C759` 绿色,与当前蓝色主视觉不一致,且绿色容易表达成功/在线状态。
- 按用户要求不备份;将联系人搜索和拨号联系人匹配的姓名/号码命中文字统一改用已有 `text_blue`(`#009CFF`),未匹配文字颜色保持不变。
## 后台通话恢复完整页面(2026-08-03)
- 日志:`C:\Users\Administrator\Desktop\同力日志\tongli_2026_08_03_14_47_26.log`。14:47:30.362 应用收到手机端去电 `DIALING`,14:47:32.176 收到 `ALERTING`,HFP 链路正常;`TelephoneWindowControl` 仅刷新了隐藏 View,没有真正显示窗口。
- 直接原因是 `TelephoneWindowControl.show()` 只允许 `INCOMING`,去电 `DIALING / ALERTING` 在入口直接返回。日志中没有 `show elapsedMilli`,证明没有执行到真正添加悬浮窗的逻辑。
- 老项目 `BtAdapterManager.actionStart()` 对来电、去电和通话中统一启动完整 `PhoningActivity`;需求确认按老项目执行:正常通话页面被 Home、Back 或其他应用切到后台后,只要通话未结束就恢复完整通话页面,不使用普通悬浮窗。
- 已调整 `BluetoothService`:任意有效单路/多路通话状态在电话 Activity 不在前台时直接启动 `BluetoothPhoneActivity`;倒车、360、待机继续保留不拉起规则,已有普通悬浮窗会先关闭。
- 已调整 `BluetoothPhoneActivity`:通话中忽略 Back,Home 或页面进入后台后延迟恢复完整通话页;通话结束、HFP 断开、倒车、360、待机时不恢复。
- 使用 Android Studio JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,19 项 SDK 单元测试通过;`git diff --check` 通过。尚未安装和实车验证,需复测后台手机去电、接通后 Home/Back、来电、第二路电话、倒车/360和挂断退出。
## 呼叫等待交互对齐老项目(2026-08-03)
- 实车日志 `C:\Users\Administrator\Desktop\同力日志\tongli_2026_08_03_15_37_33.log` 在 `16:45:06` 显示原通话 `10000` 为 `ACTIVE`,第二路 `15659813013 / 007_99卡1` 为 `WAITING`;现有页面使用全屏 `rl_multi_layout` 覆盖原通话控制区,并显示新项目遗留的三方控制按钮。
- 老项目 `PhoningActivity.setShowSecondPhoneIn()` 只显示 `second_phone_in`,文案为“呼叫等待,请在手机中操作…”;原通话信息和底部控制区保持可见。手机端接听或拒绝后,老项目在 `SECOND_PHONE_PICKUP_OR_REJECT` 或 `HELD` 状态隐藏提示。
- 按用户要求不备份。新项目完整通话页移除 `ThirdCallControlView` 覆盖层,只保留居中的呼叫等待提示;两路通话时优先显示 `ACTIVE` 原通话,`WAITING / INCOMING` 仅控制提示显示,不再暴露车机端接听、拒接、保持或切换按钮。
- `BluetoothPhoneViewModel.onHfpCallChanged()` 不再在通话数从 1 增加到 2 时提前返回,每次先更新内部快照,再发布完整通话列表;同时保留 `TelephoneWindowControl` 所需的兼容通知。这样第二路来电、手机端拒绝/接听及 `ACTIVE / HELD` 切换都能刷新完整页面状态。
- 使用 Android Studio JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,SDK 单元测试 19/19 通过,Debug APK 输出为 `YX_BlueTooth/build/outputs/apk/debug/YX_BlueTooth.apk`,`git diff --check` 通过。
- 尚需实车验收:原通话中触发第二路等待来电,确认原通话控制区不被遮挡且只出现提示;分别在手机端拒绝、接听、切换两路和全部挂断,确认提示收起、当前活动通话刷新和页面正常退出。
### 更新标记:老项目现行链路不显示呼叫等待提示
- 用户重新核对老项目实车,三方场景始终显示普通通话页面,没有出现“呼叫等待,请在手机中操作…”;该实车结果优先于先前仅依据资源和 Activity 消费代码得出的结论。
- 重新追踪发送端确认:`SECOND_PHONE_IN` 只由旧 `CallWaitingCallback()` 发送,但创建 `BtAdapter_NF2311` 的代码已经注释;老项目当前构造流程实际注册 `GocBluetoothPhoneChangeListener`。该现行回调对 `WAITING` 没有发送 `SECOND_PHONE_IN`,对 `isMultiParty + ACTIVE` 也只更新三方状态,不显示提示。
- 因此 `second_phone_in`、`setShowSecondPhoneIn()` 和 `textview_show_num_3` 属于遗留但未在当前运行链路触发的代码,不能作为现行产品交互基线。先前“等待时显示提示”的结论作废。
- 新项目已撤掉完整通话页中的呼叫等待提示和三方按钮覆盖层;第二路电话到达时读取完整通话列表,忽略 `WAITING / INCOMING` 作为主显示对象,优先显示 `ACTIVE` 通话的普通页面。保留 `BluetoothPhoneViewModel` 每次发布完整列表的修复。
- Android Studio JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,SDK 单元测试 19/19 通过,`git diff --check` 通过。仍需实车验证第二路等待、手机端接听/拒绝、切换和挂断全过程始终保持普通通话页面。
### 更新标记:多路通话显示优先级与退出幂等处理
- 按用户要求不备份。代码复核发现多路回调可能短暂包含 `TERMINATED + HELD/WAITING`;若只优先 `ACTIVE`,列表顺序可能导致页面误选已结束通话并退出。
- `BluetoothPhoneActivity` 已按 `ACTIVE > DIALING/ALERTING > HELD/HELD_BY_RESPONSE_AND_HOLD > INCOMING/WAITING > 未知状态 > TERMINATED` 选择主显示通话,保证存在有效通话时不会优先处理 `TERMINATED`。
- Activity 的所有退出入口统一经过 `finishOnce()`,避免空列表、结束回调和终止状态同时到达时重复请求 `finish()`;ViewModel 现有通知协议保持不变。
- Android Studio JBR 17 执行 `:YX_BluetoothSDK:testDebugUnitTest :YX_BlueTooth:assembleDebug` 成功,SDK 单元测试 19/19 通过,`git diff --check` 通过。仍需实车验证第二路接听、手机端切换两路以及分别挂断时页面不闪退、不重进。
### 决策记录:后期恢复三方通话小窗
- 当前移除的是完整通话页中的三方覆盖布局,不是删除 HFP 多路通话能力。`ThirdCallControlView`、`ThirdCallingControl`、`TelephoneWindowControl`、`mThirdInCallingLiveData` 以及接听、保持、切换、挂断接口仍保留,可作为后期恢复基础。
- 如产品后期要求恢复原有三方通话小窗,应优先恢复 `BluetoothService` 的小窗显示路由和 `TelephoneWindowControl` 的交互,不重新启用覆盖整个通话页的 `rl_multi_layout`。
- 恢复时必须保留本次已验证的完整通话列表发布、主通话显示优先级和 `finishOnce()` 幂等退出处理;禁止整体回退本次提交,否则会重新引入第二路回调不刷新、`TERMINATED` 误选和重复 `finish()` 问题。
- 恢复后需重新验证:`ACTIVE + WAITING`、`ACTIVE + HELD`、手机端两路切换、`TERMINATED + HELD`、分别挂断、后台与前台互斥显示,以及倒车、360、待机时不弹出小窗。