☰
炬芯ATS3089X手表的HFP通话不自动路由到设备BT的问题解决
2026/10/14 2:46:01 网站建设 项目流程

问题:通话状态只第一次在设备BT, 后面再次通话就回到手机,解决这个问题,要始终默认在BT,让设备发声。

# ATS3089P 蓝牙通话音源说明(v4)

v4 继续处理“第一通电话正常,下一通声音留在手机,K1 也无法切回开发板”的问题。**v3 的耳机类别及直接 SCO 回退没有解决用户手机的实测问题。**本版新增 Android HFP 音频策略声明,请求手机在连接和通话时优先使用本设备;它是基于源码和日志的修复尝试,实际效果仍需手机验证。

请烧录文件名带 **`bt_call_route_v4`** 的固件,不要使用此前的 v2 / v3 包复测本次修改。无需新增 shell 指令,策略在 HFP 连接后自动发送。K1 沿用已有的 `KEY_TBD` 按键映射。v4 编译、打包和软件测试已通过,仍需上板检查实际声音。

## 一、v4 新增的 Android 音频策略

本工程启用 `CONFIG_BT_HFP_ANDROID_AUDIO_POLICY=y`。每次 HFP 连接建立后,在原 `AT+NREC` 请求之后,向手机发送一次:

```text
AT+ANDROID=SINKAUDIOPOLICY,1,1,0
```

该命令通过原 HFP AT 队列发送。命令正文为 **32 个字符**,代码不额外附加回车;HFP 库自动补充 `CR`,避免超过已有 AT 队列的正文长度限制。

| 参数 | 值 | 请求含义 |
| --- | --- | --- |
| `callEstablishPolicy` | `1` | HF 发起或接听电话时,优先将通话音频路由到本设备 |
| `activeDevicePolicyAfterConnection` | `1` | 连接后优先将本设备作为活动音频设备;避免下述 `NOT_ALLOWED` 策略在挂断时清除 active device 的分支 |
| `inBandRingtonePolicy` | `0` | 铃声策略保持未配置,继续由手机的原有策略决定 |

Android 定义 `0=POLICY_UNCONFIGURED`、`1=POLICY_ALLOWED`、`2=POLICY_NOT_ALLOWED`。第二项与本问题相关:手机在挂断后若不再将本板作为 active HFP 设备,下次 BCC 和直接 SCO 都可能被拒绝。v4 明确请求连接后的活动设备策略为 `1`;第三项为 `0`,不覆盖原来的带内铃声选择。

策略设置并不等于立即切换音源。Android 参考实现的命令处理只是更新策略并存入数据库,**没有在该处理函数中立即调用 `setActiveDevice`**。因此,手机回复 `OK` 也不能独立证明已成为 active device 或已有声音,需要结合下一轮通话验证。

不支持该 Android 扩展的手机会继续原来的标准 HFP 流程。固件只记录不支持或失败结果,不循环发送策略,也不自动断开蓝牙强制重连。策略每次 HFP 连接发一次,普通挂断不会再次发送。

## 二、日志证据及判断范围

HFP 用于通话状态和控制,SCO / eSCO 承载实际通话音频。手机显示已连接蓝牙,不代表通话音频已经建立。

本机此前日志显示:第一轮在 14:45:41.118 成功建立 SCO;第二轮 14:45:55.088 拨号、14:46:01.998 进入通话后没有 SCO,四次 `AT+BCC` 及 K1 请求都返回:

```text
Send at cmd AT+BCC err 2-3
```

这里 `2` 是 CME 错误类型,`3` 是 `CME_ERROR_OPERATION_NOT_ALLOWED`,表示手机当前不允许该请求。用户确认断开蓝牙再连接后可以恢复,是手机会话状态参与连接决策的证据,但不能由此确定小米内部的唯一根因。

v3 的最新实测中,15:59 第一通 SCO 成功;16:00 第二通仍持续 BCC CME 3,并且直接 eSCO 请求的完成状态为 `0x0D`。直接 SCO 日志中的 `ret=0` 只说明 HCI opcode `0x0428` 请求已提交,后续建链完成事件仍可被手机拒绝。故设备类别和直接重建链路本身不足以恢复。

### Android 14 源码依据

已核对 GrapheneOS Android 14 的 AOSP 分支源码:

- [BluetoothSinkAudioPolicy.java](https://github.com/GrapheneOS/platform_packages_modules_Bluetooth/blob/14/framework/java/android/bluetooth/BluetoothSinkAudioPolicy.java) 定义上述三项策略,默认值为 `0`,`ALLOWED=1`、`NOT_ALLOWED=2`。
- [HeadsetService.java](https://github.com/GrapheneOS/platform_packages_modules_Bluetooth/blob/14/android/app/src/com/android/bluetooth/hfp/HeadsetService.java) 的 `CALL_STATE_IDLE` 路径在 `getActiveDevicePolicyAfterConnection()==POLICY_NOT_ALLOWED`(值为 `2`)时调用 `removeActiveDevice`。**默认 `0` 本身不满足这个条件**,不能据源码断言所有未声明策略的设备挂断都会被移除。
- [HeadsetStateMachine.java](https://github.com/GrapheneOS/platform_packages_modules_Bluetooth/blob/14/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java) 解析 `SINKAUDIOPOLICY`,并由 `setHfpCallAudioPolicy` 更新内存及数据库。该函数不会立即将设备设为 active。

对应的官方 AOSP Android 14 入口可用于交叉对照:

- [ActiveDeviceManager.java](https://android.googlesource.com/platform/packages/modules/Bluetooth/+/refs/tags/android-14.0.0_r1/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java):活动音频设备选择和设备类别筛选。
- [HeadsetService.java](https://android.googlesource.com/platform/packages/modules/Bluetooth/+/refs/tags/android-14.0.0_r1/android/app/src/com/android/bluetooth/hfp/HeadsetService.java):HFP 活动设备及通话空闲时的策略处理。
- [HeadsetStateMachine.java](https://android.googlesource.com/platform/packages/modules/Bluetooth/+/refs/tags/android-14.0.0_r1/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java):Android AT 策略解析和保存。
- [bta_ag_cmd.cc](https://android.googlesource.com/platform/packages/modules/Bluetooth/+/refs/tags/android-14.0.0_r1/system/bta/ag/bta_ag_cmd.cc):AG 侧 BCC 请求及错误响应。
- [bta_ag_sco.cc](https://android.googlesource.com/platform/packages/modules/Bluetooth/+/refs/tags/android-14.0.0_r1/system/bta/ag/bta_ag_sco.cc):SCO 建链、远端请求和连接失败处理。

这些源码支持“通过 Android 音频策略影响 active HFP 设备行为”的修复方向。小米可能有厂商定制,当前手机的策略值、数据库内容和移除 active device 的确切路径尚未直接取得;v4 不能表述为已经确定小米根因或保证修复。

### 竞品日志对比

用户提供的 `新建 文本文档 (3).txt` 只包含两轮完整电话。第一轮 14:58:32.880 收到 `+BCS:2`,设备回复 `AT+BCS=2`;14:58:33.030 附近收到 HCI Connection Request(`RX: 04 04 ... 02`,类型 `02` 为 eSCO),设备用 `TX: 01 29 04 ...` 接受,语音参数 `0x0063`,随后建立 SCO。

第二轮 14:58:43.741 拨号,43.820 手机再次主动请求 eSCO,设备接受;44.050 播放器 `open sucessed`,期间没有重新 BCC / BCS。双方 HF 声明均为 `AT+BRSF=255`、`AT+BAC=1,2`,成功音频路径均为 16 kHz。竞品这两轮是接受手机主动连接,不能称其使用了本工程的设备主动 SCO 回退。

竞品挂断时 `allow sco 3->3`,本机曾为 `3->1`,是本地许可差异;仅凭该差异不能确定手机后续不发连接请求的原因。

## 三、保留的标准 HFP 与恢复流程

| 事件 | 固件处理 |
| --- | --- |
| HFP 连接 | 正常模式开启管理层 SCO 许可,NREC 后发送一次 Android 策略 |
| 新来电或拨号 | 允许手机主动建立音频,清除上轮手动选择和请求计数 |
| 接通且已有 SCO | 保持连接并启动通话播放 |
| 接通但没有 SCO | 500 ms 后请求蓝牙音频,每 2 秒重试,最多 4 次 |
| BCC 回复 CME 3 | 仅对仍有效的请求,尝试复用本会话真实成功过的编码建立 SCO |
| 挂断且 HFP 未断开 | 取消本轮请求,正常模式保留管理层 SCO 许可 |
| HFP 断开 | 取消请求、关闭管理层许可、清除已确认编码 |

自动恢复只请求连接蓝牙,已有 SCO 时直接结束,避免双向音源切换把成功连接再切回手机。初次 BCS 协商和手机主动建连继续使用原路径;应用层 / OTA 的许可门限仍生效,`KEEP_CALL_ON_PHONE` 配置保持原行为。这里不表示所有路径都始终 `allow_sco=3`。

BCC 被拒绝后的兼容回退只使用同一 HFP 会话真实 SCO 确认过的编码:mSBC 为 16 kHz、语音参数 `0x63`;CVSD 为 8 kHz、`0x60`。仅收到协商通知不算确认,HFP 断开后不复用旧会话。回退仍可能被手机拒绝,不能代替 v4 策略和正常手机主动建连。

SCO 成功、挂断、HFP 断开或手动选择后取消自动请求;`generation` 代号让已排队的旧请求和旧 BCC 错误响应失效,避免干扰新的通话。

## 四、K1 手动切换

K1 继续在通话期间双向切换手机与开发板音频。本轮按 K1 切到手机后,停止自动请求并保留手动选择,不自动抢回;再次按 K1 可以手动切回开发板。挂断后下一通电话重新默认蓝牙。

Android 策略只在 HFP 连接时声明,本轮按键切换继续走原控制路径,不增加反复强制连接或自动断链动作。

## 五、查看日志

HFP 连接时,提交策略的日志为:

```text
call route v4 request Android audio policy=1,1,0 ret=0
```

`ret=0` 表示 AT 命令已进入发送路径。是否被手机接受,以完成回调为准:

```text
call route v4 Android audio policy accepted type=0 cme=0
call route v4 Android audio policy unavailable type=... cme=...
```

第一种是手机返回成功,第二种是失败或不支持。固件不因第二种结果循环重发,也不自动断开重连;后续标准 HFP 命令继续执行。`accepted` 也不等于音频已经建立。

自动请求日志为 `call: request BT audio 1/4`,最多到 `4/4`,耗尽时打印 `call: automatic BT audio request exhausted`。兼容回退示例:

```text
call: BCC rejected, reuse codec=2 rate=16 direct SCO ret=0
```

这里 HFP 编码编号 `2` 表示 mSBC,`rate=16` 为 16 kHz。仍须检查后续建链事件是否成功,不能仅凭 `ret=0` 判断有声音。没有确认过的编码时打印 `call: BCC rejected without confirmed codec`。

真正成功的判据是 `hfp sco cnnected`、通话播放器 `open sucessed`,以及实际能收听和上传 MIC 声音。这两个日志字符串保留了原工程拼写,搜索时按原样查找。

## 六、烧录和手机复测

1. 烧录 `bt_call_route_v4`。在手机蓝牙设置中删除本板旧配对,再重新搜索和配对,使手机重新读取耳机类 CoD `0x240404` 并建立 HFP 会话。不要求每轮通话删除配对。
2. 连接手机蓝牙一次,使新的 HFP 连接发送 v4 策略。保存从 HFP 连接开始的完整日志,确认是 `accepted` 还是 `unavailable`;此后连续通话测试期间不要断开蓝牙。
3. 保持同一 HFP 连接,连续拨打、接通和挂断至少两次,建议测三次,逐次检查 SCO、播放器及双向声音,尤其检查第二轮是否仍有 BCC CME 3 / eSCO `0x0D`。手机接听来电也测试一次。
4. 通话中按 K1 切到手机,等待超过自动重试间隔,确认没有抢回;再按 K1 切回开发板,检查双向声音。本轮切到手机后挂断,再发起下一轮,检查默认蓝牙行为。
5. 在请求期间挂断或断开 HFP,确认旧请求停止;重新连接后确认策略只发送一次,再做连续通话测试。

记录手机型号、系统版本、策略回调和完整通话日志。软件编译 / 模拟测试用于验证发送、回调、重试和取消处理,不能替代实际手机兼容性验证。

## 七、代码和固件位置

| 文件 | 作用 |
| --- | --- |
| `application/bt_watch/prj.conf` | 启用 `BT_HFP_ANDROID_AUDIO_POLICY` |
| `framework/bluetooth/bt_service/profile/btsrv_hfp.c` | HFP 连接后策略发送、完成日志、BCC 兼容回退 |
| `framework/bluetooth/bt_manager/bt_manager_hfp.c` | 正常通话许可、自动请求和取消 |
| `framework/bluetooth/bt_service/core/btsrv_rdm.c` | 本 HFP 会话真实 SCO 编码确认 |
| `framework/bluetooth/bt_stack/src/bt_stack/conn.c` | 使用匹配编码参数创建 SCO |
| `application/bt_watch/src/btcall/btcall_event.c` | 通话事件、播放器和 K1 入口 |

固件位于:

```text
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261013_bt_call_route_v4.fw
application/bt_watch/outdir/ats3089p_dev_watch/_firmware/ats3089p_dev_watch_261013_bt_call_route_v4_debug.fw
```

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询