中科蓝讯SDK开发-BT893x 面条耳机连接vivo手机后连接APP端,最后手机端设置页面手动断开蓝牙连接,此时耳机端假断开

前言

现在为止也开发了许多杰理和中科蓝讯蓝牙芯片的TWS蓝牙耳机、音响项目 SDK 的案子,在调试案子时不断的向前辈们学习到了很多关于蓝牙音响、蓝牙TWS耳机专业的知识。想在这里做一个学习汇总,方便各位同行和对中科蓝汛芯片SDK感兴趣的小伙伴们学习;

本章详细讲解BT893x 面条耳机连接vivo手机后连接APP端,最后手机端设置页面手动断开蓝牙连接,此时耳机端假断开

相关信息

  • 项目型号:AKR255PRO
  • SDK版本:sdk_bt893x_headset_v018_s13402_20260722
  • 工 程 师 :李永奉
  • 提交日期:2026-09-09

问题描述

  • 情况1:面条耳机连接vivo手机后连接APP端,再手机端设置页面手动断开蓝牙连接,此时耳机端只关闭音频流,并没有断开蓝牙连接;现象:断开提示音未播放、灯效依旧是蓝牙连接成功灯效蓝灯慢闪、手机APP端依旧显示连接中、手机端播放歌曲声音却是外放的;
  • 情况2:面条耳机连接华为手机后连接APP端,再手机端设置页面手动断开蓝牙连接,此时耳机端成功断开蓝牙连接;现象:断开提示音播放、灯效为:等待蓝牙连接灯效红蓝交替闪、手机APP端显示未连接;

原因分析

一、先补充一个讲解技术背景:耳机和手机之间的连接是"一条线 + 多个业务"

手机和耳机之间,蓝牙底层先建立一条链路(可以理解成一根"网线",日志里的 acl/num/nor 就是描述它的),在这根"网线"上再跑几个"业务通道":

业务 作用 日志里的代号
A2DP 传音乐(音频流) a2dp
AVRCP 传播放/暂停/切歌控制指令 avctp
SPP 传 APP 数据(AB-Mate APP 走这条) spp0
BLE APP 的另一条低功耗通道 ble

"正常断开" = 整根网线被拔掉(所有业务一起没),耳机立刻收到断开通知,于是播放断开提示音、灯效切红蓝闪、通知 APP。 "假断开" = 只停了音乐业务,网线还插着------耳机只收到"音乐流停止",收不到断开通知,自然什么都不做。

二、那么vivo手机是怎么做造成了这个BUG

1、vivo手机断开前,一切都正常;所有状态都正常

复制代码
`[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=1, play=0, avctp=1, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=1, play=0, avctp=1, spp0=1, ble=1`

2、vivo手机页面设置点击断开蓝牙的那一刻

复制代码
`CLOSE                               //手机开始关闭媒体
avdtp_end_of_play 0:hdl ffff        //音频流结束了
[ZDY-DBG] notice-MUSIC_STOP: disp=6, acl=0, nor=1, num=1, a2dp=1, play=0, avctp=0, spp0=1, ble=1           //AVRCP 控制通道先被拆了
[ZDY-DBG] EVT-MUSIC_STOP: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1               //A2DP音乐通道也被拆了`

3、点击断开蓝牙操作后10秒

复制代码
`[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1
[ZDY-DBG] 1S-trace: disp=6, acl=0, nor=1, num=1, a2dp=0, play=0, avctp=0, spp0=1, ble=1`

这10行每秒一次的状态,10秒纹丝未动

1、a2dp=0, avctp=0:媒体业务确实被 vivo 关了(所以手机声音外放了)

2、但 num=1, nor=1:整条"网线"(ACL 链路)还好好插着

3、spp0=1, ble=1:APP 的 SPP/BLE 通道还活着(所以 APP 一直显示"已连接")

整段日志里从头到尾没有出现 BT_NOTICE_DISCONNECT------耳机压根没收到断开通知。

三、结论

vivo 在设置页点"断开"时,看到 APP 的 SPP/BLE 通道还在用,就只拆了媒体业务(A2DP/AVRCP),把整条链路留了下来。耳机端只收到"音乐流停止"一个事件,于是:

显示状态还停在"已连接"(disp=6)→ 蓝灯低频慢闪(连接灯效)

1、没有断开事件 → 不播断开提示音

2、SPP 还连着 → APP 不显示断开

3、A2DP 已关 → 声音回手机外放

而华为手机是另一套做法:设置页断开蓝牙连接后直接拔整根网线,断开通知完整到达耳机,所以华为一切正常。差异在手机端策略,不在耳机,耳机想等通知永远等不到------只能靠自己判断后主动断。

解决方案

1.抓住可疑时刻(EVT_A2DP_MUSIC_STOP事件)

复制代码
`#if LYF_APP_VIVO_BT_DISCONNECT_BUG
    static u8 zdy_vivo_fake_disc_flag = 0;      //vivo假断开复查标志(1=EVT_A2DP_MUSIC_STOP时A2DP profile已断)
#endif // LYF_APP_VIVO_BT_DISCONNECT_BUG`

vivo 假断开时,流停止事件必定会送到消息层(L123 的 EVT-MUSIC_STOP)。在这里查一眼:

复制代码
`#if LYF_APP_VIVO_BT_DISCONNECT_BUG
        //vivo等手机设置页断开只关A2DP/AVRCP不断ACL: 流停止时profile已断即疑似假断开, 1S后复查确认
        if (a2dp_is_connect() == 0) {
            zdy_vivo_fake_disc_flag = 1;
            printf("[ZDY-DBG] fake-disc suspect: num=%d\n", bt_get_connected_num());
        }
#endif // LYF_APP_VIVO_BT_DISCONNECT_BUG`

2. 1秒后复查(借用每秒一次的MSG_SYS_1S的消息)

光有步骤1还不够-其它手机断开时也会先经历A2DP音频数据流被终止"",所以等1秒再查三个条件:

|-----------------------------|------------------------------|----------------------|
| bt_get_connected_num() > 0 | 手机跟耳机的整条链路还连着(连接数 ≥1) | 排除华为式已正常断开(那时 num=0) |
| a2dp_is_connect() == 0 | A2DP 音乐通道依然是断的(1 秒过去没恢复) | 排除只是短暂断流后又恢复 |
| !bt_is_calling() | 不在通话中 (!=取反,即"不是正在通话") | 排除通话场景,通话时绝不能乱断 |

复制代码
`#if LYF_APP_VIVO_BT_DISCONNECT_BUG
        //假断开复查: 链路仍在且A2DP已断且不在通话 → 主动断开整条链路(0xff=断开不关机)
        if (zdy_vivo_fake_disc_flag) {
            zdy_vivo_fake_disc_flag = 0;
            if (bt_get_connected_num() > 0 && a2dp_is_connect() == 0 && !bt_is_calling()) {
                printf("[ZDY-DBG] fake-disc confirmed, bt_disconnect(0xff)\n");
                bt_disconnect(0xff);
            }
        }
#endif // LYF_APP_VIVO_BT_DISCONNECT_BUG`

制作不易!喜欢的小伙伴给个小赞赞!喜欢我的小伙伴点个关注!有不懂的地方和需要的资源随时问我哟!

相关推荐
大爱编程♡1 小时前
STM32-DMA
stm32·单片机·嵌入式硬件
秦老师Q2 小时前
php零基础入门到实战,从语法到框架,一篇搞定
开发语言·php
云泽8082 小时前
STM32 USART 详解(三):硬件架构、内部寄存器与物理层收发原理
stm32·嵌入式硬件·硬件架构
天空'之城2 小时前
单片机基础核心知识点汇总(三十三)
数据库·单片机·嵌入式硬件
驭渊的小故事2 小时前
多线程02
java·开发语言·jvm
阿里嘎多学长2 小时前
2026-09-09 GitHub 热点项目精选
开发语言·程序员·github·代码托管
君顾13 小时前
本地折扣卡 CPS 平台搭建:从系统架构到落地实战
java·开发语言·折扣卡
liangshanbo12154 小时前
手写 Function.prototype.call 与 Function.prototype.apply 面试题满分答案
开发语言·javascript·原型模式
Zenova EdgeOS4 小时前
C++ 工业边缘 libcurl HTTP 客户端实战
开发语言·c++·网络协议·边缘计算