车载蓝牙功能打开-android

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010481276/article/details/88733548

需要打开媒体音频与电话音频
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 已拨通

猜你喜欢

转载自blog.csdn.net/u010481276/article/details/88733548