HFP

The purpose of this document is to provide an overview of the HFP HF and HSP HS applications based on the HFP/HSP profile. HFP defines how two devices supporting the Hands-Free Profile shall interact with each other on a point-to-point basis. The most common examples of such devices are in-car Hands-Free units used together with cellular phones, or wearable wireless headsets.

Terminology and Concepts

HFP/HSP Roles

  • Audio Gateway (AG) - This is the device that is the gateway of the audio, both for input and output. Typical devices acting as Audio Gateways are cellular phones.

  • Hands-Free unit (HF) - This is the device acting as the Audio Gateway’s remote audio input and output mechanism. It also provides some remote control means.

  • Audio Gateway (AG) - This device acts as either the end point, or a relay, for telephony quality audio, which is typically two-way. Exemplary devices acting as Audio Gateways are cellular phones and personal computers.

  • Headset (HS) - This device provides the Audio Gateway’s remote audio input and output mechanism.

HFP/HSP Profile Stack

  • As indicated below, the Hands-Free Profile is dependent upon both the RFCOMM and the Generic Access Profile.

  • Figure below shows the protocols and entities used in HSP profile.

  • The Controller, HCI, and L2CAP are the OSI layer 1 and 2 Bluetooth protocols. RFCOMM is the Bluetooth adaptation of GSM TS 07.10. SDP is the Bluetooth Service Discovery Protocol. Headset Profile and AT CMD are the entities responsible for headset specific control signaling, which is AT command based.

HFP/HSP UUID

  • Universally Unique Identifier(UUID) is a universally unique identifier that is guaranteed to be unique across all space and all time. UUIDs can be independently created in a distributed fashion. No central registry of assigned UUIDs is required. A UUID is a 128-bit value.

  • The full 128-bit value of a 16-bit or 32-bit UUID may be computed by a simple arithmetic operation:

    • 128_bit_value = 16_bit_value * 296 + Bluetooth_Base_UUID

    • 128_bit_value = 32_bit_value * 296 + Bluetooth_Base_UUID

    • The Base UUID(Bluetooth_Base_UUID) is used for calculating 128-bit UUIDs from ‘short UUIDs’ (uuid16 and uuid32) as described in the SDP Specification: 00000000-0000-1000-8000-00805F9B34FB.

    • A 16-bit UUID may be converted to 32-bit UUID format by zero-extending the 16-bit value to 32-bits. An equivalent method is to add the 16-bit UUID value toa zero-valued 32-bit UUID.

  • Blutetooth SDP UUID numbers are defined in https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/.

  • HFP HF and HSP HS UUID registration is referred in hfp_sdp_record and hsp_sdp_record.

HFP/HSP Features

HFP/HSP Service Channel

  • HFP HF and HSP HS service channels should be registered by bt_hfp_init.

  • Meanwhile, HFP HF and HSP HS service channel should be initialized in hfp_sdp_record and hsp_sdp_record as well.

Feature Set

The functions provided by HFP APIs are as below.

  1. Connecting and Disconnecting: Including functions to connect/disconnect HFP.

  2. Operating Call: Including functions to answer/reject call, etc.

  3. Notifying Call: Including functions to notify call event.

Provided APIs

The table below shows a simple description of HFP APIs. HFP related APIs are provided in the sdk\inc\framework\bt\bt_hfp.h and sdk\inc\framework\bt\bt_hfp_ag.h.

Header File

Description

API Reference

bt_hfp.h

Connect HFP, operate and notify Call, HF role.

Bluetooth HFP Profile

bt_hfp_ag.h

Connect HFP, operate and notify Call, AG role.

Bluetooth HFP AG Profile

Functions

HFP Init

bool bt_hfp_init()

Initialize HFP profile, and set HFP/HSP RFCOMM channel number. Configure HFP supported features.

void app_hfp_init(void)
{
    if (app_cfg_const.supported_profile_mask & (HFP_PROFILE_MASK | HSP_PROFILE_MASK))
    {
        bt_hfp_init(aRFC_HFP_CHANN_NUM,
                    RFC_HSP_CHANN_NUM, app_cfg_const.hfp_brsf_capability);
        audio_mgr_cback_register(app_hfp_audio_cback);
        bt_mgr_cback_register(app_hfp_bt_cback);
    }
}

HFP Connect

bool bt_hfp_connect_req()

Initiate HFP connection to a remote device, as shown below.

bool bt_hfp_disconnect_req()

Request HFP disconnection, as shown below.

bool bt_hfp_connect_cfm()

Accept or reject the incoming HFP connection from the remote device, as shown below.

HFP Audio Connect

bool bt_hfp_audio_connect_req()

Send a request to create audio connection.

bool bt_hfp_audio_disconnect_req()

Send a request to disconnect audio connection.

HFP Call Control

bool bt_hfp_send_vnd_at_cmd_req()

Send HFP vendor AT command, as shown below.

bool bt_hfp_dial_with_number_req()

Send a command to dial a specific phone number, as shown below.

bool bt_hfp_dial_last_number_req()

Send a command to recalling the last number dialed by the remote device, as shown below.

bool bt_hfp_call_answer_req()

Send a command to answer the incoming call.

bool bt_hfp_call_terminate_req()

Send a command to terminate the phone call.

bool bt_hfp_current_call_list_req()

Send a command to query current call list.

bool bt_hfp_release_held_or_waiting_call_req()

Send a command to release held or waiting call.

bool bt_hfp_release_active_call_accept_held_or_waiting_call_req()

Send a command to release active call and accept held or waiting call.

bool bt_hfp_hold_active_call_accept_held_or_waiting_call_req()

Send a command to hold active call and accept held or waiting call.

bool bt_hfp_join_two_calls_req()

Send a command to join two calls.

bool bt_hfp_dtmf_code_transmit()

Send a specific DTMF code to its network connection.

bool bt_hfp_network_operator_name_query()

Send a command to query the current network operator name.

HFP Voice_Recognition Control

bool bt_hfp_voice_recognition_enable_req()

Send a command to enable voice recognition.

bool bt_hfp_voice_recognition_disable_req()

Send a command to disable voice recognition.

HFP Audio Level Control

bool bt_hfp_speaker_gain_level_report()

Send a command to synchronize speaker gain level.

bool bt_hfp_microphone_gain_level_report()

Send a command to synchronize microphone gain level.

Source Code Overview

The following sections will describe the important source code of the HFP HF and HSP HS applications.

HFP/HSP SDP Record

  • HFP HF SDP local record and HSP HS SDP local record should be registered by bt_sdp_record_add.

    bt_sdp_record_add((void *)hfp_sdp_record);
    bt_sdp_record_add((void *)hsp_sdp_record);
    
  • For HFP HF, the 16-bit UUID 0x111E is reserved, meanwhile, for HSP HS, the 16-bit UUID 0x1108 is reserved.

    static const uint8_t hfp_sdp_record[] =
    {
        //total length
        SDP_DATA_ELEM_SEQ_HDR,
        0x37,//0x59,
    
        //attribute SDP_ATTR_SRV_CLASS_ID_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
        (uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x06,                                   //6bytes
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_HANDSFREE >> 8), //0x111E
        (uint8_t)(UUID_HANDSFREE),
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_GENERIC_AUDIO >> 8),  //0x1203
        (uint8_t)(UUID_GENERIC_AUDIO),
    
        //attribute SDP_ATTR_PROTO_DESC_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
        (uint8_t)SDP_ATTR_PROTO_DESC_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x0C,                                   //12bytes
        SDP_DATA_ELEM_SEQ_HDR,      //0x35
        0x03,                               //3bytes
        SDP_UUID16_HDR,                 //0x19
        (uint8_t)(UUID_L2CAP >> 8),     //0x0100
        (uint8_t)(UUID_L2CAP),
        SDP_DATA_ELEM_SEQ_HDR,      //0x35
        0x05,                               //5bytes
        SDP_UUID16_HDR,                 //0x19
        (uint8_t)(UUID_RFCOMM >> 8),   //0x0003
        (uint8_t)(UUID_RFCOMM),
        SDP_UNSIGNED_ONE_BYTE,           //0x08
        RFC_HFP_CHANN_NUM,  //0x02
    
        //attribute SDP_ATTR_PROFILE_DESC_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
        (uint8_t)SDP_ATTR_PROFILE_DESC_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x08,                                   //8 bytes
        SDP_DATA_ELEM_SEQ_HDR,      //0x35
        0x06,                               //6 bytes
        SDP_UUID16_HDR,                 //0x19
        (uint8_t)(UUID_HANDSFREE >> 8), //0x111E
        (uint8_t)(UUID_HANDSFREE),
        SDP_UNSIGNED_TWO_BYTE,           //0x09
        (uint8_t)(0x0107 >> 8),     //version number default hf1.7
        (uint8_t)(0x0107),
    
        //attribute SDP_ATTR_BROWSE_GROUP_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
        (uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
        SDP_DATA_ELEM_SEQ_HDR,
        0x03,
        SDP_UUID16_HDR,
        (uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
        (uint8_t)UUID_PUBLIC_BROWSE_GROUP,
        /*
            //attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST...it is used for SDP_ATTR_SRV_NAME
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
            (uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
            SDP_DATA_ELEM_SEQ_HDR,
            0x09,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_LANG_ENGLISH >> 8),
            (uint8_t)SDP_LANG_ENGLISH,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_CHARACTER_UTF8 >> 8),
            (uint8_t)SDP_CHARACTER_UTF8,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
            (uint8_t)SDP_BASE_LANG_OFFSET,
    
            //attribute SDP_ATTR_SRV_NAME
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
            (uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
            SDP_STRING_HDR,                             //0x25 text string
            0x0F,                                   //15 bytes
            0x48, 0x61, 0x6e, 0x64, 0x73, 0x2d, 0x66, 0x72, 0x65, 0x65,
            0x20, 0x75, 0x6e, 0x69, 0x74, //"Hands-free unit"
        */
        //attribute SDP_ATTR_SUPPORTED_FEATURES
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)((SDP_ATTR_SUPPORTED_FEATURES) >> 8),
        (uint8_t)(SDP_ATTR_SUPPORTED_FEATURES),
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(0x003F >> 8),
        (uint8_t)(0x003F)
    };
    static const uint8_t hsp_sdp_record[] =
    {
        //total length
        SDP_DATA_ELEM_SEQ_HDR,
        0x39,//0x58,
    
        //attribute SDP_ATTR_SRV_CLASS_ID_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_SRV_CLASS_ID_LIST >> 8),
        (uint8_t)SDP_ATTR_SRV_CLASS_ID_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x09,                                   //6bytes
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_HEADSET >> 8),       //0x1108
        (uint8_t)(UUID_HEADSET),
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_HEADSET_HS >> 8),    //0x1131
        (uint8_t)(UUID_HEADSET_HS),
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_GENERIC_AUDIO >> 8),  //0x1203
        (uint8_t)(UUID_GENERIC_AUDIO),
    
        //attribute SDP_ATTR_PROTO_DESC_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_PROTO_DESC_LIST >> 8),
        (uint8_t)SDP_ATTR_PROTO_DESC_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x0C,                                   //12 bytes
        SDP_DATA_ELEM_SEQ_HDR,      //0x35
        0x03,                               ///3 bytes
        SDP_UUID16_HDR,                 //0x19
        (uint8_t)(UUID_L2CAP >> 8),     //0x0100
        (uint8_t)(UUID_L2CAP),
        SDP_DATA_ELEM_SEQ_HDR,
        0x05,                               //5 bytes
        SDP_UUID16_HDR,                 //0x19
        (uint8_t)(UUID_RFCOMM >> 8),    //0x0003
        (uint8_t)(UUID_RFCOMM),
        SDP_UNSIGNED_ONE_BYTE,           //0x08
        RFC_HSP_CHANN_NUM,      //0x01
    
        //attribute SDP_ATTR_PROFILE_DESC_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_PROFILE_DESC_LIST >> 8),
        (uint8_t)SDP_ATTR_PROFILE_DESC_LIST,
        SDP_DATA_ELEM_SEQ_HDR,          //0x35
        0x08,                                   //8 bytes
        SDP_DATA_ELEM_SEQ_HDR,      //0x35
        0x06,                               //6 bytes
        SDP_UUID16_HDR,                     //0x19
        (uint8_t)(UUID_HEADSET >> 8),   //0x1108
        (uint8_t)(UUID_HEADSET),
        SDP_UNSIGNED_TWO_BYTE,               //0x09
        (uint8_t)(0x0102 >> 8),         //version number default hs1.2
        (uint8_t)(0x0102),
    
        //attribute SDP_ATTR_BROWSE_GROUP_LIST
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_BROWSE_GROUP_LIST >> 8),
        (uint8_t)SDP_ATTR_BROWSE_GROUP_LIST,
        SDP_DATA_ELEM_SEQ_HDR,
        0x03,
        SDP_UUID16_HDR,
        (uint8_t)(UUID_PUBLIC_BROWSE_GROUP >> 8),
        (uint8_t)UUID_PUBLIC_BROWSE_GROUP,
        /*
            //attribute SDP_ATTR_LANG_BASE_ATTR_ID_LIST...it is used for SDP_ATTR_SRV_NAME
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_ATTR_LANG_BASE_ATTR_ID_LIST >> 8),
            (uint8_t)SDP_ATTR_LANG_BASE_ATTR_ID_LIST,
            SDP_DATA_ELEM_SEQ_HDR,
            0x09,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_LANG_ENGLISH >> 8),
            (uint8_t)SDP_LANG_ENGLISH,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_CHARACTER_UTF8 >> 8),
            (uint8_t)SDP_CHARACTER_UTF8,
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)(SDP_BASE_LANG_OFFSET >> 8),
            (uint8_t)SDP_BASE_LANG_OFFSET,
    
            //attribute SDP_ATTR_SRV_NAME
            SDP_UNSIGNED_TWO_BYTE,
            (uint8_t)((SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET) >> 8),
            (uint8_t)(SDP_ATTR_SRV_NAME + SDP_BASE_LANG_OFFSET),
            SDP_STRING_HDR,                             //0x25 text string
            0x0C,                               //12 bytes
            0x48, 0x65, 0x61, 0x64, 0x73, 0x65, 0x74, 0x20, 0x75, 0x6e,
            0x69, 0x74, //"Headset unit"
        */
        //attribute SDP_ATTR_RMT_AUDIO_VOL_CTRL
        SDP_UNSIGNED_TWO_BYTE,
        (uint8_t)(SDP_ATTR_RMT_AUDIO_VOL_CTRL >> 8),
        (uint8_t)SDP_ATTR_RMT_AUDIO_VOL_CTRL,
        SDP_BOOL_ONE_BYTE,                       //0x28
        0x01                                    //True
    };
    

HFP/HSP Profile Initialization

  • HFP HF and HSP HS could be initialized as follows.

    void app_hfp_init(void)
    {
        if (app_cfg_const.supported_profile_mask & (HFP_PROFILE_MASK | HSP_PROFILE_MASK))
        {
            bt_hfp_init(app_cfg_const.hfp_link_number, RFC_HFP_CHANN_NUM,
                        RFC_HSP_CHANN_NUM, app_cfg_const.hfp_brsf_capability);
            bt_mgr_cback_register(app_hfp_bt_cback);
            gap_reg_timer_cb(app_hfp_timeout_cb, &hfp_timer_queue_id);
        }
    }
    

HFP/HSP Profile Callback Handler

  • For HFP HF and HSP HS, callback function is used to handle BT HFP HF and HSP HS related events.

    static void app_hfp_bt_cback(T_BT_EVENT event_type, void *event_buf, uint16_t buf_len)
    {
        T_BT_EVENT_PARAM *param = event_buf;
        bool handle = true;
    
        switch (event_type)
        {
        case BT_EVENT_HFP_CONN_IND:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_conn_ind.bd_addr);
                if (p_link == NULL)
                {
                    APP_PRINT_ERROR0("app_hfp_bt_cback: no acl link found");
                    return;
                }
                bt_hfp_connect_cfm(p_link->bd_addr, true);
            }
            break;
    
        case BT_EVENT_HFP_CONN_CMPL:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_conn_cmpl.bd_addr);
                if (p_link != NULL)
                {
                    uint8_t link_number;
                    uint8_t pair_idx;
    
                    p_link->call_id_type_check = true;
                    p_link->call_id_type_num = false;
    
                    bt_bond_index_get(p_link->bd_addr, &pair_idx);
                    bt_hfp_speaker_gain_level_report(p_link->bd_addr, app_cfg_nv.voice_gain_level[pair_idx]);
                    bt_hfp_microphone_gain_level_report(p_link->bd_addr, 0x0a);
    
                    if (app_src_status_hook == NULL)
                    {
                        app_hfp_batt_level_report(p_link->bd_addr);
                    }
    
                    link_number = app_connected_profile_link_num(HFP_PROFILE_MASK | HSP_PROFILE_MASK);
                    if (link_number == 1)
                    {
                        app_hfp_set_active_hf_index(p_link->bd_addr);
                        app_bond_set_priority(p_link->bd_addr);
                    }
                    if (app_db.br_link[app_db.first_hf_index].app_hf_state == APP_HF_STATE_STANDBY)
                    {
                        app_db.first_hf_index = p_link->id;
                    }
                    else
                    {
                        app_db.last_hf_index = p_link->id;
                    }
                    p_link->app_hf_state = APP_HF_STATE_CONNECTED;
                }
            }
            break;
    
        case BT_EVENT_HFP_CALL_STATUS:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_call_status.bd_addr);
                if (p_link != NULL)
                {
                    p_link->call_status = param->bt_hfp_call_status.curr_status;
    
                    if ((app_cfg_const.enable_auto_answer_incoming_call == 1) &&
                        (p_link->call_status == BT_HFP_INCOMING_CALL_ONGOING))
                    {
                        gap_start_timer(&hfp_auto_answer_call, "auto_answer_call",
                                        hfp_timer_queue_id, APP_HFP_TIMER_AUTO_ANSWER_CALL,
                                        p_link->id, app_cfg_const.timer_hfp_auto_answer_call);
                    }
    
                    if (p_link->call_status == BT_HFP_CALL_IDLE)
                    {
                        p_link->call_id_type_check = true;
                        p_link->call_id_type_num = false;
    
                        app_hfp_disallow_enter_sniff_mode(false);
                    }
    
                    app_hfp_update_call_status();
                }
    
                if (app_cfg_nv.bud_role == REMOTE_SESSION_ROLE_PRIMARY ||
                    app_cfg_nv.bud_role == REMOTE_SESSION_ROLE_SINGLE)
                {
                    if (param->bt_hfp_call_status.prev_status == BT_HFP_INCOMING_CALL_ONGOING &&
                        param->bt_hfp_call_status.curr_status == BT_HFP_CALL_IDLE)
                    {
                        if (app_db.reject_call_by_key)
                        {
                            app_db.reject_call_by_key = false;
                            app_audio_tone_play(app_cfg_const.tone_hf_call_reject, false, true);
                        }
                    }
    
                    if (param->bt_hfp_call_status.prev_status == BT_HFP_CALL_ACTIVE &&
                        param->bt_hfp_call_status.curr_status == BT_HFP_CALL_IDLE)
                    {
                        app_audio_tone_play(app_cfg_const.tone_hf_call_end, false, true);
                    }
    
                    if (param->bt_hfp_call_status.prev_status != BT_HFP_CALL_ACTIVE &&
                        param->bt_hfp_call_status.curr_status == BT_HFP_CALL_ACTIVE)
                    {
                        app_audio_tone_play(app_cfg_const.tone_hf_call_active, false, true);
                    }
    
                    if (param->bt_hfp_call_status.prev_status != BT_HFP_OUTGOING_CALL_ONGOING &&
                        param->bt_hfp_call_status.curr_status == BT_HFP_OUTGOING_CALL_ONGOING)
                    {
                        app_audio_tone_play(app_cfg_const.tone_hf_outgoing_call, false, true);
                    }
                }
    
                if (param->bt_hfp_call_status.curr_status == BT_HFP_CALL_ACTIVE ||
                    param->bt_hfp_call_status.curr_status == BT_HFP_INCOMING_CALL_ONGOING ||
                    param->bt_hfp_call_status.curr_status == BT_HFP_OUTGOING_CALL_ONGOING)
                {
                    if (app_cfg_const.timer_auto_power_off_while_phone_connected_and_anc_apt_off)
                    {
                        app_auto_power_off_disable(AUTO_POWER_OFF_MASK_SOURCE_LINK);
                    }
    
                    app_roleswap_state_machine(ROLESWAP_EVENT_STREAM_CALL_START, 0, 0);
                }
                else if (param->bt_hfp_call_status.curr_status == BT_HFP_CALL_IDLE)
                {
                    if (app_cfg_const.timer_auto_power_off_while_phone_connected_and_anc_apt_off)
                    {
                        app_auto_power_off_enable(AUTO_POWER_OFF_MASK_SOURCE_LINK,
                                                app_cfg_const.timer_auto_power_off_while_phone_connected_and_anc_apt_off);
                    }
    
                    app_roleswap_state_machine(ROLESWAP_EVENT_STREAM_CALL_STOP, 0, 0);
                }
            }
            break;
    
        case BT_EVENT_HFP_SERVICE_STATUS:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_service_status.bd_addr);
    
                if (p_link != NULL)
                {
                    p_link->service_status = param->bt_hfp_service_status.status;
                    app_hfp_check_service_status();
                }
            }
            break;
    
        case BT_EVENT_HFP_CALL_WAITING_IND:
        case BT_EVENT_HFP_CALLER_ID_IND:
            {
                if (app_cfg_nv.bud_role == REMOTE_SESSION_ROLE_SINGLE)
                {
                    T_APP_BR_LINK *br_link;
                    T_APP_LE_LINK *le_link;
                    char *number;
    
                    if (event_type == BT_EVENT_HFP_CALLER_ID_IND)
                    {
                        br_link = app_find_br_link(param->bt_hfp_caller_id_ind.bd_addr);
                        number = (char *)param->bt_hfp_caller_id_ind.number;
                    }
                    else
                    {
                        br_link = app_find_br_link(param->bt_hfp_call_waiting_ind.bd_addr);
                        number = (char *)param->bt_hfp_call_waiting_ind.number;
                    }
    
                    le_link = app_find_le_link_by_addr(param->bt_pbap_caller_id_name.bd_addr);
    
                    if (br_link != NULL)
                    {
                        /* Sanity check if BR/EDR TTS session is ongoing */
                        if (br_link->cmd_set_enable == true &&
                            br_link->tts_handle != NULL)
                        {
                            break;
                        }
    
                        /* Sanity check if BLE TTS session is ongoing */
                        if (le_link != NULL &&
                            le_link->cmd_set_enable == true &&
                            le_link->tts_handle != NULL)
                        {
                            break;
                        }
    
                        if (br_link->call_id_type_check == true)
                        {
                            if (br_link->connected_profile & PBAP_PROFILE_MASK)
                            {
                                if (bt_pbap_vcard_listing_by_number_pull(br_link->bd_addr, number) == false)
                                {
                                    br_link->call_id_type_check = false;
                                    br_link->call_id_type_num = true;
                                }
                            }
                            else
                            {
                                br_link->call_id_type_check = false;
                                br_link->call_id_type_num = true;
                            }
                        }
    
                        if (br_link->call_id_type_check == false)
                        {
                            if (br_link->call_id_type_num == true)
                            {
                                uint8_t *data;
                                uint16_t len;
    
                                len = param->bt_pbap_caller_id_name.name_len + 3;
                                data = os_mem_alloc(RAM_TYPE_DATA_ON, len);
                                if (data != NULL)
                                {
                                    data[1] = CALLER_ID_TYPE_NUMBER;
                                    data[2] = param->bt_pbap_caller_id_name.name_len;
                                    memcpy(data + 3, param->bt_pbap_caller_id_name.name_ptr, param->bt_pbap_caller_id_name.name_len);
    
                                    if (br_link->connected_profile & (SPP_PROFILE_MASK | IAP_PROFILE_MASK))
                                    {
                                        data[0] = br_link->id;
    
                                        app_report_event(CMD_PATH_SPP, EVENT_CALLER_ID, br_link->id, data, len);
                                    }
                                    else if (le_link != NULL)
                                    {
                                        data[0] = le_link->id;
    
                                        app_report_event(CMD_PATH_LE, EVENT_CALLER_ID, le_link->id, data, len);
                                    }
    
                                    os_mem_free(data);
                                }
                            }
                        }
                    }
                }
            }
            break;
    
        case BT_EVENT_HFP_RING_ALERT:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_ring_alert.bd_addr);
                if (p_link != NULL)
                {
                    p_link->is_inband_ring = param->bt_hfp_ring_alert.is_inband;
    
                    if ((app_cfg_const.always_play_hf_incoming_tone_when_incoming_call == false) &&
                        ((p_link->is_inband_ring == false) ||
                        (p_link->id != active_hf_index))) /* TODO check active sco link */
                    {
                        if (hf_ring_active == false && p_link->call_status == BT_HFP_INCOMING_CALL_ONGOING)
                        {
                            hf_ring_active = true;
    
                            app_hfp_ring_alert(p_link);
                        }
                    }
                }
            }
            break;
    
        case BT_EVENT_HFP_SPK_VOLUME_CHANGED:
            {
                T_APP_BR_LINK *p_link;
                p_link = app_find_br_link(param->bt_hfp_spk_volume_changed.bd_addr);
                if (p_link != NULL)
                {
                    if (app_src_profile_hook)
                    {
                        if ((app_hfp_get_call_status() == BT_HFP_INCOMING_CALL_ONGOING) && app_src_profile_hook())
                        {
                            break;
                        }
                    }
                    if (app_db.remote_session_state == REMOTE_SESSION_STATE_DISCONNECTED)
                    {
                        uint8_t pair_idx;
    
                        bt_bond_index_get(p_link->bd_addr, &pair_idx);
    
                        app_cfg_nv.voice_gain_level[pair_idx] = (param->bt_hfp_spk_volume_changed.volume *
                                                                app_cfg_const.voice_out_volume_max +
                                                                0x0f / 2) / 0x0f;
    
                        if (app_audio_vol_set_hook)
                        {
                            app_audio_vol_set_hook(p_link->sco_track_handle, app_cfg_nv.voice_gain_level[pair_idx]);
                        }
                        else
                        {
                            if (app_cfg_const.enable_rtk_charging_box)
                            {
                                if (app_db.local_in_case)
                                {
                                    app_adp_volume_mute();
                                }
                                else
                                {
                                    audio_track_volume_set(p_link->sco_track_handle, app_cfg_nv.voice_gain_level[pair_idx]);
                                    if (app_db.voice_muted)
                                    {
                                        app_db.voice_muted = false;
                                    }
                                }
                            }
                            else
                            {
                                audio_track_volume_set(p_link->sco_track_handle, app_cfg_nv.voice_gain_level[pair_idx]);
                                if (app_db.voice_muted)
                                {
                                    app_db.voice_muted = false;
                                }
                            }
                        }
                    }
                    else
                    {
                        uint8_t cmd_ptr[8];
                        uint8_t pair_idx;
    
                        bt_bond_index_get(p_link->bd_addr, &pair_idx);
                        app_cfg_nv.voice_gain_level[pair_idx] = (param->bt_hfp_spk_volume_changed.volume *
                                                                app_cfg_const.voice_out_volume_max +
                                                                0x0f / 2) / 0x0f;
                        memcpy(&cmd_ptr[0], p_link->bd_addr, 6);
                        cmd_ptr[6] = AUDIO_STREAM_TYPE_VOICE;
                        cmd_ptr[7] = app_cfg_nv.voice_gain_level[pair_idx];
                        remote_sync_msg_relay(app_db.relay_handle, APP_REMOTE_MSG_HFP_VOLUME_SYNC, cmd_ptr, 8,
                                            REMOTE_TIMER_HIGH_PRECISION, 0, false);
                    }
                }
            }
            break;
    
        case BT_EVENT_HFP_MIC_VOLUME_CHANGED:
            {
    
            }
            break;
    
        case BT_EVENT_HFP_DISCONN_CMPL:
            {
                T_APP_BR_LINK *p_link;
    
                p_link = app_find_br_link(param->bt_hfp_disconn_cmpl.bd_addr);
                if (p_link != NULL)
                {
                    if (param->bt_hfp_disconn_cmpl.cause == (HCI_ERR | HCI_ERR_CONN_ROLESWAP))
                    {
                        //do nothing
                    }
                    else
                    {
                        uint8_t i;
    
                        p_link->call_status = BT_HFP_CALL_IDLE;
                        p_link->app_hf_state = APP_HF_STATE_STANDBY;
                        if (app_db.first_hf_index == p_link->id)
                        {
                            app_db.first_hf_index = app_db.last_hf_index;
                        }
    
                        for (i = 0; i < MAX_BR_LINK_NUM; i++)
                        {
                            if (app_db.br_link[i].connected_profile & (HFP_PROFILE_MASK | HSP_PROFILE_MASK))
                            {
                                app_hfp_set_active_hf_index(app_db.br_link[i].bd_addr);
                                app_bond_set_priority(app_db.br_link[i].bd_addr);
                                break;
                            }
                        }
    
                        if (app_hfp_get_call_status() != BT_HFP_CALL_IDLE)
                        {
                            app_hfp_update_call_status();
                        }
                    }
    
                    if ((param->bt_hfp_disconn_cmpl.cause & ~HCI_ERR) != HCI_ERR_CONN_ROLESWAP)
                    {
                        app_hfp_check_service_status();
                    }
                }
            }
            break;
        ......
    
        default:
            handle = false;
            break;
        }
    
        if (handle == true)
        {
            APP_PRINT_INFO1("app_hfp_bt_cback: event_type 0x%04x", event_type);
        }
    }
    

Sample Projects

SDK provides a corresponding demo application for developers’ reference in development. The BR/EDR Audio gives a simple example of how to use HFP.