文章《蓝牙电话之HFP-连接》里已经对HFP的连接进行了简单分析,这篇主要和大家探讨下通话场景下的相关操作,如拨打、接听、挂断电话(AG侧、HF侧)、切换声道这几个场景。
蓝牙通话中主要涉及到以下两个方面的关注点:获取当前的通话状态和传送通话声音,这两个关注点直接决定了蓝牙电话应用是否能正常工作。
获取当前的通话状态:主要通过AT命令“+CIEV”和“+CLCC”来获取AG侧的通话状态和信息,HF侧不管如何操作都要以AG侧的状态为准。
该指令是AG侧主动将通话状态更新到HF侧的,依赖于“AT+CMER”来打开AG侧的更新标志位
从+CIEV命令的定义不难看出,和所对应的的值都在“AT+CIND=?”命令中定义清楚的,感兴趣的同学可以自行查看。通话中以下三个indicator需要重点关注
dicator对应值的含义分别是:
callsetup:电话建立过程,值为2
call:通话成功,值为1
callheld:电话保持的过程,值为7
value对应的值的含义如图所示:
这样ind + value的不同组合就可以代表不同的电话场景流程。
该指令需要HF侧主动通过“AT+CLCC”指令告知AG侧将当前的电话信息通过“+CLCC”发送过来。接收到“+CLCC”指令,HF侧就可以知道当前的电话状态和号码等关键信息。
传送通话声音:蓝牙电话应用最根本的目的是传输双方的通话声音,既要将远端的输入语音经本端手机的电话模块接收到后再通过蓝牙模块AG侧传送到HF侧播放出来,也要将本端的输出语音通过HF侧传送到AG侧,再通过本端手机的电话模块发送出去。传输通话音频的通道主要是通话链路也就是大家平常说的SCO或eSCO链路。
SCO或eSCO的建立依赖于HFP协议的Service Level Connection连接成功(详情请参考《蓝牙电话之HFP-连接》),再通过HCI命令建立通话音频链路。
音频链路当前有两种编码方式,分别为CVSD和mSBC。采用哪种编码方式是有建立连接的双方协商决定的,AT命令“+BRSF”中HF和AG双方会分别发送自己支持的features给对方,如果双方都支持编码协商即标志位Codec Negotiation,HF侧主动通过“AT+BAC”的AT命令告知AG侧两种编码方式的编号分别为:1-CVSD,2-mSBC,并且在建立音频链路时多采用mSBC的编码方式,否则都是CVSD编码。
音频链路连接对应的HCI命令为:Setup Synchronous Connection Command,如果本端Controller支持Enhanced Setup Synchronous Connection指令(详情请查看Read Local Supported Commands Command的HCI命令),则创建连接的HCI命令为:Enhanced Setup Synchronous Connection Command
音频链路SCO或eSCO建立成功后,通话语音就能在AG、HF两侧正常传输了。
以上就是蓝牙电话通话中需要了解的最重要的两点,HF侧的上层蓝牙电话应用只需监听BluetoothHeadsetClient中定义的如下两个广播就可以执行上层逻辑。
/**
* Intent sent whenever state of a call changes.
*
* It includes:
* {@link #EXTRA_CALL},
* with value of {@link BluetoothHeadsetClientCall} instance,
* representing actual call state.
*/
public static final String ACTION_CALL_CHANGED =
"android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED";
/**
* Intent sent whenever audio state changes.
*
* It includes two mandatory extras:
* {@link BluetoothProfile#EXTRA_STATE},
* {@link BluetoothProfile#EXTRA_PREVIOUS_STATE},
* with possible values:
* {@link #STATE_AUDIO_CONNECTING},
* {@link #STATE_AUDIO_CONNECTED},
* {@link #STATE_AUDIO_DISCONNECTED}
* When EXTRA_STATE
is set
* to
STATE_AUDIO_CONNECTED,
* it also includes {@link #EXTRA_AUDIO_WBS}
* indicating wide band speech support.
*/
public static final String ACTION_AUDIO_STATE_CHANGED =
"android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED";
接下来再详细分析下这两个广播是如何一步步发送出去的,见如下时序图。
下面以场景为视角简单分析上述两点在其中的作用及流程,编码方式都是mSBC。
1、 拨打电话-AG
拨打电话大体分为拨号、响铃、通话这三个部分,状态变化后AG侧都会主动通知HF侧,HF侧再通过“AT+CLCC”获取详细的电话参数信息。
2、 拨打电话-HF
HF侧的拨打电话和AG侧比较起来就多了一个步骤,通过AT命令“ATD”将需要拨打的电话信息发送到AG侧,然后通过手机的电话模块呼出电话。
蓝牙电话应用层调用BluetoothHeadsetClient.dial()后,只需监听相应广播即可。
3、 接听电话
来电接听这里还需要我们了解In-Band Ring来电响铃功能,该功能支持在来电时将手机上设置的响铃铃声通过蓝牙传送到HF侧,从而在设备端播出该铃声。AG在“BRSF”交互features时,如果In-Band Ring Tone Capability标志位设置成 true 则代表支持该功能,所以蓝牙电话应用根据自身的配置来进行取舍。
接听来电的方式就两种,手机上接听或车机来电提醒界面(车机语音控制)接听,这样的话HF侧的操作流程对比AG侧也就是多了步将接听的动作以AT命令“ATA”发送给手机执行接听流程。
车载端接听来电则蓝牙电话应用层调用BluetoothHeadsetClient.acceptCall()后监听相应广播即可。
HF侧接听来电-No In-Band Ringing:
4、 挂断电话
电话挂断和接听来电类似,HF侧操作也是通过AT命令“AT+CHUP”将挂断电话操作告知AG侧,真正的电话挂断操作是有手机的电话模块来执行的。
蓝牙电话应用层只需要调用BluetoothHeadsetClient.terminateCall()完监听AG_CALL_CHANGE广播即可。
5、 切换声道
声道的切换就是通话语音从手机端切到车载端播放,或反之从车载端切到手机端播放。由于通话音频数据的传输是在SCO链路上进行的,所以建立SCO就代表通话语音切到车载端播放,断开SCO就代表通话语音切到手机上播放。
建立和断开SCO的操作双方都可以操作,从而HF侧的蓝牙电话应用只需关心BluetoothHeadsetClient中的connectAudio()和disconnectAudio()这两个接口就可实现上述功能。
蓝牙电话通话的相关分析大概就这样了,对此感兴趣的同学可以私信我一起探讨哦。