华为手环耳机模式适配

2,866 阅读3分钟

前言

最近接到一个需求,需要在播放声音时适配华为蓝牙手环,这个手环不同于其他手环,将手环主机从腕带上取出时,就变成了蓝牙耳机,可以接听电话。如图所示:

这里为了便于理解,定义两个概念:

  • 手环模式:未将主机从腕带上取出状态
  • 耳机模式:将主机从腕带上取出状态

定位问题

遇到的问题

手环模式下,手机连接手环蓝牙后,声音由听筒播放。

为什么会从听筒中播放?

从手机设置界面观察到,该手环的音频选项在处于手环模式和耳机模式时有不同的状态,当处于手环模式时,通话音频处于开启状态,媒体音频处于关闭状态,如图:

手环模式
当处于耳机模式时,通话音频和媒体音频均为开启状态
耳机模式
由此我们可以得到一个重要的线索,如果系统设置可以判断出音频状态,我们也可以通过代码判断当前蓝牙设备的通话音频和媒体音频的状态。

接下来我们看代码,在原有的实现中,当有蓝牙设备连接时,会收到连接的广播,然后我们通过如下代码设置将声音通过蓝牙耳机播放

 private void chooseBluetooth() {
        audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
        audioManager.startBluetoothSco();      // 打开 SCO 类型蓝牙链路
        audioManager.setBluetoothScoOn(true);  // 打开 SCO 类型蓝牙链路
        audioManager.setSpeakerphoneOn(false); // 关闭扬声器
 }

audioManagerAudioManager 对象,这里简单介绍一下 audioManager.startBluetoothSco(); 这行代码的意思是打开 SCO 类型蓝牙链路。在蓝牙通信中,共有两种通信链路:

  1. 同步链路 (Synchronous Connection Oriented)
  2. 异步链路 (Asynchronous Connectionless)

同步链路 (SCO) 连接为对称连接,利用保留时隙传送数据包。连接建立后,主设备和从设备可以不被选中就发送SCO数据包。SCO数据包既可以传送话音,也可以传送数据,但在传送数据时,只用于重发被损坏的那部分的数据。主要用来传输对时间要求很高的数据通信

异步链路(ACL)就是定向发送数据包,它既支持对称连接,也支持不对称连接(既可以一对一,也可以一对多)。主设备负责控制链路带宽,并决定微微网中的每个从设备可以占用多少带宽和连接的对称性。从设备只有被选中时才能传送数据。ACL链路也支持接收主设备发给微微网中所有从设备的广播消息。

那么为什么在手环模式下声音会从听筒中播放?这里做出一个猜想,在手环模式下,Android 系统并没有主动打开媒体音频,当我们的程序执行 audioManager.startBluetoothSco(); 时执行失败,导致无法打开蓝牙链路,之后我们又设置了 audioManager.setSpeakerphoneOn(false); 关闭扬声器,声音自然就从听筒中播放。

解决方案

大致思路就是在调用 chooseBluetooth() 之前判断系统是否打开了媒体音频,如果打开了则进行蓝牙播放,否则依然使用扬声器播放,具体判断代码如下:

    boolean isA2dpOn = audioManager.isBluetoothA2dpOn();

参考文献


坚持不易,您的点赞是我写作的最大动力!