HFP

The purpose of this document is to provide an overview of the HFP and HSP 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 (HF) - This is the device acting as the Audio Gateway’s remote audio input and output mechanism. It also provides some remote control means.

../../../../../_images/hfp_roles.jpg

HFP Role

  • 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. The Headset Profile is also dependent upon both the RFCOMM and the Generic Access Profile.

../../../../../_images/hfp_profile_stack.jpg

HFP Profile Stack

  • 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 command are the entities responsible for headset specific control signaling, which is 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 SIG assigned numbers.

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

HFP/HSP Features

  • In the initialization procedure, the HF shall send command to the AG to notify the AG of the supported features in the HF, as well as to retrieve the supported features in the AG using the result code.

  • Framework supports codec negotiation and CVSD, mSBC codec types.

  • Meanwhile, HFP HF features should be registered in HFP HF SDP record hfp_sdp_record as well, shown in HFP specification.

  • HSP HS features should be registered in HSP HS SDP record hsp_sdp_record as well, shown in HSP specification.

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 API 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.

HFP API

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

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

Initiate HFP connection to a remote device, as shown below.
../../../../../_images/hfp_connect_request.PNG

HFP Connect Request

Request HFP disconnection, as shown below.
../../../../../_images/hfp_disconnect_request.PNG

HFP Disconnect Request

Accept or reject the incoming HFP connection from the remote device, as shown below.
../../../../../_images/hfp_connect_confirm.PNG

HFP Connect Confirm

HFP Audio Connect

Send a request to create audio connection.
../../../../../_images/hfp_audio_connect_req.PNG

HFP Audio Connect Request

Send a request to disconnect audio connection.
../../../../../_images/hfp_audio_disconnect_req.PNG

HFP Audio Disconnect Request

HFP Call Control

Send HFP vendor command, as shown below.
../../../../../_images/hfp_send_vnd_at_cmd_req.PNG

HFP Send Vendor Command Request

Send a command to dial a specific phone number, as shown below.
../../../../../_images/hfp_dial_with_number_req.PNG

HFP Dial with Number Request

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

HFP Dial with Last Number Request

Send a command to answer the incoming call.
../../../../../_images/hfp_call_answer_req.PNG

HFP Call Answer Request

Send a command to terminate the phone call.
../../../../../_images/hfp_call_terminate_req.PNG

HFP Call Terminate Request

Send a command to query current call list.
Send a command to release held or waiting call.
../../../../../_images/hfp_release_held_or_waiting_call_req.PNG

HFP Release Held or Waiting Call Request

Send a command to release active call and accept held or waiting call.
Send a command to hold active call and accept held or waiting call.
Send a command to join two calls.
Send a specific DTMF code to its network connection.
Send a command to query the current network operator name.

HFP Voice_Recognition Control

Send a command to enable voice recognition.
../../../../../_images/hfp_voice_recognition_enable_req.PNG

HFP Voice Recognition Enable

Send a command to disable voice recognition.
../../../../../_images/hfp_voice_recognition_disable_req.PNG

HFP Voice Recognition Disable

HFP Audio Level Control

Send a command to synchronize speaker gain level.
../../../../../_images/hfp_speaker_gain_level_report.PNG

HFP Speaker Gain Level Report

Send a command to synchronize microphone gain level.
../../../../../_images/hfp_microphone_gain_level_report.PNG

HFP Microphone Gain Level Report

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 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 in development. The BR/EDR Audio gives a simple example of how to use HFP.