Files
Obsidian/项目文档/徐工/项目md文件/agent.md
T
2026-11-05 14:35:04 +08:00

90 KiB
Raw Blame History

agent.md

当前阶段

  • 2026-06-05:排查并优化蓝牙通讯录/通话记录权限与同步 UI 状态。

已验证结论

  • 清空蓝牙本地 SQLite 表不是通讯录同步失败的直接原因;正常清空后 PBAP 同步会重新写入。
  • 日志中偶现的 ConcurrentModificationException 指向联系人保存阶段共享列表被并发访问,属于同步完成后保存/回调链路稳定性问题。
  • 2026-06-06 新复测日志 2026_0606_1105_50_063.log 显示 UI 修复生效:
    • 未出现 Dex checksum does not match
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
    • 同步中 ContactsFragment 保持 isPhoneBookSynchronized=false isPhoneBookPulling=true
    • 最终 11:08:28.044 收到 ContactListViewModel: onContactsPhoneBook = 1973
  • 2026_0606_1105_50_063.log 同时确认双进程重复同步:
    • 11:06:32.967 com.yaxon.carcontrol PID 3859 触发 phoneBookPullRequest
    • 11:06:32.977 com.yaxon.bluetooth PID 4968 触发 phoneBookPullRequest
    • 两个进程都收到 onContactsPullCompleted、读取 1973 条、执行 deleteContacts
  • 2026-06-06 复测日志 2026_0606_1136_51_087.log 确认移除 YX_CarControl 联系人/PBAP 初始化后双进程重复同步已消失:
    • PID 3793 为 com.yaxon.carcontrol,只出现配对弹窗和自动同步开关日志,未出现 BtContactsControlManager
    • PID 5101 为 com.yaxon.bluetooth,唯一执行 BluetoothPbapClient: phoneBookPullRequest
    • 联系人同步成功:11:38:25.186 contacts read completed size = 1973,11:38:41.378 onContactsPhoneBook = 1973
    • 通话记录同步成功:11:38:02.223 mCallRecordBeanList size = 51,11:38:02.953 notifyPhoneRecordList list == 51
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
  • 2026-06-06 用户现场验证 2026_0606_1136_51_087.log 对应 UI 表现符合预期:
    • 点击下载通讯录后联系人页显示“正在加载通讯录”
    • 切到通话页面时通话列表可正常显示
    • 再切回联系人页面后通讯录正常显示
    • 说明联系人同步中的空列表中间态已被 loading 承接,未再误显示 0 个联系人
  • 2026-06-06 现场重新清理 logcat 后复测,导出日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_2026_0606_1348_32_838.log:
    • com.yaxon.carcontrol PID 3957 只出现配对弹窗/自动同步开关日志,未出现 BtContactsControlManager
    • com.yaxon.bluetooth PID 5030 唯一执行 BluetoothPbapClient: phoneBookPullRequest
    • 通讯录流程成功:12:40:35.640 phoneBookPullRequest,12:41:22.565 onContactsPullCompleted,12:41:51.884 contacts read completed size = 1973
    • 通话记录流程成功:12:41:27.963 mCallRecordBeanList size = 51,12:41:28.665 notifyPhoneRecordList list == 51
    • 用户切回联系人页后读取并显示成功:12:44:48.892 onContactsPhoneBook = 1973,isPhoneBookSynchronized = true isPhoneBookPulling=false
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
  • 2026-06-06 用户再次点击“同步通讯录”后复测,导出日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_resync_2026_0606_1352_02_395.log:
    • 再次点击会重新触发一次 PBAP 通讯录同步:12:47:06.124 BluetoothPbapClient: phoneBookPullRequest
    • 仍然只有 com.yaxon.bluetooth PID 5030 执行同步,BtContactsControlManager = 0
    • 第二次同步成功:12:47:50.259 onContactsPullCompleted,13:51:27.509 contacts read completed size = 1973,13:51:45.832 onContactsPhoneBook = 1973
    • 第二次通话记录也成功刷新:12:47:55.391 mCallRecordBeanList size = 51,12:47:55.982 notifyPhoneRecordList list == 51
    • 日志中出现设备时间从 06-05 12:47 跳到 06-06 13:51 的校时现象,因此第二次同步耗时不能直接按日志墙钟计算
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
  • 2026-06-06 清空 logcat 后再次点击“同步通讯录”复测,导出日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_resync_clean_2026_0606_1355_54_369.log,补抓尾部 verify_resync_clean_tail_2026_0606_1356_44_383.log:
    • 本轮无时间跳变,可计算耗时:13:54:40.708 phoneBookPullRequest 到 13:56:06.301 onContactsPhoneBook = 1973,约 85.6 秒
    • PBAP 拉取阶段:13:54:40.708 到 13:55:24.857 onContactsPullCompleted,约 44.1 秒
    • 联系人读取阶段:13:55:24.926 cursor.getCount() = 1973 到 13:55:51.351 contacts read completed size = 1973,约 26.4 秒
    • 写库/页面回调阶段:13:55:51.352 deleteContacts 到 13:56:06.301 onContactsPhoneBook = 1973,约 14.9 秒
    • BtContactsControlManager = 0,BluetoothPairedRequestView = 0,说明手动同步未经过 YX_CarControl 配对弹窗
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败、Dex checksum does not match
  • 2026-06-06 通话页 loading UI 修复后复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1405_50_910.log:
    • 新逻辑已生效:切回通话页时出现 CallLogsFragment: onHiddenChanged hidden=false 后紧跟 setIsBtConnect=true
    • 通话记录同步完成:14:07:31.863 mCallRecordBeanList size = 51,14:07:32.670 notifyPhoneRecordList list == 51
    • 通讯录同步完成:14:07:56.555 contacts read completed size = 1973,14:08:14.822 onContactsPhoneBook = 1973
    • 切回通话页时即使 isPhoneBookPulling=true,日志仍显示 CallRecordUpdated = true 并刷新 notifyPhoneRecordList list == 51
    • 本轮未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
    • 本轮出现 Dex checksum does not match,推测为系统 APK 覆盖推送后旧 dex/oat 缓存未刷新,需重启设备或清理对应编译缓存后再做最终确认
  • 2026-06-06 重新加载/重启后复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1413_21_443.log,补抓尾部 tail_after_2026_0606_1413_21_443_2026_0606_1416_43_579.log:
    • 未再出现 Dex checksum does not match
    • 通话页切回刷新逻辑生效:CallLogsFragment: onHiddenChanged hidden=false 后出现 setIsBtConnect=true
    • 通话记录同步完成:14:14:39.747 mCallRecordBeanList size = 51,14:14:40.416 notifyPhoneRecordList list == 51
    • 通讯录同步完成:14:15:06.632 contacts read completed size = 1973,补抓尾部 14:15:22.731 onContactsPhoneBook = 1973
    • PBAP 断开完成:14:15:22.821 prevState = 2 state = 3,14:15:22.886 prevState = 3 state = 0
    • 本轮未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
  • 2026_0605_1739_21_177.log 中“先显示 0 个联系人,随后显示联系人列表”的原因不是同步失败,而是 UI 状态提前:
    • 17:40:48.576 收到 BtContactsManager: onContactsPullCompleted
    • 17:40:48.623 系统联系人 Provider 查到 1973 条
    • 17:41:19.867 SDK 读联系人完成:contacts read completed size = 1973
    • 17:41:30.114 页面判断 isPhoneBookSynchronized = true isPhoneBookPulling=true
    • 17:41:36.979 ViewModel 才收到 onContactsPhoneBook = 1973
  • isPhoneBookSynchronized() 只查 DB 是否存在 1 条联系人;保存大量联系人时第一条写入后就可能返回 true,导致联系人页提前显示空列表区域。

Bug 与修复记录

  • 2026-06-09 蓝牙音乐封面继续扩展到 YX_SystemUI / YX_Launcher:
    • 用户确认先备份,已备份 DisplayShareViewModel.java、MediaControlView.java、WidgetsMedia.java 到 .codex_backup\music_cover_systemui_launcher_20260609_194529
    • 最新日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1939_16_256.log 显示 YX_Music 已能在封面文件稳定后加载成功,剩余问题集中在 SystemUI/Launcher 空 albumUrl 或无效文件覆盖已有封面
    • YX_SystemUI 的 MediaControlView 增加蓝牙曲目 key 与封面显示状态:同一首歌收到空 albumUrl 或无效文件时保留当前封面;新歌收到空/无效封面时显示默认图,避免继续显示上一首封面
    • YX_Launcher 的 WidgetsMedia 按相同规则处理手写 widget 封面逻辑,并避免 BitmapFactory.decodeFile() 直接解码空路径
    • YX_Launcher 的 DisplayShareViewModel.btMusicAlbum 数据绑定入口增加 lastAlbum 缓存:同一首歌后续空路径/0 字节/解码失败时返回当前封面,新歌无有效封面时返回默认图
    • 已执行 .\gradlew :YX_SystemUI:assembleDebug 和 .\gradlew :YX_Launcher:assembleDebug,均编译通过
    • 已推送 YX_SystemUI.apk 到 /system/app/YX_SystemUI/YX_SystemUI.apk,推送 YX_Launcher.apk 到 /system/app/YX_Launcher/YX_Launcher.apk,并执行设备重启;重启后 sys.boot_completed=1,两个包路径正常
  • 2026-06-08 蓝牙音乐封面短暂显示后消失问题:
    • 最新日志显示 SDK 可收到 albumUrl = /sdcard/ID3/id3cover.JPEG,但 YX_Music 侧多次出现 Failed to create image decoder 和 BluetoothMusicViewModel: 获取bitmap为null
    • YX_BluetoothSDK 只转发系统蓝牙 MediaMetadata.METADATA_KEY_ALBUM_ART_URI,未发现生成 /sdcard/ID3/id3cover.JPEG 的逻辑
    • YX_Music 已调整封面加载兜底:先解码新图片,成功后再替换并回收旧图;解码失败时如果当前已有文件封面则保留当前封面
    • BluetoothMusicFragment 已调整空 albumUrl 处理:当前没有文件封面时才显示默认图,避免元数据短暂空值覆盖已有封面
  • 2026-06-08 最新日志 2026_0608_1939_20_273.log 确认 /sdcard/ID3/id3cover.JPEG 文件存在且可读,但长度始终为 0:
    • BluetoothMusicViewModel: 获取bitmap为null, filePath = /sdcard/ID3/id3cover.JPEG, exists = true, length = 0, canRead = true
    • 本轮日志中 length 0 = 53,未发现非 0 字节封面文件
    • YX_BluetoothSDK 已做最小颗粒过滤:BtMusicManager 和 BtInfoPushManager 下发封面前校验本地文件路径,0 字节/不可读/不存在的封面路径不再下发
    • 同时增加 ALBUM_ART_URI、ART_URI、DISPLAY_ICON_URI、ALBUM_ART、ART 字段诊断日志,暂不做 bitmap 落盘,避免扩大改动
  • 2026-06-08 最新日志 2026_0608_1958_40_593.log 确认切歌后封面路径晚于歌曲信息到达:
    • 点击下一首后,歌名通常先到,albumUrl 为空;约 2 到 4 秒后才出现固定路径 /sdcard/ID3/id3cover.JPEG
    • 由于系统复用同一个固定封面文件,过早解码可能读到上一首封面内容,导致“先显示上一首,过会儿显示正确”
    • BluetoothMusicFragment 已增加切歌封面调度:切歌或曲目 key 变化时先显示默认封面;固定 ID3 封面路径延迟 1 秒解码;使用版本号取消旧封面延迟任务
  • 2026-06-09 继续分析日志 2026_0608_2015_49_223.log,确认 1 秒延迟仍不足以规避固定封面文件重写竞态:
    • skip invalid album file = 111,说明 YX_BluetoothSDK 的 0 字节过滤已生效
    • BluetoothMusicViewModel: 获取bitmap为null = 8,说明 YX_Music 延迟解码时仍可能撞上 /sdcard/ID3/id3cover.JPEG 被底层清零或半写入
    • 典型时序:20:18:11.761 底层开始 get image,20:18:11.803 SDK 检测 length = 0 并过滤,20:18:11.808 YX_Music 解码同一路径失败,20:18:12.294 底层才提示 avrcp image download succeed
    • 根因是 /sdcard/ID3/id3cover.JPEG 为底层复用固定路径,单次下发时校验和页面实际解码之间仍存在文件重写竞态
    • 本轮按用户要求先只修改 YX_Music:BluetoothMusicFragment 对固定 ID3 封面路径增加文件刷新检测、连续两次长度/修改时间稳定后再解码、最多 20 次 300ms 重试;切歌/上一首/下一首时重置检测状态并先显示默认封面
    • BluetoothMusicViewModel.loadFileImageToView() 改为返回解码是否成功,解码失败时保留已有有效文件封面;无有效文件封面时才回退默认图
  • BtContactsManager 已增加联系人列表快照防御:
    • PBAP 联系人读取完成后使用 contactsSnapshot
    • 保存 DB 和回调 UI 不再直接传共享的 mContactsList
    • saveContactsList() 增加 null 判断和防御拷贝
  • ContactsFragment 已调整联系人页显示时序:
    • LiveData 收到非空联系人时,先填充 mPhoneBookList,再显示联系人列表
    • 已同步但本地联系人列表为空时,先显示 loading,不提前显示空列表
    • 点击一键同步且 PBAP 正在同步时,联系人页显示 loading,避免重复同步 Toast
    • 重新同步期间如果读取到旧库联系人,但本轮 PBAP 未完成(isPhoneBookPulling=true && getContactLoadWonType=false),忽略这批旧数据并保持 loading,避免提前显示旧通讯录列表
  • YX_CarControl 已移除配对弹窗中对 BtContactsControlManager.getInstance().init(context) 的调用:
    • YX_CarControl 只保留自动同步开关设置
    • 联系人/PBAP 同步统一由 YX_BlueTooth 进程负责
    • 避免 YX_CarControl 和 YX_BlueTooth 两个进程同时注册 PBAP 完成广播并重复删写联系人表
  • CallLogsFragment 已修复通话页切回后仍显示 loading 的 UI 问题:
    • onHiddenChanged(false) 时重新执行 setIsBtConnect(...),确保切回通话页会按最新通话记录同步状态刷新 UI
    • readCallRecordUpdated() 中已有通话记录列表时优先显示列表,不再被 isPhoneBookPulling=true 覆盖成 loading
    • 补充兜底:notifyPhoneRecordList() 收到非空通话记录时直接关闭 loading、显示通话列表,避免通话记录已到但外层状态未及时收敛导致页面仍转圈
  • 2026-06-06 复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1528_11_585.log 确认联系人页重新同步期间仍会显示旧列表:
    • 15:31:00.768 第二次 phoneBookPullRequest
    • 15:31:04.208 到 15:31:20.734 多次切回联系人页,均为 isPhoneBookSynchronized = true isPhoneBookPulling=true
    • 本轮联系人实际到达为 15:32:25.450 onContactsPhoneBook = 1973
    • 根因是切回联系人页时 syncPhoneBookSynchronized(true) 只看本地库已有联系人,未优先判断“本轮同步尚未完成”,导致旧内存列表重新显示
  • 已追加联系人同步中隐藏旧列表修复:
    • BtContactsManager.phoneBookPullRequest() 开始新一轮同步时重置 isContactsPullCompeted=false
    • ContactsFragment.syncPhoneBookSynchronized() 优先判断 isPhoneBookPulling && !getContactLoadWonType(),该状态强制显示 loading 并隐藏联系人列表
    • showContactsLoadingPanel() 统一隐藏蓝牙连接面板并调用 updateLoadPage(true)
  • 2026-06-06 复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1556_40_545.log 确认通话记录“显示空”不是同步失败:
    • 无 Dex checksum does not match
    • 15:57:41.356 通话记录同步到 mCallRecordBeanList size = 51
    • 15:57:41.377 保存通话记录 saveCallRecordList size = 51
    • 15:58:24.959 切回通话页后 notifyPhoneRecordList list == 51
    • 根因是 CallLogsFragment.notifyPhoneRecordList() 先填充字段 mCallRecordList,再把同一个 list 传给 CallRecordAdapter.setData();Adapter 内部先 clear() 再 addAll(),导致自清空,日志显示 51 但 Adapter 实际为空
  • 已修复通话记录 Adapter 自清空问题:
    • CallLogsFragment.notifyPhoneRecordList() 改为传入 new ArrayList<>(mCallRecordList),避免 Adapter 清空同一个数据源引用

验证记录

  • 2026-06-08:蓝牙音乐封面兜底修复后,.\gradlew :YX_Music:assembleDebug 编译通过。第一次沙箱内编译因读取本机 Android SDK android.jar 权限失败,使用非沙箱权限重跑通过。
  • 2026-06-08:YX_BluetoothSDK 封面路径有效性过滤后,.\gradlew :YX_BluetoothSDK:assembleDebug 编译通过;随后 .\gradlew :YX_Music:assembleDebug、.\gradlew :YX_BlueTooth:assembleDebug 集成编译通过。
  • 2026-06-08:BluetoothMusicFragment 切歌封面延迟加载修复后,.\gradlew :YX_Music:assembleDebug 编译通过。
  • 2026-06-09:YX_Music 固定 ID3 封面文件稳定性检测和重试修复后,.\gradlew :YX_Music:assembleDebug 编译通过;尚待推送设备后复测切歌封面是否仍出现默认图/上一首封面/解码失败。
  • 2026-06-09:YX_Music.apk 已推送到设备 /system/app/YX_Music/YX_Music.apk,设备旧包已备份到 /sdcard/YX_Music.apk.bak_20260609_112853;尚未重启设备,需重启后复测以避免旧 dex/oat 缓存影响。
  • 2026-06-09:重新执行 .\gradlew :YX_Music:assembleDebug 编译通过,并再次推送 /system/app/YX_Music/YX_Music.apk;推送前设备当前包备份到 /sdcard/YX_Music.apk.bak_20260609_113508,随后已重启设备。
  • 2026-06-09:重启后启动验证日志已保存到 C:\Users\Administrator\Desktop\徐工日志\yx_log\verify_music_yxmusic_only_20260609_1142.log:
    • sys.boot_completed = 1
    • pm path com.yaxon.music 指向 /system/app/YX_Music/YX_Music.apk
    • 通过 am start -n com.yaxon.music/com.yaxon.musicplay.view.UsbMainActivity 可启动应用
    • 启动阶段未发现 Dex checksum、FATAL EXCEPTION、获取bitmap为null、Failed to create image decoder
    • 当前日志显示 isMusicPlaying false,尚未覆盖蓝牙播放中切歌封面场景;需现场播放蓝牙音乐并切歌后再抓日志确认
  • 2026-06-09:分析 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1409_00_592.log,最后一段同专辑 与我无关 复用固定封面路径;14:18:50-14:18:51 底层写 /sdcard/ID3/id3cover.JPEG 时出现 length=0 -> 16146 -> 24220 -> 43843,YX_Music 已等待文件稳定,SystemUI/Launcher 仍会直接读固定路径。为复现确认,YX_Music 仅追加诊断日志:
    • BluetoothMusicFragment 记录 ID3 文件稳定确认、准备加载、加载失败重试、重置检查状态
    • BluetoothMusicViewModel 记录默认封面设置、文件封面解码成功的文件长度/修改时间/bitmap 宽高/byteCount
  • 2026-06-09:YX_Music 蓝牙封面诊断日志追加后,.\gradlew :YX_Music:assembleDebug 编译通过;已备份设备旧包到 /sdcard/YX_Music.apk.bak_20260609_143548,新包已推送到 /system/app/YX_Music/YX_Music.apk 并重启设备;重启后确认 sys.boot_completed=1,pm path com.yaxon.music 指向系统路径。
  • 2026-06-09:分析 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0609_1451_00_890.log,确认 YX_Music 未解码失败,但固定 ID3 封面稳定判断过早:
    • loadFileImageToView success = 1125,获取bitmap为null = 0,loadFileImageToView e = 0
    • 同一 lastModified 下出现多个成功加载长度,例如 1780988430000: 8072 -> 16146 -> 27885
    • 典型时序:15:00:30.103 先成功加载 length=8072,15:00:30.293 底层才 avrcp image download succeed,15:00:30.403 再成功加载 length=27885
    • 根因是元数据回调密集,连续两次长度/修改时间相同可在数毫秒内发生,当前稳定判断会把半写入文件误判为稳定
  • 2026-06-09:继续仅修改 YX_Music,延后 SystemUI/Launcher:
    • BluetoothMusicFragment 增加 ID3_ALBUM_STABLE_DURATION_MS=500,固定 ID3 封面必须长度/修改时间连续稳定满 500ms 才允许解码
    • 合并重复的 ID3 延迟加载请求,避免密集元数据回调堆叠多个相同 runnable
    • 已显示同一长度/修改时间的 ID3 文件时跳过重复解码,减少反复 setImageBitmap
    • .\gradlew :YX_Music:assembleDebug 编译通过;设备旧包备份到 /sdcard/YX_Music.apk.bak_20260609_160339,新包已推送到 /system/app/YX_Music/YX_Music.apk 并重启;重启后确认 sys.boot_completed=1,pm path com.yaxon.music 正常
  • .\gradlew :YX_BlueTooth:assembleDebug 已编译通过。
  • 最新 ContactsFragment UI 时序修改已编译通过,但在用户中断前尚未执行 adb push 到设备验证。
  • 2026-06-06:YX_BlueTooth APK 已重新 clean 编译并推送到 /system/app/YX_BlueTooth。
  • 2026-06-06:CallLogsFragment loading UI 修复后,.\gradlew :YX_BlueTooth:assembleDebug 编译通过,并已推送到 /system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小 21232834。
  • 2026-06-06:CallLogsFragment.notifyPhoneRecordList() 非空列表强制关闭 loading 兜底后,.\gradlew :YX_BlueTooth:assembleDebug 编译通过,并已推送到 /system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小 21232896。
  • 2026-06-06:ContactsFragment 重新同步期间过滤旧库联系人兜底后,.\gradlew :YX_BlueTooth:assembleDebug 编译通过,并已推送到 /system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小 21233011。
  • 2026-06-06:联系人页切回时隐藏旧列表修复后,.\gradlew :YX_BlueTooth:assembleDebug 编译通过,并已推送到 /system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小 21233092。
  • 2026-06-06:通话记录列表 Adapter 自清空修复后,.\gradlew :YX_BlueTooth:assembleDebug 编译通过,并已推送到 /system/app/YX_BlueTooth/YX_BlueTooth.apk,设备端大小 21233566。
  • 2026-06-06 复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1605_11_651.log:
    • 用户确认通话列表已有显示
    • 通话记录同步成功:16:05:58.835 mCallRecordBeanList size = 51,16:05:58.843 saveCallRecordList size = 51,16:05:59.636 notifyPhoneRecordList list == 51
    • 通讯录同步成功:16:06:24.788 contacts read completed size = 1973,16:06:40.194 onContactsPhoneBook = 1973
    • BtContactsControlManager = 0,未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败
    • 仍出现 Dex checksum does not match,最终验收建议重启设备后再抓一轮无 dex 缓存异常日志
  • 2026-06-06 最新复测日志 C:\Users\Administrator\Desktop\徐工日志\yx_log\2026_0606_1612_17_622.log 作为当前最终有效验证:
    • Dex checksum does not match = 0
    • com.yaxon.carcontrol 只出现 BluetoothPairedRequestView 配对弹窗/自动同步开关日志,BtContactsControlManager = 0
    • com.yaxon.bluetooth 唯一触发 16:13:20.668 phoneBookPullRequest
    • 通话记录同步和显示成功:16:14:11.907 mCallRecordBeanList size = 51,16:14:11.921 saveCallRecordList size = 51,16:14:12.673 notifyPhoneRecordList list == 51;同步完成后切回通话页 16:15:36.739 notifyPhoneRecordList list == 51
    • 通讯录同步成功:16:14:35.249 contacts read completed size = 1973,16:14:51.422 onContactsPhoneBook = 1973
    • 未出现 ConcurrentModificationException、onContactsPullFailed、通讯录同步失败

待办事项

  • 清空蓝牙联系人/通话记录表后重新验证:
    • 联系人权限允许后点击一键同步
    • 同步过程中切换联系人页/通话记录页
    • 确认不再短暂显示 0 个联系人列表
    • 确认同步完成后显示实际联系人数量
    • 确认无 ConcurrentModificationException 和“通讯录同步失败” Toast
  • 编译并推送 YX_CarControl.apk 后重启设备,重新确认只剩 com.yaxon.bluetooth 一个进程执行 BtContactsManager 联系人同步。

更新记录

  • 2026-06-23:调整语音 playDislike(不喜欢这首歌)处理逻辑:
    • 用户确认本次不做文件备份。
    • YX_Voice/src/main/java/com/yaxon/voice/protocol/VoiceProtocol.java 中 CMD_PLAY_DISLIKE 从原先复用下一首逻辑改为音频源取消收藏逻辑。
    • 视频源仍保持下一个视频:sendVideoController(CMD_VIDEO_NEXT)。
    • 收音机源改为复用 sendMusicUnFavorites(false),走现有收音机取消收藏按键链路。
    • USB/蓝牙音乐源改为复用 sendMusicUnFavorites(true),向音乐模块发送 CMD_PLAY_UNFAVORITES。
    • 验证:.\gradlew :YX_Voice:assembleDebug 编译通过。
  • 2026-06-22:按 TXZ 反馈调整“可见即可说”验证方向:
    • TXZ 确认 shadow.register 属于网页、小程序、自绘等非常规页面的主动插词注册方式;普通原生页面不需要主动同步给 TXZ,语音侧会通过无障碍扫描 id/text/contentDescription/clickable/scrollable。
    • 本次选 YX_CarControl 的语音设置页 fragment_voice.xml 做普通页面扫描验证:为语音唤醒、免唤醒、免唤醒时长、系统语音包、唤醒词入口补充 contentDescription、clickable/focusable 和滚动容器 id。
    • CustomTriStateSwitch 按文档要求在 onInitializeAccessibilityNodeInfo() 中暴露 checkable/checked/clickable/enabled,便于 TXZ 判断自定义开关状态。
    • VoiceFragment 根据开关当前状态动态更新 contentDescription:关闭态暴露“打开/开启”,开启态暴露“关闭/关掉”,避免状态型控件同时暴露正反插词导致动作反向。
    • 暂停 YX_CarControl 启动后自动注册测试插词,不再自动发送 shadow.register,但保留 registerVisibleSpeakKeywords(...) / unregisterVisibleSpeakKeywords(...) 预留接口供后续非常规页面使用。
    • 验证:.\gradlew :YX_CarControl:assembleDebug 编译通过。
  • 2026-06-22 更新:语音设置页可能与既有语义命令重叠,不适合作为无障碍扫描效果验证页,已新增维保页独立测试点:
    • fragment_maintenance.xml 为保养里程选项和保养重置按钮补充独特测试插词:可见测试关闭保养里程、可见测试五千公里、可见测试一万公里、可见测试三万公里、可见测试保养重置。
    • MaintenanceFragment 在对应点击入口增加 visible speak test click: ... 日志,便于判断语音命中后是否真实触发控件点击。
    • 验证:.\gradlew :YX_CarControl:assembleDebug 编译通过;新包已推送到 /system/app/YX_CarControl/YX_CarControl.apk 并重启设备,sys.boot_completed=1,pm path com.yaxon.carcontrol 指向系统路径。
  • 2026-06-22 更新:维保页 RadioButton/contentDescription 测试现场未响应,进一步简化为系统页可见标准按钮测试:
    • 在 fragment_system.xml 顶部新增标准 Button,可见文本为 可见测试按钮,依赖 TXZ 扫描 android:text,避免 RadioButton、隐藏标签、既有语义干扰。
    • SystemFragment 绑定按钮点击后打印 visible speak simple test button clicked 并 Toast 可见测试按钮已点击。
    • 验证:.\gradlew :YX_CarControl:assembleDebug 编译通过;新包已推送到 /system/app/YX_CarControl/YX_CarControl.apk 并重启设备,sys.boot_completed=1,pm path com.yaxon.carcontrol 指向系统路径。
  • 2026-06-13:抽取“可见即可说”业务侧公共工具:
    • 用户确认本次不做文件备份。
    • 新增 YX_Lib_CommonBase/src/main/java/com/yaxon/common/base/voice/VisibleSpeakHelper.java,统一封装业务模块向 YX_Voice 发送 cmd.visibleSpeakRegister / cmd.visibleSpeakUnregister 的 Bundle 组装和 IPC 发送逻辑。
    • VisibleSpeakHelper 同时提供 parseExecute(Bundle),业务模块收到 cmd.visibleSpeakExecute 后可直接解析出 packageName、className、pageName、keyword。
    • YX_Lib_CommonBase/build.gradle 增加 implementation project(path: ':YX_IPC_SDK'),用于公共工具内部调用 YxIPC。
    • YX_CarControl 的 VoiceSettingManager 保留测试插词注册时机和命中日志,但注册/注销发送改为复用 VisibleSpeakHelper,避免后续模块重复手写 Bundle。
    • 验证:.\gradlew :YX_CarControl:assembleDebug 编译通过;.\gradlew :YX_Voice:assembleDebug 编译通过。
  • 2026-06-13 更新:为避免 YX_Lib_CommonBase 扩大 IPC 依赖面,已移除 YX_Lib_CommonBase/build.gradle 中的 implementation project(path: ':YX_IPC_SDK'):
    • VisibleSpeakHelper 改为只负责 buildRegisterBundle(...)、buildUnregisterBundle(...)、parseExecute(...) 等参数构造/解析,不再直接 import 或调用 YxIPC。
    • 业务模块仍由自身已有的 YX_IPC_SDK 依赖执行 YxIPC.getInstance().sendRemoteNoCall(...);当前 YX_CarControl 已按此方式调整。
    • 验证:.\gradlew :YX_CarControl:assembleDebug、.\gradlew :YX_Voice:assembleDebug 编译通过;单独 .\gradlew :YX_Lib_CommonBase:assembleDebug 在沙箱内因本机 Android SDK android.jar 读取权限失败,非沙箱授权请求被审批服务异常拒绝,但 YX_CarControl/YX_Voice 编译已覆盖该公共库改动。
  • 2026-06-13:预留“可见即可说”自动注册框架接口:
    • 用户确认本次不做文件备份。
    • 阅读《同行者可见即可说自动注册框架对接方案.docx》后,按文档预留 shadow.register、shadow.unregister、shadow.execute 三段链路。
    • YX_Lib_CommonBase 的 VoiceNewConstant 增加可见即可说 IPC 命令和参数 key:包名、类名、页面名、插词列表。
    • YX_Voice 的 VoiceNewProtocolConst 增加 TXZ 侧 method/字段常量:key_type、action、keywordList、cmd 等。
    • YX_Voice 的 BaseProtocol 增加可见即可说注册/注销/执行分发;VoiceEventHelper 负责把应用注册转成 TXZ 请求,并把命中的插词回传给目标应用;VoiceProtocol 接收 TXZ shadow.execute 后回传应用。
    • YX_CarControl 的 VoiceSettingManager 增加 registerVisibleSpeakKeywords(...) / unregisterVisibleSpeakKeywords(...) 预留方法,并在语音 IPC 和 TXZ bind 都就绪后注册一组测试插词 可见即可说测试#打开可见即可说测试,命中后当前只记录 handleVisibleSpeakExecute 日志,不执行真实业务动作。
    • 验证:.\gradlew :YX_Voice:assembleDebug 编译通过;.\gradlew :YX_CarControl:assembleDebug 编译通过。
    • 待现场验证:推送 YX_Voice.apk / YX_CarControl.apk 后观察日志中是否出现 registerVisibleSpeakKeywords、sendCommandTxz → method= shadow.register;说出测试插词后确认是否出现 shadow.execute 和 handleVisibleSpeakExecute。
    • 已知风险:TXZ 侧 shadow.register 参数结构目前按文档文字和流程图推断为 keywordList 使用 # 分隔字符串;若同行者实际要求 array/list 或字段名不同,需要按现场日志调整。
  • 2026-06-12:修复语音前台 TTS 播报转发命令:
    • 用户确认本次不做文件备份。
    • 核对链路:YX_CarControl 的 VoiceSettingManager.sendForegroundTtsPlay(...) 发送 cmd.syncForegroundTtsText 到 YX_Voice。
    • YX_Voice 的 BaseProtocol 已能将 cmd.syncForegroundTtsText 分发到 VoiceEventHelper.playTtsOnUi(...)。
    • 修复点:VoiceEventHelper.playTtsOnUi(...) 转发 TXZ 时由 syncBackgroundTtsText 改为 syncForegroundTtsText,后台 playTtsInBackground(...) 保持 syncBackgroundTtsText 不变。
    • 验证:.\gradlew :YX_Voice:assembleDebug 编译通过。
    • 待现场验证:触发仪表脱手报警“请手握方向盘”或其它前台 TTS 场景,确认日志出现 sendForegroundTtsPlay、playTtsOnUi 且 TXZ 前台播报生效。
  • 2026-06-09:新增语音前台 TTS 文本同步接口 syncForegroundTtsText:
    • VoiceNewConstant 增加 IPC 入站命令 CMD_SYNC_FOREGROUND_TTS_TEXT = "cmd.syncForegroundTtsText"
    • VoiceNewProtocolConst 增加 TXZ 协议命令 CMD_SYNC_FOREGROUND_TTS_TEXT = "syncForegroundTtsText"
    • BaseProtocol 新增前台 TTS 分支,收到 cmd.syncForegroundTtsText 后调用 VoiceEventHelper.playTtsOnUi(...)
    • VoiceEventHelper.playTtsOnUi(...) 改为向 TXZ 发送 syncForegroundTtsText,后台 syncBackgroundTtsText 保持不变
    • VoiceSettingManager 新增 sendForegroundTtsPlay(String tts),供 YX_CarControl 侧发送前台 TTS
    • .\gradlew :YX_Voice:assembleDebug 编译通过
    • .\gradlew :YX_CarControl:assembleDebug 编译通过
  • 2026-06-09:继续仅修改 YX_Music 蓝牙音乐封面加载策略:
    • 切歌后 albumUrl 为空时,不再立刻判定无封面;进入固定 ID3 封面 /sdcard/ID3/id3cover.JPEG 等待链路。
    • 同一首歌已经显示文件封面后,后续空 albumUrl 不清空当前封面。
    • ID3 封面重试从原约 6 秒扩展为前 20 次 300ms、后续 1s、最多 50 次,总窗口约 36 秒,用于覆盖封面二十几秒后才下发的场景。
    • .\gradlew :YX_Music:assembleDebug 编译通过;YX_Music.apk 已推送到 /system/app/YX_Music/YX_Music.apk 并重启设备,sys.boot_completed=1,pm path com.yaxon.music 指向系统路径。
  • 2026-06-16:修复蓝牙联系人页权限恢复后“正在同步通讯录”卡住:
    • 用户确认本次不做文件备份。
    • 复现日志 2026_0616_1437_51_345.log 显示本轮 phoneBookPullRequest = 0、onContactsPullCompleted = 0,不是 PBAP 真同步;权限置 0 后 ContactListViewModel 仍回调 onContactsPhoneBook = 257,但 ContactsFragment 会先清空 mPhoneBookList 并因无权限返回。
    • 权限重新确认后,页面状态为 isPhoneBookSynchronized = true isPhoneBookPulling=false,但内存列表为空,旧逻辑直接显示 llLoading,导致“正在同步通讯录”没有后续回调关闭。
    • ContactsFragment 增加本地通讯录重读收敛:已同步且列表为空、未处于 PBAP 拉取时,只触发一次 readPhoneBook() 从本地库回填;若回填为空则关闭 loading 并显示下载入口,避免无限 loading。
    • 验证:.\gradlew :YX_BlueTooth:assembleDebug 编译通过。
  • 2026-06-16:更新版本后复核日志 2026_0616_1437_51_345.log:
    • 日志更新时间为 2026/6/16 15:14:16,大小 16500679。
    • 15:09:33.236 与 15:13:01.840 均触发 BluetoothPbapClient: phoneBookPullRequest,随后分别在 15:09:40.295、15:13:09.663 出现 onContactsPullCompleted。
    • 两轮 PBAP 均读到联系人 257 和通话记录 51,最终状态均回到 isPhoneBookSynchronized=true isPhoneBookPulling=false,说明真实同步链路已完成。
    • 本次日志未出现新修复分支日志 phonebook synchronized but local list is empty, reload local phonebook,暂不能证明本地空列表兜底分支已被执行。
    • 日志仍出现 /system/app/YX_BlueTooth/YX_BlueTooth.apk!classes2.dex 的 Dex checksum does not match,建议推送系统 APK 后重启设备或清理旧 oat/dex 缓存再复测,避免运行旧代码或混合 dex。
  • 2026-06-16:复测日志 2026_0616_1533_52_917.log 验证蓝牙联系人权限恢复兜底:
    • 日志更新时间为 2026/6/16 15:37:06,大小 11242513,本轮未检出 Dex checksum does not match。
    • 15:35:03、15:35:27、15:36:06、15:36:38 多次出现通讯录权限置 0;权限关闭期间本地库仍能回调 onContactsPhoneBook = 257,但 UI 按权限拒绝分支处理。
    • 15:35:16.420 和 15:36:50.420 出现 phonebook synchronized but local list is empty, reload local phonebook,证明新增兜底分支已执行。
    • 兜底触发后立即执行 BtContactsManager: readPhoneBook,随后回调 ContactListViewModel: onContactsPhoneBook = 257,页面可从本地通讯录恢复,不再停留在无限 loading。
    • 用户手动/后续触发真实 PBAP 同步时,15:35:34.363、15:36:15.769、15:36:52.932 出现 phoneBookPullRequest,并分别完成 onContactsPullCompleted、联系人 257、通话记录 51、getStartPullLivedata = false。
    • 当前结论:修复有效;后续可考虑减少权限恢复时的重复 readPhoneBook() 触发,但不影响主问题闭环。
  • 2026-06-23:按 TXZ 无障碍服务 process is bad/扫描不起来问题调整 YX_Voice 恢复策略:
    • 用户前置确认本次不做文件备份,直接修改。
    • VoiceService 不在 onCreate() 常态执行无障碍绑定恢复,改为 TXZ onConnected() 后延迟 2 秒执行一次。
    • 新增 mTxzConnected 与 mAccessibilityServiceRefreshDone 状态保护:TXZ 断开后取消待执行任务并允许下次连接重新恢复;已成功恢复后不重复重绑。
    • 恢复逻辑先读取当前 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,保留已有服务,只针对 com.txzing.voiceassistant/com.txznet.shadow.words.ShadowAccessibilityService 做移除再加回,触发系统重新绑定 TXZ 无障碍服务。
    • 不主动追加 YX_Factory 无障碍服务,避免扩大影响面;如果 Factory 已在列表中会被原样保留。
    • 补充空列表保护,避免 ENABLED_ACCESSIBILITY_SERVICES 为 null 或空字符串时 containsAccessibilityService() 空指针。
    • 验证:.\gradlew :YX_Voice:assembleDebug 编译通过。后续推送设备后重点看 refreshAccessibilityServiceBinding rebind txz、ensureAccessibilityServiceEnabled、ShadowService: onServiceConnected、VisualCommandModule: tryUseAccessibilityService: result = 1、registerKeywords。
  • 2026-06-23:修复语音 playDislike/playUnfavorites 把未收藏歌曲反向收藏的问题:
    • 用户确认本次不做文件备份。
    • 根因:YX_MusicSDK 的 MusicService 将 CMD_PLAY_FAVORITES 和 CMD_PLAY_UNFAVORITES 合并到 onCollectCurrentMusic();下游 SongProvider.collectPlayRecord() 是 toggle 逻辑,当前歌曲 isCollect=0 时执行“取消收藏”会变成收藏。
    • MusicService 将收藏和取消收藏分支拆开;CMD_PLAY_UNFAVORITES 改调 VoiceControlManager.onUnCollectCurrentMusic()。
    • VoiceControlManager.onUnCollectCurrentMusic() 先读取当前播放歌曲,只有已收藏时才复用原 toggle 取消收藏;未收藏时跳过,避免反向收藏。
    • 收音机链路同步修正:VoiceEventHelper.sendMusicUnFavorites(false) 不再发送 KEYCODE_MEDIA_RECORD toggle 按键,改为向 YX_Radio 发送 CMD_PLAY_UNFAVORITES;VoiceRadioManager 收到后仅在当前电台已收藏时调用 cancelCollection()。
    • 验证:.\gradlew :YX_Music:assembleDebug :YX_Voice:assembleDebug :YX_Radio:assembleDebug 编译通过。

2026-06-24 playDislike atomic handling

  • Continue voice playDislike fix without backup.
  • Further narrowed playDislike to one IPC command from YX_Voice to the target media module instead of sending cancel-favorite and next as two independent actions.
  • VoiceEventHelper.sendMusicDislike(...) now sends cmd.playDislike and carries current media type with typeKey.
  • VoiceProtocol keeps video behavior as next video; radio and music now dispatch only sendMusicDislike(...).
  • YX_MusicSDK handles cmd.playDislike in MusicService: USB music performs safe un-favorite only when collected, then next track; Bluetooth music does not touch USB favorite state and only sends Bluetooth next when it has focus.
  • YX_Radio handles cmd.playDislike in VoiceRadioManager: cancel current station only when already collected, then call nextAutoSearch().
  • Verification: .\gradlew :YX_Music:assembleDebug :YX_Voice:assembleDebug :YX_Radio:assembleDebug completed successfully.

2026-06-24 YX_CarControl visible-speak cleanup

  • User confirmed no backup before editing.
  • Reverted temporary visible-speak validation code from YX_CarControl: removed the system page 可见测试按钮, its click log/toast, maintenance page 可见测试... content descriptions, and maintenance test click logs.
  • Kept formal accessibility/visible-speak enhancements for the voice settings page: dynamic switch contentDescription, voice settings control descriptions, and CustomTriStateSwitch accessibility node state.
  • Verified no remaining 可见测试, visible speak test, visible speak simple, btn_visible_speak_simple_test, or maintenance_scroll_view test markers under YX_CarControl/src/main.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: removed the shared CustomTriStateSwitch accessibility-node change to keep this submission scoped to the voice settings page only; remaining YX_CarControl diff is limited to VoiceFragment.java, fragment_voice.xml, and strings.xml.
  • Verification after removal: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: kept multi-keyword visible-speak text with #, but changed all added fragment_voice.xml contentDescription values from hard-coded text to @string/... references for maintainability.
  • Verification after XML string-reference change: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: added matching values-en/strings.xml translations for all new voice_speak_* resources while preserving # multi-keyword format.
  • Verification after English resource completion: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl EnergyFragment visible-speak

  • User confirmed no backup before this round of edits.
  • Added native visible-speak accessibility text for the Energy page without using shadow.register, following TXZ native page scanning via contentDescription.
  • Scope:
    • EnergyFragment.java: dynamically updates charge/discharge switch contentDescription according to current checked state, so closed state exposes open keywords and opened state exposes close keywords.
    • fragment_energy.xml: added contentDescription for charge allow, discharge allow, reset trip after last charge, more trips, and reset statistics controls.
    • dialog_trip_record.xml: added contentDescription for subtotal trip A/B reset buttons.
    • values/strings.xml and values-en/strings.xml: added matching energy_speak_* multi-keyword strings using #.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: moved energy_speak_* resources into the voice settings resource section and labeled them with 可见即可说 - 能源页面; also labeled existing visible-speak groups for System and Voice pages in both Chinese and English string files.
  • Verification after resource regrouping: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl SystemFragment visible-speak

  • Followed reference commit ef20ec3ca823aece8d1beb8e45442cdab7ba420f and reused the native page scanning approach.
  • Added contentDescription visible-speak keywords to System page clickable containers: factory reset, USB upgrade, system update, time setting, language setting, unit setting, disclaimer, privacy policy, and permission authorization.
  • Added matching system_speak_* string resources in values/strings.xml and values-en/strings.xml, with multiple keywords separated by #.
  • No Java behavior change was needed in SystemFragment.java; existing click listeners already dispatch the actions after TXZ triggers the scanned views.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: moved the new system_speak_* resources into the voice settings resource section in both Chinese and English string files, because these entries are visible-speak keywords rather than ordinary System page labels.
  • Verification after resource regrouping: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_Voice background TTS toast

  • User confirmed no backup before editing.
  • Requirement: both playTtsOnUi() and playTtsInBackground() are in use, but foreground TTS remains only a reserved API; actual playback must continue through syncBackgroundTtsText.
  • Added VoiceToastHelper in YX_Voice using system Toast with custom view, based on the existing YX_CarControl designed Toast style instead of using a Dialog-style helper.
  • Added independent YX_Voice Toast resources: voice_toast_view.xml, voice_toast_bg.xml, day/night colors, and Toast dimensions.
  • Refactored VoiceEventHelper so playTtsOnUi() and playTtsInBackground() both call sendBackgroundTtsWithToast(...); this shows the designed Toast and sends TXZ syncBackgroundTtsText.
  • Kept foreground TTS command disabled; playTtsOnUi() comment now explicitly states foreground TTS is not supported and the reserved entry still uses background TTS.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-24 YX_CarControl SoundFragment visible-speak

  • User requested native visible-speak support for the Sound page and previously confirmed no backup before editing.
  • Added Sound page keywords through native TXZ accessibility scanning via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_sound.xml: added visible-speak keywords for sound field options, media/phone/navigation/voice volume bars, mute switch, and key tone switch.
    • layout_eq.xml: added visible-speak keywords for default, classical, pop, jazz, electronic, rock, and custom EQ options.
    • SoundFragment.java: dynamically refreshes mute and key tone switch contentDescription by current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added sound_speak_* resources under 可见即可说 - 声音页面.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Known validation boundary: the volume bars are custom ItemCustomSeekBarView controls; keywords are exposed for scanning, but exact voice-side adjustment behavior depends on TXZ accessibility handling of custom drag/click views.
  • Follow-up: per user decision, removed visible-speak keywords from the four custom volume bars for now; Sound page visible-speak scope remains sound field, EQ options, mute switch, and key tone switch.

2026-06-24 YX_CarControl ADAS hands-off TTS debounce

  • User confirmed no backup before editing.
  • Symptom from 2026_0624_1540_03_018.log: 请手握方向盘 was heard as repeated leading fragments like 请 请 请 请 请 手握方向盘.
  • Root cause: sendAdasLdsHandsOffWarnStChange(2) was triggered repeatedly while the warning stayed active, so sendForegroundTtsPlay("请手握方向盘") and the downstream background TTS command were sent repeatedly; each new request interrupted the previous playback.
  • Fix scope: VoiceSettingManager.sendAdasLdsHandsOffWarnStChange(int sts) now only sends TTS when the ADAS hands-off warning enters 0x02 from a non-warning state, with a 5-second debounce against rapid signal chatter.
  • Expected validation: repeated sendAdasLdsHandsOffWarnStChange sts = 2 logs may still appear, but repeated TTS requests should be replaced by sendAdasLdsHandsOffWarnStChange skip duplicate tts; sendForegroundTtsPlay tts = 请手握方向盘, playTtsOnUi, and TXZ syncBackgroundTtsText should no longer repeat at millisecond level.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up after reboot log 2026_0624_1612_11_547.log: ADAS warning status still bounced between 1 and 2; the previous 5-second debounce allowed another TTS after the time window expired, producing 15 sends and 15 duplicate skips.
  • Updated strategy: replace fixed 5-second replay debounce with a warning-cycle state gate. The first sts=2 still plays immediately, but the warning is not considered cleared until sts != 2 remains stable for 2 seconds; only after that can a later sts=2 play again.
  • Verification after stable-clear update: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl ShowFragment visible-speak

  • User requested visible-speak support for the Display page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_show.xml: added visible-speak keywords for screen off, theme mode, hidden display mode controls, wallpaper options, projection options, and video safety mode.
    • ShowFragment.java: dynamically refreshes Bluetooth phone projection, media audio projection, and video safety mode contentDescription according to current selected/checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added show_speak_* resources under 可见即可说 - 显示页面.
  • Brightness bars are custom ItemCustomSeekBarView controls and were intentionally not added per the latest user decision to skip sliders for now.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-07-01 YX_CarControl SoundFragment visible-speak short keywords

  • Log C:\Users\Administrator\Desktop\徐工日志\2026_07_01_20_22_01\yx_log\2026_0701_2010_07_922.log showed TXZ accessibility was connected and Sound page keywords were scanned into VisualCommandModule offline, but short utterances such as 古典, 爵士, and 主驾 returned TxzVisualCommand matchedEntry = null.
  • Root cause: Sound page contentDescription entries only exposed longer phrases such as 古典音效 and 主驾音场; TXZ visible-command matching did not treat short utterances as substring matches.
  • Fix: prepend standalone short keywords to Sound page visible-speak string resources: 全车, 主驾, 副驾, 默认, 古典, 流行, 爵士, 打击, and 摇滚; mirrored the same style in values-en.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up from log C:\Users\Administrator\Desktop\徐工日志\9_45_17\yx_log\2026_0622_1451_07_056.log: utterance 自定义 still returned TxzVisualCommand matchedEntry = null because the scanned Sound page list only contained 自定义音效#... and 自定义音场#....
  • Final decision: do not add a bare 自定义 keyword because the Sound page has two visible 自定义 controls. Keep explicit 自定义音效 for EQ custom and 自定义音场 for sound-field custom to avoid duplicate keyword ambiguity.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl ConnectFragment visible-speak

  • User requested visible-speak support for the Connect page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_connect.xml: added visible-speak keywords for Bluetooth settings entry, WLAN settings entry, Bluetooth switch, and WLAN switch.
    • ConnectFragment.java: dynamically refreshes Bluetooth and WLAN switch contentDescription according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added connect_speak_* resources under 可见即可说 - 连接页面.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl SmartDriveFragment visible-speak

  • User requested visible-speak support for the Smart Drive page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_smart_drive.xml: added visible-speak keywords for ACC time gap, LDW switch/sensitivity, AEBS switch, FCW sensitivity, blind spot monitor, driver monitor, and traffic sign recognition.
    • SmartDriveFragment.java: dynamically refreshes LDW, AEBS, blind spot monitor, driver monitor, and traffic sign recognition switch contentDescription according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added smart_speak_* resources under visible-speak resource groups.
  • Hidden BSIS/MOIS switches were intentionally not added because this fragment has no matching business listener for them in the current implementation.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl LightControlFragment visible-speak

  • User requested visible-speak support for the Light page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_light_control.xml: added visible-speak keywords for ambient light, breathing mode, auto dome light, safety light radio options, smart welcome light, smart high/low beam, and logo light.
    • LightControlFragment.java: dynamically refreshes ambient light, breathing mode, logo light, auto dome light, smart welcome light, and smart high/low beam switch contentDescription according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added light_speak_* resources under visible-speak resource groups.
  • Custom color picker, ambient light brightness, and key backlight selector were intentionally not added because they are touch-coordinate based custom selector/slider controls.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl CarControlFragment visible-speak

  • User requested visible-speak support for the Car Control page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_car_control.xml: added visible-speak keywords for adaptive suspension modes, ESC OFF, climb mode, hill start assist, downhill assist, brake energy recovery, smart door lock, smart roof, rain sensor sensitivity, and wireless charging.
    • CarControlFragment.java: dynamically refreshes switch contentDescription according to current checked state, so disabled state exposes open keywords and enabled state exposes close keywords.
    • values/strings.xml and values-en/strings.xml: added car_speak_* resources under visible-speak resource groups.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl MaintenanceFragment visible-speak

  • User requested visible-speak support for the Maintenance page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_maintenance.xml: added visible-speak keywords for maintenance mileage off, 5000 km, 10000 km, 30000 km, and maintenance reset.
    • values/strings.xml and values-en/strings.xml: added maintenance_speak_* resources under visible-speak resource groups.
  • No Java behavior change was needed; existing click listeners still handle mileage selection and reset confirmation.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-24 YX_CarControl PermissionAuthorizationFragment visible-speak

  • User requested visible-speak support for the Permission Authorization secondary page and previously confirmed no backup before editing.
  • Added native TXZ accessibility scanning keywords via contentDescription; this page does not use shadow.register.
  • Scope:
    • fragment_permission_authorization.xml: added visible-speak keywords for back, authorization duration off/180 days/360 days, and all visible permission switches.
    • PermissionAuthorizationFragment.java: dynamically refreshes permission switch contentDescription according to current checked state, so disabled/off state exposes open keywords and enabled/on state exposes close keywords.
    • dialog_permission_confirm.xml: added visible-speak keywords for confirm close and cancel, covering the permission close confirmation flow.
    • values/strings.xml and values-en/strings.xml: added permission_speak_* resources under visible-speak resource groups.
  • Existing authorization duration, Settings.System persistence, permission confirmation, and switch enable/disable business logic were kept unchanged.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.

2026-06-26 YX_Voice drowsy TTS reminder dialog

  • User confirmed no backup for this TTS reminder work.
  • Requirement update: fatigue reminder content must be read directly from VoiceNewConstant.DROWSY_DRIVING.
  • Kept the previous design decision that foreground TTS is only a reserved API; both playTtsOnUi() and playTtsInBackground() still send real audio through TXZ syncBackgroundTtsText.
  • Added VoiceDrowsyReminderHelper in YX_Voice:
    • Only triggers for VoiceNewConstant.DROWSY_DRIVING, with punctuation/blank normalization so half-width ! input can still match the full-width constant.
    • Shows a service-safe overlay reminder dialog after the existing toast path.
    • Dialog title: 消息提醒; content: VoiceNewConstant.DROWSY_DRIVING; buttons: 稍后提醒 and 不再提醒.
    • 稍后提醒 only dismisses the current dialog; 不再提醒 persists a local SharedPreferences flag and suppresses future fatigue dialogs without suppressing TTS audio or toast.
  • Added SYSTEM_ALERT_WINDOW permission and released the overlay from VoiceService.onDestroy() to avoid stale windows during service restart.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-26 YX_Voice remindLaterOrNever command

  • User requested voice command remindLaterOrNever for the fatigue reminder dialog buttons 稍后提醒 and 不再提醒.
  • Added TXZ protocol constant CMD_REMIND_LATER_OR_NEVER = "remindLaterOrNever" in YX_Voice.
  • VoiceProtocol now consumes remindLaterOrNever locally and calls VoiceDrowsyReminderHelper directly, because the reminder overlay belongs to the voice process and does not need cross-process dispatch.
  • Parameter parsing is tolerant across type, value, action, cmd, text, word, keywordList, name, and raw JSON fallback:
    • Contains 不再 / never / noMore / not_remind: persist 不再提醒 and dismiss.
    • Contains 稍后 / later / snooze / delay: dismiss only.
  • Touch buttons and voice command now share the same internal action handler.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-26 YX_Voice drowsy TTS test broadcast

  • Added handling for the already registered com.voice.test.tts.yb test broadcast in YX_Voice TestReceiver.
  • If the broadcast has no tts extra, it defaults to VoiceNewConstant.DROWSY_DRIVING, so a single adb command can trigger the fatigue TTS toast/dialog chain.
  • If --es tts ... is supplied, that custom text is sent through the same playTtsOnUi() path.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-26 YX_Voice drowsy dialog day mode

  • Continued without backup per user preference.
  • Matched the drowsy reminder dialog day/night colors to the YX_CarControl system version dialog style: light day background/text/buttons, dark night background/text/buttons, blue pressed state.
  • Added blank-area dismiss for the overlay root while the dialog container consumes inner clicks, matching setCanceledOnTouchOutside(true) behavior from the reference dialog.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.
  • Follow-up: moved the drowsy dialog title to strings.xml, and moved fixed remindLaterOrNever protocol attr values 稍后提醒 / 不再提醒 to VoiceNewProtocolConst.
  • Verification after constant/resource cleanup: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-26 YX_Voice dialog/toast day-night compatibility

  • Continued without backup per user preference.
  • Requirement: the voice reminder dialog and TTS toast must follow the current day/night theme, referencing the vehicle settings theme behavior.
  • Added VoiceThemeHelper in YX_Voice:
    • Reads the actual theme first from Settings.Global.REQUEST_THEME_MODE, which is written by the car-control theme manager.
    • Falls back to Settings.System[DataUtils.THEME_MODE_CHANGED].
    • In auto mode, falls back to Settings.Global.POST_LIGHT_STATUS.
    • Finally falls back to the current resource Configuration.UI_MODE_NIGHT_MASK.
  • Updated VoiceDialogHelper and VoiceToastHelper to inflate their custom views with the themed context, so values/colors.xml and values-night/colors.xml are selected correctly.
  • Existing day/night color resources are kept in YX_Voice: light dialog/toast colors for day mode, dark dialog/toast colors for night mode.

2026-06-26 YX_Voice dialog theme refresh while showing

  • User reported the dialog did not change after switching day/night mode; log 2026_0626_1644_36_540.log was analyzed.
  • Key timeline from the log:
    • 16:45:28.982 voice received the smoking reckless driving TTS and scheduled the safety dialog.
    • 16:45:30.044 VoiceDialogHelper: showInternal: drowsy reminder shown, so the overlay view was already added to WindowManager.
    • 16:45:34.464 VoiceEventHelper: themeMode 1 and syncScreenMode status = darkMode, meaning the theme switch happened several seconds after the overlay was created.
  • Root cause: a custom WindowManager overlay does not automatically reinflate or re-resolve values-night resources when the system theme changes after it is already visible.
  • Fix:
    • Added VoiceDialogHelper.onThemeChanged() to refresh an already-visible dialog by preserving the current title/content, removing the old overlay view, and adding a new themed view.
    • SettingCommonObserver now calls the refresh hook on THEME_MODE_CHANGED and also listens to Settings.Global.REQUEST_THEME_MODE.
    • VoiceThemeHelper now prefers the current Settings.System[THEME_MODE_CHANGED] value for manual light/dark mode; auto mode still uses REQUEST_THEME_MODE / POST_LIGHT_STATUS fallback.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-26 YX_Voice toast theme refresh while showing

  • User reported the TTS toast did not change after switching day/night mode; log 2026_0626_1705_01_941.log was analyzed.
  • Key timeline:
    • 17:06:15.791 / 17:06:17.489 VoiceEventHelper: playTtsOnUi received HVAC CO2 TTS text and showed toast.
    • 17:06:18.900 user clicked dark mode switch, then 17:06:18.917 voice synced lightMode.
    • The screenshot still showed black toast because the custom toast view was created while the theme was still dark, before the theme switch.
  • Root cause: custom Toast views also do not re-resolve resources after being shown; cancelling/recreating is required if theme changes during the toast display window.
  • Fix:
    • VoiceToastHelper now keeps the last visible toast text and visibility window.
    • On THEME_MODE_CHANGED / REQUEST_THEME_MODE observer callback, it delays briefly, cancels the old toast, and re-shows the same text with the current themed context.
    • Added VoiceToastHelper: refreshForThemeChangedInternal log for validation.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-27 YX_CarControl fatigue reminder interval UI

  • User confirmed no backup and required only adding the new fatigue reminder feature, without changing historical feature logic.
  • Added the new Smart Drive "疲劳监测提醒" UI under existing "驾驶人员监测": switch + reminder interval control.
  • Interval requirement: 0~60 minutes, default 20 minutes, step 10 minutes; progress area is 702dp wide and 100dp high, so each 10-minute step maps to 117dp.
  • Existing progress controls were checked:
    • ItemCustomSeekBarView is shared by Sound/Light/Display pages and normally displays an icon.
    • CustomLightSelector is a 1~5 backlight selector with icon and dividers.
    • EQ/native SeekBars are not suitable for this horizontal reminder UI.
  • To avoid impacting shared historical controls, the temporary shared-control icon-removal approach was dropped; a dedicated no-icon FatigueReminderIntervalSeekBarView is used only by the new Smart Drive fatigue reminder interval layout.
  • Verification: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Follow-up: the new fatigue reminder switch/interval still uses local keys in SmartDriveFragment, but reads/writes through DataManager + DataUtils.STATE_URI and now registers ContentObserver callbacks, matching the existing data-center sync style. YX_CarControl compiles against sdklibs/yx_support_classes.jar, so adding constants only in YX_Support source does not affect this module build unless the support jar is regenerated or the dependency is changed.
  • Verification after the data-center observer adjustment: .\gradlew :YX_CarControl:assembleDebug completed successfully.
  • Update: per user request, moved the fatigue reminder keys to YX_Support DataUtils: FATIGUE_REMINDER_SWITCH and FATIGUE_REMINDER_INTERVAL_MINUTE. The interval value is stored as an integer minute value, default 20.
  • Data center parity with FATIGUE_MONITOR_SWITCH: added YX_DataCenterService initialization in YXContentProvider.mHasState, update dispatch in YXContentProvider.update(), new MessageEvent IDs 2097/2098, and ProviderData SharedPreferences putInt/getInt persistence for the reminder switch and integer interval.
  • Build note: both YX_CarControl and YX_DataCenterService compile against sdklibs/yx_support_classes.jar, so YX_Support:makeJar or the existing support-jar replacement flow is needed before compiling these consumers with the new DataUtils constants.

2026-06-28 YX_Voice fatigue reminder interval

  • User confirmed YX_Voice must associate the SmartDrive fatigue reminder settings:
    • DataUtils.FATIGUE_REMINDER_SWITCH
    • DataUtils.FATIGUE_REMINDER_INTERVAL_MINUTE
  • DROWSY_DRIVING, DISTRACTED_DRIVING, SMOKING_RECKLESS_DRIVING, and CALLING_RECKLESS_DRIVING reminder dialogs are now gated by the reminder switch and throttled by the interval minute value.
  • VoiceDialogHelper reads the DataCenter switch before showing; when enabled, it stores the last shown time and skips repeated dialogs until the configured interval expires. Interval 0 means no throttling.
  • Update: user clarified 不再提醒 and 稍后提醒 must not persist locally and must recover after reboot. VoiceDialogHelper now keeps both the last shown time and the current-process "never remind" flag only in static memory; it does not write SharedPreferences or change FATIGUE_REMINDER_SWITCH for these two dialog actions.
  • Update: user clarified the four dialog reminders are four independent functions. The current-process "never remind" flag and the last shown time are now tracked separately for DROWSY_DRIVING, DISTRACTED_DRIVING, SMOKING_RECKLESS_DRIVING, and CALLING_RECKLESS_DRIVING; one type's button action or interval throttle does not suppress the other types.
  • SettingCommonObserver and BaseVoiceService now observe the two fatigue reminder URIs; turning the switch off dismisses a pending/showing reminder overlay.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-06-29 YX_Voice radio syncMediaFocus

  • User confirmed no backup before editing.
  • Log 2026_0629_1152_07_466.log showed Radio playback was active and TXZ received syncFocusPlayStatus=true plus syncRadioPlayType=fm, but no syncMediaFocus: radio.
  • Root cause: MediaObserver only sent media focus on SOURCE_ID changes; Radio startup in the log updated RADIO_INFO playback state without a reliable SOURCE_ID change notification.
  • Fix: when RADIO_INFO reports mState == 2 and current SOURCE_ID is YXUtils.SOURCE_RADIO, MediaObserver now sends notifySyncMediaFocus(VoiceNewProtocolConst.RADIO) before syncing play status.
  • Verification: .\gradlew :YX_Voice:assembleDebug completed successfully.

2026-07-01 YX_BlueTooth ACC state cleanup

  • User confirmed no backup before editing.
  • Requirement: check whether Bluetooth still depends on legacy ACC_STATE; Xugong currently follows power mode rather than old ACC state.
  • Finding: YX_BlueTooth BluetoothService still registered ACC_STATE, but the old disconnect/reconnect logic in that branch had already been commented out and only logged ACC state.
  • Fix: removed the unused ACC_STATE_URI constant, removed the ACC_STATE observer registration, and deleted the dead case ACC_STATE_URI branch.
  • Kept the active LOW_VOLTAGE_POWER_STATE logic unchanged because it still performs Bluetooth phone/headset disconnect and phone reconnect behavior; only changed misleading logs/comments from CAN ACC OFF/ON to LOW_VOLTAGE_POWER_STATE OFF/ON.
  • Verification: .\gradlew :YX_BlueTooth:assembleDebug --console plain completed successfully.

2026-07-02 YX_CarControl sound page visible-speak coverage check

  • Rechecked Sound page main visible controls:
    • Sound field: all-cabin, driver, passenger, custom sound field.
    • Sound effect: default, classical, pop, jazz, electronic, rock, custom sound effect.
    • Switches: mute and key tone.
  • Sliders are intentionally not added to visible-speak yet, matching the previous decision.
  • Fixes after the coverage check:
    • The clickable custom-effect triangle in layout_eq.xml now uses sound_speak_eq_user instead of the literal Triangle Button.
    • sound_speak_close_mute now also includes bare jingyin / Mute, so the same short utterance can match both on and off states.
  • Boundary note: the custom EQ floating window (layout_eq_user.xml / EqUserFloatView) has its own EQ buttons and seek bars but is a service overlay / secondary UI, not currently treated as part of the Sound main-page visible-speak scope.
  • Verification: .\gradlew :YX_CarControl:assembleDebug --console plain completed successfully.

2026-07-02 YX_CarControl voice page visible-speak short keywords

  • Analyzed log 2026_0702_1025_08_858.log for Voice page visible-speak.
  • At 15:08:37, VoiceFragment: onStart and syncVoiceSettingStatus status = true were logged.
  • TXZ offline list contained Voice page entries such as 打开免唤醒#开启免唤醒, 免唤醒10秒#设置免唤醒10秒#选择10秒, 免唤醒15秒#设置免唤醒15秒#选择15秒, and 免唤醒30秒#设置免唤醒30秒#选择30秒.
  • User utterance 免唤醒 failed because the visible-speak entry only had open/close phrases, not the bare short keyword.
  • User utterance 10秒 was recognized by TXZ ASR as 十秒; the visible-speak entry did not include 十秒, so matchedEntry = null.
  • Fix:
    • Added bare 语音唤醒 to both voice wakeup switch states.
    • Added bare 免唤醒 to both wake-free switch states.
    • Added short numeric and Chinese-number forms for duration entries: 10秒#十秒, 15秒#十五秒, 30秒#三十秒.
  • Verification: .\gradlew :YX_CarControl:assembleDebug --console plain completed successfully.

2026-07-02 YX_CarControl sound page visible-speak key tone

  • User reported saying bare 按键音 on Sound page did not respond.
  • Finding: SoundFragment dynamically sets the key-tone switch contentDescription based on current state; original entries only exposed 打开按键音#开启按键音 or 关闭按键音#关掉按键音.
  • Root cause inference: TXZ visible-speak matching does not reliably treat 按键音 as a substring match for these longer phrases.
  • Fix: added bare 按键音 to both sound_speak_open_key_tone and sound_speak_close_key_tone; English resources added matching bare Key tone.
  • Expected verification: on Sound page, say 按键音; log should show the offline entry containing 按键音#... and TxzVisualCommand matched entry for 按键音.

2026-07-02 YX_CarControl SmartDrive fatigue visible-speak short keywords

  • Analyzed log C:/Users/Administrator/Desktop/徐工日志/2026_07_02_15_25_54/yx_log/2026_0702_1025_08_858.log.
  • Root cause: SmartDrive page offline visible-speak list only exposed state-specific phrases such as 关闭疲劳监测提醒#关掉疲劳监测提醒; the bare utterance 疲劳监测提醒 produced matchedEntry = null.
  • Fix: added bare short keywords to both open and close resources for smart_speak_*_fatigue_monitor and smart_speak_*_fatigue_reminder.
  • Verification: .\gradlew :YX_CarControl:assembleDebug --console plain completed successfully.

2026-07-03 YX_Launcher boot-time Bluetooth music widget play

  • User confirmed no backup before editing.
  • Requirement: after boot, clicking the Launcher main-page music card play button should play Bluetooth music.
  • Root cause:
    • YX_Launcher main page uses WidgetsMedia, whose play/prev/next buttons only sent global media keys.
    • On cold boot, Bluetooth music focus/MediaSession may not yet be established, so KEYCODE_MEDIA_PLAY_PAUSE can be ignored and does not explicitly trigger Bluetooth AVRCP play.
    • WidgetsMedia.init() queried current focus package but hard-coded the initial client id as USB music, so com.yaxon.music could be treated as USB music even when SOURCE_ID was Bluetooth music.
    • SystemUtils.startBtMusic() sends is_click_bluetooth_music=true, but UsbMainActivity only handled VoiceControlManager.PAGE_TYPE; the legacy Bluetooth-music extra was ignored.
  • Fix:
    • WidgetsMedia now resolves the initial card source from SOURCE_ID when focus package is com.yaxon.music or null.
    • For Bluetooth music source, Launcher card play/prev/next sends explicit broadcasts to YX_Music instead of relying only on global media keys.
    • Added BluetoothMusicControlReceiver in YX_Music to call BtMusicControlManager directly for play/pause/previous/next.
    • Play from stopped/cold state retries for up to 10 attempts at 500 ms intervals, with a request token so a later pause cancels stale retries.
    • UsbMainActivity now handles is_click_bluetooth_music=true in both initial launch and onNewIntent() and switches to the Bluetooth music page.
  • Verification:
    • .\gradlew :YX_Music:assembleDebug --console plain completed successfully.
    • .\gradlew :YX_Launcher:assembleDebug --console plain completed successfully.

2026-07-03 YX_Launcher Bluetooth music widget retest log analysis

  • User provided retest logs:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_12_02/yx_log/2026_0703_1050_00_000/2026_0703_1049_41_742.log
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_12_02/yx_log/2026_0703_1050_33_402.log
  • Key evidence:
    • At 07-03 11:02:37.547, WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE: appears, proving the Launcher card play button click reached WidgetsMedia.
    • No WidgetMedia: sendBtMusicControl action = ..., no BluetoothMusicControlReceiver: onReceive action = ..., no BtMusicControlManager: playBtMusic, and no BluetoothAvrcpClient: play appear in the same retest logs.
    • Around the click, NuPlayerDriver / MediaPlayer logs appear, indicating the click path did not execute the explicit Bluetooth music broadcast control path.
    • Earlier logs show WidgetMedia: initMedia: packageName ->null ,clientId: com.yaxon.musicsdk.manager and initMedia:onMusicInfo, so the Launcher widget initially identified the source as USB music.
    • Later WidgetMedia: onBtMusicInfo logs prove Bluetooth music metadata can still update the card, but metadata display alone does not prove the button control path is using Bluetooth control.
  • Current conclusion: this retest log is consistent with either an old Launcher/Music package still running, or isBtMusicControlSource() evaluating false when the click happens. The click reached Launcher, but did not reach the new explicit Bluetooth music control receiver.
  • Next validation/fix direction:
    • On device, confirm the flashed YX_Launcher.apk contains the sendBtMusicControl code and YX_Music.apk contains BluetoothMusicControlReceiver.
    • Add a click-time diagnostic log for mCurrentMediaSource and Utils.isBtMusicSource() if another code change is needed.
    • If source detection is false at click time, make WidgetsMedia prefer the currently displayed BtMusicInfo / latest card mode for play/prev/next routing, not only SOURCE_ID.

2026-07-03 YX_BluetoothSDK Bluetooth music auto-pause after boot

  • User asked to re-analyze log C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_28_17/yx_log/2026_0703_1127_33_000.log after reverting YX_Launcher WidgetsMedia and the YX_Music explicit receiver changes.
  • Key evidence:
    • This log does not contain WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE, so it does not prove the Launcher play button click entered WidgetsMedia in this capture.
    • At 07-03 11:27:30.608, Bluetooth A2DP reports Stream started.
    • At 07-03 11:27:30.618/30.619, BluetoothMediaBrowserService reports PlaybackState state=3, meaning Bluetooth music did enter playing state.
    • At 07-03 11:27:30.643, BtMusicManager: onPlaybackStateChanged isPlaying = true.
    • Immediately after, BtMusicAudioManager: setA2dpMute, informAudioFocusStateNative:0, BtMusicManager: onPlaybackStateChanged pauseBtMusic>, BtMusicManager: pauseBtMusic, and BluetoothAvrcpClient: pause appear.
  • Root cause inference from current code:
    • BtMusicManager.onPlaybackStateChanged() contains protection logic: if Bluetooth music starts while !isForeground() and !mBtMusicAudioManager.isFocus(), it mutes A2DP and calls pauseBtMusic().
    • During Launcher main-page startup, Bluetooth music fragment is not foreground and Bluetooth music audio focus is not held, so when phone-side/A2DP playback starts, the SDK treats it as unauthorized background playback and actively pauses it.
  • Current conclusion: this log points to YX_BluetoothSDK foreground/focus guard actively stopping Bluetooth music, not to AVRCP/A2DP inability to play. The system can play briefly, then the SDK pauses it because the Bluetooth music page is not foreground and focus is false.
  • Next fix direction: for Launcher card play-after-boot, ensure the Bluetooth music control path first marks/request-focus for Bluetooth music or create a controlled exception in BtMusicManager for a user-initiated Launcher card play request; avoid relying only on raw global media key when the Bluetooth music page is not foreground.

2026-07-03 YX_Launcher Bluetooth music widget latest 11:33 log analysis

  • User provided latest log:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_11_34_00/yx_log/2026_0703_1133_34_699.log
  • Current code state after revert:
    • YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java play/previous/next buttons only call YXUtils.sendKey(...).
    • No sendBtMusicControl, no explicit YX_Music receiver, and no current-source based Bluetooth control path exists in the reverted code.
  • Key evidence:
    • At 07-03 11:33:27.386, BtMusicManager receives Bluetooth metadata: artist 王菲, title 如愿 (电影《我和我的父辈》主题推广曲).
    • At 07-03 11:33:27.429, Bluetooth playback state is stopped: BtMusicManager: onPlaybackStateChanged isPlaying = false.
    • At 07-03 11:33:27.735, Launcher receives WidgetMedia: onBtMusicInfo, so the card can display Bluetooth music information.
    • At 07-03 11:33:42.946 and 07-03 11:33:48.389, the Launcher card play button click reaches WidgetsMedia: KEYCODE_MEDIA_PLAY_PAUSE.
    • At 07-03 11:33:49.325, previous button click reaches WidgetsMedia: KEYCODE_MEDIA_PREVIOUS.
    • Immediately after these clicks, logs only show NuPlayerDriver / MediaPlayer / bus7_system_sound_out short system-audio playback and stop/reset.
    • The log contains no BtMusicControlManager: playBtMusic, no BtMusicManager: playBtMusic, no BluetoothAvrcpClient: play, no bluetooth music requestAudioFocus, and no setA2dpUnMute after the Launcher button clicks.
  • Current conclusion:
    • This log proves the Launcher button click is received, but the click is not converted into a Bluetooth music play command.
    • Metadata display is working through BtInfoPushManager -> WidgetMedia, but control still depends on global media keys.
    • In the boot/main-Launcher scene, Bluetooth music is not foreground and does not hold focus, so the Bluetooth SDK media session is not ready to receive/respond to KEYCODE_MEDIA_PLAY_PAUSE.
    • Combined with the 11:27 log, the failure is a two-part issue: Launcher sends only global media keys, and BtMusicManager blocks or pauses Bluetooth playback when it starts without foreground/focus.
  • Next fix direction:
    • Do not rely on raw global media keys for Launcher Bluetooth card controls.
    • Add a controlled Bluetooth-music play entry that first requests/marks Bluetooth music focus, then calls playBtMusic(), or explicitly route Launcher card play/previous/next to a Bluetooth control API when the card is showing Bluetooth music.
    • Keep the BtMusicManager background-play guard intact unless there is a verified user-initiated Launcher play flag/focus path.

2026-07-03 YX_BluetoothSDK media-key fallback for Launcher BT widget

  • User clarified: do not modify YX_Launcher.
  • Fix scope:
    • YX_Launcher remains unchanged; WidgetsMedia still sends KEYCODE_MEDIA_PLAY_PAUSE / previous / next.
    • Change is limited to YX_BluetoothSDK/src/main/java/com/yaxon/bluetoothsdk/audio/BtMusicAudioManager.java.
  • Fix:
    • BtMusicAudioManager now registers its Bluetooth music MediaSession during initialization instead of only after Bluetooth music obtains audio focus.
    • The session pre-handles media keys and only takes over when Bluetooth music already has focus, current SOURCE_ID is SOURCE_BT_MUSIC, or boot-time SOURCE_ID is SOURCE_NULL while valid BT_MUSIC_INFO exists.
    • When it takes over a play/previous/next key without focus, it requests Bluetooth music audio focus first, then calls playBtMusic() / preBtMusic() / nextBtMusic().
    • releaseAudioFocus() no longer releases the MediaSession; it downgrades the session after focus loss so boot-time Launcher media keys can still reach Bluetooth music later.
  • Verification:
    • .\gradlew :YX_BluetoothSDK:assembleDebug --console plain completed successfully.
    • Confirmed no diff in YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java.
  • Retest log expectation:
    • After clicking Launcher card play, logs should show BtMusicAudioManager: onMediaButtonEvent pre-handle, bluetooth music requestAudioFocus, setA2dpUnMute, BtMusicManager: playBtMusic, and BluetoothAvrcpClient: play.
    • The previous failure pattern should disappear: click should no longer only show NuPlayerDriver / MediaPlayer short system sound with no Bluetooth play command.

2026-07-03 YX_BluetoothSDK first-click media-key routing follow-up

  • User provided retest log:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_13_50_05/yx_log/2026_0703_1348_33_113.log
  • Key evidence:
    • At 07-03 13:48:28.358, Launcher already received Bluetooth music info: WidgetMedia: onBtMusicInfo ... mPlaySts = 0.
    • First click around 07-03 13:48:51.498 only shows McuService: WindowManagerGlobal = ACTION_DOWN and system click-audio logs. There is no MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE, no BtMusicAudioManager: onMediaButtonEvent, and no BtMusicManager: playBtMusic.
    • After the first click, Android reports Media button session is changed to com.yaxon.music/Music.
    • Second click around 07-03 13:48:54.527 is routed correctly: MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music, then BtMusicAudioManager: onMediaButtonEvent, bluetooth music requestAudioFocus: result = 1, setA2dpUnMute, BtMusicManager: playBtMusic, and BluetoothAvrcpClient: play.
  • Updated conclusion:
    • The remaining symptom is not AVRCP play failure. The second click proves the Bluetooth SDK play path works.
    • The first click fails because the Bluetooth music MediaSession becomes the active media-button session only after the first click/system playback activity, so the first media key is not delivered to Bluetooth music.
  • Fix update:
    • Keep YX_Launcher and YX_Music unchanged.
    • In BtMusicAudioManager, allow FLAG_EXCLUSIVE_GLOBAL_PRIORITY when Bluetooth music is not focused but is a safe inactive media-button candidate: A2DP connected and either SOURCE_ID == SOURCE_BT_MUSIC, or SOURCE_ID == SOURCE_NULL with valid Bluetooth music info.
    • In BtMusicManager, refresh the Bluetooth music MediaSession after A2DP connect, playback-state updates, and metadata updates so the session is ready before the first Launcher card click.
  • Verification:
    • .\gradlew :YX_BluetoothSDK:assembleDebug --console plain completed successfully.
    • Confirmed no diff in YX_Launcher/src/main/java/com/yaxon/launcher/view/WidgetsMedia.java.
    • Confirmed no diff under YX_Music.
  • Next retest expectation:
    • The first Launcher card play click should immediately show MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music followed by BtMusicAudioManager: onMediaButtonEvent pre-handle, bluetooth music requestAudioFocus, setA2dpUnMute, BtMusicManager: playBtMusic, and BluetoothAvrcpClient: play.

2026-07-03 YX_BluetoothSDK 14:54 log coverage check

  • User provided log:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_14_54_53/yx_log/2026_0703_1454_33_435.log
  • Coverage result:
    • The log has only about 5720 lines and mainly covers early boot around 01-01 08:00:01 to 01-01 08:00:10 plus Android startup timestamps around 06-30 11:09:15 to 06-30 11:09:16.
    • It does not contain WidgetMedia, KEYCODE_MEDIA_PLAY_PAUSE, BtMusicAudioManager, BtMusicManager, BluetoothAvrcpClient, MediaSessionService: Sending KeyEvent, or 07-03 14:54 runtime events.
    • The same directory only contains an empty zipped log entry 2026_0703_1454_05_407.log.
  • Conclusion:
    • This capture does not cover the Launcher music-card click or Bluetooth music playback path, so it cannot validate whether the first-click fix succeeded or failed.
  • Next log requirement:
    • Need a log segment that includes the actual click time and contains at least Launcher WidgetMedia or system MediaSessionService media-key lines.

2026-07-03 YX_BluetoothSDK 15:02 retest validates first-click fix

  • User provided log:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_15_03_26/yx_log/2026_0703_1502_33_334.log
  • Key evidence:
    • At 07-03 15:02:28.813, Bluetooth metadata arrived: BtMusicManager: artist = 伍佰 title = 世界第一等(Live).
    • At 07-03 15:02:28.878, before any Launcher click, Android changed the global priority session to com.yaxon.music/Music.
    • At 07-03 15:02:29.311, Launcher received Bluetooth info and displayed the card: WidgetMedia: onBtMusicInfo ... mPlaySts = 0.
    • At 07-03 15:02:49.194, the first Launcher card play click reached WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE.
    • The same first click was routed immediately to Bluetooth music: MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music.
    • BtMusicAudioManager received the media button while isFocus=false, requested focus, unmuted A2DP, then called BtMusicManager: playBtMusic and BluetoothAvrcpClient: play.
    • At 07-03 15:02:49.633 and continuously afterward, BtMusicManager: onPlaybackStateChanged isPlaying = true appears, with no subsequent pauseBtMusic or BluetoothAvrcpClient: pause.
  • Conclusion:
    • The SDK fix is effective for this retest. First Launcher card click now starts Bluetooth music directly; the previous "first click only arms the session, second click plays" pattern is gone.

2026-07-03 YX_BluetoothSDK 15:04 retest also validates first-click fix

  • User provided log:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_03_15_05_45/yx_log/2026_0703_1504_32_996.log
  • Key evidence:
    • At 07-03 15:04:27.802, Bluetooth metadata arrived: BtMusicManager: artist = 任贤齐 title = 伤心太平洋 (电视剧《神雕侠侣》片尾曲).
    • At 07-03 15:04:27.835, before the click, global priority session changed to com.yaxon.music/Music.
    • At 07-03 15:04:28.211, Launcher card displayed Bluetooth music info with mPlaySts = 0.
    • At 07-03 15:04:50.143, first Launcher play click logged WidgetMedia: KEYCODE_MEDIA_PLAY_PAUSE.
    • The first click was immediately routed to Bluetooth music: MediaSessionService: Sending KeyEvent ... KEYCODE_MEDIA_PLAY_PAUSE ... to com.yaxon.music/Music.
    • BtMusicAudioManager received both down/up events while isFocus=false, requested focus, unmuted A2DP, called BtMusicManager: playBtMusic, and BluetoothAvrcpClient: play.
    • At 07-03 15:04:50.599 and afterward, playback state remained isPlaying = true, with no pauseBtMusic or AVRCP pause afterward.
  • Conclusion:
    • This second retest confirms the fix is stable for repeated boot/Launcher-card first-click scenarios.

2026-07-03 YX_BluetoothSDK BT music restore after power off

  • New requirement: if Bluetooth music is playing and power-off interrupts it, next power-on should restore Bluetooth music playback.
  • Scope remains limited to YX_BluetoothSDK; YX_Launcher and YX_Music are not modified.
  • Fix:
    • Added SDK private persisted flags bt_music_had_focus and bt_music_was_playing.
    • BtMusicAudioManager persists Bluetooth focus ownership after successful focus request.
    • BtMusicManager persists Bluetooth playback state only when Bluetooth music is the explicit SOURCE_ID or has focus.
    • Added ACC_STATE and POWER_MODE observers. POWER_MODE=0/3 snapshots power-off state; POWER_MODE=1 and ACC_STATE=ON trigger restore checks.
    • Restore requires all conditions: persisted had-focus, persisted was-playing, explicit SOURCE_ID == SOURCE_BT_MUSIC, ACC/IGN on, A2DP connected, and currently not playing.
    • Clear restore state only during normal ACC-on source/focus loss paths, not during POWER_MODE=0/3/4 power-down phases.
  • Risk control:
    • Auto-restore does not use the Launcher default Bluetooth-source fallback. Missing SOURCE_ID uses SOURCE_NULL and will not auto-play.
    • USB music and radio are protected because restore requires explicit Bluetooth source.
    • Manual pause clears bt_music_was_playing while powered on and focused.
  • Verification:
    • .\gradlew :YX_BluetoothSDK:assembleDebug --console plain completed successfully.
    • Confirmed git diff -- YX_Launcher YX_Music is empty.

2026-07-07 YX_BluetoothSDK pause Bluetooth music during TXZ TTS

  • User confirmed no backup before editing.
  • Log analyzed:
    • C:/Users/Administrator/Desktop/徐工日志/2026_07_07_19_07_57/yx_log/2026_0707_1849_48_792.log
  • Finding:
    • TXZ TTS requests Synthesis audio focus with focusGain=2.
    • Bluetooth music receives AUDIOFOCUS_LOSS_TRANSIENT, but previous BtMusicAudioManager handling only called setA2dpMute(), so the phone-side Bluetooth playback could continue progressing.
    • Radio and USB music handle transient focus loss as a real paused playback state, which explains the different behavior.
  • Fix:
    • In BtMusicAudioManager, AUDIOFOCUS_LOSS_TRANSIENT now records whether Bluetooth music was playing, calls setA2dpMute(), and calls pauseBtMusic() when it was playing.
    • Existing AUDIOFOCUS_GAIN recovery logic remains responsible for auto-resuming only when isPlayingLossFocus is true and the user did not pause manually.
  • Verification:
    • .\gradlew :YX_BluetoothSDK:assembleDebug completed successfully.