Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit adaf3732 authored by Mudumba Ananth's avatar Mudumba Ananth Committed by Andre Eisenbach
Browse files

HFP 1.7 profile update (1/4)

-> Added AG support for HFP 1.7 version upgrade which
   adds new type of indicator called HF(headset) indicators.
-> Added support for two new AT commands AT + BIND and AT + BIEV
   which have been appended to Hands-Free SLC sequence.
-> Added support to propagate the above commands and their data to
   the upper layers.

Bug: 19983867
Change-Id: I93d5b2af949f9fb99507a954e623cd0927ddc976
parent e94429ca
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -432,6 +432,9 @@ void bta_ag_rfc_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
    p_scb->hsp_version = HSP_VERSION_1_2;
    bta_ag_at_reinit(&p_scb->at_cb);

    memset(&(p_scb->peer_hf_indicators), 0, sizeof(p_scb->peer_hf_indicators));
    memset(&(p_scb->local_hf_indicators), 0, sizeof(p_scb->local_hf_indicators));

    /* stop timers */
    alarm_cancel(p_scb->ring_timer);
#if (BTM_WBS_INCLUDED == TRUE)
+15 −0
Original line number Diff line number Diff line
@@ -52,9 +52,24 @@
                                                                 BTM_SCO_PKT_TYPES_MASK_NO_3_EV5)
#endif

#ifndef BTA_AG_BIND_INFO
#define BTA_AG_BIND_INFO "(1)"
#endif

const tBTA_AG_HF_IND bta_ag_local_hf_ind_cfg[] =
{
    /* The first row contains the number of indicators. Need to be updated accordingly */
    {BTA_AG_NUM_LOCAL_HF_IND,   0,  0,  0,  0},

    {1,    1,   1,   0,    1},     /* Enhanced Driver Status, supported, enabled, range 0 ~ 1 */
    {2,    1,   1,   0,    100}    /* Battery Level Status, supported, enabled, range 0 ~ 100 */
};

const tBTA_AG_CFG bta_ag_cfg =
{
    BTA_AG_CIND_INFO,
    BTA_AG_BIND_INFO,
    BTA_AG_NUM_LOCAL_HF_IND,
    BTA_AG_CONN_TIMEOUT,
    BTA_AG_SCO_PKT_TYPES,
    BTA_AG_CHLD_VAL_ECC,
+309 −5
Original line number Diff line number Diff line
@@ -111,6 +111,8 @@ enum
    BTA_AG_HF_CMD_CBC,
    BTA_AG_HF_CMD_BCC,
    BTA_AG_HF_CMD_BCS,
    BTA_AG_HF_CMD_BIND,
    BTA_AG_HF_CMD_BIEV,
    BTA_AG_HF_CMD_BAC
};

@@ -152,6 +154,8 @@ const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] =
    {"+CBC",    BTA_AG_AT_SET,                      BTA_AG_AT_INT,   0,   100},
    {"+BCC",    BTA_AG_AT_NONE,                     BTA_AG_AT_STR,   0,   0},
    {"+BCS",    BTA_AG_AT_SET,                      BTA_AG_AT_INT,   0,   BTA_AG_CMD_MAX_VAL},
    {"+BIND",   BTA_AG_AT_SET | BTA_AG_AT_READ | BTA_AG_AT_TEST , BTA_AG_AT_STR,   0,   0},
    {"+BIEV",   BTA_AG_AT_SET,                      BTA_AG_AT_STR,   0,   0},
    {"+BAC",    BTA_AG_AT_SET,                      BTA_AG_AT_STR,   0,   0},
    {"",        BTA_AG_AT_NONE,                     BTA_AG_AT_STR,   0,   0}
};
@@ -194,6 +198,7 @@ enum
    BTA_AG_RES_COPS,
    BTA_AG_RES_CMEE,
    BTA_AG_RES_BCS,
    BTA_AG_RES_BIND,
    BTA_AG_RES_UNAT
};

@@ -223,6 +228,7 @@ const tBTA_AG_RESULT bta_ag_result_tbl[] =
    {"+COPS: ", BTA_AG_RES_FMT_STR},
    {"+CME ERROR: ", BTA_AG_RES_FMT_INT},
    {"+BCS: ",  BTA_AG_RES_FMT_INT},
    {"+BIND: ", BTA_AG_RES_FMT_STR},
    {"",        BTA_AG_RES_FMT_STR}
};

@@ -268,6 +274,8 @@ const tBTA_AG_EVT bta_ag_hfp_cb_evt[] =
    BTA_AG_AT_CBC_EVT,      /* BTA_AG_HF_CMD_CBC */
    0,                      /* BTA_AG_HF_CMD_BCC */
    BTA_AG_AT_BCS_EVT,      /* BTA_AG_HF_CMD_BCS */
    BTA_AG_AT_BIND_EVT,     /* BTA_AG_HF_CMD_BIND */
    BTA_AG_AT_BIEV_EVT,     /* BTA_AG_HF_CMD_BIEV */
    BTA_AG_AT_BAC_EVT       /* BTA_AG_HF_CMD_BAC */
};

@@ -294,6 +302,7 @@ const UINT8 bta_ag_trans_result[] =
    0,                  /* BTA_AG_CALL_CANCEL_RES */
    0,                  /* BTA_AG_END_CALL_RES */
    0,                  /* BTA_AG_IN_CALL_HELD_RES */
    BTA_AG_RES_BIND,    /* BTA_AG_BIND_RES */
    BTA_AG_RES_UNAT     /* BTA_AG_UNAT_RES */
};

@@ -319,7 +328,8 @@ const UINT8 bta_ag_callsetup_ind_tbl[] =
    BTA_AG_CALLSETUP_NONE,      /* BTA_AG_OUT_CALL_CONN_RES */
    BTA_AG_CALLSETUP_NONE,      /* BTA_AG_CALL_CANCEL_RES */
    BTA_AG_CALLSETUP_NONE,      /* BTA_AG_END_CALL_RES */
    BTA_AG_CALLSETUP_NONE       /* BTA_AG_IN_CALL_HELD_RES */
    BTA_AG_CALLSETUP_NONE,      /* BTA_AG_IN_CALL_HELD_RES */
    0                           /* BTA_AG_BIND_RES */
};

/*******************************************************************************
@@ -856,6 +866,222 @@ void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
    (*bta_ag_cb.p_cback)(bta_ag_hsp_cb_evt[cmd], (tBTA_AG *) &val);
}

/*******************************************************************************
**
** Function         bta_ag_find_empty_hf_ind)
**
** Description      This function returns the index of an empty HF indicator
**                  structure.
**
** Returns          int : index of the empty HF indicator structure or
**                            -1 if no empty indicator
**                            is available.
**
*******************************************************************************/
static int bta_ag_find_empty_hf_ind(tBTA_AG_SCB *p_scb)
{
    for (int index = 0; index < BTA_AG_MAX_NUM_PEER_HF_IND; index++)
    {
        if (p_scb->peer_hf_indicators[index].ind_id == 0)
            return index;
    }

    return -1;
}


/*******************************************************************************
**
** Function         bta_ag_find_hf_ind_by_id
**
** Description      This function returns the index of the HF indicator
**                  structure by the indicator id
**
** Returns          int : index of the HF indicator structure
**                            -1 if the indicator
**                            was not found.
**
*******************************************************************************/
static int bta_ag_find_hf_ind_by_id(tBTA_AG_HF_IND *p_hf_ind, int size, uint32_t ind_id)
{
    for (int index = 0; index < size; index++)
    {
        if (p_hf_ind[index].ind_id == ind_id)
            return index;
    }

    return -1;
}

/*******************************************************************************
**
** Function         bta_ag_parse_bind_set
**
** Description      Parse AT+BIND set command and save the indicators
**
** Returns          true if successful
**
*******************************************************************************/
static bool bta_ag_parse_bind_set(tBTA_AG_SCB *p_scb, tBTA_AG_VAL val)
{
    char *p_token = strtok(val.str, ",");
    if (p_token == NULL)
        return false;

    while (p_token != NULL)
    {
        uint16_t rcv_ind_id = atoi(p_token);
        int index = bta_ag_find_empty_hf_ind(p_scb);
        if (index == -1)
        {
            APPL_TRACE_WARNING("%s Can't save more indicators", __func__);
            return false;
        }

        p_scb->peer_hf_indicators[index].ind_id = rcv_ind_id;
        APPL_TRACE_DEBUG("%s peer_hf_ind[%d] = %d", __func__, index, rcv_ind_id);

        p_token = strtok(NULL, ",");
    }

    return true;
}

/*******************************************************************************
**
** Function         bta_ag_bind_response
**
** Description      Send response for the AT+BIND command (HFP 1.7) received
**                  from the headset based on the argument types.
**
** Returns          Void
**
*******************************************************************************/
static void bta_ag_bind_response(tBTA_AG_SCB *p_scb, uint8_t arg_type)
{
    char buffer[BTA_AG_AT_MAX_LEN];
    memset(buffer, 0, BTA_AG_AT_MAX_LEN);

    if (arg_type == BTA_AG_AT_TEST)
    {
        int index = 0;
        buffer[index++] = '(';

        for (uint32_t i = 0; i < bta_ag_local_hf_ind_cfg[0].ind_id; i++)
        {
            if (bta_ag_local_hf_ind_cfg[i+1].is_supported == true)
            {
                /* Add ',' from second indicator */
                if (index > 1)
                    buffer[index++] = ',';
                sprintf(&buffer[index++], "%d", bta_ag_local_hf_ind_cfg[i+1].ind_id);
            }
        }

        buffer[index++] = ')';

        bta_ag_send_result(p_scb, BTA_AG_RES_BIND, buffer, 0);
        bta_ag_send_ok(p_scb);
    }
    else if (arg_type == BTA_AG_AT_READ)
    {
        char *p = buffer;

        /* bta_ag_local_hf_ind_cfg[0].ind_id is used as BTA_AG_NUM_LOCAL_HF_IND */
        for (uint32_t i = 0; i < bta_ag_local_hf_ind_cfg[0].ind_id; i++)
        {
            if (i == BTA_AG_MAX_NUM_LOCAL_HF_IND)
            {
                APPL_TRACE_WARNING("%s No space for more HF indicators", __func__);
                break;
            }

            p_scb->local_hf_indicators[i].ind_id = bta_ag_local_hf_ind_cfg[i+1].ind_id;
            p_scb->local_hf_indicators[i].is_supported = bta_ag_local_hf_ind_cfg[i+1].is_supported;
            p_scb->local_hf_indicators[i].is_enable = bta_ag_local_hf_ind_cfg[i+1].is_enable;

            int peer_index = bta_ag_find_hf_ind_by_id(p_scb->peer_hf_indicators,
                                                    BTA_AG_MAX_NUM_PEER_HF_IND,
                                                    p_scb->local_hf_indicators[i].ind_id);

            /* Check whether local and peer sides support this indicator */
            if (p_scb->local_hf_indicators[i].is_supported == true && peer_index != -1)
            {
                /* In the format of ind, state */
                p += utl_itoa((uint16_t) p_scb->local_hf_indicators[i].ind_id, p);
                *p++ = ',';
                p += utl_itoa((uint16_t) p_scb->local_hf_indicators[i].is_enable, p);

                bta_ag_send_result(p_scb, BTA_AG_RES_BIND, buffer, 0);

                memset(buffer, 0, sizeof(buffer));
                p = buffer;
            } else {
                /* If indicator is not supported, also set it to disable */
                p_scb->local_hf_indicators[i].is_enable = false;
            }
        }

        bta_ag_send_ok(p_scb);

        /* If the service level connection wan't already open, now it's open */
        if (!p_scb->svc_conn)
            bta_ag_svc_conn_open(p_scb, NULL);
    }
}

/*******************************************************************************
**
** Function         bta_ag_parse_biev_response
**
** Description      Send response for AT+BIEV command (HFP 1.7) received from
**                  the headset based on the argument types.
**
** Returns          true if the response was parsed successfully
**
*******************************************************************************/
static bool bta_ag_parse_biev_response(tBTA_AG_SCB *p_scb, tBTA_AG_VAL *val)
{
    char *p_token = strtok(val->str, ",");
    uint16_t rcv_ind_id = atoi(p_token);

    p_token = strtok(NULL, ",");
    uint16_t rcv_ind_val = atoi(p_token);

    APPL_TRACE_DEBUG("%s BIEV indicator id %d, value %d", __func__, rcv_ind_id, rcv_ind_val);

    /* Check whether indicator ID is valid or not */
    if (rcv_ind_id > BTA_AG_NUM_LOCAL_HF_IND)
    {
        APPL_TRACE_WARNING("%s received invalid indicator id %d", __func__, rcv_ind_id);
        return false;
    }

    /* Check this indicator is support or not and enabled or not */
    int local_index = bta_ag_find_hf_ind_by_id(p_scb->local_hf_indicators,
                                BTA_AG_MAX_NUM_LOCAL_HF_IND, rcv_ind_id);
    if (local_index == -1 ||
        p_scb->local_hf_indicators[local_index].is_supported != true ||
        p_scb->local_hf_indicators[local_index].is_enable != true)
    {
        APPL_TRACE_WARNING("%s indicator id %d not supported or disabled", __func__, rcv_ind_id);
        return false;
    }

    /* For each indicator ID, check whether the indicator value is in range */
    if (rcv_ind_val < bta_ag_local_hf_ind_cfg[rcv_ind_id].ind_min_val ||
        rcv_ind_val > bta_ag_local_hf_ind_cfg[rcv_ind_id].ind_max_val)
    {
        APPL_TRACE_WARNING("%s invalid ind_val %d", __func__, rcv_ind_val);
        return false;
    }

    val->lidx = rcv_ind_id;
    val->num = rcv_ind_val;

    return true;
}

/*******************************************************************************
**
** Function         bta_ag_at_hfp_cback
@@ -889,6 +1115,7 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,

    val.hdr.handle = bta_ag_scb_to_idx(p_scb);
    val.hdr.app_id = p_scb->app_id;
    val.hdr.status = BTA_AG_SUCCESS;
    val.num = int_arg;
    bdcpy(val.bd_addr, p_scb->peer_addr);
    strlcpy(val.str, p_arg, BTA_AG_AT_MAX_LEN);
@@ -1017,6 +1244,37 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
            }
            break;

        case BTA_AG_HF_CMD_BIND:
            APPL_TRACE_DEBUG("%s BTA_AG_HF_CMD_BIND arg_type: %d", __func__, arg_type);
            if (arg_type == BTA_AG_AT_SET)
            {
                if (bta_ag_parse_bind_set(p_scb, val))
                {
                    bta_ag_send_ok(p_scb);
                } else {
                    event = 0;/* don't call callback */
                    bta_ag_send_error(p_scb, BTA_AG_ERR_INVALID_INDEX);
                }
            } else {
                bta_ag_bind_response(p_scb, arg_type);

                /* Need not pass this command beyond BTIF.*/
                /* Stack handles it internally */
                event = 0;/* don't call callback */
            }
            break;

        case BTA_AG_HF_CMD_BIEV:
            if (bta_ag_parse_biev_response(p_scb, &val))
            {
                bta_ag_send_ok(p_scb);
            } else {
                bta_ag_send_error(p_scb, BTA_AG_ERR_INVALID_INDEX);
                /* don't call callback receiving invalid indicator */
                event = 0;
            }
            break;

        case BTA_AG_HF_CMD_CIND:
            if (arg_type == BTA_AG_AT_TEST)
            {
@@ -1089,7 +1347,12 @@ void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,

        case BTA_AG_HF_CMD_BRSF:
            /* store peer features */
            p_scb->peer_features = (UINT16) int_arg;
            p_scb->peer_features = (uint16_t) int_arg;
#if (BTA_HFP_VERSION < HFP_VERSION_1_7 || BTA_HFP_HF_IND_SUPPORTED != true)
            p_scb->features &= ~BTA_AG_FEAT_HF_IND;
#endif
            APPL_TRACE_DEBUG("%s BRSF HF: 0x%x, phone: 0x%x", __func__,
                p_scb->peer_features, p_scb->features);

            /* send BRSF, send OK */
            bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL,
@@ -1317,6 +1580,7 @@ void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg)
    {
        val.hdr.handle = bta_ag_scb_to_idx(p_scb);
        val.hdr.app_id = p_scb->app_id;
        val.hdr.status = BTA_AG_SUCCESS;
        val.num = 0;
        strlcpy(val.str, p_arg, BTA_AG_AT_MAX_LEN);
        (*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG *) &val);
@@ -1452,7 +1716,6 @@ void bta_ag_hsp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result)
void bta_ag_hfp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result)
{
    UINT8 code = bta_ag_trans_result[p_result->result];

    APPL_TRACE_DEBUG("bta_ag_hfp_result : res = %d", p_result->result);

    switch(p_result->result)
@@ -1709,12 +1972,53 @@ void bta_ag_hfp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result)
            }
            break;

        case BTA_AG_BIND_RES:
        {
            /* Find whether ind_id is supported by local device or not */
            int local_index = bta_ag_find_hf_ind_by_id(p_scb->local_hf_indicators,
                                      BTA_AG_MAX_NUM_LOCAL_HF_IND, p_result->data.ind.id);
            if (local_index == -1)
            {
                APPL_TRACE_WARNING("%s Invalid HF Indicator ID %d", __func__,
                    p_result->data.ind.id);
                return;
            }

            /* Find whether ind_id is supported by peer device or not */
            int peer_index = bta_ag_find_hf_ind_by_id(p_scb->peer_hf_indicators,
                                      BTA_AG_MAX_NUM_PEER_HF_IND, p_result->data.ind.id);
            if (peer_index == -1)
            {
                APPL_TRACE_WARNING("%s Invalid HF Indicator ID %d", __func__,
                    p_result->data.ind.id);
                return;
            } else {
                /* If the current state is different from the one upper layer request
                   change current state and send out the result */
                if (p_scb->local_hf_indicators[local_index].is_enable != p_result->data.ind.on_demand)
                {
                    char buffer[BTA_AG_AT_MAX_LEN] = {0};
                    char *p = buffer;

                    p_scb->local_hf_indicators[local_index].is_enable = p_result->data.ind.on_demand;
                    p += utl_itoa(p_result->data.ind.id, p);
                    *p++ = ',';
                    p += utl_itoa(p_scb->local_hf_indicators[local_index].is_enable, p);

                    bta_ag_send_result(p_scb, code, buffer, 0);
                } else {
                    APPL_TRACE_DEBUG("%s HF Indicator %d already %s", p_result->data.ind.id,
                        (p_result->data.ind.on_demand == true) ? "Enabled" : "Disabled");
                }
            }
            break;
        }

        default:
            break;
    }
}


/*******************************************************************************
**
** Function         bta_ag_result
+10 −7
Original line number Diff line number Diff line
@@ -42,13 +42,6 @@
/*****************************************************************************
**  Constants
*****************************************************************************/
#define HFP_VERSION_1_1         0x0101
#define HFP_VERSION_1_5         0x0105
#define HFP_VERSION_1_6         0x0106

#define HSP_VERSION_1_0         0x0100
#define HSP_VERSION_1_2         0x0102

/* Number of SCBs (AG service instances that can be registered) */
#ifndef BTA_AG_NUM_SCB
#define BTA_AG_NUM_SCB          2
@@ -62,6 +55,10 @@
/* RFCOMM MTU SIZE */
#define BTA_AG_MTU              256

/* Max number of peer and local HF indicators */
#define BTA_AG_MAX_NUM_PEER_HF_IND     20
#define BTA_AG_MAX_NUM_LOCAL_HF_IND    4

/* Internal profile indexes */
#define BTA_AG_HSP              0       /* index for HSP */
#define BTA_AG_HFP              1       /* index for HFP */
@@ -77,6 +74,7 @@
                                      BTA_AG_FEAT_VTAG | BTA_AG_FEAT_REJECT  | \
                                      BTA_AG_FEAT_ECS  | BTA_AG_FEAT_ECC     | \
                                      BTA_AG_FEAT_EXTERR | BTA_AG_FEAT_CODEC | \
                                      BTA_AG_FEAT_HF_IND | BTA_AG_FEAT_ESCO  | \
                                      BTA_AG_FEAT_VOIP)

#define BTA_AG_SDP_FEAT_SPEC         (BTA_AG_FEAT_3WAY | BTA_AG_FEAT_ECNR    | \
@@ -298,6 +296,10 @@ typedef struct
    tBTA_AG_SCO_MSBC_SETTINGS codec_msbc_settings; /* settings to be used for the impending eSCO */
#endif

    tBTA_AG_HF_IND      peer_hf_indicators[BTA_AG_MAX_NUM_PEER_HF_IND];   /* Peer supported
                                                                      HF indicators */
    tBTA_AG_HF_IND      local_hf_indicators[BTA_AG_MAX_NUM_LOCAL_HF_IND]; /* Local supported
                                                                      HF indicators */
} tBTA_AG_SCB;

/* type for sco data */
@@ -342,6 +344,7 @@ extern tBTA_AG_CB *bta_ag_cb_ptr;

/* config struct */
extern tBTA_AG_CFG *p_bta_ag_cfg;
extern tBTA_AG_HF_IND bta_ag_local_hf_ind_cfg[];

/*****************************************************************************
**  Function prototypes
+1 −1
Original line number Diff line number Diff line
@@ -159,7 +159,7 @@ BOOLEAN bta_ag_add_record(UINT16 service_uuid, char *p_service_name, UINT8 scn,
    if (service_uuid == UUID_SERVCLASS_AG_HANDSFREE)
    {
        profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
        version = HFP_VERSION_1_6;
        version = BTA_HFP_VERSION;
    }
    else
    {
Loading