需要打开媒体音频与电话音频
a2dp sink
headset HF role角色
本平台为msm8909
需要打开
HfpClientConnectionService
A2dpSinkService
HeadsetClientService
PRODUCT_PROPERTY_OVERRIDES += persist.service.bt.a2dp.sink=true
PRODUCT_PROPERTY_OVERRIDES += persist.service.bt.hfp.client=true
Enable HF Client (HF role)
1.Set below flag in packages/apps/Bluetooth/res/values/config.xml to true
<bool name="profile_supported_hfpclient">true</bool>
<bool name="hfp_client_connection_service_enabled">true</bool>
2.adb shell setprop persist.service.bt.hfp.client true
3.Make sure following is present in packages/apps/Bluetooth/AndroidManifest.xml
<service
android:process="@string/process"
android:name = ".hfpclient.HeadsetClientService"
android:enabled="@bool/profile_supported_hfpclient">
<intent-filter>
<action android:name="android.bluetooth.IBluetoothHeadsetClient" />
</intent-filter>
</service>
<service
android:process="@string/process"
android:name = ".hfpclient.connserv.HfpClientConnectionService"
android:permission= "android.permission.BIND_CONNECTION_SERVICE"
android:enabled="@bool/hfp_client_connection_service_enabled">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
但打开后可以收到接听电话的提示,仍接听电话后仍没有电话音频从对端发过来,目前正在check这个音频,发现的sco_tx到quat_rx的音频通道没有建立
建立时用的mix_path的use case为:hfp-sco-wb
将对应的节点修改为quat
<path name="hfp-sco">
<ctl name="QUAT_MI2S_RX Port Mixer INTERNAL_BT_SCO_TX" value="1" />
<ctl name="INTERNAL_BT_SCO_RX Audio Mixer MultiMedia6" value="1" />
<ctl name="MultiMedia6 Mixer QUAT_MI2S_TX" value="1" />
<ctl name="HFP_INT_UL_HL Switch" value="1" />
<ctl name="QUAT_MI2S_RX_DL_HL Switch" value="1" />
</path>
<path name="hfp-sco-wb">
<ctl name="Internal BTSCO SampleRate" value="16000" />
<path name="hfp-sco" />
</path>
Index: android/hardware/qcom/audio/configs/msm8909/mixer_paths_msm8909_pm8916.xml
===================================================================
--- android/hardware/qcom/audio/configs/msm8909/mixer_paths_msm8909_pm8916.xml (revision 274)
+++ android/hardware/qcom/audio/configs/msm8909/mixer_paths_msm8909_pm8916.xml (revision 275)
@@ -491,6 +491,7 @@
<ctl name="INTERNAL_BT_SCO_RX Audio Mixer MultiMedia6" value="1" />
<ctl name="MultiMedia6 Mixer QUAT_MI2S_TX" value="1" />
<ctl name="HFP_INT_UL_HL Switch" value="1" />
+ <ctl name="QUAT_MI2S_RX_DL_HL Switch" value="1" />
</path>
<path name="hfp-sco-wb">
Index: android/kernel/msm-3.18/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
===================================================================
--- android/kernel/msm-3.18/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c (revision 274)
+++ android/kernel/msm-3.18/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c (revision 275)
@@ -7661,6 +7661,9 @@
SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
msm_routing_put_port_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+ msm_routing_put_port_mixer),
};
@@ -12864,6 +12867,7 @@
{"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
{"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+ {"QUAT_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX","INT_BT_SCO_TX"},
{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"},
/* Backend Enablement */
平台上的设备上连接蓝牙后并手机有电话进来后,start_hfp会打开平台四个设备
static int32_t start_hfp(struct audio_device *adev,
struct str_parms *parms __unused)
{
int32_t ret = 0;
struct audio_usecase *uc_info;
int32_t pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, pcm_dev_asm_tx_id;
ALOGD("%s: enter", __func__);
uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
if (!uc_info)
return -ENOMEM;
uc_info->id = hfpmod.ucid;
uc_info->type = PCM_HFP_CALL;
uc_info->stream.out = adev->primary_output;
uc_info->devices = adev->primary_output->devices;
uc_info->in_snd_device = SND_DEVICE_NONE;
uc_info->out_snd_device = SND_DEVICE_NONE;
list_add_tail(&adev->usecase_list, &uc_info->list);
select_devices(adev, hfpmod.ucid);
pcm_dev_rx_id = platform_get_pcm_device_id(uc_info->id, PCM_PLAYBACK);
pcm_dev_tx_id = platform_get_pcm_device_id(uc_info->id, PCM_CAPTURE);
pcm_dev_asm_rx_id = hfpmod.hfp_pcm_dev_id;
pcm_dev_asm_tx_id = hfpmod.hfp_pcm_dev_id;
if (pcm_dev_rx_id < 0 || pcm_dev_tx_id < 0 ||
pcm_dev_asm_rx_id < 0 || pcm_dev_asm_tx_id < 0 ) {
ALOGE("%s: Invalid PCM devices (rx: %d tx: %d asm: rx tx %d) for the usecase(%d)",
__func__, pcm_dev_rx_id, pcm_dev_tx_id, pcm_dev_asm_rx_id, uc_info->id);
ret = -EIO;
goto exit;
}
ALOGD("%s: HFP PCM devices (rx: %d tx: %d pcm dev id: %d) usecase(%d)",
__func__, pcm_dev_rx_id, pcm_dev_tx_id, hfpmod.hfp_pcm_dev_id, uc_info->id);
hfpmod.hfp_sco_rx = pcm_open(adev->snd_card,
pcm_dev_asm_rx_id,
PCM_OUT, &pcm_config_hfp);
if (hfpmod.hfp_sco_rx && !pcm_is_ready(hfpmod.hfp_sco_rx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_rx));
ret = -EIO;
goto exit;
}
hfpmod.hfp_pcm_rx = pcm_open(adev->snd_card,
pcm_dev_rx_id,
PCM_OUT, &pcm_config_hfp);
if (hfpmod.hfp_pcm_rx && !pcm_is_ready(hfpmod.hfp_pcm_rx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_rx));
ret = -EIO;
goto exit;
}
hfpmod.hfp_sco_tx = pcm_open(adev->snd_card,
pcm_dev_asm_tx_id,
PCM_IN, &pcm_config_hfp);
if (hfpmod.hfp_sco_tx && !pcm_is_ready(hfpmod.hfp_sco_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_sco_tx));
ret = -EIO;
goto exit;
}
hfpmod.hfp_pcm_tx = pcm_open(adev->snd_card,
pcm_dev_tx_id,
PCM_IN, &pcm_config_hfp);
if (hfpmod.hfp_pcm_tx && !pcm_is_ready(hfpmod.hfp_pcm_tx)) {
ALOGE("%s: %s", __func__, pcm_get_error(hfpmod.hfp_pcm_tx));
ret = -EIO;
goto exit;
}
if (pcm_start(hfpmod.hfp_sco_rx) < 0) {
ALOGE("%s: pcm start for hfp sco rx failed", __func__);
ret = -EINVAL;
goto exit;
}
if (pcm_start(hfpmod.hfp_sco_tx) < 0) {
ALOGE("%s: pcm start for hfp sco tx failed", __func__);
ret = -EINVAL;
goto exit;
}
if (pcm_start(hfpmod.hfp_pcm_rx) < 0) {
ALOGE("%s: pcm start for hfp pcm rx failed", __func__);
ret = -EINVAL;
goto exit;
}
if (pcm_start(hfpmod.hfp_pcm_tx) < 0) {
ALOGE("%s: pcm start for hfp pcm tx failed", __func__);
ret = -EINVAL;
goto exit;
}
hfpmod.is_hfp_running = true;
hfp_set_volume(adev, hfpmod.hfp_volume);
ALOGD("%s: exit: status(%d)", __func__, ret);
return 0;
exit:
stop_hfp(adev);
ALOGE("%s: Problem in HFP start: status(%d)", __func__, ret);
return ret;
}
pcm_open需要打开正确的节点才可:
00-17: INT_HFP_BT Hostless () : : playback 1 : capture 1
00-18: MultiMedia6 () : : playback 1 : capture 1
00-25: QUAT_MI2S Hostless (*) : : playback 1
即:
追代码,这里需要将#define HFP_PCM_RX 25修改为25
android\hardware\qcom\audio\hal\msm8916\platform.h
之后debug可知
hardware/qcom/audio/hal/audio_extn/hfp.c
ALOGD("%s: HFP PCM devices (rx: %d tx: %d pcm dev id: %d) usecase(%d)",
__func__, pcm_dev_rx_id, pcm_dev_tx_id, hfpmod.hfp_pcm_dev_id, uc_info->id);
01-03 16:30:04.297 395 718 D audio_hw_hfp: start_hfp: HFP PCM devices (rx: 25 tx: 17 pcm dev id: 18) usecase(17)```
具体的设备端口号从下可以查
msm8909:/proc/asound # cat pcm
cat pcm
00-00: MultiMedia1 (*) : : playback 1 : capture 1
00-01: MultiMedia2 (*) : : playback 1 : capture 1
00-02: CS-Voice (*) : : playback 1 : capture 1
00-03: VoIP (*) : : playback 1 : capture 1
00-04: ULL (*) : : playback 1
00-05: Primary MI2S_RX Hostless (*) : : playback 1
00-06: INT_FM Hostless (*) : : capture 1
00-07: AFE-PROXY RX msm-stub-rx-7 : : playback 1
00-08: AFE-PROXY TX msm-stub-tx-8 : : capture 1
00-09: (Compress1) : : playback 1 : capture 1
00-10: AUXPCM Hostless (*) : : playback 1 : capture 1
00-11: Tertiary MI2S_TX Hostless (*) : : capture 1
00-12: MultiMedia5 (*) : : playback 1 : capture 1
00-13: Voice2 (*) : : playback 1 : capture 1
00-14: MultiMedia9 (*) : : playback 1 : capture 1
00-15: VoLTE (*) : : playback 1 : capture 1
00-16: VoWLAN (*) : : playback 1 : capture 1
00-17: INT_HFP_BT Hostless (*) : : playback 1 : capture 1
00-18: MultiMedia6 (*) : : playback 1 : capture 1
00-19: Listen 1 Audio Service (*) : : capture 1
00-20: Listen 2 Audio Service (*) : : capture 1
00-21: Listen 3 Audio Service (*) : : capture 1
00-22: Listen 4 Audio Service (*) : : capture 1
00-23: Listen 5 Audio Service (*) : : capture 1
00-24: (Compress2) : : playback 1
00-25: QUAT_MI2S Hostless (*) : : playback 1
00-26: Senary_mi2s Capture cajon_vifeedback-26 : : capture 1
00-27: (Compress3) : : playback 1
00-28: (Compress4) : : playback 1
00-29: (Compress5) : : playback 1
00-30: (Compress6) : : playback 1
00-31: (Compress7) : : playback 1
00-32: (Compress8) : : playback 1
00-33: (Compress9) : : playback 1
00-34: VoiceMMode1 (*) : : playback 1 : capture 1
00-35: VoiceMMode2 (*) : : playback 1 : capture 1
00-36: MultiMedia8 (*) : : playback 1 : capture 1
00-37: QCHAT (*) : : playback 1 : capture 1
00-38: (Compress10) : : capture 1
00-39: (Compress11) : : capture 1
00-40: (Compress12) : : capture 1
00-41: (Compress13) : : capture 1
00-42: (Compress14) : : capture 1
00-43: (Primary MI2S Playback) : : playback 1
00-44: (Secondary MI2S Playback) : : playback 1
00-45: (Tertiary MI2S Capture) : : capture 1
00-46: (Quaternary MI2S Playback) : : playback 1
00-47: (Quaternary MI2S Capture) : : capture 1
00-48: (AUX PCM Playback) : : playback 1
00-49: (AUX PCM Capture) : : capture 1
00-50: (Internal BT-SCO Playback) : : playback 1
00-51: (Internal BT-SCO Capture) : : capture 1
00-52: (Internal FM Playback) : : playback 1
00-53: (Internal FM Capture) : : capture 1
00-54: (AFE Playback) : : playback 1
00-55: (AFE Capture) : : capture 1
00-56: (Voice Uplink Capture) : : capture 1
00-57: (Voice Downlink Capture) : : capture 1
00-58: (Voice Farend Playback) : : playback 1
00-59: (Voice2 Farend Playback) : : playback 1
00-60: (Quinary MI2S Capture) : : capture 1
00-61: (Quinary MI2S Playback) : : playback 1
00-62: (Internal BT-A2DP Playback) : : playback 1
msm8909:/proc/asound # cat pcm|grep BT
cat pcm|grep BT
00-17: INT_HFP_BT Hostless (*) : : playback 1 : capture 1
00-50: (Internal BT-SCO Playback) : : playback 1
00-51: (Internal BT-SCO Capture) : : capture 1
00-62: (Internal BT-A2DP Playback) : : playback 1
以上带 Hostless字段的表示不经过soc dsp路由
最后车载的hf就可以使用了,有回声,不过平台并不支持回声消除,需要另外的软件算法或硬件处理
后面发现蓝牙层用routeHfpAudio路由向audio hal层申请打开路由,与telecom与打电话dialer应用没有关系,完全靠平台商的音频路由支持到外接设备
static synchronized void routeHfpAudio(boolean enable) {
if (DBG) {
Log.d(TAG, "hfp_enable=" + enable);
}
if (enable && !sAudioIsRouted) {
sAudioManager.setParameters("hfp_enable=true");
// sAudioManager.setParameters("SET_LOOPBACK_TYPE=32,2");/*device value must be 1 2 3*/
} else if (!enable) {
sAudioManager.setParameters("hfp_enable=false");
// sAudioManager.setParameters("SET_LOOPBACK_TYPE=0,0");/*device value must be 1 2 3*/
}
sAudioIsRouted = enable;
}
添加接电话挂电话的功能
用平台的两个按键支持,这里为电话键是接听挂断,录音按键是拒接
只需要在原来接听按键的基础上添加判断正在通话,再挂断电话,在录音按键中处理挂断按键的功能
Index: android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
===================================================================
--- android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java (revision 279)
+++ android/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java (revision 280)
@@ -6305,6 +6305,38 @@
msg.setAsynchronous(true);
msg.sendToTarget();
}
+ if (down) {
+ TelecomManager telecomManager = getTelecommService();
+ boolean hungUp = false;
+ if (telecomManager != null) {
+ hungUp = telecomManager.endCall();
+ }
+ if (interactive && !hungUp) {
+ mEndCallKeyHandled = false;
+ mHandler.postDelayed(mEndCallLongPress,
+ ViewConfiguration.get(mContext).getDeviceGlobalActionKeyTimeout());
+ } else {
+ mEndCallKeyHandled = true;
+ }
+ } else {
+ if (!mEndCallKeyHandled) {
+ mHandler.removeCallbacks(mEndCallLongPress);
+ if (!canceled) {
+ if ((mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_HOME) != 0) {
+ if (goHome()) {
+ break;
+ }
+ }
+ if ((mEndcallBehavior
+ & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
+ goToSleep(event.getEventTime(),
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+ isWakeKey = false;
+ }
+ }
+ }
+ }
break;
}
@@ -6321,6 +6353,11 @@
// (which is presumably the InCallScreen.)
result &= ~ACTION_PASS_TO_USER;
}
+ //add by zhongyk
+ else if (telecomManager.isInCall()){
+ telecomManager.endCall();
+ result &= ~ACTION_PASS_TO_USER;
+ }
}
}
break;
HF功能是先连接hands-free再在通话进来时建立esco连接
发起连接根据spec主从设备都可以发现,但我发现更多的是Master发起
btm_send_connect_request发起esco连接:
支持esco走else:
再加上mi6支持enhanced sco则btsnd_hcic_enhanced_set_up_synchronous_connection:
if (controller_get_interface()
->supports_enhanced_setup_synchronous_connection() &&
(osi_property_get("qcom.bluetooth.soc", value, "qcombtsoc") &&
strcmp(value, "cherokee") == 0)) {
/* Use the saved SCO routing */
p_setup->input_data_path = p_setup->output_data_path =
btm_cb.sco_cb.sco_route;
BTM_TRACE_DEBUG(
"%s: txbw 0x%x, rxbw 0x%x, "
"lat 0x%x, retrans 0x%02x, pkt 0x%04x, path %u",
__func__, p_setup->transmit_bandwidth, p_setup->receive_bandwidth,
p_setup->max_latency_ms, p_setup->retransmission_effort,
p_setup->packet_types, p_setup->input_data_path);
btsnd_hcic_enhanced_set_up_synchronous_connection(acl_handle, p_setup);
p_setup->packet_types = saved_packet_types;
} else { /* Use older command */
首先查看平台是否支持esco连接:
代码分析,
对esco的支持是在page0上,说明这个返回有很多页,分几次读
只有一个地方调用
查看controller->get_features_classic(i)这个数据结构
#define HCI_READ_LOCAL_EXT_FEATURES (0x0004 | HCI_GRP_INFORMATIONAL_PARAMS)
即命令为1004
#define HCI_SCO_LINK_SUPPORTED(x) ((x)[1] & 0x08)
#define HCI_HV2_PACKETS_SUPPORTED(x) ((x)[1] & 0x10)
#define HCI_HV3_PACKETS_SUPPORTED(x) ((x)[1] & 0x20)
因为大小端则sco对应 SCO link: Supported
hv2,hv3分别为 HV2 packets: Supported, HV3 packets: Supported
以下两个平台都是支持hv2 ,hv3 sco link的
如米6与平台连接:
由spec可以知连接流程:
Mi6这边发起:
平台这边接收:接收到一个连接请求事件再发送接收请求命令给mi6
关于+CIEV, AG向HF汇报自身的状态
比较常见的是:
+CIEV:1,1 接听电话
+CIEV:2,0 当前没有等待的电话需要处理
+CIEV:2,2 拨打电话
+CIEV:2,3 已拨通