普通文本  |  6110行  |  227.47 KB

/******************************************************************************
 *
 *  Copyright (C) 1999-2012 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  This file contains functions for the Bluetooth Security Manager
 *
 ******************************************************************************/

#define LOG_TAG "bt_btm_sec"

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#include "device/include/controller.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"
#include "osi/include/time.h"

#include "bt_types.h"
#include "bt_utils.h"
#include "btm_int.h"
#include "btu.h"
#include "hcimsgs.h"
#include "l2c_int.h"

#include "gatt_int.h"

#define BTM_SEC_MAX_COLLISION_DELAY (5000)

extern fixed_queue_t* btu_general_alarm_queue;

#ifdef APPL_AUTH_WRITE_EXCEPTION
bool(APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR bd_addr);
#endif

/*******************************************************************************
 *             L O C A L    F U N C T I O N     P R O T O T Y P E S            *
 ******************************************************************************/
tBTM_SEC_SERV_REC* btm_sec_find_first_serv(bool is_originator, uint16_t psm);
static tBTM_SEC_SERV_REC* btm_sec_find_next_serv(tBTM_SEC_SERV_REC* p_cur);
static tBTM_SEC_SERV_REC* btm_sec_find_mx_serv(uint8_t is_originator,
                                               uint16_t psm,
                                               uint32_t mx_proto_id,
                                               uint32_t mx_chan_id);

static tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec);
static void btm_sec_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec);
static void btm_sec_start_encryption(tBTM_SEC_DEV_REC* p_dev_rec);
static void btm_sec_collision_timeout(void* data);
static void btm_restore_mode(void);
static void btm_sec_pairing_timeout(void* data);
static tBTM_STATUS btm_sec_dd_create_conn(tBTM_SEC_DEV_REC* p_dev_rec);
static void btm_sec_change_pairing_state(tBTM_PAIRING_STATE new_state);

static const char* btm_pair_state_descr(tBTM_PAIRING_STATE state);

static void btm_sec_check_pending_reqs(void);
static bool btm_sec_queue_mx_request(BD_ADDR bd_addr, uint16_t psm,
                                     bool is_orig, uint32_t mx_proto_id,
                                     uint32_t mx_chan_id,
                                     tBTM_SEC_CALLBACK* p_callback,
                                     void* p_ref_data);
static void btm_sec_bond_cancel_complete(void);
static void btm_send_link_key_notif(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_sec_check_prefetch_pin(tBTM_SEC_DEV_REC* p_dev_rec);

static uint8_t btm_sec_start_authorization(tBTM_SEC_DEV_REC* p_dev_rec);
bool btm_sec_are_all_trusted(uint32_t p_mask[]);

static tBTM_STATUS btm_sec_send_hci_disconnect(tBTM_SEC_DEV_REC* p_dev_rec,
                                               uint8_t reason,
                                               uint16_t conn_handle);
uint8_t btm_sec_start_role_switch(tBTM_SEC_DEV_REC* p_dev_rec);
tBTM_SEC_DEV_REC* btm_sec_find_dev_by_sec_state(uint8_t state);

static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type,
                                       const char* p_name, uint8_t service_id,
                                       uint16_t sec_level, uint16_t psm,
                                       uint32_t mx_proto_id,
                                       uint32_t mx_chan_id);

static bool btm_dev_authenticated(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_dev_encrypted(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_dev_authorized(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_serv_trusted(tBTM_SEC_DEV_REC* p_dev_rec,
                             tBTM_SEC_SERV_REC* p_serv_rec);
static bool btm_sec_is_serv_level0(uint16_t psm);
static uint16_t btm_sec_set_serv_level4_flags(uint16_t cur_security,
                                              bool is_originator);

static bool btm_sec_queue_encrypt_request(BD_ADDR bd_addr,
                                          tBT_TRANSPORT transport,
                                          tBTM_SEC_CALLBACK* p_callback,
                                          void* p_ref_data,
                                          tBTM_BLE_SEC_ACT sec_act);
static void btm_sec_check_pending_enc_req(tBTM_SEC_DEV_REC* p_dev_rec,
                                          tBT_TRANSPORT transport,
                                          uint8_t encr_enable);

static bool btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC* p_dev_rec);
static bool btm_sec_is_master(tBTM_SEC_DEV_REC* p_dev_rec);

/* true - authenticated link key is possible */
static const bool btm_sec_io_map[BTM_IO_CAP_MAX][BTM_IO_CAP_MAX] = {
    /*   OUT,    IO,     IN,     NONE */
    /* OUT  */ {false, false, true, false},
    /* IO   */ {false, true, true, false},
    /* IN   */ {true, true, true, false},
    /* NONE */ {false, false, false, false}};
/*  BTM_IO_CAP_OUT      0   DisplayOnly */
/*  BTM_IO_CAP_IO       1   DisplayYesNo */
/*  BTM_IO_CAP_IN       2   KeyboardOnly */
/*  BTM_IO_CAP_NONE     3   NoInputNoOutput */

/*******************************************************************************
 *
 * Function         btm_dev_authenticated
 *
 * Description      check device is authenticated
 *
 * Returns          bool    true or false
 *
 ******************************************************************************/
static bool btm_dev_authenticated(tBTM_SEC_DEV_REC* p_dev_rec) {
  if (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) {
    return (true);
  }
  return (false);
}

/*******************************************************************************
 *
 * Function         btm_dev_encrypted
 *
 * Description      check device is encrypted
 *
 * Returns          bool    true or false
 *
 ******************************************************************************/
static bool btm_dev_encrypted(tBTM_SEC_DEV_REC* p_dev_rec) {
  if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) {
    return (true);
  }
  return (false);
}

/*******************************************************************************
 *
 * Function         btm_dev_authorized
 *
 * Description      check device is authorized
 *
 * Returns          bool    true or false
 *
 ******************************************************************************/
static bool btm_dev_authorized(tBTM_SEC_DEV_REC* p_dev_rec) {
  if (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) {
    return (true);
  }
  return (false);
}

/*******************************************************************************
 *
 * Function         btm_dev_16_digit_authenticated
 *
 * Description      check device is authenticated by using 16 digit pin or MITM
 *
 * Returns          bool    true or false
 *
 ******************************************************************************/
static bool btm_dev_16_digit_authenticated(tBTM_SEC_DEV_REC* p_dev_rec) {
  // BTM_SEC_16_DIGIT_PIN_AUTHED is set if MITM or 16 digit pin is used
  if (p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) {
    return (true);
  }
  return (false);
}

/*******************************************************************************
 *
 * Function         btm_serv_trusted
 *
 * Description      check service is trusted
 *
 * Returns          bool    true or false
 *
 ******************************************************************************/
static bool btm_serv_trusted(tBTM_SEC_DEV_REC* p_dev_rec,
                             tBTM_SEC_SERV_REC* p_serv_rec) {
  if (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask,
                                 p_serv_rec->service_id)) {
    return (true);
  }
  return (false);
}

/*******************************************************************************
 *
 * Function         BTM_SecRegister
 *
 * Description      Application manager calls this function to register for
 *                  security services.  There can be one and only one
 *                  application saving link keys.  BTM allows only first
 *                  registration.
 *
 * Returns          true if registered OK, else false
 *
 ******************************************************************************/
bool BTM_SecRegister(tBTM_APPL_INFO* p_cb_info) {
  BT_OCTET16 temp_value = {0};

  BTM_TRACE_EVENT("%s application registered", __func__);

  LOG_INFO(LOG_TAG, "%s p_cb_info->p_le_callback == 0x%p", __func__,
           p_cb_info->p_le_callback);
  if (p_cb_info->p_le_callback) {
    BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )", __func__);
    SMP_Register(btm_proc_smp_cback);
    /* if no IR is loaded, need to regenerate all the keys */
    if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) {
      btm_ble_reset_id();
    }
  } else {
    LOG_WARN(LOG_TAG, "%s p_cb_info->p_le_callback == NULL", __func__);
  }

  btm_cb.api = *p_cb_info;
  LOG_INFO(LOG_TAG, "%s btm_cb.api.p_le_callback = 0x%p ", __func__,
           btm_cb.api.p_le_callback);
  BTM_TRACE_EVENT("%s application registered", __func__);
  return (true);
}

/*******************************************************************************
 *
 * Function         BTM_SecRegisterLinkKeyNotificationCallback
 *
 * Description      Application manager calls this function to register for
 *                  link key notification.  When there is nobody registered
 *                  we should avoid changing link key
 *
 * Returns          true if registered OK, else false
 *
 ******************************************************************************/
bool BTM_SecRegisterLinkKeyNotificationCallback(
    tBTM_LINK_KEY_CALLBACK* p_callback) {
  btm_cb.api.p_link_key_callback = p_callback;
  return true;
}

/*******************************************************************************
 *
 * Function         BTM_SecAddRmtNameNotifyCallback
 *
 * Description      Any profile can register to be notified when name of the
 *                  remote device is resolved.
 *
 * Returns          true if registered OK, else false
 *
 ******************************************************************************/
bool BTM_SecAddRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) {
  int i;

  for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) {
    if (btm_cb.p_rmt_name_callback[i] == NULL) {
      btm_cb.p_rmt_name_callback[i] = p_callback;
      return (true);
    }
  }

  return (false);
}

/*******************************************************************************
 *
 * Function         BTM_SecDeleteRmtNameNotifyCallback
 *
 * Description      Any profile can deregister notification when a new Link Key
 *                  is generated per connection.
 *
 * Returns          true if OK, else false
 *
 ******************************************************************************/
bool BTM_SecDeleteRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) {
  int i;

  for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) {
    if (btm_cb.p_rmt_name_callback[i] == p_callback) {
      btm_cb.p_rmt_name_callback[i] = NULL;
      return (true);
    }
  }

  return (false);
}

/*******************************************************************************
 *
 * Function         BTM_GetSecurityFlags
 *
 * Description      Get security flags for the device
 *
 * Returns          bool    true or false is device found
 *
 ******************************************************************************/
bool BTM_GetSecurityFlags(BD_ADDR bd_addr, uint8_t* p_sec_flags) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  p_dev_rec = btm_find_dev(bd_addr);
  if (p_dev_rec != NULL) {
    *p_sec_flags = (uint8_t)p_dev_rec->sec_flags;
    return (true);
  }
  BTM_TRACE_ERROR("BTM_GetSecurityFlags false");
  return (false);
}

/*******************************************************************************
 *
 * Function         BTM_GetSecurityFlagsByTransport
 *
 * Description      Get security flags for the device on a particular transport
 *
 * Returns          bool    true or false is device found
 *
 ******************************************************************************/
bool BTM_GetSecurityFlagsByTransport(BD_ADDR bd_addr, uint8_t* p_sec_flags,
                                     tBT_TRANSPORT transport) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  p_dev_rec = btm_find_dev(bd_addr);
  if (p_dev_rec != NULL) {
    if (transport == BT_TRANSPORT_BR_EDR)
      *p_sec_flags = (uint8_t)p_dev_rec->sec_flags;
    else
      *p_sec_flags = (uint8_t)(p_dev_rec->sec_flags >> 8);

    return (true);
  }
  BTM_TRACE_ERROR("BTM_GetSecurityFlags false");
  return (false);
}

/*******************************************************************************
 *
 * Function         BTM_SetPinType
 *
 * Description      Set PIN type for the device.
 *
 * Returns          void
 *
 ******************************************************************************/
void BTM_SetPinType(uint8_t pin_type, PIN_CODE pin_code, uint8_t pin_code_len) {
  BTM_TRACE_API(
      "BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d",
      pin_type, (char*)pin_code, pin_code_len);

  /* If device is not up security mode will be set as a part of startup */
  if ((btm_cb.cfg.pin_type != pin_type) &&
      controller_get_interface()->get_is_ready()) {
    btsnd_hcic_write_pin_type(pin_type);
  }

  btm_cb.cfg.pin_type = pin_type;
  btm_cb.cfg.pin_code_len = pin_code_len;
  memcpy(btm_cb.cfg.pin_code, pin_code, pin_code_len);
}

/*******************************************************************************
 *
 * Function         BTM_SetPairableMode
 *
 * Description      Enable or disable pairing
 *
 * Parameters       allow_pairing - (true or false) whether or not the device
 *                      allows pairing.
 *                  connect_only_paired - (true or false) whether or not to
 *                      only allow paired devices to connect.
 *
 * Returns          void
 *
 ******************************************************************************/
void BTM_SetPairableMode(bool allow_pairing, bool connect_only_paired) {
  BTM_TRACE_API(
      "BTM_SetPairableMode()  allow_pairing: %u   connect_only_paired: %u",
      allow_pairing, connect_only_paired);

  btm_cb.pairing_disabled = !allow_pairing;
  btm_cb.connect_only_paired = connect_only_paired;
}

/*******************************************************************************
 *
 * Function         BTM_SetSecureConnectionsOnly
 *
 * Description      Enable or disable default treatment for Mode 4 Level 0
 *                  services
 *
 * Parameter        secure_connections_only_mode -
 *                  true means that the device should treat Mode 4 Level 0
 *                       services as services of other levels.
 *                  false means that the device should provide default
 *                        treatment for Mode 4 Level 0 services.
 *
 * Returns          void
 *
 ******************************************************************************/
void BTM_SetSecureConnectionsOnly(bool secure_connections_only_mode) {
  BTM_TRACE_API("%s: Mode : %u", __func__, secure_connections_only_mode);

  btm_cb.devcb.secure_connections_only = secure_connections_only_mode;
  btm_cb.security_mode = BTM_SEC_MODE_SC;
}
#define BTM_NO_AVAIL_SEC_SERVICES ((uint16_t)0xffff)

/*******************************************************************************
 *
 * Function         BTM_SetSecurityLevel
 *
 * Description      Register service security level with Security Manager
 *
 * Parameters:      is_originator - true if originating the connection
 *                  p_name      - Name of the service relevant only if
 *                                authorization will show this name to user.
 *                                Ignored if BTM_SEC_SERVICE_NAME_LEN is 0.
 *                  service_id  - service ID for the service passed to
 *                                authorization callback
 *                  sec_level   - bit mask of the security features
 *                  psm         - L2CAP PSM
 *                  mx_proto_id - protocol ID of multiplexing proto below
 *                  mx_chan_id  - channel ID of multiplexing proto below
 *
 * Returns          true if registered OK, else false
 *
 ******************************************************************************/
bool BTM_SetSecurityLevel(bool is_originator, const char* p_name,
                          uint8_t service_id, uint16_t sec_level, uint16_t psm,
                          uint32_t mx_proto_id, uint32_t mx_chan_id) {
#if (L2CAP_UCD_INCLUDED == TRUE)
  CONNECTION_TYPE conn_type;

  if (is_originator)
    conn_type = CONN_ORIENT_ORIG;
  else
    conn_type = CONN_ORIENT_TERM;

  return (btm_sec_set_security_level(conn_type, p_name, service_id, sec_level,
                                     psm, mx_proto_id, mx_chan_id));
#else
  return (btm_sec_set_security_level(is_originator, p_name, service_id,
                                     sec_level, psm, mx_proto_id, mx_chan_id));
#endif
}

/*******************************************************************************
 *
 * Function         btm_sec_set_security_level
 *
 * Description      Register service security level with Security Manager
 *
 * Parameters:      conn_type   - true if originating the connection
 *                  p_name      - Name of the service relevant only if
 *                                authorization will show this name to user.
 *                                Ignored if BTM_SEC_SERVICE_NAME_LEN is 0.
 *                  service_id  - service ID for the service passed to
 *                                authorization callback
 *                  sec_level   - bit mask of the security features
 *                  psm         - L2CAP PSM
 *                  mx_proto_id - protocol ID of multiplexing proto below
 *                  mx_chan_id  - channel ID of multiplexing proto below
 *
 * Returns          true if registered OK, else false
 *
 ******************************************************************************/
static bool btm_sec_set_security_level(CONNECTION_TYPE conn_type,
                                       const char* p_name, uint8_t service_id,
                                       uint16_t sec_level, uint16_t psm,
                                       uint32_t mx_proto_id,
                                       uint32_t mx_chan_id) {
  tBTM_SEC_SERV_REC* p_srec;
  uint16_t index;
  uint16_t first_unused_record = BTM_NO_AVAIL_SEC_SERVICES;
  bool record_allocated = false;
  bool is_originator;
#if (L2CAP_UCD_INCLUDED == TRUE)
  bool is_ucd;

  if (conn_type & CONNECTION_TYPE_ORIG_MASK)
    is_originator = true;
  else
    is_originator = false;

  if (conn_type & CONNECTION_TYPE_CONNLESS_MASK) {
    is_ucd = true;
  } else {
    is_ucd = false;
  }
#else
  is_originator = conn_type;
#endif

  BTM_TRACE_API("%s : sec: 0x%x", __func__, sec_level);

  /* See if the record can be reused (same service name, psm, mx_proto_id,
     service_id, and mx_chan_id), or obtain the next unused record */

  p_srec = &btm_cb.sec_serv_rec[0];

  for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) {
    /* Check if there is already a record for this service */
    if (p_srec->security_flags & BTM_SEC_IN_USE) {
#if BTM_SEC_SERVICE_NAME_LEN > 0
      if (p_srec->psm == psm && p_srec->mx_proto_id == mx_proto_id &&
          service_id == p_srec->service_id && p_name &&
          (!strncmp(p_name, (char*)p_srec->orig_service_name,
                    /* strlcpy replaces end char with termination char*/
                    BTM_SEC_SERVICE_NAME_LEN - 1) ||
           !strncmp(p_name, (char*)p_srec->term_service_name,
                    /* strlcpy replaces end char with termination char*/
                    BTM_SEC_SERVICE_NAME_LEN - 1)))
#else
      if (p_srec->psm == psm && p_srec->mx_proto_id == mx_proto_id &&
          service_id == p_srec->service_id)
#endif
      {
        record_allocated = true;
        break;
      }
    }
    /* Mark the first available service record */
    else if (!record_allocated) {
      memset(p_srec, 0, sizeof(tBTM_SEC_SERV_REC));
      record_allocated = true;
      first_unused_record = index;
    }
  }

  if (!record_allocated) {
    BTM_TRACE_WARNING("BTM_SEC_REG: Out of Service Records (%d)",
                      BTM_SEC_MAX_SERVICE_RECORDS);
    return (record_allocated);
  }

  /* Process the request if service record is valid */
  /* If a duplicate service wasn't found, use the first available */
  if (index >= BTM_SEC_MAX_SERVICE_RECORDS) {
    index = first_unused_record;
    p_srec = &btm_cb.sec_serv_rec[index];
  }

  p_srec->psm = psm;
  p_srec->service_id = service_id;
  p_srec->mx_proto_id = mx_proto_id;

  if (is_originator) {
    p_srec->orig_mx_chan_id = mx_chan_id;
#if BTM_SEC_SERVICE_NAME_LEN > 0
    strlcpy((char*)p_srec->orig_service_name, p_name,
            BTM_SEC_SERVICE_NAME_LEN + 1);
#endif
/* clear out the old setting, just in case it exists */
#if (L2CAP_UCD_INCLUDED == TRUE)
    if (is_ucd) {
      p_srec->ucd_security_flags &= ~(
          BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT |
          BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | BTM_SEC_FORCE_MASTER |
          BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE);
    } else
#endif
    {
      p_srec->security_flags &= ~(
          BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT |
          BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | BTM_SEC_FORCE_MASTER |
          BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE);
    }

    /* Parameter validation.  Originator should not set requirements for
     * incoming connections */
    sec_level &=
        ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE |
          BTM_SEC_IN_MITM | BTM_SEC_IN_MIN_16_DIGIT_PIN);

    if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
        btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
        btm_cb.security_mode == BTM_SEC_MODE_SC) {
      if (sec_level & BTM_SEC_OUT_AUTHENTICATE) sec_level |= BTM_SEC_OUT_MITM;
    }

    /* Make sure the authenticate bit is set, when encrypt bit is set */
    if (sec_level & BTM_SEC_OUT_ENCRYPT) sec_level |= BTM_SEC_OUT_AUTHENTICATE;

/* outgoing connections usually set the security level right before
 * the connection is initiated.
 * set it to be the outgoing service */
#if (L2CAP_UCD_INCLUDED == TRUE)
    if (is_ucd == false)
#endif
    {
      btm_cb.p_out_serv = p_srec;
    }
  } else {
    p_srec->term_mx_chan_id = mx_chan_id;
#if BTM_SEC_SERVICE_NAME_LEN > 0
    strlcpy((char*)p_srec->term_service_name, p_name,
            BTM_SEC_SERVICE_NAME_LEN + 1);
#endif
/* clear out the old setting, just in case it exists */
#if (L2CAP_UCD_INCLUDED == TRUE)
    if (is_ucd) {
      p_srec->ucd_security_flags &=
          ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT |
            BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | BTM_SEC_FORCE_MASTER |
            BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE |
            BTM_SEC_ATTEMPT_SLAVE | BTM_SEC_IN_MIN_16_DIGIT_PIN);
    } else
#endif
    {
      p_srec->security_flags &=
          ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT |
            BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | BTM_SEC_FORCE_MASTER |
            BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE |
            BTM_SEC_ATTEMPT_SLAVE | BTM_SEC_IN_MIN_16_DIGIT_PIN);
    }

    /* Parameter validation.  Acceptor should not set requirements for outgoing
     * connections */
    sec_level &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT |
                   BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM);

    if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
        btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
        btm_cb.security_mode == BTM_SEC_MODE_SC) {
      if (sec_level & BTM_SEC_IN_AUTHENTICATE) sec_level |= BTM_SEC_IN_MITM;
    }

    /* Make sure the authenticate bit is set, when encrypt bit is set */
    if (sec_level & BTM_SEC_IN_ENCRYPT) sec_level |= BTM_SEC_IN_AUTHENTICATE;
  }

#if (L2CAP_UCD_INCLUDED == TRUE)
  if (is_ucd) {
    p_srec->security_flags |= (uint16_t)(BTM_SEC_IN_USE);
    p_srec->ucd_security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE);
  } else {
    p_srec->security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE);
  }

  BTM_TRACE_API(
      "BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, "
      "chan_id %d",
      index, service_id, conn_type, psm, mx_proto_id, mx_chan_id);

  BTM_TRACE_API(
      "               : security_flags: 0x%04x, ucd_security_flags: 0x%04x",
      p_srec->security_flags, p_srec->ucd_security_flags);

#if BTM_SEC_SERVICE_NAME_LEN > 0
  BTM_TRACE_API("               : service name [%s] (up to %d chars saved)",
                p_name, BTM_SEC_SERVICE_NAME_LEN);
#endif
#else
  p_srec->security_flags |= (uint16_t)(sec_level | BTM_SEC_IN_USE);

  BTM_TRACE_API(
      "BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d",
      index, service_id, is_originator, psm, mx_proto_id, mx_chan_id);

#if BTM_SEC_SERVICE_NAME_LEN > 0
  BTM_TRACE_API(
      "               : sec: 0x%x, service name [%s] (up to %d chars saved)",
      p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN);
#endif
#endif

  return (record_allocated);
}

/*******************************************************************************
 *
 * Function         BTM_SecClrService
 *
 * Description      Removes specified service record(s) from the security
 *                  database. All service records with the specified name are
 *                  removed. Typically used only by devices with limited RAM so
 *                  that it can reuse an old security service record.
 *
 *                  Note: Unpredictable results may occur if a service is
 *                      cleared that is still in use by an application/profile.
 *
 * Parameters       Service ID - Id of the service to remove. '0' removes all
 *                          service records (except SDP).
 *
 * Returns          Number of records that were freed.
 *
 ******************************************************************************/
uint8_t BTM_SecClrService(uint8_t service_id) {
  tBTM_SEC_SERV_REC* p_srec = &btm_cb.sec_serv_rec[0];
  uint8_t num_freed = 0;
  int i;

  for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) {
    /* Delete services with specified name (if in use and not SDP) */
    if ((p_srec->security_flags & BTM_SEC_IN_USE) &&
        (p_srec->psm != BT_PSM_SDP) &&
        (!service_id || (service_id == p_srec->service_id))) {
      BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d", i, service_id);
      p_srec->security_flags = 0;
#if (L2CAP_UCD_INCLUDED == TRUE)
      p_srec->ucd_security_flags = 0;
#endif
      num_freed++;
    }
  }

  return (num_freed);
}

/*******************************************************************************
 *
 * Function         btm_sec_clr_service_by_psm
 *
 * Description      Removes specified service record from the security database.
 *                  All service records with the specified psm are removed.
 *                  Typically used by L2CAP to free up the service record used
 *                  by dynamic PSM clients when the channel is closed.
 *                  The given psm must be a virtual psm.
 *
 * Parameters       Service ID - Id of the service to remove. '0' removes all
 *                          service records (except SDP).
 *
 * Returns          Number of records that were freed.
 *
 ******************************************************************************/
uint8_t btm_sec_clr_service_by_psm(uint16_t psm) {
  tBTM_SEC_SERV_REC* p_srec = &btm_cb.sec_serv_rec[0];
  uint8_t num_freed = 0;
  int i;

  for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) {
    /* Delete services with specified name (if in use and not SDP) */
    if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm)) {
      BTM_TRACE_API("BTM_SEC_CLR[%d]: id %d ", i, p_srec->service_id);
      p_srec->security_flags = 0;
      num_freed++;
    }
  }
  BTM_TRACE_API("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d", psm,
                num_freed);

  return (num_freed);
}

/*******************************************************************************
 *
 * Function         btm_sec_clr_temp_auth_service
 *
 * Description      Removes specified device record's temporary authorization
 *                  flag from the security database.
 *
 * Parameters       Device address to be cleared
 *
 * Returns          void.
 *
 ******************************************************************************/
void btm_sec_clr_temp_auth_service(BD_ADDR bda) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  p_dev_rec = btm_find_dev(bda);
  if (p_dev_rec == NULL) {
    BTM_TRACE_WARNING("btm_sec_clr_temp_auth_service() - no dev CB");
    return;
  }

  /* Reset the temporary authorized flag so that next time (untrusted) service
   * is accessed autorization will take place */
  if (p_dev_rec->last_author_service_id != BTM_SEC_NO_LAST_SERVICE_ID &&
      p_dev_rec->p_cur_service) {
    BTM_TRACE_DEBUG(
        "btm_sec_clr_auth_service_by_psm [clearing device: "
        "%02x:%02x:%02x:%02x:%02x:%02x]",
        bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);

    p_dev_rec->last_author_service_id = BTM_SEC_NO_LAST_SERVICE_ID;
  }
}

/*******************************************************************************
 *
 * Function         BTM_PINCodeReply
 *
 * Description      This function is called after Security Manager submitted
 *                  PIN code request to the UI.
 *
 * Parameters:      bd_addr      - Address of the device for which PIN was
 *                                 requested
 *                  res          - result of the operation BTM_SUCCESS
 *                                 if success
 *                  pin_len      - length in bytes of the PIN Code
 *                  p_pin        - pointer to array with the PIN Code
 *                  trusted_mask - bitwise OR of trusted services
 *                                 (array of uint32_t)
 *
 ******************************************************************************/
void BTM_PINCodeReply(BD_ADDR bd_addr, uint8_t res, uint8_t pin_len,
                      uint8_t* p_pin, uint32_t trusted_mask[]) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  BTM_TRACE_API(
      "BTM_PINCodeReply(): PairState: %s   PairFlags: 0x%02x  PinLen:%d  "
      "Result:%d",
      btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len,
      res);

  /* If timeout already expired or has been canceled, ignore the reply */
  if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) {
    BTM_TRACE_WARNING("BTM_PINCodeReply() - Wrong State: %d",
                      btm_cb.pairing_state);
    return;
  }

  if (memcmp(bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) {
    BTM_TRACE_ERROR("BTM_PINCodeReply() - Wrong BD Addr");
    return;
  }

  p_dev_rec = btm_find_dev(bd_addr);
  if (p_dev_rec == NULL) {
    BTM_TRACE_ERROR("BTM_PINCodeReply() - no dev CB");
    return;
  }

  if ((pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL))
    res = BTM_ILLEGAL_VALUE;

  if (res != BTM_SUCCESS) {
    /* if peer started dd OR we started dd and pre-fetch pin was not used send
     * negative reply */
    if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) ||
        ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
         (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE))) {
      /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed
       * event */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
      btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;

      btsnd_hcic_pin_code_neg_reply(bd_addr);
    } else {
      p_dev_rec->security_required = BTM_SEC_NONE;
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
    }
    return;
  }
  if (trusted_mask)
    BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);
  p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED;
  p_dev_rec->pin_code_length = pin_len;
  if (pin_len >= 16) {
    p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
  }

  if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
      (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) &&
      (btm_cb.security_mode_changed == false)) {
    /* This is start of the dedicated bonding if local device is 2.0 */
    btm_cb.pin_code_len = pin_len;
    memcpy(btm_cb.pin_code, p_pin, pin_len);

    btm_cb.security_mode_changed = true;
#ifdef APPL_AUTH_WRITE_EXCEPTION
    if (!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr))
#endif
      btsnd_hcic_write_auth_enable(true);

    btm_cb.acl_disc_reason = 0xff;

    /* if we rejected incoming connection request, we have to wait
     * HCI_Connection_Complete event */
    /*  before originating  */
    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) {
      BTM_TRACE_WARNING(
          "BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected "
          "incoming connection");
      /* we change state little bit early so btm_sec_connected() will originate
       * connection */
      /*   when existing ACL link is down completely */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
    }
    /* if we already accepted incoming connection from pairing device */
    else if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) {
      BTM_TRACE_WARNING(
          "BTM_PINCodeReply(): link is connecting so wait pin code request "
          "from peer");
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
    } else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) {
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED;

      if (btm_cb.api.p_auth_complete_callback)
        (*btm_cb.api.p_auth_complete_callback)(
            p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
            HCI_ERR_AUTH_FAILURE);
    }
    return;
  }

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
  btm_cb.acl_disc_reason = HCI_SUCCESS;

  btsnd_hcic_pin_code_req_reply(bd_addr, pin_len, p_pin);
}

/*******************************************************************************
 *
 * Function         btm_sec_bond_by_transport
 *
 * Description      this is the bond function that will start either SSP or SMP.
 *
 * Parameters:      bd_addr      - Address of the device to bond
 *                  pin_len      - length in bytes of the PIN Code
 *                  p_pin        - pointer to array with the PIN Code
 *                  trusted_mask - bitwise OR of trusted services
 *                                 (array of uint32_t)
 *
 *  Note: After 2.1 parameters are not used and preserved here not to change API
 ******************************************************************************/
tBTM_STATUS btm_sec_bond_by_transport(BD_ADDR bd_addr, tBT_TRANSPORT transport,
                                      uint8_t pin_len, uint8_t* p_pin,
                                      uint32_t trusted_mask[]) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_STATUS status;
  uint8_t* p_features;
  uint8_t ii;
  tACL_CONN* p = btm_bda_to_acl(bd_addr, transport);
  BTM_TRACE_API("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x",
                bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4],
                bd_addr[5]);

  BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d", transport);

  /* Other security process is in progress */
  if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {
    BTM_TRACE_ERROR("BTM_SecBond: already busy in state: %s",
                    btm_pair_state_descr(btm_cb.pairing_state));
    return (BTM_WRONG_MODE);
  }

  p_dev_rec = btm_find_or_alloc_dev(bd_addr);
  if (p_dev_rec == NULL) {
    return (BTM_NO_RESOURCES);
  }

  if (!controller_get_interface()->get_is_ready()) {
    BTM_TRACE_ERROR("%s controller module is not ready", __func__);
    return (BTM_NO_RESOURCES);
  }

  BTM_TRACE_DEBUG("before update sec_flags=0x%x", p_dev_rec->sec_flags);

  /* Finished if connection is active and already paired */
  if (((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) &&
       transport == BT_TRANSPORT_BR_EDR &&
       (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) ||
      ((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) &&
       transport == BT_TRANSPORT_LE &&
       (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))) {
    BTM_TRACE_WARNING("BTM_SecBond -> Already Paired");
    return (BTM_SUCCESS);
  }

  /* Tell controller to get rid of the link key if it has one stored */
  if ((BTM_DeleteStoredLinkKey(bd_addr, NULL)) != BTM_SUCCESS)
    return (BTM_NO_RESOURCES);

  /* Save the PIN code if we got a valid one */
  if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) {
    btm_cb.pin_code_len = pin_len;
    p_dev_rec->pin_code_length = pin_len;
    memcpy(btm_cb.pin_code, p_pin, PIN_CODE_LEN);
  }

  memcpy(btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN);

  btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD;

  p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;
  p_dev_rec->is_originator = true;
  if (trusted_mask)
    BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);

  if (transport == BT_TRANSPORT_LE) {
    btm_ble_init_pseudo_addr(p_dev_rec, bd_addr);
    p_dev_rec->sec_flags &= ~BTM_SEC_LE_MASK;

    if (SMP_Pair(bd_addr) == SMP_STARTED) {
      btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE;
      p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
      return BTM_CMD_STARTED;
    }

    btm_cb.pairing_flags = 0;
    return (BTM_NO_RESOURCES);
  }

  p_dev_rec->sec_flags &=
      ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED |
        BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);

  BTM_TRACE_DEBUG("after update sec_flags=0x%x", p_dev_rec->sec_flags);
  if (!controller_get_interface()->supports_simple_pairing()) {
    /* The special case when we authenticate keyboard.  Set pin type to fixed */
    /* It would be probably better to do it from the application, but it is */
    /* complicated */
    if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) ==
         BTM_COD_MAJOR_PERIPHERAL) &&
        (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) &&
        (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) {
      btm_cb.pin_type_changed = true;
      btsnd_hcic_write_pin_type(HCI_PIN_TYPE_FIXED);
    }
  }

  for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) {
    p_features = p_dev_rec->feature_pages[ii];
    BTM_TRACE_EVENT("  remote_features page[%1d] = %02x-%02x-%02x-%02x", ii,
                    p_features[0], p_features[1], p_features[2], p_features[3]);
    BTM_TRACE_EVENT("                              %02x-%02x-%02x-%02x",
                    p_features[4], p_features[5], p_features[6], p_features[7]);
  }

  BTM_TRACE_EVENT("BTM_SecBond: Remote sm4: 0x%x  HCI Handle: 0x%04x",
                  p_dev_rec->sm4, p_dev_rec->hci_handle);

#if (BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE)
  p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN;
#endif

  /* If connection already exists... */
  if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE) {
    btm_sec_start_authentication(p_dev_rec);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);

    /* Mark lcb as bonding */
    l2cu_update_lcb_4_bonding(bd_addr, true);
    return (BTM_CMD_STARTED);
  }

  BTM_TRACE_DEBUG("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);
  if (!controller_get_interface()->supports_simple_pairing() ||
      (p_dev_rec->sm4 == BTM_SM4_KNOWN)) {
    if (btm_sec_check_prefetch_pin(p_dev_rec)) return (BTM_CMD_STARTED);
  }
  if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
       btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
       btm_cb.security_mode == BTM_SEC_MODE_SC) &&
      BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
    /* local is 2.1 and peer is unknown */
    if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) {
      /* we are not accepting connection request from peer
       * -> RNR (to learn if peer is 2.1)
       * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
      status = BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
    } else {
      /* We are accepting connection request from peer */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
      status = BTM_CMD_STARTED;
    }
    BTM_TRACE_DEBUG("State:%s sm4: 0x%x sec_state:%d",
                    btm_pair_state_descr(btm_cb.pairing_state), p_dev_rec->sm4,
                    p_dev_rec->sec_state);
  } else {
    /* both local and peer are 2.1  */
    status = btm_sec_dd_create_conn(p_dev_rec);
  }

  if (status != BTM_CMD_STARTED) {
    BTM_TRACE_ERROR(
        "%s BTM_ReadRemoteDeviceName or btm_sec_dd_create_conn error: 0x%x",
        __func__, (int)status);
    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
  }

  return status;
}

/*******************************************************************************
 *
 * Function         BTM_SecBondByTransport
 *
 * Description      This function is called to perform bonding with peer device.
 *                  If the connection is already up, but not secure, pairing
 *                  is attempted.  If already paired BTM_SUCCESS is returned.
 *
 * Parameters:      bd_addr      - Address of the device to bond
 *                  transport    - doing SSP over BR/EDR or SMP over LE
 *                  pin_len      - length in bytes of the PIN Code
 *                  p_pin        - pointer to array with the PIN Code
 *                  trusted_mask - bitwise OR of trusted services
 *                                 (array of uint32_t)
 *
 *  Note: After 2.1 parameters are not used and preserved here not to change API
 ******************************************************************************/
tBTM_STATUS BTM_SecBondByTransport(BD_ADDR bd_addr, tBT_TRANSPORT transport,
                                   uint8_t pin_len, uint8_t* p_pin,
                                   uint32_t trusted_mask[]) {
  tBT_DEVICE_TYPE dev_type;
  tBLE_ADDR_TYPE addr_type;

  BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type);
  /* LE device, do SMP pairing */
  if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) ||
      (transport == BT_TRANSPORT_BR_EDR &&
       (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) {
    return BTM_ILLEGAL_ACTION;
  }
  return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin,
                                   trusted_mask);
}

/*******************************************************************************
 *
 * Function         BTM_SecBond
 *
 * Description      This function is called to perform bonding with peer device.
 *                  If the connection is already up, but not secure, pairing
 *                  is attempted.  If already paired BTM_SUCCESS is returned.
 *
 * Parameters:      bd_addr      - Address of the device to bond
 *                  pin_len      - length in bytes of the PIN Code
 *                  p_pin        - pointer to array with the PIN Code
 *                  trusted_mask - bitwise OR of trusted services
 *                                 (array of uint32_t)
 *
 *  Note: After 2.1 parameters are not used and preserved here not to change API
 ******************************************************************************/
tBTM_STATUS BTM_SecBond(BD_ADDR bd_addr, uint8_t pin_len, uint8_t* p_pin,
                        uint32_t trusted_mask[]) {
  tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
  if (BTM_UseLeLink(bd_addr)) transport = BT_TRANSPORT_LE;
  return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin,
                                   trusted_mask);
}
/*******************************************************************************
 *
 * Function         BTM_SecBondCancel
 *
 * Description      This function is called to cancel ongoing bonding process
 *                  with peer device.
 *
 * Parameters:      bd_addr      - Address of the peer device
 *                  transport    - false for BR/EDR link; true for LE link
 *
 ******************************************************************************/
tBTM_STATUS BTM_SecBondCancel(BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  BTM_TRACE_API("BTM_SecBondCancel()  State: %s flags:0x%x",
                btm_pair_state_descr(btm_cb.pairing_state),
                btm_cb.pairing_flags);
  p_dev_rec = btm_find_dev(bd_addr);
  if ((p_dev_rec == NULL) ||
      (memcmp(btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0)) {
    return BTM_UNKNOWN_ADDR;
  }

  if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_LE_ACTIVE) {
    if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
      BTM_TRACE_DEBUG("Cancel LE pairing");
      if (SMP_PairCancel(bd_addr)) {
        return BTM_CMD_STARTED;
      }
    }
    return BTM_WRONG_MODE;
  }

  BTM_TRACE_DEBUG("hci_handle:0x%x sec_state:%d", p_dev_rec->hci_handle,
                  p_dev_rec->sec_state);
  if (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state &&
      BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) {
    /* pre-fetching pin for dedicated bonding */
    btm_sec_bond_cancel_complete();
    return BTM_SUCCESS;
  }

  /* If this BDA is in a bonding procedure */
  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) {
    /* If the HCI link is up */
    if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) {
      /* If some other thread disconnecting, we do not send second command */
      if ((p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING) ||
          (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH))
        return (BTM_CMD_STARTED);

      /* If the HCI link was set up by Bonding process */
      if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)
        return btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER,
                                           p_dev_rec->hci_handle);
      else
        l2cu_update_lcb_4_bonding(bd_addr, false);

      return BTM_NOT_AUTHORIZED;
    } else /*HCI link is not up */
    {
      /* If the HCI link creation was started by Bonding process */
      if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) {
        btsnd_hcic_create_conn_cancel(bd_addr);
        return BTM_CMD_STARTED;
      }
      if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) {
        BTM_CancelRemoteDeviceName();
        btm_cb.pairing_flags |= BTM_PAIR_FLAGS_WE_CANCEL_DD;
        return BTM_CMD_STARTED;
      }
      return BTM_NOT_AUTHORIZED;
    }
  }

  return BTM_WRONG_MODE;
}

/*******************************************************************************
 *
 * Function         BTM_SecGetDeviceLinkKey
 *
 * Description      This function is called to obtain link key for the device
 *                  it returns BTM_SUCCESS if link key is available, or
 *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
 *                  the device or device record does not contain link key info
 *
 * Parameters:      bd_addr      - Address of the device
 *                  link_key     - Link Key is copied into this array
 *
 ******************************************************************************/
tBTM_STATUS BTM_SecGetDeviceLinkKey(BD_ADDR bd_addr, LINK_KEY link_key) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  p_dev_rec = btm_find_dev(bd_addr);
  if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
    memcpy(link_key, p_dev_rec->link_key, LINK_KEY_LEN);
    return (BTM_SUCCESS);
  }
  return (BTM_UNKNOWN_ADDR);
}

/*******************************************************************************
 *
 * Function         BTM_SecGetDeviceLinkKeyType
 *
 * Description      This function is called to obtain link key type for the
 *                  device.
 *                  it returns BTM_SUCCESS if link key is available, or
 *                  BTM_UNKNOWN_ADDR if Security Manager does not know about
 *                  the device or device record does not contain link key info
 *
 * Returns          BTM_LKEY_TYPE_IGNORE if link key is unknown, link type
 *                  otherwise.
 *
 ******************************************************************************/
tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType(BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);

  if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
    return p_dev_rec->link_key_type;
  }
  return BTM_LKEY_TYPE_IGNORE;
}

/*******************************************************************************
 *
 * Function         BTM_SetEncryption
 *
 * Description      This function is called to ensure that connection is
 *                  encrypted.  Should be called only on an open connection.
 *                  Typically only needed for connections that first want to
 *                  bring up unencrypted links, then later encrypt them.
 *
 * Parameters:      bd_addr       - Address of the peer device
 *                  transport     - Link transport
 *                  p_callback    - Pointer to callback function called if
 *                                  this function returns PENDING after required
 *                                  procedures are completed.  Can be set to
 *                                  NULL if status is not desired.
 *                  p_ref_data    - pointer to any data the caller wishes to
 *                                  receive in the callback function upon
 *                                  completion. can be set to NULL if not used.
 *                  sec_act       - LE security action, unused for BR/EDR
 *
 * Returns          BTM_SUCCESS   - already encrypted
 *                  BTM_PENDING   - command will be returned in the callback
 *                  BTM_WRONG_MODE- connection not up.
 *                  BTM_BUSY      - security procedures are currently active
 *                  BTM_MODE_UNSUPPORTED - if security manager not linked in.
 *
 ******************************************************************************/
tBTM_STATUS BTM_SetEncryption(BD_ADDR bd_addr, tBT_TRANSPORT transport,
                              tBTM_SEC_CBACK* p_callback, void* p_ref_data,
                              tBTM_BLE_SEC_ACT sec_act) {
  tBTM_STATUS rc = 0;

  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
  if (!p_dev_rec || (transport == BT_TRANSPORT_BR_EDR &&
                     p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) ||
      (transport == BT_TRANSPORT_LE &&
       p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE)) {
    /* Connection should be up and runnning */
    BTM_TRACE_WARNING("Security Manager: BTM_SetEncryption not connected");

    if (p_callback)
      (*p_callback)(bd_addr, transport, p_ref_data, BTM_WRONG_MODE);

    return (BTM_WRONG_MODE);
  }

  if (transport == BT_TRANSPORT_BR_EDR &&
      (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)) {
    BTM_TRACE_EVENT("Security Manager: BTM_SetEncryption already encrypted");

    if (p_callback) (*p_callback)(bd_addr, transport, p_ref_data, BTM_SUCCESS);

    return (BTM_SUCCESS);
  }

  /* enqueue security request if security is active */
  if (p_dev_rec->p_callback || (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE)) {
    BTM_TRACE_WARNING(
        "Security Manager: BTM_SetEncryption busy, enqueue request");

    if (btm_sec_queue_encrypt_request(bd_addr, transport, p_callback,
                                      p_ref_data, sec_act)) {
      return BTM_CMD_STARTED;
    } else {
      if (p_callback)
        (*p_callback)(bd_addr, transport, p_ref_data, BTM_NO_RESOURCES);
      return BTM_NO_RESOURCES;
    }
  }

  p_dev_rec->p_callback = p_callback;
  p_dev_rec->p_ref_data = p_ref_data;
  p_dev_rec->security_required |=
      (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT);
  p_dev_rec->is_originator = false;

  BTM_TRACE_API(
      "Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x "
      "Required:0x%x",
      p_dev_rec->hci_handle, p_dev_rec->sec_state, p_dev_rec->sec_flags,
      p_dev_rec->security_required);

  if (transport == BT_TRANSPORT_LE) {
    tACL_CONN* p = btm_bda_to_acl(bd_addr, transport);
    if (p) {
      rc = btm_ble_set_encryption(bd_addr, sec_act, p->link_role);
    } else {
      rc = BTM_WRONG_MODE;
      BTM_TRACE_WARNING("%s: cannot call btm_ble_set_encryption, p is NULL",
                        __func__);
    }
  } else {
    rc = btm_sec_execute_procedure(p_dev_rec);
  }

  if (rc != BTM_CMD_STARTED && rc != BTM_BUSY) {
    if (p_callback) {
      p_dev_rec->p_callback = NULL;
      (*p_callback)(bd_addr, transport, p_dev_rec->p_ref_data, rc);
    }
  }

  return (rc);
}

/*******************************************************************************
 * disconnect the ACL link, if it's not done yet.
 ******************************************************************************/
static tBTM_STATUS btm_sec_send_hci_disconnect(tBTM_SEC_DEV_REC* p_dev_rec,
                                               uint8_t reason,
                                               uint16_t conn_handle) {
  uint8_t old_state = p_dev_rec->sec_state;
  tBTM_STATUS status = BTM_CMD_STARTED;

  BTM_TRACE_EVENT("btm_sec_send_hci_disconnect:  handle:0x%x, reason=0x%x",
                  conn_handle, reason);

  /* send HCI_Disconnect on a transport only once */
  switch (old_state) {
    case BTM_SEC_STATE_DISCONNECTING:
      if (conn_handle == p_dev_rec->hci_handle) return status;

      p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH;
      break;

    case BTM_SEC_STATE_DISCONNECTING_BLE:
      if (conn_handle == p_dev_rec->ble_hci_handle) return status;

      p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH;
      break;

    case BTM_SEC_STATE_DISCONNECTING_BOTH:
      return status;

    default:
      p_dev_rec->sec_state = (conn_handle == p_dev_rec->hci_handle)
                                 ? BTM_SEC_STATE_DISCONNECTING
                                 : BTM_SEC_STATE_DISCONNECTING_BLE;

      break;
  }

  /* If a role switch is in progress, delay the HCI Disconnect to avoid
   * controller problem */
  if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING &&
      p_dev_rec->hci_handle == conn_handle) {
    BTM_TRACE_DEBUG(
        "RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect "
        "to delay disconnect");
    p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING;
    status = BTM_SUCCESS;
  }
  /* Tear down the HCI link */
  else {
    btsnd_hcic_disconnect(conn_handle, reason);
  }

  return status;
}

/*******************************************************************************
 *
 * Function         BTM_ConfirmReqReply
 *
 * Description      This function is called to confirm the numeric value for
 *                  Simple Pairing in response to BTM_SP_CFM_REQ_EVT
 *
 * Parameters:      res           - result of the operation BTM_SUCCESS if
 *                                  success
 *                  bd_addr       - Address of the peer device
 *
 ******************************************************************************/
void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  BTM_TRACE_EVENT("BTM_ConfirmReqReply() State: %s  Res: %u",
                  btm_pair_state_descr(btm_cb.pairing_state), res);

  /* If timeout already expired or has been canceled, ignore the reply */
  if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM) ||
      (memcmp(btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0))
    return;

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);

  if ((res == BTM_SUCCESS) || (res == BTM_SUCCESS_NO_SECURITY)) {
    btm_cb.acl_disc_reason = HCI_SUCCESS;

    if (res == BTM_SUCCESS) {
      p_dev_rec = btm_find_dev(bd_addr);
      if (p_dev_rec != NULL) {
        p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED;
        p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
      }
    }

    btsnd_hcic_user_conf_reply(bd_addr, true);
  } else {
    /* Report authentication failed event from state
     * BTM_PAIR_STATE_WAIT_AUTH_COMPLETE */
    btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;
    btsnd_hcic_user_conf_reply(bd_addr, false);
  }
}

/*******************************************************************************
 *
 * Function         BTM_PasskeyReqReply
 *
 * Description      This function is called to provide the passkey for
 *                  Simple Pairing in response to BTM_SP_KEY_REQ_EVT
 *
 * Parameters:      res     - result of the operation BTM_SUCCESS if success
 *                  bd_addr - Address of the peer device
 *                  passkey - numeric value in the range of
 *                  BTM_MIN_PASSKEY_VAL(0) -
 *                  BTM_MAX_PASSKEY_VAL(999999(0xF423F)).
 *
 ******************************************************************************/
#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, uint32_t passkey) {
  BTM_TRACE_API("BTM_PasskeyReqReply: State: %s  res:%d",
                btm_pair_state_descr(btm_cb.pairing_state), res);

  if ((btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) ||
      (memcmp(btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0)) {
    return;
  }

  /* If timeout already expired or has been canceled, ignore the reply */
  if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) &&
      (res != BTM_SUCCESS)) {
    tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
    if (p_dev_rec != NULL) {
      btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;

      if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)
        btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_AUTH_FAILURE,
                                    p_dev_rec->hci_handle);
      else
        BTM_SecBondCancel(bd_addr);

      p_dev_rec->sec_flags &=
          ~(BTM_SEC_LINK_KEY_AUTHED | BTM_SEC_LINK_KEY_KNOWN);

      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      return;
    }
  } else if (btm_cb.pairing_state != BTM_PAIR_STATE_KEY_ENTRY)
    return;

  if (passkey > BTM_MAX_PASSKEY_VAL) res = BTM_ILLEGAL_VALUE;

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);

  if (res != BTM_SUCCESS) {
    /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed
     * event */
    btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;
    btsnd_hcic_user_passkey_neg_reply(bd_addr);
  } else {
    btm_cb.acl_disc_reason = HCI_SUCCESS;
    btsnd_hcic_user_passkey_reply(bd_addr, passkey);
  }
}
#endif

/*******************************************************************************
 *
 * Function         BTM_SendKeypressNotif
 *
 * Description      This function is used during the passkey entry model
 *                  by a device with KeyboardOnly IO capabilities
 *                  (very likely to be a HID Device).
 *                  It is called by a HID Device to inform the remote device
 *                  when a key has been entered or erased.
 *
 * Parameters:      bd_addr - Address of the peer device
 *                  type - notification type
 *
 ******************************************************************************/
#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type) {
  /* This API only make sense between PASSKEY_REQ and SP complete */
  if (btm_cb.pairing_state == BTM_PAIR_STATE_KEY_ENTRY)
    btsnd_hcic_send_keypress_notif(bd_addr, type);
}
#endif

/*******************************************************************************
 *
 * Function         BTM_IoCapRsp
 *
 * Description      This function is called in response to BTM_SP_IO_REQ_EVT
 *                  When the event data io_req.oob_data is set to
 *                  BTM_OOB_UNKNOWN by the tBTM_SP_CALLBACK implementation,
 *                  this function is called to provide the actual response
 *
 * Parameters:      bd_addr - Address of the peer device
 *                  io_cap  - The IO capability of local device.
 *                  oob     - BTM_OOB_NONE or BTM_OOB_PRESENT.
 *                  auth_req- MITM protection required or not.
 *
 ******************************************************************************/
void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, tBTM_OOB_DATA oob,
                  tBTM_AUTH_REQ auth_req) {
  BTM_TRACE_EVENT("BTM_IoCapRsp: state: %s  oob: %d io_cap: %d",
                  btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap);

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS) ||
      (memcmp(btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0))
    return;

  if (oob < BTM_OOB_UNKNOWN && io_cap < BTM_IO_CAP_MAX) {
    btm_cb.devcb.loc_auth_req = auth_req;
    btm_cb.devcb.loc_io_caps = io_cap;

    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
      auth_req = (BTM_AUTH_DD_BOND | (auth_req & BTM_AUTH_YN_BIT));

    btsnd_hcic_io_cap_req_reply(bd_addr, io_cap, oob, auth_req);
  }
}

/*******************************************************************************
 *
 * Function         BTM_ReadLocalOobData
 *
 * Description      This function is called to read the local OOB data from
 *                  LM
 *
 ******************************************************************************/
void BTM_ReadLocalOobData(void) { btsnd_hcic_read_local_oob_data(); }

/*******************************************************************************
 *
 * Function         BTM_RemoteOobDataReply
 *
 * Description      This function is called to provide the remote OOB data for
 *                  Simple Pairing in response to BTM_SP_RMT_OOB_EVT
 *
 * Parameters:      bd_addr     - Address of the peer device
 *                  c           - simple pairing Hash C.
 *                  r           - simple pairing Randomizer  C.
 *
 ******************************************************************************/
void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, BT_OCTET16 c,
                            BT_OCTET16 r) {
  BTM_TRACE_EVENT("%s() - State: %s res: %d", __func__,
                  btm_pair_state_descr(btm_cb.pairing_state), res);

  /* If timeout already expired or has been canceled, ignore the reply */
  if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP) return;

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);

  if (res != BTM_SUCCESS) {
    /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed
     * event */
    btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;
    btsnd_hcic_rem_oob_neg_reply(bd_addr);
  } else {
    btm_cb.acl_disc_reason = HCI_SUCCESS;
    btsnd_hcic_rem_oob_reply(bd_addr, c, r);
  }
}

/*******************************************************************************
 *
 * Function         BTM_BuildOobData
 *
 * Description      This function is called to build the OOB data payload to
 *                  be sent over OOB (non-Bluetooth) link
 *
 * Parameters:      p_data  - the location for OOB data
 *                  max_len - p_data size.
 *                  c       - simple pairing Hash C.
 *                  r       - simple pairing Randomizer  C.
 *                  name_len- 0, local device name would not be included.
 *                            otherwise, the local device name is included for
 *                            up to this specified length
 *
 * Returns          Number of bytes in p_data.
 *
 ******************************************************************************/
uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, BT_OCTET16 c,
                          BT_OCTET16 r, uint8_t name_len) {
  uint8_t* p = p_data;
  uint16_t len = 0;
  uint16_t name_size;
  uint8_t name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE;

  if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) {
    /* add mandatory part */
    UINT16_TO_STREAM(p, len);
    BDADDR_TO_STREAM(p, controller_get_interface()->get_address()->address);

    len = BTM_OOB_MANDATORY_SIZE;
    max_len -= len;

    /* now optional part */

    /* add Hash C */
    uint16_t delta = BTM_OOB_HASH_C_SIZE + 2;
    if (max_len >= delta) {
      *p++ = BTM_OOB_HASH_C_SIZE + 1;
      *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE;
      ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE);
      len += delta;
      max_len -= delta;
    }

    /* add Rand R */
    delta = BTM_OOB_RAND_R_SIZE + 2;
    if (max_len >= delta) {
      *p++ = BTM_OOB_RAND_R_SIZE + 1;
      *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE;
      ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE);
      len += delta;
      max_len -= delta;
    }

    /* add class of device */
    delta = BTM_OOB_COD_SIZE + 2;
    if (max_len >= delta) {
      *p++ = BTM_OOB_COD_SIZE + 1;
      *p++ = BTM_EIR_OOB_COD_TYPE;
      DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class);
      len += delta;
      max_len -= delta;
    }
    name_size = name_len;
    if (name_size > strlen(btm_cb.cfg.bd_name)) {
      name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE;
      name_size = (uint16_t)strlen(btm_cb.cfg.bd_name);
    }
    delta = name_size + 2;
    if (max_len >= delta) {
      *p++ = name_size + 1;
      *p++ = name_type;
      ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, name_size);
      len += delta;
      max_len -= delta;
    }
    /* update len */
    p = p_data;
    UINT16_TO_STREAM(p, len);
  }
  return len;
}

/*******************************************************************************
 *
 * Function         BTM_BothEndsSupportSecureConnections
 *
 * Description      This function is called to check if both the local device
 *                  and the peer device specified by bd_addr support BR/EDR
 *                  Secure Connections.
 *
 * Parameters:      bd_addr - address of the peer
 *
 * Returns          true if BR/EDR Secure Connections are supported by both
 *                  local and the remote device, else false.
 *
 ******************************************************************************/
bool BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr) {
  return ((controller_get_interface()->supports_secure_connections()) &&
          (BTM_PeerSupportsSecureConnections(bd_addr)));
}

/*******************************************************************************
 *
 * Function         BTM_PeerSupportsSecureConnections
 *
 * Description      This function is called to check if the peer supports
 *                  BR/EDR Secure Connections.
 *
 * Parameters:      bd_addr - address of the peer
 *
 * Returns          true if BR/EDR Secure Connections are supported by the peer,
 *                  else false.
 *
 ******************************************************************************/
bool BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  p_dev_rec = btm_find_dev(bd_addr);
  if (p_dev_rec == NULL) {
    BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x", __func__,
                      (bd_addr[0] << 24) + (bd_addr[1] << 16) +
                          (bd_addr[2] << 8) + bd_addr[3],
                      (bd_addr[4] << 8) + bd_addr[5]);
    return false;
  }

  return (p_dev_rec->remote_supports_secure_connections);
}

/*******************************************************************************
 *
 * Function         BTM_ReadOobData
 *
 * Description      This function is called to parse the OOB data payload
 *                  received over OOB (non-Bluetooth) link
 *
 * Parameters:      p_data  - the location for OOB data
 *                  eir_tag - The associated EIR tag to read the data.
 *                  *p_len(output) - the length of the data with the given tag.
 *
 * Returns          the beginning of the data with the given tag.
 *                  NULL, if the tag is not found.
 *
 ******************************************************************************/
uint8_t* BTM_ReadOobData(uint8_t* p_data, uint8_t eir_tag, uint8_t* p_len) {
  uint8_t* p = p_data;
  uint16_t max_len;
  uint8_t len, type;
  uint8_t* p_ret = NULL;
  uint8_t ret_len = 0;

  if (p_data) {
    STREAM_TO_UINT16(max_len, p);
    if (max_len >= BTM_OOB_MANDATORY_SIZE) {
      if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) {
        p_ret = p; /* the location for bd_addr */
        ret_len = BTM_OOB_BD_ADDR_SIZE;
      } else {
        p += BD_ADDR_LEN;
        max_len -= BTM_OOB_MANDATORY_SIZE;
        /* now the optional data in EIR format */
        while (max_len > 0) {
          len = *p++; /* tag data len + 1 */
          type = *p++;
          if (eir_tag == type) {
            p_ret = p;
            ret_len = len - 1;
            break;
          }
          /* the data size of this tag is len + 1 (tag data len + 2) */
          if (max_len > len) {
            max_len -= len;
            max_len--;
            len--;
            p += len;
          } else
            max_len = 0;
        }
      }
    }
  }

  if (p_len) *p_len = ret_len;

  return p_ret;
}

/*******************************************************************************
 *
 * Function         BTM_SetOutService
 *
 * Description      This function is called to set the service for
 *                  outgoing connections.
 *
 *                  If the profile/application calls BTM_SetSecurityLevel
 *                  before initiating a connection, this function does not
 *                  need to be called.
 *
 * Returns          void
 *
 ******************************************************************************/
void BTM_SetOutService(BD_ADDR bd_addr, uint8_t service_id,
                       uint32_t mx_chan_id) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_SEC_SERV_REC* p_serv_rec = &btm_cb.sec_serv_rec[0];

  btm_cb.p_out_serv = p_serv_rec;
  p_dev_rec = btm_find_dev(bd_addr);

  for (int i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) {
    if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) &&
        (p_serv_rec->service_id == service_id) &&
        (p_serv_rec->orig_mx_chan_id == mx_chan_id)) {
      BTM_TRACE_API(
          "BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, "
          "chan_id %d",
          p_serv_rec->service_id, p_serv_rec->psm, p_serv_rec->mx_proto_id,
          p_serv_rec->orig_mx_chan_id);
      btm_cb.p_out_serv = p_serv_rec;
      if (p_dev_rec) p_dev_rec->p_cur_service = p_serv_rec;
      break;
    }
  }
}

/************************************************************************
 *              I N T E R N A L     F U N C T I O N S
 ************************************************************************/
/*******************************************************************************
 *
 * Function         btm_sec_is_upgrade_possible
 *
 * Description      This function returns true if the existing link key
 *                  can be upgraded or if the link key does not exist.
 *
 * Returns          bool
 *
 ******************************************************************************/
static bool btm_sec_is_upgrade_possible(tBTM_SEC_DEV_REC* p_dev_rec,
                                        bool is_originator) {
  uint16_t mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM;
  bool is_possible = true;

  if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) {
    is_possible = false;
    if (p_dev_rec->p_cur_service) {
      BTM_TRACE_DEBUG(
          "%s() id: %d, link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x, "
          "flags: 0x%x",
          __func__, p_dev_rec->p_cur_service->service_id,
          p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check,
          p_dev_rec->p_cur_service->security_flags);
    } else {
      BTM_TRACE_DEBUG(
          "%s() link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x", __func__,
          p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check);
    }
    /* Already have a link key to the connected peer. Is the link key secure
    *enough?
    ** Is a link key upgrade even possible?
    */
    if ((p_dev_rec->security_required & mtm_check) /* needs MITM */
        && ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB) ||
            (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256))
        /* has unauthenticated
        link key */
        && (p_dev_rec->rmt_io_caps < BTM_IO_CAP_MAX) /* a valid peer IO cap */
        && (btm_sec_io_map[p_dev_rec->rmt_io_caps][btm_cb.devcb.loc_io_caps]))
    /* authenticated
    link key is possible */
    {
      /* upgrade is possible: check if the application wants the upgrade.
       * If the application is configured to use a global MITM flag,
       * it probably would not want to upgrade the link key based on the
       * security level database */
      is_possible = true;
    }
  }
  BTM_TRACE_DEBUG("%s() is_possible: %d sec_flags: 0x%x", __func__, is_possible,
                  p_dev_rec->sec_flags);
  return is_possible;
}

/*******************************************************************************
 *
 * Function         btm_sec_check_upgrade
 *
 * Description      This function is called to check if the existing link key
 *                  needs to be upgraded.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC* p_dev_rec,
                                  bool is_originator) {
  BTM_TRACE_DEBUG("%s()", __func__);

  /* Only check if link key already exists */
  if (!(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) return;

  if (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == true) {
    BTM_TRACE_DEBUG("need upgrade!! sec_flags:0x%x", p_dev_rec->sec_flags);
    /* upgrade is possible: check if the application wants the upgrade.
     * If the application is configured to use a global MITM flag,
     * it probably would not want to upgrade the link key based on the security
     * level database */
    tBTM_SP_UPGRADE evt_data;
    memcpy(evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
    evt_data.upgrade = true;
    if (btm_cb.api.p_sp_callback)
      (*btm_cb.api.p_sp_callback)(BTM_SP_UPGRADE_EVT,
                                  (tBTM_SP_EVT_DATA*)&evt_data);

    BTM_TRACE_DEBUG("evt_data.upgrade:0x%x", evt_data.upgrade);
    if (evt_data.upgrade) {
      /* if the application confirms the upgrade, set the upgrade bit */
      p_dev_rec->sm4 |= BTM_SM4_UPGRADE;

      /* Clear the link key known to go through authentication/pairing again */
      p_dev_rec->sec_flags &=
          ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED);
      p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED;
      BTM_TRACE_DEBUG("sec_flags:0x%x", p_dev_rec->sec_flags);
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_l2cap_access_req
 *
 * Description      This function is called by the L2CAP to grant permission to
 *                  establish L2CAP connection to or from the peer device.
 *
 * Parameters:      bd_addr       - Address of the peer device
 *                  psm           - L2CAP PSM
 *                  is_originator - true if protocol above L2CAP originates
 *                                  connection
 *                  p_callback    - Pointer to callback function called if
 *                                  this function returns PENDING after required
 *                                  procedures are complete. MUST NOT BE NULL.
 *
 * Returns          tBTM_STATUS
 *
 ******************************************************************************/
tBTM_STATUS btm_sec_l2cap_access_req(BD_ADDR bd_addr, uint16_t psm,
                                     uint16_t handle, CONNECTION_TYPE conn_type,
                                     tBTM_SEC_CALLBACK* p_callback,
                                     void* p_ref_data) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_SEC_SERV_REC* p_serv_rec;
  uint16_t security_required;
  uint16_t old_security_required;
  bool old_is_originator;
  tBTM_STATUS rc = BTM_SUCCESS;
  bool chk_acp_auth_done = false;
  bool is_originator;
  tBT_TRANSPORT transport =
      BT_TRANSPORT_BR_EDR; /* should check PSM range in LE connection oriented
                              L2CAP connection */

#if (L2CAP_UCD_INCLUDED == TRUE)
  if (conn_type & CONNECTION_TYPE_ORIG_MASK)
    is_originator = true;
  else
    is_originator = false;

  BTM_TRACE_DEBUG("%s() conn_type: 0x%x, 0x%x", __func__, conn_type,
                  p_ref_data);
#else
  is_originator = conn_type;

  BTM_TRACE_DEBUG("%s() is_originator:%d, 0x%x", __func__, is_originator,
                  p_ref_data);
#endif

  /* Find or get oldest record */
  p_dev_rec = btm_find_or_alloc_dev(bd_addr);

  p_dev_rec->hci_handle = handle;

  /* Find the service record for the PSM */
  p_serv_rec = btm_sec_find_first_serv(conn_type, psm);

  /* If there is no application registered with this PSM do not allow connection
   */
  if (!p_serv_rec) {
    BTM_TRACE_WARNING("%s() PSM: %d no application registerd", __func__, psm);
    (*p_callback)(bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED);
    return (BTM_MODE_UNSUPPORTED);
  }

  /* Services level0 by default have no security */
  if ((btm_sec_is_serv_level0(psm)) &&
      (!btm_cb.devcb.secure_connections_only)) {
    (*p_callback)(bd_addr, transport, p_ref_data, BTM_SUCCESS_NO_SECURITY);
    return (BTM_SUCCESS);
  }
#if (L2CAP_UCD_INCLUDED == TRUE)
  if (conn_type & CONNECTION_TYPE_CONNLESS_MASK) {
    if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
      security_required = btm_sec_set_serv_level4_flags(
          p_serv_rec->ucd_security_flags, is_originator);
    } else {
      security_required = p_serv_rec->ucd_security_flags;
    }

    rc = BTM_CMD_STARTED;
    if (is_originator) {
      if (((security_required & BTM_SEC_OUT_FLAGS) == 0) ||
          ((((security_required & BTM_SEC_OUT_FLAGS) ==
             BTM_SEC_OUT_AUTHENTICATE) &&
            (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) ||
          ((((security_required & BTM_SEC_OUT_FLAGS) ==
             (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) &&
            (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) ||
          ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) &&
            (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED)))) {
        rc = BTM_SUCCESS;
      }
    } else {
      if (((security_required & BTM_SEC_IN_FLAGS) == 0) ||
          ((((security_required & BTM_SEC_IN_FLAGS) ==
             BTM_SEC_IN_AUTHENTICATE) &&
            (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) ||
          ((((security_required & BTM_SEC_IN_FLAGS) ==
             (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) &&
            (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) ||
          ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) &&
            (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED)))) {
        // Check for 16 digits (or MITM)
        if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) ||
            (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) ==
              BTM_SEC_IN_MIN_16_DIGIT_PIN) &&
             btm_dev_16_digit_authenticated(p_dev_rec))) {
          rc = BTM_SUCCESS;
        }
      }
    }

    if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) &&
        (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
      rc = BTM_CMD_STARTED;
    }

    if (rc == BTM_SUCCESS) {
      if (p_callback)
        (*p_callback)(bd_addr, transport, (void*)p_ref_data, BTM_SUCCESS);

      return (BTM_SUCCESS);
    }
  } else
#endif
  {
    if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
      security_required = btm_sec_set_serv_level4_flags(
          p_serv_rec->security_flags, is_originator);
    } else {
      security_required = p_serv_rec->security_flags;
    }
  }

  BTM_TRACE_DEBUG(
      "%s: security_required 0x%04x, is_originator 0x%02x, psm  0x%04x",
      __func__, security_required, is_originator, psm);

  if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4)) {
    bool local_supports_sc =
        controller_get_interface()->supports_secure_connections();
    /* acceptor receives L2CAP Channel Connect Request for Secure Connections
     * Only service */
    if (!(local_supports_sc) ||
        !(p_dev_rec->remote_supports_secure_connections)) {
      BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d",
                      "rmt_support_for_sc : %d -> fail pairing", __func__,
                      local_supports_sc,
                      p_dev_rec->remote_supports_secure_connections);
      if (p_callback)
        (*p_callback)(bd_addr, transport, (void*)p_ref_data,
                      BTM_MODE4_LEVEL4_NOT_SUPPORTED);

      return (BTM_MODE4_LEVEL4_NOT_SUPPORTED);
    }
  }

  /* there are some devices (moto KRZR) which connects to several services at
   * the same time */
  /* we will process one after another */
  if ((p_dev_rec->p_callback) ||
      (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)) {
    BTM_TRACE_EVENT("%s() - busy - PSM:%d delayed  state: %s mode:%d, sm4:0x%x",
                    __func__, psm, btm_pair_state_descr(btm_cb.pairing_state),
                    btm_cb.security_mode, p_dev_rec->sm4);
    BTM_TRACE_EVENT("security_flags:x%x, sec_flags:x%x", security_required,
                    p_dev_rec->sec_flags);
    rc = BTM_CMD_STARTED;
    if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED ||
         btm_cb.security_mode == BTM_SEC_MODE_NONE ||
         btm_cb.security_mode == BTM_SEC_MODE_SERVICE ||
         btm_cb.security_mode == BTM_SEC_MODE_LINK) ||
        (BTM_SM4_KNOWN == p_dev_rec->sm4) ||
        (BTM_SEC_IS_SM4(p_dev_rec->sm4) &&
         (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == false))) {
      /* legacy mode - local is legacy or local is lisbon/peer is legacy
       * or SM4 with no possibility of link key upgrade */
      if (is_originator) {
        if (((security_required & BTM_SEC_OUT_FLAGS) == 0) ||
            ((((security_required & BTM_SEC_OUT_FLAGS) ==
               BTM_SEC_OUT_AUTHENTICATE) &&
              btm_dev_authenticated(p_dev_rec))) ||
            ((((security_required & BTM_SEC_OUT_FLAGS) ==
               (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) &&
              btm_dev_encrypted(p_dev_rec))) ||
            ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) &&
              btm_dev_authorized(p_dev_rec) && btm_dev_encrypted(p_dev_rec)))) {
          rc = BTM_SUCCESS;
        }
      } else {
        if (((security_required & BTM_SEC_IN_FLAGS) == 0) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              BTM_SEC_IN_AUTHENTICATE) &&
             btm_dev_authenticated(p_dev_rec)) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) &&
             btm_dev_encrypted(p_dev_rec)) ||
            (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) &&
             (btm_dev_authorized(p_dev_rec) ||
              btm_serv_trusted(p_dev_rec, p_serv_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_AUTHORIZE)) &&
             ((btm_dev_authorized(p_dev_rec) ||
               btm_serv_trusted(p_dev_rec, p_serv_rec)) &&
              btm_dev_authenticated(p_dev_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              (BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE)) &&
             ((btm_dev_authorized(p_dev_rec) ||
               btm_serv_trusted(p_dev_rec, p_serv_rec)) &&
              btm_dev_encrypted(p_dev_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) &&
             btm_dev_encrypted(p_dev_rec) &&
             (btm_dev_authorized(p_dev_rec) ||
              btm_serv_trusted(p_dev_rec, p_serv_rec)))) {
          // Check for 16 digits (or MITM)
          if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) ||
              (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) ==
                BTM_SEC_IN_MIN_16_DIGIT_PIN) &&
               btm_dev_16_digit_authenticated(p_dev_rec))) {
            rc = BTM_SUCCESS;
          }
        }
      }

      if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) &&
          (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
        rc = BTM_CMD_STARTED;
      }

      if (rc == BTM_SUCCESS) {
        if (p_callback)
          (*p_callback)(bd_addr, transport, (void*)p_ref_data, BTM_SUCCESS);
        return (BTM_SUCCESS);
      }
    }

    btm_cb.sec_req_pending = true;
    return (BTM_CMD_STARTED);
  }

  /* Save pointer to service record */
  p_dev_rec->p_cur_service = p_serv_rec;

  /* Modify security_required in btm_sec_l2cap_access_req for Lisbon */
  if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
      btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
      btm_cb.security_mode == BTM_SEC_MODE_SC) {
    if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
      if (is_originator) {
        /* SM4 to SM4 -> always authenticate & encrypt */
        security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT);
      } else /* acceptor */
      {
        /* SM4 to SM4: the acceptor needs to make sure the authentication is
         * already done */
        chk_acp_auth_done = true;
        /* SM4 to SM4 -> always authenticate & encrypt */
        security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT);
      }
    } else if (!(BTM_SM4_KNOWN & p_dev_rec->sm4)) {
      /* the remote features are not known yet */
      BTM_TRACE_DEBUG("%s: (%s) remote features unknown!!sec_flags:0x%02x",
                      __func__, (is_originator) ? "initiator" : "acceptor",
                      p_dev_rec->sec_flags);

      p_dev_rec->sm4 |= BTM_SM4_REQ_PEND;
      return (BTM_CMD_STARTED);
    }
  }

  BTM_TRACE_DEBUG(
      "%s()  sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", __func__,
      p_dev_rec->sm4, p_dev_rec->sec_flags, security_required,
      chk_acp_auth_done);

  old_security_required = p_dev_rec->security_required;
  old_is_originator = p_dev_rec->is_originator;
  p_dev_rec->security_required = security_required;
  p_dev_rec->p_ref_data = p_ref_data;
  p_dev_rec->is_originator = is_originator;

#if (L2CAP_UCD_INCLUDED == TRUE)
  if (conn_type & CONNECTION_TYPE_CONNLESS_MASK)
    p_dev_rec->is_ucd = true;
  else
    p_dev_rec->is_ucd = false;
#endif

/* If there are multiple service records used through the same PSM */
/* leave security decision for the multiplexor on the top */
#if (L2CAP_UCD_INCLUDED == TRUE)
  if (((btm_sec_find_next_serv(p_serv_rec)) != NULL) &&
      (!(conn_type & CONNECTION_TYPE_CONNLESS_MASK))) /* if not UCD */
#else
  if ((btm_sec_find_next_serv(p_serv_rec)) != NULL)
#endif
  {
    BTM_TRACE_DEBUG("no next_serv sm4:0x%x, chk:%d", p_dev_rec->sm4,
                    chk_acp_auth_done);
    if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
      BTM_TRACE_EVENT(
          "Security Manager: l2cap_access_req PSM:%d postponed for multiplexer",
          psm);
      /* pre-Lisbon: restore the old settings */
      p_dev_rec->security_required = old_security_required;
      p_dev_rec->is_originator = old_is_originator;

      (*p_callback)(bd_addr, transport, p_ref_data, BTM_SUCCESS);

      return (BTM_SUCCESS);
    }
  }

  /* if the originator is using dynamic PSM in legacy mode, do not start any
   * security process now
   * The layer above L2CAP needs to carry out the security requirement after
   * L2CAP connect
   * response is received */
  if (is_originator && ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED ||
                         btm_cb.security_mode == BTM_SEC_MODE_NONE ||
                         btm_cb.security_mode == BTM_SEC_MODE_SERVICE ||
                         btm_cb.security_mode == BTM_SEC_MODE_LINK) ||
                        !BTM_SEC_IS_SM4(p_dev_rec->sm4)) &&
      (psm >= 0x1001)) {
    BTM_TRACE_EVENT(
        "dynamic PSM:0x%x in legacy mode - postponed for upper layer", psm);
    /* restore the old settings */
    p_dev_rec->security_required = old_security_required;
    p_dev_rec->is_originator = old_is_originator;

    (*p_callback)(bd_addr, transport, p_ref_data, BTM_SUCCESS);

    return (BTM_SUCCESS);
  }

  if (chk_acp_auth_done) {
    BTM_TRACE_DEBUG(
        "(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: "
        "x%x",
        (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED),
        (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED));
    /* SM4, but we do not know for sure which level of security we need.
     * as long as we have a link key, it's OK */
    if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) ||
        (0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) {
      rc = BTM_DELAY_CHECK;
      /*
      2046 may report HCI_Encryption_Change and L2C Connection Request out of
      sequence
      because of data path issues. Delay this disconnect a little bit
      */
      LOG_INFO(
          LOG_TAG,
          "%s peer should have initiated security process by now (SM4 to SM4)",
          __func__);
      p_dev_rec->p_callback = p_callback;
      p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC;
      (*p_callback)(bd_addr, transport, p_ref_data, rc);

      return BTM_SUCCESS;
    }
  }

  p_dev_rec->p_callback = p_callback;

  if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID ||
      p_dev_rec->last_author_service_id !=
          p_dev_rec->p_cur_service->service_id) {
    /* Although authentication and encryption are per connection
    ** authorization is per access request.  For example when serial connection
    ** is up and authorized and client requests to read file (access to other
    ** scn), we need to request user's permission again.
    */
    p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED;
  }

  if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
    if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
        (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
      /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case
       */
      if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) {
        p_dev_rec->sm4 |= BTM_SM4_UPGRADE;
      }
      p_dev_rec->sec_flags &=
          ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
            BTM_SEC_AUTHENTICATED);
      BTM_TRACE_DEBUG("%s: sec_flags:0x%x", __func__, p_dev_rec->sec_flags);
    } else {
      /* If we already have a link key to the connected peer, is it secure
       * enough? */
      btm_sec_check_upgrade(p_dev_rec, is_originator);
    }
  }

  BTM_TRACE_EVENT(
      "%s() PSM:%d Handle:%d State:%d Flags: 0x%x Required: 0x%x Service ID:%d",
      __func__, psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags,
      p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id);

  rc = btm_sec_execute_procedure(p_dev_rec);
  if (rc != BTM_CMD_STARTED) {
    p_dev_rec->p_callback = NULL;
    (*p_callback)(bd_addr, transport, p_dev_rec->p_ref_data, (uint8_t)rc);
  }

  return (rc);
}

/*******************************************************************************
 *
 * Function         btm_sec_mx_access_request
 *
 * Description      This function is called by all Multiplexing Protocols during
 *                  establishing connection to or from peer device to grant
 *                  permission to establish application connection.
 *
 * Parameters:      bd_addr       - Address of the peer device
 *                  psm           - L2CAP PSM
 *                  is_originator - true if protocol above L2CAP originates
 *                                  connection
 *                  mx_proto_id   - protocol ID of the multiplexer
 *                  mx_chan_id    - multiplexer channel to reach application
 *                  p_callback    - Pointer to callback function called if
 *                                  this function returns PENDING after required
 *                                  procedures are completed
 *                  p_ref_data    - Pointer to any reference data needed by the
 *                                  the callback function.
 *
 * Returns          BTM_CMD_STARTED
 *
 ******************************************************************************/
tBTM_STATUS btm_sec_mx_access_request(BD_ADDR bd_addr, uint16_t psm,
                                      bool is_originator, uint32_t mx_proto_id,
                                      uint32_t mx_chan_id,
                                      tBTM_SEC_CALLBACK* p_callback,
                                      void* p_ref_data) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_SEC_SERV_REC* p_serv_rec;
  tBTM_STATUS rc;
  uint16_t security_required;
  bool transport = false; /* should check PSM range in LE connection oriented
                             L2CAP connection */

  BTM_TRACE_DEBUG("%s() is_originator: %d", __func__, is_originator);
  /* Find or get oldest record */
  p_dev_rec = btm_find_or_alloc_dev(bd_addr);

  /* Find the service record for the PSM */
  p_serv_rec =
      btm_sec_find_mx_serv(is_originator, psm, mx_proto_id, mx_chan_id);

  /* If there is no application registered with this PSM do not allow connection
   */
  if (!p_serv_rec) {
    if (p_callback)
      (*p_callback)(bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED);

    BTM_TRACE_ERROR(
        "Security Manager: MX service not found PSM:%d Proto:%d SCN:%d", psm,
        mx_proto_id, mx_chan_id);
    return BTM_NO_RESOURCES;
  }

  if ((btm_cb.security_mode == BTM_SEC_MODE_SC) &&
      (!btm_sec_is_serv_level0(psm))) {
    security_required = btm_sec_set_serv_level4_flags(
        p_serv_rec->security_flags, is_originator);
  } else {
    security_required = p_serv_rec->security_flags;
  }

  /* there are some devices (moto phone) which connects to several services at
   * the same time */
  /* we will process one after another */
  if ((p_dev_rec->p_callback) ||
      (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)) {
    BTM_TRACE_EVENT("%s() service PSM:%d Proto:%d SCN:%d delayed  state: %s",
                    __func__, psm, mx_proto_id, mx_chan_id,
                    btm_pair_state_descr(btm_cb.pairing_state));

    rc = BTM_CMD_STARTED;

    if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED ||
         btm_cb.security_mode == BTM_SEC_MODE_NONE ||
         btm_cb.security_mode == BTM_SEC_MODE_SERVICE ||
         btm_cb.security_mode == BTM_SEC_MODE_LINK) ||
        (BTM_SM4_KNOWN == p_dev_rec->sm4) ||
        (BTM_SEC_IS_SM4(p_dev_rec->sm4) &&
         (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == false))) {
      /* legacy mode - local is legacy or local is lisbon/peer is legacy
       * or SM4 with no possibility of link key upgrade */
      if (is_originator) {
        if (((security_required & BTM_SEC_OUT_FLAGS) == 0) ||
            ((((security_required & BTM_SEC_OUT_FLAGS) ==
               BTM_SEC_OUT_AUTHENTICATE) &&
              btm_dev_authenticated(p_dev_rec))) ||
            ((((security_required & BTM_SEC_OUT_FLAGS) ==
               (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) &&
              btm_dev_encrypted(p_dev_rec)))) {
          rc = BTM_SUCCESS;
        }
      } else {
        if (((security_required & BTM_SEC_IN_FLAGS) == 0) ||
            ((((security_required & BTM_SEC_IN_FLAGS) ==
               BTM_SEC_IN_AUTHENTICATE) &&
              btm_dev_authenticated(p_dev_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) &&
             (btm_dev_authorized(p_dev_rec) ||
              btm_serv_trusted(p_dev_rec, p_serv_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_AUTHENTICATE)) &&
             ((btm_dev_authorized(p_dev_rec) ||
               btm_serv_trusted(p_dev_rec, p_serv_rec)) &&
              btm_dev_authenticated(p_dev_rec))) ||
            (((security_required & BTM_SEC_IN_FLAGS) ==
              (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT)) &&
             ((btm_dev_authorized(p_dev_rec) ||
               btm_serv_trusted(p_dev_rec, p_serv_rec)) &&
              btm_dev_encrypted(p_dev_rec))) ||
            ((((security_required & BTM_SEC_IN_FLAGS) ==
               (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) &&
              btm_dev_encrypted(p_dev_rec)))) {
          // Check for 16 digits (or MITM)
          if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) ||
              (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) ==
                BTM_SEC_IN_MIN_16_DIGIT_PIN) &&
               btm_dev_16_digit_authenticated(p_dev_rec))) {
            rc = BTM_SUCCESS;
          }
        }
      }
      if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) &&
          (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
        rc = BTM_CMD_STARTED;
      }
    }

    if (rc == BTM_SUCCESS) {
      BTM_TRACE_EVENT("%s: allow to bypass, checking authorization", __func__);
      /* the security in BTM_SEC_IN_FLAGS is fullfilled so far, check the
       * requirements in */
      /* btm_sec_execute_procedure */
      if ((is_originator &&
           (p_serv_rec->security_flags & BTM_SEC_OUT_AUTHORIZE)) ||
          (!is_originator &&
           (p_serv_rec->security_flags & BTM_SEC_IN_AUTHORIZE))) {
        BTM_TRACE_EVENT("%s: still need authorization", __func__);
        rc = BTM_CMD_STARTED;
      }
    }

    /* Check whether there is a pending security procedure, if so we should
     * always queue */
    /* the new security request */
    if (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE) {
      BTM_TRACE_EVENT("%s: There is a pending security procedure", __func__);
      rc = BTM_CMD_STARTED;
    }
    if (rc == BTM_CMD_STARTED) {
      BTM_TRACE_EVENT("%s: call btm_sec_queue_mx_request", __func__);
      btm_sec_queue_mx_request(bd_addr, psm, is_originator, mx_proto_id,
                               mx_chan_id, p_callback, p_ref_data);
    } else /* rc == BTM_SUCCESS */
    {
      /* access granted */
      if (p_callback) {
        (*p_callback)(bd_addr, transport, p_ref_data, (uint8_t)rc);
      }
    }

    BTM_TRACE_EVENT("%s: return with rc = 0x%02x in delayed state %s", __func__,
                    rc, btm_pair_state_descr(btm_cb.pairing_state));
    return rc;
  }

  if ((!is_originator) && ((security_required & BTM_SEC_MODE4_LEVEL4) ||
                           (btm_cb.security_mode == BTM_SEC_MODE_SC))) {
    bool local_supports_sc =
        controller_get_interface()->supports_secure_connections();
    /* acceptor receives service connection establishment Request for */
    /* Secure Connections Only service */
    if (!(local_supports_sc) ||
        !(p_dev_rec->remote_supports_secure_connections)) {
      BTM_TRACE_DEBUG("%s: SC only service,local_support_for_sc %d,",
                      "remote_support_for_sc %d: fail pairing", __func__,
                      local_supports_sc,
                      p_dev_rec->remote_supports_secure_connections);

      if (p_callback)
        (*p_callback)(bd_addr, transport, (void*)p_ref_data,
                      BTM_MODE4_LEVEL4_NOT_SUPPORTED);

      return (BTM_MODE4_LEVEL4_NOT_SUPPORTED);
    }
  }

  p_dev_rec->p_cur_service = p_serv_rec;
  p_dev_rec->security_required = security_required;

  if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
      btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
      btm_cb.security_mode == BTM_SEC_MODE_SC) {
    if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
      if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
          (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
        /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case
         */
        if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) {
          p_dev_rec->sm4 |= BTM_SM4_UPGRADE;
        }

        p_dev_rec->sec_flags &=
            ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
              BTM_SEC_AUTHENTICATED);
        BTM_TRACE_DEBUG("%s: sec_flags:0x%x", __func__, p_dev_rec->sec_flags);
      } else {
        /* If we already have a link key, check if that link key is good enough
         */
        btm_sec_check_upgrade(p_dev_rec, is_originator);
      }
    }
  }

  p_dev_rec->is_originator = is_originator;
  p_dev_rec->p_callback = p_callback;
  p_dev_rec->p_ref_data = p_ref_data;

  /* Although authentication and encryption are per connection */
  /* authorization is per access request.  For example when serial connection */
  /* is up and authorized and client requests to read file (access to other */
  /* scn, we need to request user's permission again. */
  p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED);

  BTM_TRACE_EVENT(
      "%s() proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service "
      "ID:%d",
      __func__, mx_proto_id, mx_chan_id, p_dev_rec->sec_state,
      p_dev_rec->sec_flags, p_dev_rec->security_required,
      p_dev_rec->p_cur_service->service_id);

  rc = btm_sec_execute_procedure(p_dev_rec);
  if (rc != BTM_CMD_STARTED) {
    if (p_callback) {
      p_dev_rec->p_callback = NULL;
      (*p_callback)(bd_addr, transport, p_ref_data, (uint8_t)rc);
    }
  }

  return rc;
}

/*******************************************************************************
 *
 * Function         btm_sec_conn_req
 *
 * Description      This function is when the peer device is requesting
 *                  connection
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_conn_req(uint8_t* bda, uint8_t* dc) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda);

  /* Some device may request a connection before we are done with the HCI_Reset
   * sequence */
  if (!controller_get_interface()->get_is_ready()) {
    BTM_TRACE_EVENT("Security Manager: connect request when device not ready");
    btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE);
    return;
  }

  /* Security guys wants us not to allow connection from not paired devices */

  /* Check if connection is allowed for only paired devices */
  if (btm_cb.connect_only_paired) {
    if (!p_dev_rec || !(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)) {
      BTM_TRACE_EVENT(
          "Security Manager: connect request from non-paired device");
      btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE);
      return;
    }
  }

#if (BTM_ALLOW_CONN_IF_NONDISCOVER == FALSE)
  /* If non-discoverable, only allow known devices to connect */
  if (btm_cb.btm_inq_vars.discoverable_mode == BTM_NON_DISCOVERABLE) {
    if (!p_dev_rec) {
      BTM_TRACE_EVENT(
          "Security Manager: connect request from not paired device");
      btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE);
      return;
    }
  }
#endif

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
      (!memcmp(btm_cb.pairing_bda, bda, BD_ADDR_LEN))) {
    BTM_TRACE_EVENT(
        "Security Manager: reject connect request from bonding device");

    /* incoming connection from bonding device is rejected */
    btm_cb.pairing_flags |= BTM_PAIR_FLAGS_REJECTED_CONNECT;
    btsnd_hcic_reject_conn(bda, HCI_ERR_HOST_REJECT_DEVICE);
    return;
  }

  /* Host is not interested or approved connection.  Save BDA and DC and */
  /* pass request to L2CAP */
  memcpy(btm_cb.connecting_bda, bda, BD_ADDR_LEN);
  memcpy(btm_cb.connecting_dc, dc, DEV_CLASS_LEN);

  if (l2c_link_hci_conn_req(bda)) {
    if (!p_dev_rec) {
      /* accept the connection -> allocate a device record */
      p_dev_rec = btm_sec_alloc_dev(bda);
    }
    if (p_dev_rec) {
      p_dev_rec->sm4 |= BTM_SM4_CONN_PEND;
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_bond_cancel_complete
 *
 * Description      This function is called to report bond cancel complete
 *                  event.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_sec_bond_cancel_complete(void) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) ||
      (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state &&
       BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) ||
      (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME &&
       BTM_PAIR_FLAGS_WE_CANCEL_DD & btm_cb.pairing_flags)) {
    /* for dedicated bonding in legacy mode, authentication happens at "link
     * level"
     * btm_sec_connected is called with failed status.
     * In theory, the code that handles is_pairing_device/true should clean out
     * security related code.
     * However, this function may clean out the security related flags and
     * btm_sec_connected would not know
     * this function also needs to do proper clean up.
     */
    p_dev_rec = btm_find_dev(btm_cb.pairing_bda);
    if (p_dev_rec != NULL) p_dev_rec->security_required = BTM_SEC_NONE;
    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

    /* Notify application that the cancel succeeded */
    if (btm_cb.api.p_bond_cancel_cmpl_callback)
      btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS);
  }
}

/*******************************************************************************
 *
 * Function         btm_create_conn_cancel_complete
 *
 * Description      This function is called when the command complete message
 *                  is received from the HCI for the create connection cancel
 *                  command.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_create_conn_cancel_complete(uint8_t* p) {
  uint8_t status;

  STREAM_TO_UINT8(status, p);
  BTM_TRACE_EVENT("btm_create_conn_cancel_complete(): in State: %s  status:%d",
                  btm_pair_state_descr(btm_cb.pairing_state), status);

  /* if the create conn cancel cmd was issued by the bond cancel,
  ** the application needs to be notified that bond cancel succeeded
  */
  switch (status) {
    case HCI_SUCCESS:
      btm_sec_bond_cancel_complete();
      break;
    case HCI_ERR_CONNECTION_EXISTS:
    case HCI_ERR_NO_CONNECTION:
    default:
      /* Notify application of the error */
      if (btm_cb.api.p_bond_cancel_cmpl_callback)
        btm_cb.api.p_bond_cancel_cmpl_callback(BTM_ERR_PROCESSING);
      break;
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_check_pending_reqs
 *
 * Description      This function is called at the end of the security procedure
 *                  to let L2CAP and RFCOMM know to re-submit any pending
 *                  requests
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_check_pending_reqs(void) {
  if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) {
    /* First, resubmit L2CAP requests */
    if (btm_cb.sec_req_pending) {
      btm_cb.sec_req_pending = false;
      l2cu_resubmit_pending_sec_req(NULL);
    }

    /* Now, re-submit anything in the mux queue */
    fixed_queue_t* bq = btm_cb.sec_pending_q;

    btm_cb.sec_pending_q = fixed_queue_new(SIZE_MAX);

    tBTM_SEC_QUEUE_ENTRY* p_e;
    while ((p_e = (tBTM_SEC_QUEUE_ENTRY*)fixed_queue_try_dequeue(bq)) != NULL) {
      /* Check that the ACL is still up before starting security procedures */
      if (btm_bda_to_acl(p_e->bd_addr, p_e->transport) != NULL) {
        if (p_e->psm != 0) {
          BTM_TRACE_EVENT(
              "%s PSM:0x%04x Is_Orig:%u mx_proto_id:%u mx_chan_id:%u", __func__,
              p_e->psm, p_e->is_orig, p_e->mx_proto_id, p_e->mx_chan_id);

          btm_sec_mx_access_request(p_e->bd_addr, p_e->psm, p_e->is_orig,
                                    p_e->mx_proto_id, p_e->mx_chan_id,
                                    p_e->p_callback, p_e->p_ref_data);
        } else {
          BTM_SetEncryption(p_e->bd_addr, p_e->transport, p_e->p_callback,
                            p_e->p_ref_data, p_e->sec_act);
        }
      }

      osi_free(p_e);
    }
    fixed_queue_free(bq, NULL);
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_init
 *
 * Description      This function is on the SEC startup
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_init(uint8_t sec_mode) {
  btm_cb.security_mode = sec_mode;
  memset(btm_cb.pairing_bda, 0xff, BD_ADDR_LEN);
  btm_cb.max_collision_delay = BTM_SEC_MAX_COLLISION_DELAY;
}

/*******************************************************************************
 *
 * Function         btm_sec_device_down
 *
 * Description      This function should be called when device is disabled or
 *                  turned off
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_device_down(void) {
  BTM_TRACE_EVENT("%s() State: %s", __func__,
                  btm_pair_state_descr(btm_cb.pairing_state));
  btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
}

/*******************************************************************************
 *
 * Function         btm_sec_dev_reset
 *
 * Description      This function should be called after device reset
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_dev_reset(void) {
  if (controller_get_interface()->supports_simple_pairing()) {
    /* set the default IO capabilities */
    btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS;
    /* add mx service to use no security */
    BTM_SetSecurityLevel(false, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX,
                         BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0);
  } else {
    btm_cb.security_mode = BTM_SEC_MODE_SERVICE;
  }

  BTM_TRACE_DEBUG("btm_sec_dev_reset sec mode: %d", btm_cb.security_mode);
}

/*******************************************************************************
 *
 * Function         btm_sec_abort_access_req
 *
 * Description      This function is called by the L2CAP or RFCOMM to abort
 *                  the pending operation.
 *
 * Parameters:      bd_addr       - Address of the peer device
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_abort_access_req(BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);

  if (!p_dev_rec) return;

  if ((p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) &&
      (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING))
    return;

  p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
  p_dev_rec->p_callback = NULL;
}

/*******************************************************************************
 *
 * Function         btm_sec_dd_create_conn
 *
 * Description      This function is called to create the ACL connection for
 *                  the dedicated boding process
 *
 * Returns          void
 *
 ******************************************************************************/
static tBTM_STATUS btm_sec_dd_create_conn(tBTM_SEC_DEV_REC* p_dev_rec) {
  tL2C_LCB* p_lcb =
      l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR);
  if (p_lcb && (p_lcb->link_state == LST_CONNECTED ||
                p_lcb->link_state == LST_CONNECTING)) {
    BTM_TRACE_WARNING("%s Connection already exists", __func__);
    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);
    return BTM_CMD_STARTED;
  }

  /* Make sure an L2cap link control block is available */
  if (!p_lcb &&
      (p_lcb = l2cu_allocate_lcb(p_dev_rec->bd_addr, true,
                                 BT_TRANSPORT_BR_EDR)) == NULL) {
    BTM_TRACE_WARNING(
        "Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]",
        p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
        p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);

    return (BTM_NO_RESOURCES);
  }

  /* set up the control block to indicated dedicated bonding */
  btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE;

  if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == false) {
    BTM_TRACE_WARNING(
        "Security Manager: failed create  [%02x%02x%02x%02x%02x%02x]",
        p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
        p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);

    l2cu_release_lcb(p_lcb);
    return (BTM_NO_RESOURCES);
  }

  btm_acl_update_busy_level(BTM_BLI_PAGE_EVT);

  BTM_TRACE_DEBUG(
      "Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]",
      p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
      p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_PIN_REQ);

  return (BTM_CMD_STARTED);
}

bool is_state_getting_name(void* data, void* context) {
  tBTM_SEC_DEV_REC* p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(data);

  if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) {
    return false;
  }
  return true;
}

/*******************************************************************************
 *
 * Function         btm_sec_rmt_name_request_complete
*
 * Description      This function is called when remote name was obtained from
 *                  the peer device
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_rmt_name_request_complete(uint8_t* p_bd_addr, uint8_t* p_bd_name,
                                       uint8_t status) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  int i;
  DEV_CLASS dev_class;
  uint8_t old_sec_state;

  BTM_TRACE_EVENT("btm_sec_rmt_name_request_complete");
  if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) ||
      ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr))) {
    btm_acl_resubmit_page();
  }

  /* If remote name request failed, p_bd_addr is null and we need to search */
  /* based on state assuming that we are doing 1 at a time */
  if (p_bd_addr)
    p_dev_rec = btm_find_dev(p_bd_addr);
  else {
    list_node_t* node =
        list_foreach(btm_cb.sec_dev_rec, is_state_getting_name, NULL);
    if (node != NULL) {
      p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(list_node(node));
      p_bd_addr = p_dev_rec->bd_addr;
    } else {
      p_dev_rec = NULL;
    }
  }

  /* Commenting out trace due to obf/compilation problems.
  */
  if (!p_bd_name) p_bd_name = (uint8_t*)"";

  if (p_dev_rec) {
    BTM_TRACE_EVENT(
        "%s PairState: %s  RemName: %s  status: %d State:%d  p_dev_rec: "
        "0x%08x ",
        __func__, btm_pair_state_descr(btm_cb.pairing_state), p_bd_name, status,
        p_dev_rec->sec_state, p_dev_rec);
  } else {
    BTM_TRACE_EVENT("%s PairState: %s  RemName: %s  status: %d", __func__,
                    btm_pair_state_descr(btm_cb.pairing_state), p_bd_name,
                    status);
  }

  if (p_dev_rec) {
    old_sec_state = p_dev_rec->sec_state;
    if (status == HCI_SUCCESS) {
      strlcpy((char*)p_dev_rec->sec_bd_name, (char*)p_bd_name,
              BTM_MAX_REM_BD_NAME_LEN);
      p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN;
      BTM_TRACE_EVENT("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x",
                      p_dev_rec->sec_flags);
    } else {
      /* Notify all clients waiting for name to be resolved even if it failed so
       * clients can continue */
      p_dev_rec->sec_bd_name[0] = 0;
    }

    if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)
      p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;

    /* Notify all clients waiting for name to be resolved */
    for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) {
      if (btm_cb.p_rmt_name_callback[i] && p_bd_addr)
        (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class,
                                         p_dev_rec->sec_bd_name);
    }
  } else {
    dev_class[0] = 0;
    dev_class[1] = 0;
    dev_class[2] = 0;

    /* Notify all clients waiting for name to be resolved even if not found so
     * clients can continue */
    for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) {
      if (btm_cb.p_rmt_name_callback[i] && p_bd_addr)
        (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, dev_class, (uint8_t*)"");
    }

    return;
  }

  /* If we were delaying asking UI for a PIN because name was not resolved, ask
   * now */
  if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr &&
      (memcmp(btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0)) {
    BTM_TRACE_EVENT(
        "%s() delayed pin now being requested flags:0x%x, "
        "(p_pin_callback=0x%p)",
        __func__, btm_cb.pairing_flags, btm_cb.api.p_pin_callback);

    if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0 &&
        btm_cb.api.p_pin_callback) {
      BTM_TRACE_EVENT("%s() calling pin_callback", __func__);
      btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD;
      (*btm_cb.api.p_pin_callback)(
          p_dev_rec->bd_addr, p_dev_rec->dev_class, p_bd_name,
          (p_dev_rec->p_cur_service == NULL)
              ? false
              : (p_dev_rec->p_cur_service->security_flags &
                 BTM_SEC_IN_MIN_16_DIGIT_PIN));
    }

    /* Set the same state again to force the timer to be restarted */
    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_PIN);
    return;
  }

  /* Check if we were delaying bonding because name was not resolved */
  if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) {
    if (p_bd_addr && memcmp(btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) {
      BTM_TRACE_EVENT("%s() continue bonding sm4: 0x%04x, status:0x%x",
                      __func__, p_dev_rec->sm4, status);
      if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD) {
        btm_sec_bond_cancel_complete();
        return;
      }

      if (status != HCI_SUCCESS) {
        btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

        if (btm_cb.api.p_auth_complete_callback)
          (*btm_cb.api.p_auth_complete_callback)(
              p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
              status);
        return;
      }

      /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is
       * not reported */
      if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
        /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not
         * set.*/
        /* If it is set, there may be a race condition */
        BTM_TRACE_DEBUG("%s IS_SM4_UNKNOWN Flags:0x%04x", __func__,
                        btm_cb.pairing_flags);
        if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0)
          p_dev_rec->sm4 |= BTM_SM4_KNOWN;
      }

      BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d",
                      __func__, p_dev_rec->sm4,
                      BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4),
                      BTM_SEC_IS_SM4(p_dev_rec->sm4),
                      BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4));

      /* BT 2.1 or carkit, bring up the connection to force the peer to request
      *PIN.
      ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if
      *needed)
      */
      if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) ||
          !btm_sec_check_prefetch_pin(p_dev_rec)) {
        /* if we rejected incoming connection request, we have to wait
         * HCI_Connection_Complete event */
        /*  before originating  */
        if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) {
          BTM_TRACE_WARNING(
              "%s: waiting HCI_Connection_Complete after rejecting connection",
              __func__);
        }
        /* Both we and the peer are 2.1 - continue to create connection */
        else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) {
          BTM_TRACE_WARNING("%s: failed to start connection", __func__);

          btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

          if (btm_cb.api.p_auth_complete_callback) {
            (*btm_cb.api.p_auth_complete_callback)(
                p_dev_rec->bd_addr, p_dev_rec->dev_class,
                p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL);
          }
        }
      }
      return;
    } else {
      BTM_TRACE_WARNING("%s: wrong BDA, retry with pairing BDA", __func__);
      if (BTM_ReadRemoteDeviceName(btm_cb.pairing_bda, NULL,
                                   BT_TRANSPORT_BR_EDR) != BTM_CMD_STARTED) {
        BTM_TRACE_ERROR("%s: failed to start remote name request", __func__);
        if (btm_cb.api.p_auth_complete_callback) {
          (*btm_cb.api.p_auth_complete_callback)(
              p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
              HCI_ERR_MEMORY_FULL);
        }
      };
      return;
    }
  }

  /* check if we were delaying link_key_callback because name was not resolved
   */
  if (p_dev_rec->link_key_not_sent) {
    /* If HCI connection complete has not arrived, wait for it */
    if (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) return;

    p_dev_rec->link_key_not_sent = false;
    btm_send_link_key_notif(p_dev_rec);

    /* If its not us who perform authentication, we should tell stackserver */
    /* that some authentication has been completed                          */
    /* This is required when different entities receive link notification and
     * auth complete */
    if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) {
      if (btm_cb.api.p_auth_complete_callback)
        (*btm_cb.api.p_auth_complete_callback)(
            p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
            HCI_SUCCESS);
    }
  }

  /* If this is a bonding procedure can disconnect the link now */
  if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
      (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) {
    BTM_TRACE_WARNING("btm_sec_rmt_name_request_complete (none/ce)");
    p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHENTICATE);
    l2cu_start_post_bond_timer(p_dev_rec->hci_handle);
    return;
  }

  if (old_sec_state != BTM_SEC_STATE_GETTING_NAME) return;

  /* If get name failed, notify the waiting layer */
  if (status != HCI_SUCCESS) {
    btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, false);
    return;
  }

  if (p_dev_rec->sm4 & BTM_SM4_REQ_PEND) {
    BTM_TRACE_EVENT("waiting for remote features!!");
    return;
  }

  /* Remote Name succeeded, execute the next security procedure, if any */
  status = (uint8_t)btm_sec_execute_procedure(p_dev_rec);

  /* If result is pending reply from the user or from the device is pending */
  if (status == BTM_CMD_STARTED) return;

  /* There is no next procedure or start of procedure failed, notify the waiting
   * layer */
  btm_sec_dev_rec_cback_event(p_dev_rec, status, false);
}

/*******************************************************************************
 *
 * Function         btm_sec_rmt_host_support_feat_evt
 *
 * Description      This function is called when the
 *                  HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_rmt_host_support_feat_evt(uint8_t* p) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  BD_ADDR bd_addr; /* peer address */
  BD_FEATURES features;

  STREAM_TO_BDADDR(bd_addr, p);
  p_dev_rec = btm_find_or_alloc_dev(bd_addr);

  BTM_TRACE_EVENT("btm_sec_rmt_host_support_feat_evt  sm4: 0x%x  p[0]: 0x%x",
                  p_dev_rec->sm4, p[0]);

  if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
    p_dev_rec->sm4 = BTM_SM4_KNOWN;
    STREAM_TO_ARRAY(features, p, HCI_FEATURE_BYTES_PER_PAGE);
    if (HCI_SSP_HOST_SUPPORTED(features)) {
      p_dev_rec->sm4 = BTM_SM4_TRUE;
    }
    BTM_TRACE_EVENT(
        "btm_sec_rmt_host_support_feat_evt sm4: 0x%x features[0]: 0x%x",
        p_dev_rec->sm4, features[0]);
  }
}

/*******************************************************************************
 *
 * Function         btm_io_capabilities_req
 *
 * Description      This function is called when LM request for the IO
 *                  capability of the local device and
 *                  if the OOB data is present for the device in the event
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_io_capabilities_req(uint8_t* p) {
  tBTM_SP_IO_REQ evt_data;
  uint8_t err_code = 0;
  tBTM_SEC_DEV_REC* p_dev_rec;
  bool is_orig = true;
  uint8_t callback_rc = BTM_SUCCESS;

  STREAM_TO_BDADDR(evt_data.bd_addr, p);

  /* setup the default response according to compile options */
  /* assume that the local IO capability does not change
   * loc_io_caps is initialized with the default value */
  evt_data.io_cap = btm_cb.devcb.loc_io_caps;
  evt_data.oob_data = BTM_OOB_NONE;
  evt_data.auth_req = BTM_DEFAULT_AUTH_REQ;

  BTM_TRACE_EVENT("%s: State: %s", __func__,
                  btm_pair_state_descr(btm_cb.pairing_state));

  p_dev_rec = btm_find_or_alloc_dev(evt_data.bd_addr);

  BTM_TRACE_DEBUG("%s:Security mode: %d, Num Read Remote Feat pages: %d",
                  __func__, btm_cb.security_mode, p_dev_rec->num_read_pages);

  if ((btm_cb.security_mode == BTM_SEC_MODE_SC) &&
      (p_dev_rec->num_read_pages == 0)) {
    BTM_TRACE_EVENT("%s: Device security mode is SC only.",
                    "To continue need to know remote features.", __func__);

    p_dev_rec->remote_features_needed = true;
    return;
  }

  p_dev_rec->sm4 |= BTM_SM4_TRUE;

  BTM_TRACE_EVENT("%s: State: %s  Flags: 0x%04x  p_cur_service: 0x%08x",
                  __func__, btm_pair_state_descr(btm_cb.pairing_state),
                  btm_cb.pairing_flags, p_dev_rec->p_cur_service);

  if (p_dev_rec->p_cur_service) {
    BTM_TRACE_EVENT("%s: cur_service psm: 0x%04x, security_flags: 0x%04x",
                    __func__, p_dev_rec->p_cur_service->psm,
                    p_dev_rec->p_cur_service->security_flags);
  }

  switch (btm_cb.pairing_state) {
    /* initiator connecting */
    case BTM_PAIR_STATE_IDLE:
      // TODO: Handle Idle pairing state
      // security_required = p_dev_rec->security_required;
      break;

    /* received IO capability response already->acceptor */
    case BTM_PAIR_STATE_INCOMING_SSP:
      is_orig = false;

      if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) {
        /* acceptor in dedicated bonding */
        evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ;
      }
      break;

    /* initiator, at this point it is expected to be dedicated bonding
    initiated by local device */
    case BTM_PAIR_STATE_WAIT_PIN_REQ:
      if (!memcmp(evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN)) {
        evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ;
      } else {
        err_code = HCI_ERR_HOST_BUSY_PAIRING;
      }
      break;

    /* any other state is unexpected */
    default:
      err_code = HCI_ERR_HOST_BUSY_PAIRING;
      BTM_TRACE_ERROR("%s: Unexpected Pairing state received %d", __func__,
                      btm_cb.pairing_state);
      break;
  }

  if (btm_cb.pairing_disabled) {
    /* pairing is not allowed */
    BTM_TRACE_DEBUG("%s: Pairing is not allowed -> fail pairing.", __func__);
    err_code = HCI_ERR_PAIRING_NOT_ALLOWED;
  } else if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
    bool local_supports_sc =
        controller_get_interface()->supports_secure_connections();
    /* device in Secure Connections Only mode */
    if (!(local_supports_sc) ||
        !(p_dev_rec->remote_supports_secure_connections)) {
      BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d,",
                      " remote_support_for_sc 0x%02x -> fail pairing", __func__,
                      local_supports_sc,
                      p_dev_rec->remote_supports_secure_connections);

      err_code = HCI_ERR_PAIRING_NOT_ALLOWED;
    }
  }

  if (err_code != 0) {
    btsnd_hcic_io_cap_req_neg_reply(evt_data.bd_addr, err_code);
    return;
  }

  evt_data.is_orig = is_orig;

  if (is_orig) {
    /* local device initiated the pairing non-bonding -> use p_cur_service */
    if (!(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
        p_dev_rec->p_cur_service &&
        (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_AUTHENTICATE)) {
      if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
        /* SC only mode device requires MITM protection */
        evt_data.auth_req = BTM_AUTH_SP_YES;
      } else {
        evt_data.auth_req =
            (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_MITM)
                ? BTM_AUTH_SP_YES
                : BTM_AUTH_SP_NO;
      }
    }
  }

  /* Notify L2CAP to increase timeout */
  l2c_pin_code_request(evt_data.bd_addr);

  memcpy(btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN);

  if (!memcmp(evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN))
    memcpy(p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN);

  btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS);

  callback_rc = BTM_SUCCESS;
  if (p_dev_rec->sm4 & BTM_SM4_UPGRADE) {
    p_dev_rec->sm4 &= ~BTM_SM4_UPGRADE;

    /* link key upgrade: always use SPGB_YES - assuming we want to save the link
     * key */
    evt_data.auth_req = BTM_AUTH_SPGB_YES;
  } else if (btm_cb.api.p_sp_callback) {
    /* the callback function implementation may change the IO capability... */
    callback_rc = (*btm_cb.api.p_sp_callback)(BTM_SP_IO_REQ_EVT,
                                              (tBTM_SP_EVT_DATA*)&evt_data);
  }

  if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != evt_data.oob_data)) {
    if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) {
      evt_data.auth_req =
          (BTM_AUTH_DD_BOND | (evt_data.auth_req & BTM_AUTH_YN_BIT));
    }

    if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
      /* At this moment we know that both sides are SC capable, device in */
      /* SC only mode requires MITM for any service so let's set MITM bit */
      evt_data.auth_req |= BTM_AUTH_YN_BIT;
      BTM_TRACE_DEBUG(
          "%s: for device in \"SC only\" mode set auth_req to 0x%02x", __func__,
          evt_data.auth_req);
    }

    /* if the user does not indicate "reply later" by setting the oob_data to
     * unknown */
    /* send the response right now. Save the current IO capability in the
     * control block */
    btm_cb.devcb.loc_auth_req = evt_data.auth_req;
    btm_cb.devcb.loc_io_caps = evt_data.io_cap;

    BTM_TRACE_EVENT("%s: State: %s  IO_CAP:%d oob_data:%d auth_req:%d",
                    __func__, btm_pair_state_descr(btm_cb.pairing_state),
                    evt_data.io_cap, evt_data.oob_data, evt_data.auth_req);

    btsnd_hcic_io_cap_req_reply(evt_data.bd_addr, evt_data.io_cap,
                                evt_data.oob_data, evt_data.auth_req);
  }
}

/*******************************************************************************
 *
 * Function         btm_io_capabilities_rsp
 *
 * Description      This function is called when the IO capability of the
 *                  specified device is received
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_io_capabilities_rsp(uint8_t* p) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_SP_IO_RSP evt_data;

  STREAM_TO_BDADDR(evt_data.bd_addr, p);
  STREAM_TO_UINT8(evt_data.io_cap, p);
  STREAM_TO_UINT8(evt_data.oob_data, p);
  STREAM_TO_UINT8(evt_data.auth_req, p);

  /* Allocate a new device record or reuse the oldest one */
  p_dev_rec = btm_find_or_alloc_dev(evt_data.bd_addr);

  /* If no security is in progress, this indicates incoming security */
  if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) {
    memcpy(btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_INCOMING_SSP);

    /* Make sure we reset the trusted mask to help against attacks */
    BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask);

    /* work around for FW bug */
    btm_inq_stop_on_ssp();
  }

  /* Notify L2CAP to increase timeout */
  l2c_pin_code_request(evt_data.bd_addr);

  /* We must have a device record here.
   * Use the connecting device's CoD for the connection */
  if (!memcmp(evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN))
    memcpy(p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN);

  /* peer sets dedicated bonding bit and we did not initiate dedicated bonding
   */
  if (btm_cb.pairing_state ==
          BTM_PAIR_STATE_INCOMING_SSP /* peer initiated bonding */
      && (evt_data.auth_req &
          BTM_AUTH_DD_BOND)) /* and dedicated bonding bit is set */
  {
    btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PEER_STARTED_DD;
  }

  /* save the IO capability in the device record */
  p_dev_rec->rmt_io_caps = evt_data.io_cap;
  p_dev_rec->rmt_auth_req = evt_data.auth_req;

  if (btm_cb.api.p_sp_callback)
    (*btm_cb.api.p_sp_callback)(BTM_SP_IO_RSP_EVT,
                                (tBTM_SP_EVT_DATA*)&evt_data);
}

/*******************************************************************************
 *
 * Function         btm_proc_sp_req_evt
 *
 * Description      This function is called to process/report
 *                  HCI_USER_CONFIRMATION_REQUEST_EVT
 *                  or HCI_USER_PASSKEY_REQUEST_EVT
 *                  or HCI_USER_PASSKEY_NOTIFY_EVT
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_proc_sp_req_evt(tBTM_SP_EVT event, uint8_t* p) {
  tBTM_STATUS status = BTM_ERR_PROCESSING;
  tBTM_SP_EVT_DATA evt_data;
  uint8_t* p_bda = evt_data.cfm_req.bd_addr;
  tBTM_SEC_DEV_REC* p_dev_rec;

  /* All events start with bd_addr */
  STREAM_TO_BDADDR(p_bda, p);

  BTM_TRACE_EVENT(
      "btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s",
      (p_bda[0] << 24) + (p_bda[1] << 16) + (p_bda[2] << 8) + p_bda[3],
      (p_bda[4] << 8) + p_bda[5], event,
      btm_pair_state_descr(btm_cb.pairing_state));

  p_dev_rec = btm_find_dev(p_bda);
  if ((p_dev_rec != NULL) && (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0)) {
    memcpy(evt_data.cfm_req.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
    memcpy(evt_data.cfm_req.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN);

    strlcpy((char*)evt_data.cfm_req.bd_name, (char*)p_dev_rec->sec_bd_name,
            BTM_MAX_REM_BD_NAME_LEN);

    switch (event) {
      case BTM_SP_CFM_REQ_EVT:
        /* Numeric confirmation. Need user to conf the passkey */
        btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM);

        /* The device record must be allocated in the "IO cap exchange" step */
        STREAM_TO_UINT32(evt_data.cfm_req.num_val, p);
        BTM_TRACE_DEBUG("BTM_SP_CFM_REQ_EVT:  num_val: %u",
                        evt_data.cfm_req.num_val);

        evt_data.cfm_req.just_works = true;

/* process user confirm req in association with the auth_req param */
#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_IO)
        if (p_dev_rec->rmt_io_caps == BTM_IO_CAP_UNKNOWN) {
          BTM_TRACE_ERROR(
              "%s did not receive IO cap response prior"
              " to BTM_SP_CFM_REQ_EVT, failing pairing request",
              __func__);
          status = BTM_WRONG_MODE;
          BTM_ConfirmReqReply(status, p_bda);
          return;
        }
        if ((p_dev_rec->rmt_io_caps == BTM_IO_CAP_IO) &&
            (btm_cb.devcb.loc_io_caps == BTM_IO_CAP_IO) &&
            ((p_dev_rec->rmt_auth_req & BTM_AUTH_SP_YES) ||
             (btm_cb.devcb.loc_auth_req & BTM_AUTH_SP_YES))) {
          /* Both devices are DisplayYesNo and one or both devices want to
             authenticate -> use authenticated link key */
          evt_data.cfm_req.just_works = false;
        }
#endif
        BTM_TRACE_DEBUG(
            "btm_proc_sp_req_evt()  just_works:%d, io loc:%d, rmt:%d, auth "
            "loc:%d, rmt:%d",
            evt_data.cfm_req.just_works, btm_cb.devcb.loc_io_caps,
            p_dev_rec->rmt_io_caps, btm_cb.devcb.loc_auth_req,
            p_dev_rec->rmt_auth_req);

        evt_data.cfm_req.loc_auth_req = btm_cb.devcb.loc_auth_req;
        evt_data.cfm_req.rmt_auth_req = p_dev_rec->rmt_auth_req;
        evt_data.cfm_req.loc_io_caps = btm_cb.devcb.loc_io_caps;
        evt_data.cfm_req.rmt_io_caps = p_dev_rec->rmt_io_caps;
        break;

      case BTM_SP_KEY_NOTIF_EVT:
        /* Passkey notification (other side is a keyboard) */
        STREAM_TO_UINT32(evt_data.key_notif.passkey, p);
        BTM_TRACE_DEBUG("BTM_SP_KEY_NOTIF_EVT:  passkey: %u",
                        evt_data.key_notif.passkey);

        btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
        break;

#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
      case BTM_SP_KEY_REQ_EVT:
        /* HCI_USER_PASSKEY_REQUEST_EVT */
        btm_sec_change_pairing_state(BTM_PAIR_STATE_KEY_ENTRY);
        break;
#endif
    }

    if (btm_cb.api.p_sp_callback) {
      status = (*btm_cb.api.p_sp_callback)(event, (tBTM_SP_EVT_DATA*)&evt_data);
      if (status != BTM_NOT_AUTHORIZED) {
        return;
      }
      /* else BTM_NOT_AUTHORIZED means when the app wants to reject the req
       * right now */
    } else if ((event == BTM_SP_CFM_REQ_EVT) &&
               (evt_data.cfm_req.just_works == true)) {
      /* automatically reply with just works if no sp_cback */
      status = BTM_SUCCESS;
    }

    if (event == BTM_SP_CFM_REQ_EVT) {
      BTM_TRACE_DEBUG("calling BTM_ConfirmReqReply with status: %d", status);
      BTM_ConfirmReqReply(status, p_bda);
    }
#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
    else if (event == BTM_SP_KEY_REQ_EVT) {
      BTM_PasskeyReqReply(status, p_bda, 0);
    }
#endif
    return;
  }

  /* Something bad. we can only fail this connection */
  btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;

  if (BTM_SP_CFM_REQ_EVT == event) {
    btsnd_hcic_user_conf_reply(p_bda, false);
  } else if (BTM_SP_KEY_NOTIF_EVT == event) {
    /* do nothing -> it very unlikely to happen.
    This event is most likely to be received by a HID host when it first
    connects to a HID device.
    Usually the Host initiated the connection in this case.
    On Mobile platforms, if there's a security process happening,
    the host probably can not initiate another connection.
    BTW (PC) is another story.  */
    p_dev_rec = btm_find_dev(p_bda);
    if (p_dev_rec != NULL) {
      btm_sec_disconnect(p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE);
    }
  }
#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
  else {
    btsnd_hcic_user_passkey_neg_reply(p_bda);
  }
#endif
}

/*******************************************************************************
 *
 * Function         btm_keypress_notif_evt
 *
 * Description      This function is called when a key press notification is
 *                  received
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_keypress_notif_evt(uint8_t* p) {
  tBTM_SP_KEYPRESS evt_data;
  uint8_t* p_bda;

  /* parse & report BTM_SP_KEYPRESS_EVT */
  if (btm_cb.api.p_sp_callback) {
    p_bda = evt_data.bd_addr;

    STREAM_TO_BDADDR(p_bda, p);
    evt_data.notif_type = *p;

    (*btm_cb.api.p_sp_callback)(BTM_SP_KEYPRESS_EVT,
                                (tBTM_SP_EVT_DATA*)&evt_data);
  }
}

/*******************************************************************************
 *
 * Function         btm_simple_pair_complete
 *
 * Description      This function is called when simple pairing process is
 *                  complete
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_simple_pair_complete(uint8_t* p) {
  tBTM_SP_COMPLT evt_data;
  tBTM_SEC_DEV_REC* p_dev_rec;
  uint8_t status;
  bool disc = false;

  status = *p++;
  STREAM_TO_BDADDR(evt_data.bd_addr, p);

  p_dev_rec = btm_find_dev(evt_data.bd_addr);
  if (p_dev_rec == NULL) {
    BTM_TRACE_ERROR("btm_simple_pair_complete() with unknown BDA: %08x%04x",
                    (evt_data.bd_addr[0] << 24) + (evt_data.bd_addr[1] << 16) +
                        (evt_data.bd_addr[2] << 8) + evt_data.bd_addr[3],
                    (evt_data.bd_addr[4] << 8) + evt_data.bd_addr[5]);
    return;
  }

  BTM_TRACE_EVENT(
      "btm_simple_pair_complete()  Pair State: %s  Status:%d  sec_state: %u",
      btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state);

  evt_data.status = BTM_ERR_PROCESSING;
  if (status == HCI_SUCCESS) {
    evt_data.status = BTM_SUCCESS;
    p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;
  } else {
    if (status == HCI_ERR_PAIRING_NOT_ALLOWED) {
      /* The test spec wants the peer device to get this failure code. */
      btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_DISCONNECT);

      /* Change the timer to 1 second */
      alarm_set_on_queue(btm_cb.pairing_timer, BT_1SEC_TIMEOUT_MS,
                         btm_sec_pairing_timeout, NULL,
                         btu_general_alarm_queue);
    } else if (memcmp(btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN) == 0) {
      /* stop the timer */
      alarm_cancel(btm_cb.pairing_timer);

      if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) {
        /* the initiating side: will receive auth complete event. disconnect ACL
         * at that time */
        disc = true;
      }
    } else
      disc = true;
  }

  /* Let the pairing state stay active, p_auth_complete_callback will report the
   * failure */
  memcpy(evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
  memcpy(evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN);

  if (btm_cb.api.p_sp_callback)
    (*btm_cb.api.p_sp_callback)(BTM_SP_COMPLT_EVT,
                                (tBTM_SP_EVT_DATA*)&evt_data);

  if (disc) {
    /* simple pairing failed */
    /* Avoid sending disconnect on HCI_ERR_PEER_USER */
    if ((status != HCI_ERR_PEER_USER) &&
        (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)) {
      btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_AUTH_FAILURE,
                                  p_dev_rec->hci_handle);
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_rem_oob_req
 *
 * Description      This function is called to process/report
 *                  HCI_REMOTE_OOB_DATA_REQUEST_EVT
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_rem_oob_req(uint8_t* p) {
  uint8_t* p_bda;
  tBTM_SP_RMT_OOB evt_data;
  tBTM_SEC_DEV_REC* p_dev_rec;
  BT_OCTET16 c;
  BT_OCTET16 r;

  p_bda = evt_data.bd_addr;

  STREAM_TO_BDADDR(p_bda, p);

  BTM_TRACE_EVENT("btm_rem_oob_req() BDA: %02x:%02x:%02x:%02x:%02x:%02x",
                  p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]);
  p_dev_rec = btm_find_dev(p_bda);
  if ((p_dev_rec != NULL) && btm_cb.api.p_sp_callback) {
    memcpy(evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
    memcpy(evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN);
    strlcpy((char*)evt_data.bd_name, (char*)p_dev_rec->sec_bd_name,
            BTM_MAX_REM_BD_NAME_LEN);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP);
    if ((*btm_cb.api.p_sp_callback)(BTM_SP_RMT_OOB_EVT,
                                    (tBTM_SP_EVT_DATA*)&evt_data) ==
        BTM_NOT_AUTHORIZED) {
      BTM_RemoteOobDataReply(true, p_bda, c, r);
    }
    return;
  }

  /* something bad. we can only fail this connection */
  btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY;
  btsnd_hcic_rem_oob_neg_reply(p_bda);
}

/*******************************************************************************
 *
 * Function         btm_read_local_oob_complete
 *
 * Description      This function is called when read local oob data is
 *                  completed by the LM
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_read_local_oob_complete(uint8_t* p) {
  tBTM_SP_LOC_OOB evt_data;
  uint8_t status = *p++;

  BTM_TRACE_EVENT("btm_read_local_oob_complete:%d", status);
  if (status == HCI_SUCCESS) {
    evt_data.status = BTM_SUCCESS;
    STREAM_TO_ARRAY16(evt_data.c, p);
    STREAM_TO_ARRAY16(evt_data.r, p);
  } else
    evt_data.status = BTM_ERR_PROCESSING;

  if (btm_cb.api.p_sp_callback)
    (*btm_cb.api.p_sp_callback)(BTM_SP_LOC_OOB_EVT,
                                (tBTM_SP_EVT_DATA*)&evt_data);
}

/*******************************************************************************
 *
 * Function         btm_sec_auth_collision
 *
 * Description      This function is called when authentication or encryption
 *                  needs to be retried at a later time.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_sec_auth_collision(uint16_t handle) {
  tBTM_SEC_DEV_REC* p_dev_rec;

  if (!btm_cb.collision_start_time)
    btm_cb.collision_start_time = time_get_os_boottime_ms();

  if ((time_get_os_boottime_ms() - btm_cb.collision_start_time) <
      btm_cb.max_collision_delay) {
    if (handle == BTM_SEC_INVALID_HANDLE) {
      p_dev_rec = btm_sec_find_dev_by_sec_state(BTM_SEC_STATE_AUTHENTICATING);
      if (p_dev_rec == NULL)
        p_dev_rec = btm_sec_find_dev_by_sec_state(BTM_SEC_STATE_ENCRYPTING);
    } else
      p_dev_rec = btm_find_dev_by_handle(handle);

    if (p_dev_rec != NULL) {
      BTM_TRACE_DEBUG(
          "btm_sec_auth_collision: state %d (retrying in a moment...)",
          p_dev_rec->sec_state);
      /* We will restart authentication after timeout */
      if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING ||
          p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING)
        p_dev_rec->sec_state = 0;

      btm_cb.p_collided_dev_rec = p_dev_rec;
      alarm_set_on_queue(btm_cb.sec_collision_timer, BT_1SEC_TIMEOUT_MS,
                         btm_sec_collision_timeout, NULL,
                         btu_general_alarm_queue);
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_auth_complete
 *
 * Description      This function is when authentication of the connection is
 *                  completed by the LM
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_auth_complete(uint16_t handle, uint8_t status) {
  uint8_t old_sm4;
  tBTM_PAIRING_STATE old_state = btm_cb.pairing_state;
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
  bool are_bonding = false;

  if (p_dev_rec) {
    BTM_TRACE_EVENT(
        "Security Manager: auth_complete PairState: %s  handle:%u  status:%d  "
        "dev->sec_state: %u  Bda:%08x, RName:%s",
        btm_pair_state_descr(btm_cb.pairing_state), handle, status,
        p_dev_rec->sec_state,
        (p_dev_rec->bd_addr[2] << 24) + (p_dev_rec->bd_addr[3] << 16) +
            (p_dev_rec->bd_addr[4] << 8) + p_dev_rec->bd_addr[5],
        p_dev_rec->sec_bd_name);
  } else {
    BTM_TRACE_EVENT(
        "Security Manager: auth_complete PairState: %s  handle:%u  status:%d",
        btm_pair_state_descr(btm_cb.pairing_state), handle, status);
  }

  /* For transaction collision we need to wait and repeat.  There is no need */
  /* for random timeout because only slave should receive the result */
  if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) ||
      (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) {
    btm_sec_auth_collision(handle);
    return;
  }
  btm_cb.collision_start_time = 0;

  btm_restore_mode();

  /* Check if connection was made just to do bonding.  If we authenticate
     the connection that is up, this is the last event received.
  */
  if (p_dev_rec && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
      !(btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) {
    p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;

    l2cu_start_post_bond_timer(p_dev_rec->hci_handle);
  }

  if (!p_dev_rec) return;

  /* keep the old sm4 flag and clear the retry bit in control block */
  old_sm4 = p_dev_rec->sm4;
  p_dev_rec->sm4 &= ~BTM_SM4_RETRY;

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) &&
      (memcmp(p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0))
    are_bonding = true;

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0))
    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

  if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) {
    if ((btm_cb.api.p_auth_complete_callback && status != HCI_SUCCESS) &&
        (old_state != BTM_PAIR_STATE_IDLE)) {
      (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
                                             p_dev_rec->dev_class,
                                             p_dev_rec->sec_bd_name, status);
    }
    return;
  }

  /* There can be a race condition, when we are starting authentication and
  ** the peer device is doing encryption.
  ** If first we receive encryption change up, then initiated authentication
  ** can not be performed.  According to the spec we can not do authentication
  ** on the encrypted link, so device is correct.
  */
  if ((status == HCI_ERR_COMMAND_DISALLOWED) &&
      ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) ==
       (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) {
    status = HCI_SUCCESS;
  }
  /* Currently we do not notify user if it is a keyboard which connects */
  /* User probably Disabled the keyboard while it was asleap.  Let her try */
  if (btm_cb.api.p_auth_complete_callback) {
    /* report the suthentication status */
    if ((old_state != BTM_PAIR_STATE_IDLE) || (status != HCI_SUCCESS))
      (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
                                             p_dev_rec->dev_class,
                                             p_dev_rec->sec_bd_name, status);
  }

  p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;

  /* If this is a bonding procedure can disconnect the link now */
  if (are_bonding) {
    p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;

    if (status != HCI_SUCCESS) {
      if (((status != HCI_ERR_PEER_USER) &&
           (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)))
        btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER,
                                    p_dev_rec->hci_handle);
    } else {
      BTM_TRACE_DEBUG("TRYING TO DECIDE IF CAN USE SMP_BR_CHNL");
      if (p_dev_rec->new_encryption_key_is_p256 &&
          (btm_sec_use_smp_br_chnl(p_dev_rec))
          /* no LE keys are available, do deriving */
          && (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) ||
              /* or BR key is higher security than existing LE keys */
              (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) &&
               (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)))) {
        BTM_TRACE_DEBUG(
            "link encrypted afer dedic bonding can use SMP_BR_CHNL");

        if (btm_sec_is_master(p_dev_rec)) {
          // Encryption is required to start SM over BR/EDR
          // indicate that this is encryption after authentication
          BTM_SetEncryption(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, NULL, NULL,
                            0);
        }
      }
      l2cu_start_post_bond_timer(p_dev_rec->hci_handle);
    }

    return;
  }

  /* If authentication failed, notify the waiting layer */
  if (status != HCI_SUCCESS) {
    if ((old_sm4 & BTM_SM4_RETRY) == 0) {
      /* allow retry only once */
      if (status == HCI_ERR_LMP_ERR_TRANS_COLLISION) {
        /* not retried yet. set the retry bit */
        p_dev_rec->sm4 |= BTM_SM4_RETRY;
        BTM_TRACE_DEBUG("Collision retry sm4:x%x sec_flags:0x%x",
                        p_dev_rec->sm4, p_dev_rec->sec_flags);
      }
      /* this retry for missing key is for Lisbon or later only.
       * Legacy device do not need this. the controller will drive the retry
       * automatically */
      else if (HCI_ERR_KEY_MISSING == status &&
               BTM_SEC_IS_SM4(p_dev_rec->sm4)) {
        /* not retried yet. set the retry bit */
        p_dev_rec->sm4 |= BTM_SM4_RETRY;
        p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN;
        BTM_TRACE_DEBUG("Retry for missing key sm4:x%x sec_flags:0x%x",
                        p_dev_rec->sm4, p_dev_rec->sec_flags);

        /* With BRCM controller, we do not need to delete the stored link key in
        controller.
        If the stack may sit on top of other controller, we may need this
        BTM_DeleteStoredLinkKey (bd_addr, NULL); */
      }

      if (p_dev_rec->sm4 & BTM_SM4_RETRY) {
        btm_sec_execute_procedure(p_dev_rec);
        return;
      }
    }

    btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, false);

    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) {
      btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_AUTH_FAILURE,
                                  p_dev_rec->hci_handle);
    }
    return;
  }

  p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;

  if (p_dev_rec->pin_code_length >= 16 ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
    // If we have MITM protection we have a higher level of security than
    // provided by 16 digits PIN
    p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
  }

  /* Authentication succeeded, execute the next security procedure, if any */
  status = btm_sec_execute_procedure(p_dev_rec);

  /* If there is no next procedure, or procedure failed to start, notify the
   * caller */
  if (status != BTM_CMD_STARTED)
    btm_sec_dev_rec_cback_event(p_dev_rec, status, false);
}

/*******************************************************************************
 *
 * Function         btm_sec_encrypt_change
 *
 * Description      This function is when encryption of the connection is
 *                  completed by the LM
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_encrypt_change(uint16_t handle, uint8_t status,
                            uint8_t encr_enable) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
  tACL_CONN* p_acl = NULL;
  uint8_t acl_idx = btm_handle_to_acl_index(handle);
  BTM_TRACE_EVENT(
      "Security Manager: encrypt_change status:%d State:%d, encr_enable = %d",
      status, (p_dev_rec) ? p_dev_rec->sec_state : 0, encr_enable);
  BTM_TRACE_DEBUG("before update p_dev_rec->sec_flags=0x%x",
                  (p_dev_rec) ? p_dev_rec->sec_flags : 0);

  /* For transaction collision we need to wait and repeat.  There is no need */
  /* for random timeout because only slave should receive the result */
  if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) ||
      (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) {
    btm_sec_auth_collision(handle);
    return;
  }
  btm_cb.collision_start_time = 0;

  if (!p_dev_rec) return;

  if ((status == HCI_SUCCESS) && encr_enable) {
    if (p_dev_rec->hci_handle == handle) {
      p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED);
      if (p_dev_rec->pin_code_length >= 16 ||
          p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
          p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
        p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
      }
    } else {
      p_dev_rec->sec_flags |= (BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED);
    }
  }

  /* It is possible that we decrypted the link to perform role switch */
  /* mark link not to be encrypted, so that when we execute security next time
   * it will kick in again */
  if ((status == HCI_SUCCESS) && !encr_enable) {
    if (p_dev_rec->hci_handle == handle)
      p_dev_rec->sec_flags &= ~BTM_SEC_ENCRYPTED;
    else
      p_dev_rec->sec_flags &= ~BTM_SEC_LE_ENCRYPTED;
  }

  BTM_TRACE_DEBUG("after update p_dev_rec->sec_flags=0x%x",
                  p_dev_rec->sec_flags);

  if (acl_idx != MAX_L2CAP_LINKS) p_acl = &btm_cb.acl_db[acl_idx];

  if (p_acl != NULL)
    btm_sec_check_pending_enc_req(p_dev_rec, p_acl->transport, encr_enable);

  if (p_acl && p_acl->transport == BT_TRANSPORT_LE) {
    if (status == HCI_ERR_KEY_MISSING || status == HCI_ERR_AUTH_FAILURE ||
        status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) {
      p_dev_rec->sec_flags &= ~(BTM_SEC_LE_LINK_KEY_KNOWN);
      p_dev_rec->ble.key_type = BTM_LE_KEY_NONE;
    }
    btm_ble_link_encrypted(p_dev_rec->ble.pseudo_addr, encr_enable);
    return;
  } else {
    /* BR/EDR connection, update the encryption key size to be 16 as always */
    p_dev_rec->enc_key_size = 16;
  }

  BTM_TRACE_DEBUG("in %s new_encr_key_256 is %d", __func__,
                  p_dev_rec->new_encryption_key_is_p256);

  if ((status == HCI_SUCCESS) && encr_enable &&
      (p_dev_rec->hci_handle == handle)) {
    /* if BR key is temporary no need for LE LTK derivation */
    bool derive_ltk = true;
    if (p_dev_rec->rmt_auth_req == BTM_AUTH_SP_NO &&
        btm_cb.devcb.loc_auth_req == BTM_AUTH_SP_NO) {
      derive_ltk = false;
      BTM_TRACE_DEBUG("%s: BR key is temporary, skip derivation of LE LTK",
                      __func__);
    }
    if (p_dev_rec->new_encryption_key_is_p256) {
      if (btm_sec_use_smp_br_chnl(p_dev_rec) && btm_sec_is_master(p_dev_rec) &&
          /* if LE key is not known, do deriving */
          (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) ||
           /* or BR key is higher security than existing LE keys */
           (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) &&
            (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED))) &&
          derive_ltk) {
        /* BR/EDR is encrypted with LK that can be used to derive LE LTK */
        p_dev_rec->new_encryption_key_is_p256 = false;

        if (p_dev_rec->no_smp_on_br) {
          BTM_TRACE_DEBUG("%s NO SM over BR/EDR", __func__);
        } else {
          BTM_TRACE_DEBUG("%s start SM over BR/EDR", __func__);
          SMP_BR_PairWith(p_dev_rec->bd_addr);
        }
      }
    } else {
      // BR/EDR is successfully encrypted. Correct LK type if needed
      // (BR/EDR LK derived from LE LTK was used for encryption)
      if ((encr_enable == 1) && /* encryption is ON for SSP */
          /* LK type is for BR/EDR SC */
          (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256 ||
           p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
        if (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256)
          p_dev_rec->link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB;
        else /* BTM_LKEY_TYPE_AUTH_COMB_P_256 */
          p_dev_rec->link_key_type = BTM_LKEY_TYPE_AUTH_COMB;

        BTM_TRACE_DEBUG("updated link key type to %d",
                        p_dev_rec->link_key_type);
        btm_send_link_key_notif(p_dev_rec);
      }
    }
  }

  /* If this encryption was started by peer do not need to do anything */
  if (p_dev_rec->sec_state != BTM_SEC_STATE_ENCRYPTING) {
    if (BTM_SEC_STATE_DELAY_FOR_ENC == p_dev_rec->sec_state) {
      p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
      p_dev_rec->p_callback = NULL;
      l2cu_resubmit_pending_sec_req(p_dev_rec->bd_addr);
    }
    return;
  }

  p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
  /* If encryption setup failed, notify the waiting layer */
  if (status != HCI_SUCCESS) {
    btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, false);
    return;
  }

  /* Encryption setup succeeded, execute the next security procedure, if any */
  status = (uint8_t)btm_sec_execute_procedure(p_dev_rec);
  /* If there is no next procedure, or procedure failed to start, notify the
   * caller */
  if (status != BTM_CMD_STARTED)
    btm_sec_dev_rec_cback_event(p_dev_rec, status, false);
}

/*******************************************************************************
 *
 * Function         btm_sec_connect_after_reject_timeout
 *
 * Description      Connection for bonding could not start because of the
 *                  collision. Initiate outgoing connection
 *
 * Returns          Pointer to the TLE struct
 *
 ******************************************************************************/
static void btm_sec_connect_after_reject_timeout(UNUSED_ATTR void* data) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_cb.p_collided_dev_rec;

  BTM_TRACE_EVENT("%s", __func__);
  btm_cb.p_collided_dev_rec = 0;

  if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) {
    BTM_TRACE_WARNING("Security Manager: %s: failed to start connection",
                      __func__);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

    if (btm_cb.api.p_auth_complete_callback)
      (*btm_cb.api.p_auth_complete_callback)(
          p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
          HCI_ERR_MEMORY_FULL);
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_connected
 *
 * Description      This function is when a connection to the peer device is
 *                  establsihed
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_connected(uint8_t* bda, uint16_t handle, uint8_t status,
                       uint8_t enc_mode) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda);
  uint8_t res;
  bool is_pairing_device = false;
  tACL_CONN* p_acl_cb;
  uint8_t bit_shift = 0;

  btm_acl_resubmit_page();

  if (p_dev_rec) {
    BTM_TRACE_EVENT(
        "Security Manager: btm_sec_connected in state: %s  handle:%d status:%d "
        "enc_mode:%d  bda:%x RName:%s",
        btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode,
        (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5],
        p_dev_rec->sec_bd_name);
  } else {
    BTM_TRACE_EVENT(
        "Security Manager: btm_sec_connected in state: %s  handle:%d status:%d "
        "enc_mode:%d  bda:%x ",
        btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode,
        (bda[2] << 24) + (bda[3] << 16) + (bda[4] << 8) + bda[5]);
  }

  if (!p_dev_rec) {
    /* There is no device record for new connection.  Allocate one */
    if (status == HCI_SUCCESS) {
      p_dev_rec = btm_sec_alloc_dev(bda);
    } else {
      /* If the device matches with stored paring address
       * reset the paring state to idle */
      if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
          (memcmp(btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0)) {
        btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      }

      /* can not find the device record and the status is error,
       * just ignore it */
      return;
    }
  } else /* Update the timestamp for this device */
  {
    bit_shift = (handle == p_dev_rec->ble_hci_handle) ? 8 : 0;
    p_dev_rec->timestamp = btm_cb.dev_rec_count++;
    if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) {
      /* tell L2CAP it's a bonding connection. */
      if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
          (memcmp(btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) &&
          (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) {
        /* if incoming connection failed while pairing, then try to connect and
         * continue */
        /* Motorola S9 disconnects without asking pin code */
        if ((status != HCI_SUCCESS) &&
            (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ)) {
          BTM_TRACE_WARNING(
              "Security Manager: btm_sec_connected: incoming connection failed "
              "without asking PIN");

          p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND;
          if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) {
            /* Start timer with 0 to initiate connection with new LCB */
            /* because L2CAP will delete current LCB with this event  */
            btm_cb.p_collided_dev_rec = p_dev_rec;
            alarm_set_on_queue(btm_cb.sec_collision_timer, 0,
                               btm_sec_connect_after_reject_timeout, NULL,
                               btu_general_alarm_queue);
          } else {
            btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
            if (BTM_ReadRemoteDeviceName(p_dev_rec->bd_addr, NULL,
                                         BT_TRANSPORT_BR_EDR) !=
                BTM_CMD_STARTED) {
              BTM_TRACE_ERROR("%s cannot read remote name", __func__);
              btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
            }
          }
#if (BTM_DISC_DURING_RS == TRUE)
          p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
#endif
          return;
        } else {
          l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, true);
        }
      }
      /* always clear the pending flag */
      p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND;
    }
  }

  p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR;

#if (BTM_DISC_DURING_RS == TRUE)
  p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
#endif

  p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0)) {
    /* if we rejected incoming connection from bonding device */
    if ((status == HCI_ERR_HOST_REJECT_DEVICE) &&
        (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) {
      BTM_TRACE_WARNING(
          "Security Manager: btm_sec_connected: HCI_Conn_Comp Flags:0x%04x, "
          "sm4: 0x%x",
          btm_cb.pairing_flags, p_dev_rec->sm4);

      btm_cb.pairing_flags &= ~BTM_PAIR_FLAGS_REJECTED_CONNECT;
      if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) {
        /* Try again: RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
        btm_sec_change_pairing_state(BTM_PAIR_STATE_GET_REM_NAME);
        if (BTM_ReadRemoteDeviceName(bda, NULL, BT_TRANSPORT_BR_EDR) !=
            BTM_CMD_STARTED) {
          BTM_TRACE_ERROR("%s cannot read remote name", __func__);
          btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
        }
        return;
      }

      /* if we already have pin code */
      if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) {
        /* Start timer with 0 to initiate connection with new LCB */
        /* because L2CAP will delete current LCB with this event  */
        btm_cb.p_collided_dev_rec = p_dev_rec;
        alarm_set_on_queue(btm_cb.sec_collision_timer, 0,
                           btm_sec_connect_after_reject_timeout, NULL,
                           btu_general_alarm_queue);
      }

      return;
    }
    /* wait for incoming connection without resetting pairing state */
    else if (status == HCI_ERR_CONNECTION_EXISTS) {
      BTM_TRACE_WARNING(
          "Security Manager: btm_sec_connected: Wait for incoming connection");
      return;
    }

    is_pairing_device = true;
  }

  /* If connection was made to do bonding restore link security if changed */
  btm_restore_mode();

  /* if connection fails during pin request, notify application */
  if (status != HCI_SUCCESS) {
    /* If connection failed because of during pairing, need to tell user */
    if (is_pairing_device) {
      p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;
      p_dev_rec->sec_flags &=
          ~((BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED) << bit_shift);
      BTM_TRACE_DEBUG("security_required:%x ", p_dev_rec->security_required);

      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

      /* We need to notify host that the key is not known any more */
      if (btm_cb.api.p_auth_complete_callback) {
        (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
                                               p_dev_rec->dev_class,
                                               p_dev_rec->sec_bd_name, status);
      }
    }
    /*
        Do not send authentication failure, if following conditions hold good
         1.  BTM Sec Pairing state is idle
         2.  Link key for the remote device is present.
         3.  Remote is SSP capable.
     */
    else if ((p_dev_rec->link_key_type <= BTM_LKEY_TYPE_REMOTE_UNIT) &&
             (((status == HCI_ERR_AUTH_FAILURE) ||
               (status == HCI_ERR_KEY_MISSING) ||
               (status == HCI_ERR_HOST_REJECT_SECURITY) ||
               (status == HCI_ERR_PAIRING_NOT_ALLOWED) ||
               (status == HCI_ERR_UNIT_KEY_USED) ||
               (status == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) ||
               (status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) ||
               (status == HCI_ERR_REPEATED_ATTEMPTS)))) {
      p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;
      p_dev_rec->sec_flags &= ~(BTM_SEC_LE_LINK_KEY_KNOWN << bit_shift);

#ifdef BRCM_NOT_4_BTE
      /* If we rejected pairing, pass this special result code */
      if (btm_cb.acl_disc_reason == HCI_ERR_HOST_REJECT_SECURITY) {
        status = HCI_ERR_HOST_REJECT_SECURITY;
      }
#endif

      /* We need to notify host that the key is not known any more */
      if (btm_cb.api.p_auth_complete_callback) {
        (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
                                               p_dev_rec->dev_class,
                                               p_dev_rec->sec_bd_name, status);
      }
    }

    if (status == HCI_ERR_CONNECTION_TOUT ||
        status == HCI_ERR_LMP_RESPONSE_TIMEOUT ||
        status == HCI_ERR_UNSPECIFIED || status == HCI_ERR_PAGE_TIMEOUT)
      btm_sec_dev_rec_cback_event(p_dev_rec, BTM_DEVICE_TIMEOUT, false);
    else
      btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, false);

    return;
  }

  /* If initiated dedicated bonding, return the link key now, and initiate
   * disconnect */
  /* If dedicated bonding, and we now have a link key, we are all done */
  if (is_pairing_device && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
    if (p_dev_rec->link_key_not_sent) {
      p_dev_rec->link_key_not_sent = false;
      btm_send_link_key_notif(p_dev_rec);
    }

    p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE;

    /* remember flag before it is initialized */
    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
      res = true;
    else
      res = false;

    if (btm_cb.api.p_auth_complete_callback)
      (*btm_cb.api.p_auth_complete_callback)(
          p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
          HCI_SUCCESS);

    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);

    if (res) {
      /* Let l2cap start bond timer */
      l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, true);
    }

    return;
  }

  p_dev_rec->hci_handle = handle;

  /* role may not be correct here, it will be updated by l2cap, but we need to
   */
  /* notify btm_acl that link is up, so starting of rmt name request will not */
  /* set paging flag up */
  p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR);
  if (p_acl_cb) {
/* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT
 * event */
#if (BTM_BYPASS_EXTRA_ACL_SETUP == FALSE)
    /* For now there are a some devices that do not like sending */
    /* commands events and data at the same time. */
    /* Set the packet types to the default allowed by the device */
    btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported);

    if (btm_cb.btm_def_link_policy)
      BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy);
#endif
  }
  btm_acl_created(bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle,
                  HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR);

  /* Initialize security flags.  We need to do that because some            */
  /* authorization complete could have come after the connection is dropped */
  /* and that would set wrong flag that link has been authorized already    */
  p_dev_rec->sec_flags &= ~((BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED |
                             BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED)
                            << bit_shift);

  if (enc_mode != HCI_ENCRYPT_MODE_DISABLED)
    p_dev_rec->sec_flags |=
        ((BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED) << bit_shift);

  if (btm_cb.security_mode == BTM_SEC_MODE_LINK)
    p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED << bit_shift);

  if (p_dev_rec->pin_code_length >= 16 ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
    p_dev_rec->sec_flags |= (BTM_SEC_16_DIGIT_PIN_AUTHED << bit_shift);
  }

  p_dev_rec->link_key_changed = false;

  /* After connection is established we perform security if we do not know */
  /* the name, or if we are originator because some procedure can have */
  /* been scheduled while connection was down */
  BTM_TRACE_DEBUG("is_originator:%d ", p_dev_rec->is_originator);
  if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) ||
      p_dev_rec->is_originator) {
    res = btm_sec_execute_procedure(p_dev_rec);
    if (res != BTM_CMD_STARTED)
      btm_sec_dev_rec_cback_event(p_dev_rec, res, false);
  }
  return;
}

/*******************************************************************************
 *
 * Function         btm_sec_disconnect
 *
 * Description      This function is called to disconnect HCI link
 *
 * Returns          btm status
 *
 ******************************************************************************/
tBTM_STATUS btm_sec_disconnect(uint16_t handle, uint8_t reason) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);

  /* In some weird race condition we may not have a record */
  if (!p_dev_rec) {
    btsnd_hcic_disconnect(handle, reason);
    return (BTM_SUCCESS);
  }

  /* If we are in the process of bonding we need to tell client that auth failed
   */
  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) &&
      (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) {
    /* we are currently doing bonding.  Link will be disconnected when done */
    btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE;
    return (BTM_BUSY);
  }

  return (btm_sec_send_hci_disconnect(p_dev_rec, reason, handle));
}

/*******************************************************************************
 *
 * Function         btm_sec_disconnected
 *
 * Description      This function is when a connection to the peer device is
 *                  dropped
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_disconnected(uint16_t handle, uint8_t reason) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
  uint8_t old_pairing_flags = btm_cb.pairing_flags;
  int result = HCI_ERR_AUTH_FAILURE;
  tBTM_SEC_CALLBACK* p_callback = NULL;
  tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;

  /* If page was delayed for disc complete, can do it now */
  btm_cb.discing = false;

  btm_acl_resubmit_page();

  if (!p_dev_rec) return;

  transport =
      (handle == p_dev_rec->hci_handle) ? BT_TRANSPORT_BR_EDR : BT_TRANSPORT_LE;

  p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */

#if (BTM_DISC_DURING_RS == TRUE)
  LOG_INFO(LOG_TAG, "%s clearing pending flag handle:%d reason:%d", __func__,
           handle, reason);
  p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
#endif

  /* clear unused flags */
  p_dev_rec->sm4 &= BTM_SM4_TRUE;

  uint8_t* bd_addr = (uint8_t*)p_dev_rec->bd_addr;
  BTM_TRACE_EVENT(
      "%s sec_req:x%x state:%s reason:%d bd_addr:%02x:%02x:%02x:%02x:%02x:%02x"
      "  remote_name:%s",
      __func__, p_dev_rec->security_required,
      btm_pair_state_descr(btm_cb.pairing_state), reason, bd_addr[0],
      bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5],
      p_dev_rec->sec_bd_name);

  BTM_TRACE_EVENT("%s before update sec_flags=0x%x", __func__,
                  p_dev_rec->sec_flags);

  /* If we are in the process of bonding we need to tell client that auth failed
   */
  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0)) {
    btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
    p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN;
    if (btm_cb.api.p_auth_complete_callback) {
      /* If the disconnection reason is REPEATED_ATTEMPTS,
         send this error message to complete callback function
         to display the error message of Repeated attempts.
         All others, send HCI_ERR_AUTH_FAILURE. */
      if (reason == HCI_ERR_REPEATED_ATTEMPTS) {
        result = HCI_ERR_REPEATED_ATTEMPTS;
      } else if (old_pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) {
        result = HCI_ERR_HOST_REJECT_SECURITY;
      }
      (*btm_cb.api.p_auth_complete_callback)(p_dev_rec->bd_addr,
                                             p_dev_rec->dev_class,
                                             p_dev_rec->sec_bd_name, result);
    }
  }

  btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, p_dev_rec->bd_addr,
                                HCI_SUCCESS);
  /* see sec_flags processing in btm_acl_removed */

  if (transport == BT_TRANSPORT_LE) {
    p_dev_rec->ble_hci_handle = BTM_SEC_INVALID_HANDLE;
    p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED);
    p_dev_rec->enc_key_size = 0;
  } else {
    p_dev_rec->hci_handle = BTM_SEC_INVALID_HANDLE;
    p_dev_rec->sec_flags &=
        ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED |
          BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED);
  }

  if (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH) {
    p_dev_rec->sec_state = (transport == BT_TRANSPORT_LE)
                               ? BTM_SEC_STATE_DISCONNECTING
                               : BTM_SEC_STATE_DISCONNECTING_BLE;
    return;
  }
  p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
  p_dev_rec->security_required = BTM_SEC_NONE;

  p_callback = p_dev_rec->p_callback;

  /* if security is pending, send callback to clean up the security state */
  if (p_callback) {
    p_dev_rec->p_callback =
        NULL; /* when the peer device time out the authentication before
                 we do, this call back must be reset here */
    (*p_callback)(p_dev_rec->bd_addr, transport, p_dev_rec->p_ref_data,
                  BTM_ERR_PROCESSING);
  }

  BTM_TRACE_EVENT("%s after update sec_flags=0x%x", __func__,
                  p_dev_rec->sec_flags);
}

/*******************************************************************************
 *
 * Function         btm_sec_link_key_notification
 *
 * Description      This function is called when a new connection link key is
 *                  generated
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
void btm_sec_link_key_notification(uint8_t* p_bda, uint8_t* p_link_key,
                                   uint8_t key_type) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(p_bda);
  bool we_are_bonding = false;
  bool ltk_derived_lk = false;

  BTM_TRACE_EVENT(
      "btm_sec_link_key_notification()  BDA:%04x%08x, TYPE: %d",
      (p_bda[0] << 8) + p_bda[1],
      (p_bda[2] << 24) + (p_bda[3] << 16) + (p_bda[4] << 8) + p_bda[5],
      key_type);

  if ((key_type >= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_COMBINATION) &&
      (key_type <=
       BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
    ltk_derived_lk = true;
    key_type -= BTM_LTK_DERIVED_LKEY_OFFSET;
  }
  /* If connection was made to do bonding restore link security if changed */
  btm_restore_mode();

  if (key_type != BTM_LKEY_TYPE_CHANGED_COMB)
    p_dev_rec->link_key_type = key_type;

  p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN;

  /*
   * Until this point in time, we do not know if MITM was enabled, hence we
   * add the extended security flag here.
   */
  if (p_dev_rec->pin_code_length >= 16 ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
      p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
    p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
  }

  /* BR/EDR connection, update the encryption key size to be 16 as always */
  p_dev_rec->enc_key_size = 16;
  memcpy(p_dev_rec->link_key, p_link_key, LINK_KEY_LEN);

  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
      (memcmp(btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0)) {
    if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
      we_are_bonding = true;
    else
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
  }

  /* save LTK derived LK no matter what */
  if (ltk_derived_lk) {
    if (btm_cb.api.p_link_key_callback) {
      BTM_TRACE_DEBUG("%s() Save LTK derived LK (key_type = %d)", __func__,
                      p_dev_rec->link_key_type);
      (*btm_cb.api.p_link_key_callback)(p_bda, p_dev_rec->dev_class,
                                        p_dev_rec->sec_bd_name, p_link_key,
                                        p_dev_rec->link_key_type);
    }
  } else {
    if ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256) ||
        (p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
      p_dev_rec->new_encryption_key_is_p256 = true;
      BTM_TRACE_DEBUG("%s set new_encr_key_256 to %d", __func__,
                      p_dev_rec->new_encryption_key_is_p256);
    }
  }

  /* If name is not known at this point delay calling callback until the name is
   */
  /* resolved. Unless it is a HID Device and we really need to send all link
   * keys. */
  if ((!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) &&
       ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) !=
        BTM_COD_MAJOR_PERIPHERAL)) &&
      !ltk_derived_lk) {
    BTM_TRACE_EVENT(
        "btm_sec_link_key_notification()  Delayed BDA: %08x%04x Type:%d",
        (p_bda[0] << 24) + (p_bda[1] << 16) + (p_bda[2] << 8) + p_bda[3],
        (p_bda[4] << 8) + p_bda[5], key_type);

    p_dev_rec->link_key_not_sent = true;

    /* If it is for bonding nothing else will follow, so we need to start name
     * resolution */
    if (we_are_bonding) {
      btsnd_hcic_rmt_name_req(p_bda, HCI_PAGE_SCAN_REP_MODE_R1,
                              HCI_MANDATARY_PAGE_SCAN_MODE, 0);
    }

    BTM_TRACE_EVENT("rmt_io_caps:%d, sec_flags:x%x, dev_class[1]:x%02x",
                    p_dev_rec->rmt_io_caps, p_dev_rec->sec_flags,
                    p_dev_rec->dev_class[1])
    return;
  }

  /* If its not us who perform authentication, we should tell stackserver */
  /* that some authentication has been completed                          */
  /* This is required when different entities receive link notification and auth
   * complete */
  if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)
      /* for derived key, always send authentication callback for BR channel */
      || ltk_derived_lk) {
    if (btm_cb.api.p_auth_complete_callback)
      (*btm_cb.api.p_auth_complete_callback)(
          p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
          HCI_SUCCESS);
  }

/* We will save link key only if the user authorized it - BTE report link key in
 * all cases */
#ifdef BRCM_NONE_BTE
  if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)
#endif
  {
    if (btm_cb.api.p_link_key_callback) {
      if (ltk_derived_lk) {
        BTM_TRACE_DEBUG(
            "btm_sec_link_key_notification()  LTK derived LK is saved already"
            " (key_type = %d)",
            p_dev_rec->link_key_type);
      } else {
        (*btm_cb.api.p_link_key_callback)(p_bda, p_dev_rec->dev_class,
                                          p_dev_rec->sec_bd_name, p_link_key,
                                          p_dev_rec->link_key_type);
      }
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_link_key_request
 *
 * Description      This function is called when controller requests link key
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
void btm_sec_link_key_request(uint8_t* p_bda) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(p_bda);

  BTM_TRACE_EVENT(
      "btm_sec_link_key_request()  BDA: %02x:%02x:%02x:%02x:%02x:%02x",
      p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]);

  if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) &&
      (btm_cb.collision_start_time != 0) &&
      (memcmp(btm_cb.p_collided_dev_rec->bd_addr, p_bda, BD_ADDR_LEN) == 0)) {
    BTM_TRACE_EVENT(
        "btm_sec_link_key_request() rejecting link key req "
        "State: %d START_TIMEOUT : %d",
        btm_cb.pairing_state, btm_cb.collision_start_time);
    btsnd_hcic_link_key_neg_reply(p_bda);
    return;
  }
  if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) {
    btsnd_hcic_link_key_req_reply(p_bda, p_dev_rec->link_key);
    return;
  }

  /* Notify L2CAP to increase timeout */
  l2c_pin_code_request(p_bda);

  /* The link key is not in the database and it is not known to the manager */
  btsnd_hcic_link_key_neg_reply(p_bda);
}

/*******************************************************************************
 *
 * Function         btm_sec_pairing_timeout
 *
 * Description      This function is called when host does not provide PIN
 *                  within requested time
 *
 * Returns          Pointer to the TLE struct
 *
 ******************************************************************************/
static void btm_sec_pairing_timeout(UNUSED_ATTR void* data) {
  tBTM_CB* p_cb = &btm_cb;
  tBTM_SEC_DEV_REC* p_dev_rec;
#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_NONE)
  tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_NO;
#else
  tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_YES;
#endif
  uint8_t name[2];

  p_dev_rec = btm_find_dev(p_cb->pairing_bda);

  BTM_TRACE_EVENT("%s  State: %s   Flags: %u", __func__,
                  btm_pair_state_descr(p_cb->pairing_state),
                  p_cb->pairing_flags);

  switch (p_cb->pairing_state) {
    case BTM_PAIR_STATE_WAIT_PIN_REQ:
      btm_sec_bond_cancel_complete();
      break;

    case BTM_PAIR_STATE_WAIT_LOCAL_PIN:
      if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PRE_FETCH_PIN) == 0)
        btsnd_hcic_pin_code_neg_reply(p_cb->pairing_bda);
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      /* We need to notify the UI that no longer need the PIN */
      if (btm_cb.api.p_auth_complete_callback) {
        if (p_dev_rec == NULL) {
          name[0] = 0;
          (*btm_cb.api.p_auth_complete_callback)(p_cb->pairing_bda, NULL, name,
                                                 HCI_ERR_CONNECTION_TOUT);
        } else
          (*btm_cb.api.p_auth_complete_callback)(
              p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
              HCI_ERR_CONNECTION_TOUT);
      }
      break;

    case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM:
      btsnd_hcic_user_conf_reply(p_cb->pairing_bda, false);
      /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */
      break;

#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
    case BTM_PAIR_STATE_KEY_ENTRY:
      btsnd_hcic_user_passkey_neg_reply(p_cb->pairing_bda);
      /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */
      break;
#endif /* !BTM_IO_CAP_NONE */

    case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS:
      if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
        auth_req |= BTM_AUTH_DD_BOND;

      btsnd_hcic_io_cap_req_reply(p_cb->pairing_bda, btm_cb.devcb.loc_io_caps,
                                  BTM_OOB_NONE, auth_req);
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      break;

    case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP:
      btsnd_hcic_rem_oob_neg_reply(p_cb->pairing_bda);
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      break;

    case BTM_PAIR_STATE_WAIT_DISCONNECT:
      /* simple pairing failed. Started a 1-sec timer at simple pairing
       * complete.
       * now it's time to tear down the ACL link*/
      if (p_dev_rec == NULL) {
        BTM_TRACE_ERROR(
            "%s BTM_PAIR_STATE_WAIT_DISCONNECT unknown BDA: %08x%04x", __func__,
            (p_cb->pairing_bda[0] << 24) + (p_cb->pairing_bda[1] << 16) +
                (p_cb->pairing_bda[2] << 8) + p_cb->pairing_bda[3],
            (p_cb->pairing_bda[4] << 8) + p_cb->pairing_bda[5]);
        break;
      }
      btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_AUTH_FAILURE,
                                  p_dev_rec->hci_handle);
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      break;

    case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE:
    case BTM_PAIR_STATE_GET_REM_NAME:
      /* We need to notify the UI that timeout has happened while waiting for
       * authentication*/
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      if (btm_cb.api.p_auth_complete_callback) {
        if (p_dev_rec == NULL) {
          name[0] = 0;
          (*btm_cb.api.p_auth_complete_callback)(p_cb->pairing_bda, NULL, name,
                                                 HCI_ERR_CONNECTION_TOUT);
        } else
          (*btm_cb.api.p_auth_complete_callback)(
              p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
              HCI_ERR_CONNECTION_TOUT);
      }
      break;

    default:
      BTM_TRACE_WARNING("%s not processed state: %s", __func__,
                        btm_pair_state_descr(btm_cb.pairing_state));
      btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE);
      break;
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_pin_code_request
 *
 * Description      This function is called when controller requests PIN code
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
void btm_sec_pin_code_request(uint8_t* p_bda) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_CB* p_cb = &btm_cb;

  BTM_TRACE_EVENT(
      "btm_sec_pin_code_request()  State: %s, BDA:%04x%08x",
      btm_pair_state_descr(btm_cb.pairing_state), (p_bda[0] << 8) + p_bda[1],
      (p_bda[2] << 24) + (p_bda[3] << 16) + (p_bda[4] << 8) + p_bda[5]);

  if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) {
    if ((memcmp(p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) &&
        (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE)) {
      btsnd_hcic_pin_code_neg_reply(p_bda);
      return;
    } else if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_PIN_REQ) ||
               memcmp(p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) {
      BTM_TRACE_WARNING("btm_sec_pin_code_request() rejected - state: %s",
                        btm_pair_state_descr(btm_cb.pairing_state));
      btsnd_hcic_pin_code_neg_reply(p_bda);
      return;
    }
  }

  p_dev_rec = btm_find_or_alloc_dev(p_bda);
  /* received PIN code request. must be non-sm4 */
  p_dev_rec->sm4 = BTM_SM4_KNOWN;

  if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) {
    memcpy(btm_cb.pairing_bda, p_bda, BD_ADDR_LEN);

    btm_cb.pairing_flags = BTM_PAIR_FLAGS_PEER_STARTED_DD;
    /* Make sure we reset the trusted mask to help against attacks */
    BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask);
  }

  if (!p_cb->pairing_disabled && (p_cb->cfg.pin_type == HCI_PIN_TYPE_FIXED)) {
    BTM_TRACE_EVENT("btm_sec_pin_code_request fixed pin replying");
    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
    btsnd_hcic_pin_code_req_reply(p_bda, p_cb->cfg.pin_code_len,
                                  p_cb->cfg.pin_code);
    return;
  }

  /* Use the connecting device's CoD for the connection */
  if ((!memcmp(p_bda, p_cb->connecting_bda, BD_ADDR_LEN)) &&
      (p_cb->connecting_dc[0] || p_cb->connecting_dc[1] ||
       p_cb->connecting_dc[2]))
    memcpy(p_dev_rec->dev_class, p_cb->connecting_dc, DEV_CLASS_LEN);

  /* We could have started connection after asking user for the PIN code */
  if (btm_cb.pin_code_len != 0) {
    BTM_TRACE_EVENT("btm_sec_pin_code_request bonding sending reply");
    btsnd_hcic_pin_code_req_reply(p_bda, btm_cb.pin_code_len, p_cb->pin_code);

    /* Mark that we forwarded received from the user PIN code */
    btm_cb.pin_code_len = 0;

    /* We can change mode back right away, that other connection being
     * established */
    /* is not forced to be secure - found a FW issue, so we can not do this
    btm_restore_mode(); */

    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
  }

  /* If pairing disabled OR (no PIN callback and not bonding) */
  /* OR we could not allocate entry in the database reject pairing request */
  else if (
      p_cb->pairing_disabled || (p_cb->api.p_pin_callback == NULL)

      /* OR Microsoft keyboard can for some reason try to establish connection
         */
      /*  the only thing we can do here is to shut it up.  Normally we will be
         originator */
      /*  for keyboard bonding */
      || (!p_dev_rec->is_originator &&
          ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) ==
           BTM_COD_MAJOR_PERIPHERAL) &&
          (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD))) {
    BTM_TRACE_WARNING(
        "btm_sec_pin_code_request(): Pairing disabled:%d; PIN callback:%x, Dev "
        "Rec:%x!",
        p_cb->pairing_disabled, p_cb->api.p_pin_callback, p_dev_rec);

    btsnd_hcic_pin_code_neg_reply(p_bda);
  }
  /* Notify upper layer of PIN request and start expiration timer */
  else {
    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_PIN);
    /* Pin code request can not come at the same time as connection request */
    memcpy(p_cb->connecting_bda, p_bda, BD_ADDR_LEN);
    memcpy(p_cb->connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN);

    /* Check if the name is known */
    /* Even if name is not known we might not be able to get one */
    /* this is the case when we are already getting something from the */
    /* device, so HCI level is flow controlled */
    /* Also cannot send remote name request while paging, i.e. connection is not
     * completed */
    if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) {
      BTM_TRACE_EVENT("btm_sec_pin_code_request going for callback");

      btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD;
      if (p_cb->api.p_pin_callback) {
        (*p_cb->api.p_pin_callback)(
            p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
            (p_dev_rec->p_cur_service == NULL)
                ? false
                : (p_dev_rec->p_cur_service->security_flags &
                   BTM_SEC_IN_MIN_16_DIGIT_PIN));
      }
    } else {
      BTM_TRACE_EVENT("btm_sec_pin_code_request going for remote name");

      /* We received PIN code request for the device with unknown name */
      /* it is not user friendly just to ask for the PIN without name */
      /* try to get name at first */
      btsnd_hcic_rmt_name_req(p_dev_rec->bd_addr, HCI_PAGE_SCAN_REP_MODE_R1,
                              HCI_MANDATARY_PAGE_SCAN_MODE, 0);
    }
  }

  return;
}

/*******************************************************************************
 *
 * Function         btm_sec_update_clock_offset
 *
 * Description      This function is called to update clock offset
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_update_clock_offset(uint16_t handle, uint16_t clock_offset) {
  tBTM_SEC_DEV_REC* p_dev_rec;
  tBTM_INQ_INFO* p_inq_info;

  p_dev_rec = btm_find_dev_by_handle(handle);
  if (p_dev_rec == NULL) return;

  p_dev_rec->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;

  p_inq_info = BTM_InqDbRead(p_dev_rec->bd_addr);
  if (p_inq_info == NULL) return;

  p_inq_info->results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;
}

/******************************************************************
 * S T A T I C     F U N C T I O N S
 ******************************************************************/

/*******************************************************************************
 *
 * Function         btm_sec_execute_procedure
 *
 * Description      This function is called to start required security
 *                  procedure.  There is a case when multiplexing protocol
 *                  calls this function on the originating side, connection to
 *                  the peer will not be established.  This function in this
 *                  case performs only authorization.
 *
 * Returns          BTM_SUCCESS     - permission is granted
 *                  BTM_CMD_STARTED - in process
 *                  BTM_NO_RESOURCES  - permission declined
 *
 ******************************************************************************/
static tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec) {
  BTM_TRACE_EVENT(
      "btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d",
      p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state);

  /* There is a chance that we are getting name.  Wait until done. */
  if (p_dev_rec->sec_state != 0) return (BTM_CMD_STARTED);

  /* If any security is required, get the name first */
  if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) &&
      (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) {
    BTM_TRACE_EVENT("Security Manager: Start get name");
    if (!btm_sec_start_get_name(p_dev_rec)) {
      return (BTM_NO_RESOURCES);
    }
    return (BTM_CMD_STARTED);
  }

  /* If connection is not authenticated and authentication is required */
  /* start authentication and return PENDING to the caller */
  if ((((!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) &&
        ((p_dev_rec->is_originator &&
          (p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) ||
         (!p_dev_rec->is_originator &&
          (p_dev_rec->security_required & BTM_SEC_IN_AUTHENTICATE)))) ||
       (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) &&
        (!p_dev_rec->is_originator &&
         (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) &&
      (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) {
/*
 * We rely on BTM_SEC_16_DIGIT_PIN_AUTHED being set if MITM is in use,
 * as 16 DIGIT is only needed if MITM is not used. Unfortunately, the
 * BTM_SEC_AUTHENTICATED is used for both MITM and non-MITM
 * authenticated connections, hence we cannot distinguish here.
 */

#if (L2CAP_UCD_INCLUDED == TRUE)
    /* if incoming UCD packet, discard it */
    if (!p_dev_rec->is_originator && (p_dev_rec->is_ucd == true))
      return (BTM_FAILED_ON_SECURITY);
#endif

    BTM_TRACE_EVENT("Security Manager: Start authentication");

    /*
     * If we do have a link-key, but we end up here because we need an
     * upgrade, then clear the link-key known and authenticated flag before
     * restarting authentication.
     * WARNING: If the controller has link-key, it is optional and
     * recommended for the controller to send a Link_Key_Request.
     * In case we need an upgrade, the only alternative would be to delete
     * the existing link-key. That could lead to very bad user experience
     * or even IOP issues, if a reconnect causes a new connection that
     * requires an upgrade.
     */
    if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) &&
        (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) &&
         (!p_dev_rec->is_originator &&
          (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) {
      p_dev_rec->sec_flags &=
          ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
            BTM_SEC_AUTHENTICATED);
    }

    btm_sec_start_authentication(p_dev_rec);
    return (BTM_CMD_STARTED);
  }

  /* If connection is not encrypted and encryption is required */
  /* start encryption and return PENDING to the caller */
  if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) &&
      ((p_dev_rec->is_originator &&
        (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT)) ||
       (!p_dev_rec->is_originator &&
        (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT))) &&
      (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) {
#if (L2CAP_UCD_INCLUDED == TRUE)
    /* if incoming UCD packet, discard it */
    if (!p_dev_rec->is_originator && (p_dev_rec->is_ucd == true))
      return (BTM_FAILED_ON_SECURITY);
#endif

    BTM_TRACE_EVENT("Security Manager: Start encryption");

    btm_sec_start_encryption(p_dev_rec);
    return (BTM_CMD_STARTED);
  }

  if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
      (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) {
    BTM_TRACE_EVENT(
        "%s: Security Manager: SC only service, but link key type is 0x%02x -",
        "security failure", __func__, p_dev_rec->link_key_type);
    return (BTM_FAILED_ON_SECURITY);
  }

  /* If connection is not authorized and authorization is required */
  /* start authorization and return PENDING to the caller */
  if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) &&
      ((p_dev_rec->is_originator &&
        (p_dev_rec->security_required & BTM_SEC_OUT_AUTHORIZE)) ||
       (!p_dev_rec->is_originator &&
        (p_dev_rec->security_required & BTM_SEC_IN_AUTHORIZE)))) {
    BTM_TRACE_EVENT(
        "service id:%d, is trusted:%d", p_dev_rec->p_cur_service->service_id,
        (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask,
                                    p_dev_rec->p_cur_service->service_id)));
    if ((btm_sec_are_all_trusted(p_dev_rec->trusted_mask) == false) &&
        (p_dev_rec->p_cur_service->service_id < BTM_SEC_MAX_SERVICES) &&
        (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask,
                                    p_dev_rec->p_cur_service->service_id) ==
         false)) {
      BTM_TRACE_EVENT("Security Manager: Start authorization");
      return (btm_sec_start_authorization(p_dev_rec));
    }
  }

  /* All required  security procedures already established */
  p_dev_rec->security_required &=
      ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE |
        BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
        BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT | BTM_SEC_FORCE_MASTER |
        BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE);

  BTM_TRACE_EVENT("Security Manager: trusted:0x%04x%04x",
                  p_dev_rec->trusted_mask[1], p_dev_rec->trusted_mask[0]);
  BTM_TRACE_EVENT("Security Manager: access granted");

  return (BTM_SUCCESS);
}

/*******************************************************************************
 *
 * Function         btm_sec_start_get_name
 *
 * Description      This function is called to start get name procedure
 *
 * Returns          true if started
 *
 ******************************************************************************/
static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec) {
  uint8_t tempstate = p_dev_rec->sec_state;

  p_dev_rec->sec_state = BTM_SEC_STATE_GETTING_NAME;

  /* 0 and NULL are as timeout and callback params because they are not used in
   * security get name case */
  if ((btm_initiate_rem_name(p_dev_rec->bd_addr, BTM_RMT_NAME_SEC, 0, NULL)) !=
      BTM_CMD_STARTED) {
    p_dev_rec->sec_state = tempstate;
    return (false);
  }

  return (true);
}

/*******************************************************************************
 *
 * Function         btm_sec_start_authentication
 *
 * Description      This function is called to start authentication
 *
 ******************************************************************************/
static void btm_sec_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec) {
  p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
  btsnd_hcic_auth_request(p_dev_rec->hci_handle);
}

/*******************************************************************************
 *
 * Function         btm_sec_start_encryption
 *
 * Description      This function is called to start encryption
 *
 ******************************************************************************/
static void btm_sec_start_encryption(tBTM_SEC_DEV_REC* p_dev_rec) {
  btsnd_hcic_set_conn_encrypt(p_dev_rec->hci_handle, true);
  p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING;
}

/*******************************************************************************
 *
 * Function         btm_sec_start_authorization
 *
 * Description      This function is called to start authorization
 *
 * Returns          true if started
 *
 ******************************************************************************/
static uint8_t btm_sec_start_authorization(tBTM_SEC_DEV_REC* p_dev_rec) {
  uint8_t result;
  uint8_t* p_service_name = NULL;
  uint8_t service_id;

  if ((p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) ||
      (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) {
    if (!btm_cb.api.p_authorize_callback) return (BTM_MODE_UNSUPPORTED);

    if (p_dev_rec->p_cur_service) {
#if BTM_SEC_SERVICE_NAME_LEN > 0
      if (p_dev_rec->is_originator)
        p_service_name = p_dev_rec->p_cur_service->orig_service_name;
      else
        p_service_name = p_dev_rec->p_cur_service->term_service_name;
#endif
      service_id = p_dev_rec->p_cur_service->service_id;
    } else
      service_id = 0;

    /* Send authorization request if not already sent during this service
     * connection */
    if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID ||
        p_dev_rec->last_author_service_id != service_id) {
      p_dev_rec->sec_state = BTM_SEC_STATE_AUTHORIZING;
      result = (*btm_cb.api.p_authorize_callback)(
          p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
          p_service_name, service_id, p_dev_rec->is_originator);
    }

    else /* Already authorized once for this L2CAP bringup */
    {
      BTM_TRACE_DEBUG(
          "btm_sec_start_authorization: (Ignoring extra Authorization prompt "
          "for service %d)",
          service_id);
      return (BTM_SUCCESS);
    }

    if (result == BTM_SUCCESS) {
      p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED;

      /* Save the currently authorized service in case we are asked again by
       * another multiplexer layer */
      if (!p_dev_rec->is_originator)
        p_dev_rec->last_author_service_id = service_id;

      p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
    }
    return (result);
  }
  btm_sec_start_get_name(p_dev_rec);
  return (BTM_CMD_STARTED);
}

/*******************************************************************************
 *
 * Function         btm_sec_are_all_trusted
 *
 * Description      This function is called check if all services are trusted
 *
 * Returns          true if all are trusted, otherwise false
 *
 ******************************************************************************/
bool btm_sec_are_all_trusted(uint32_t p_mask[]) {
  uint32_t trusted_inx;
  for (trusted_inx = 0; trusted_inx < BTM_SEC_SERVICE_ARRAY_SIZE;
       trusted_inx++) {
    if (p_mask[trusted_inx] != BTM_SEC_TRUST_ALL) return (false);
  }

  return (true);
}

/*******************************************************************************
 *
 * Function         btm_sec_find_first_serv
 *
 * Description      Look for the first record in the service database
 *                  with specified PSM
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
tBTM_SEC_SERV_REC* btm_sec_find_first_serv(CONNECTION_TYPE conn_type,
                                           uint16_t psm) {
  tBTM_SEC_SERV_REC* p_serv_rec = &btm_cb.sec_serv_rec[0];
  int i;
  bool is_originator;

#if (L2CAP_UCD_INCLUDED == TRUE)

  if (conn_type & CONNECTION_TYPE_ORIG_MASK)
    is_originator = true;
  else
    is_originator = false;
#else
  is_originator = conn_type;
#endif

  if (is_originator && btm_cb.p_out_serv && btm_cb.p_out_serv->psm == psm) {
    /* If this is outgoing connection and the PSM matches p_out_serv,
     * use it as the current service */
    return btm_cb.p_out_serv;
  }

  /* otherwise, just find the first record with the specified PSM */
  for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) {
    if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) &&
        (p_serv_rec->psm == psm))
      return (p_serv_rec);
  }
  return (NULL);
}

/*******************************************************************************
 *
 * Function         btm_sec_find_next_serv
 *
 * Description      Look for the next record in the service database
 *                  with specified PSM
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
static tBTM_SEC_SERV_REC* btm_sec_find_next_serv(tBTM_SEC_SERV_REC* p_cur) {
  tBTM_SEC_SERV_REC* p_serv_rec = &btm_cb.sec_serv_rec[0];
  int i;

  for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) {
    if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) &&
        (p_serv_rec->psm == p_cur->psm)) {
      if (p_cur != p_serv_rec) {
        return (p_serv_rec);
      }
    }
  }
  return (NULL);
}

/*******************************************************************************
 *
 * Function         btm_sec_find_mx_serv
 *
 * Description      Look for the record in the service database with specified
 *                  PSM and multiplexor channel information
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
static tBTM_SEC_SERV_REC* btm_sec_find_mx_serv(uint8_t is_originator,
                                               uint16_t psm,
                                               uint32_t mx_proto_id,
                                               uint32_t mx_chan_id) {
  tBTM_SEC_SERV_REC* p_out_serv = btm_cb.p_out_serv;
  tBTM_SEC_SERV_REC* p_serv_rec = &btm_cb.sec_serv_rec[0];
  int i;

  BTM_TRACE_DEBUG("%s()", __func__);
  if (is_originator && p_out_serv && p_out_serv->psm == psm &&
      p_out_serv->mx_proto_id == mx_proto_id &&
      p_out_serv->orig_mx_chan_id == mx_chan_id) {
    /* If this is outgoing connection and the parameters match p_out_serv,
     * use it as the current service */
    return btm_cb.p_out_serv;
  }

  /* otherwise, the old way */
  for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) {
    if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) &&
        (p_serv_rec->psm == psm) && (p_serv_rec->mx_proto_id == mx_proto_id) &&
        ((is_originator && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) ||
         (!is_originator && (p_serv_rec->term_mx_chan_id == mx_chan_id)))) {
      return (p_serv_rec);
    }
  }
  return (NULL);
}

/*******************************************************************************
 *
 * Function         btm_sec_collision_timeout
 *
 * Description      Encryption could not start because of the collision
 *                  try to do it again
 *
 * Returns          Pointer to the TLE struct
 *
 ******************************************************************************/
static void btm_sec_collision_timeout(UNUSED_ATTR void* data) {
  BTM_TRACE_EVENT("%s()", __func__);

  tBTM_STATUS status = btm_sec_execute_procedure(btm_cb.p_collided_dev_rec);

  /* If result is pending reply from the user or from the device is pending */
  if (status != BTM_CMD_STARTED) {
    /* There is no next procedure or start of procedure failed, notify the
     * waiting layer */
    btm_sec_dev_rec_cback_event(btm_cb.p_collided_dev_rec, status, false);
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_link_key_request
 *
 * Description      This function is called when controller requests link key
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
static void btm_send_link_key_notif(tBTM_SEC_DEV_REC* p_dev_rec) {
  if (btm_cb.api.p_link_key_callback)
    (*btm_cb.api.p_link_key_callback)(
        p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
        p_dev_rec->link_key, p_dev_rec->link_key_type);
}

/*******************************************************************************
 *
 * Function         BTM_ReadTrustedMask
 *
 * Description      Get trusted mask for the peer device
 *
 * Parameters:      bd_addr   - Address of the device
 *
 * Returns          NULL, if the device record is not found.
 *                  otherwise, the trusted mask
 *
 ******************************************************************************/
uint32_t* BTM_ReadTrustedMask(BD_ADDR bd_addr) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr);
  if (p_dev_rec != NULL) return (p_dev_rec->trusted_mask);
  return NULL;
}

/*******************************************************************************
 *
 * Function         btm_restore_mode
 *
 * Description      This function returns the security mode to previous setting
 *                  if it was changed during bonding.
 *
 *
 * Parameters:      void
 *
 ******************************************************************************/
static void btm_restore_mode(void) {
  if (btm_cb.security_mode_changed) {
    btm_cb.security_mode_changed = false;
    BTM_TRACE_DEBUG("%s() Auth enable -> %d", __func__,
                    (btm_cb.security_mode == BTM_SEC_MODE_LINK));
    btsnd_hcic_write_auth_enable(
        (uint8_t)(btm_cb.security_mode == BTM_SEC_MODE_LINK));
  }

  if (btm_cb.pin_type_changed) {
    btm_cb.pin_type_changed = false;
    btsnd_hcic_write_pin_type(btm_cb.cfg.pin_type);
  }
}

bool is_sec_state_equal(void* data, void* context) {
  tBTM_SEC_DEV_REC* p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(data);
  uint8_t* state = static_cast<uint8_t*>(context);

  if (p_dev_rec->sec_state == *state) return false;

  return true;
}

/*******************************************************************************
 *
 * Function         btm_sec_find_dev_by_sec_state
 *
 * Description      Look for the record in the device database for the device
 *                  which is being authenticated or encrypted
 *
 * Returns          Pointer to the record or NULL
 *
 ******************************************************************************/
tBTM_SEC_DEV_REC* btm_sec_find_dev_by_sec_state(uint8_t state) {
  list_node_t* n = list_foreach(btm_cb.sec_dev_rec, is_sec_state_equal, &state);
  if (n) return static_cast<tBTM_SEC_DEV_REC*>(list_node(n));

  return NULL;
}

/*******************************************************************************
 *
 * Function         btm_sec_change_pairing_state
 *
 * Description      This function is called to change pairing state
 *
 ******************************************************************************/
static void btm_sec_change_pairing_state(tBTM_PAIRING_STATE new_state) {
  tBTM_PAIRING_STATE old_state = btm_cb.pairing_state;

  BTM_TRACE_EVENT("%s()  Old: %s", __func__,
                  btm_pair_state_descr(btm_cb.pairing_state));
  BTM_TRACE_EVENT("%s()  New: %s pairing_flags:0x%x", __func__,
                  btm_pair_state_descr(new_state), btm_cb.pairing_flags);

  btm_cb.pairing_state = new_state;

  if (new_state == BTM_PAIR_STATE_IDLE) {
    alarm_cancel(btm_cb.pairing_timer);

    btm_cb.pairing_flags = 0;
    btm_cb.pin_code_len = 0;

    /* Make sure the the lcb shows we are not bonding */
    l2cu_update_lcb_4_bonding(btm_cb.pairing_bda, false);

    btm_restore_mode();
    btm_sec_check_pending_reqs();
    btm_inq_clear_ssp();

    memset(btm_cb.pairing_bda, 0xFF, BD_ADDR_LEN);
  } else {
    /* If transitioning out of idle, mark the lcb as bonding */
    if (old_state == BTM_PAIR_STATE_IDLE)
      l2cu_update_lcb_4_bonding(btm_cb.pairing_bda, true);

    alarm_set_on_queue(btm_cb.pairing_timer, BTM_SEC_TIMEOUT_VALUE * 1000,
                       btm_sec_pairing_timeout, NULL, btu_general_alarm_queue);
  }
}

/*******************************************************************************
 *
 * Function         btm_pair_state_descr
 *
 * Description      Return state description for tracing
 *
 ******************************************************************************/
static const char* btm_pair_state_descr(tBTM_PAIRING_STATE state) {
  switch (state) {
    case BTM_PAIR_STATE_IDLE:
      return ("IDLE");
    case BTM_PAIR_STATE_GET_REM_NAME:
      return ("GET_REM_NAME");
    case BTM_PAIR_STATE_WAIT_PIN_REQ:
      return ("WAIT_PIN_REQ");
    case BTM_PAIR_STATE_WAIT_LOCAL_PIN:
      return ("WAIT_LOCAL_PIN");
    case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM:
      return ("WAIT_NUM_CONFIRM");
    case BTM_PAIR_STATE_KEY_ENTRY:
      return ("KEY_ENTRY");
    case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP:
      return ("WAIT_LOCAL_OOB_RSP");
    case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS:
      return ("WAIT_LOCAL_IOCAPS");
    case BTM_PAIR_STATE_INCOMING_SSP:
      return ("INCOMING_SSP");
    case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE:
      return ("WAIT_AUTH_COMPLETE");
    case BTM_PAIR_STATE_WAIT_DISCONNECT:
      return ("WAIT_DISCONNECT");
  }

  return ("???");
}

/*******************************************************************************
 *
 * Function         btm_sec_dev_rec_cback_event
 *
 * Description      This function calls the callback function with the given
 *                  result and clear the callback function.
 *
 * Parameters:      void
 *
 ******************************************************************************/
void btm_sec_dev_rec_cback_event(tBTM_SEC_DEV_REC* p_dev_rec, uint8_t res,
                                 bool is_le_transport) {
  tBTM_SEC_CALLBACK* p_callback = p_dev_rec->p_callback;

  if (p_dev_rec->p_callback) {
    p_dev_rec->p_callback = NULL;

    if (is_le_transport)
      (*p_callback)(p_dev_rec->ble.pseudo_addr, BT_TRANSPORT_LE,
                    p_dev_rec->p_ref_data, res);
    else
      (*p_callback)(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR,
                    p_dev_rec->p_ref_data, res);
  }

  btm_sec_check_pending_reqs();
}

/*******************************************************************************
 *
 * Function         btm_sec_queue_mx_request
 *
 * Description      Return state description for tracing
 *
 ******************************************************************************/
static bool btm_sec_queue_mx_request(BD_ADDR bd_addr, uint16_t psm,
                                     bool is_orig, uint32_t mx_proto_id,
                                     uint32_t mx_chan_id,
                                     tBTM_SEC_CALLBACK* p_callback,
                                     void* p_ref_data) {
  tBTM_SEC_QUEUE_ENTRY* p_e =
      (tBTM_SEC_QUEUE_ENTRY*)osi_malloc(sizeof(tBTM_SEC_QUEUE_ENTRY));

  p_e->psm = psm;
  p_e->is_orig = is_orig;
  p_e->p_callback = p_callback;
  p_e->p_ref_data = p_ref_data;
  p_e->mx_proto_id = mx_proto_id;
  p_e->mx_chan_id = mx_chan_id;
  p_e->transport = BT_TRANSPORT_BR_EDR;
  p_e->sec_act = 0;

  memcpy(p_e->bd_addr, bd_addr, BD_ADDR_LEN);

  BTM_TRACE_EVENT(
      "%s() PSM: 0x%04x  Is_Orig: %u  mx_proto_id: %u  mx_chan_id: %u",
      __func__, psm, is_orig, mx_proto_id, mx_chan_id);

  fixed_queue_enqueue(btm_cb.sec_pending_q, p_e);

  return true;
}

static bool btm_sec_check_prefetch_pin(tBTM_SEC_DEV_REC* p_dev_rec) {
  uint8_t major = (uint8_t)(p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK);
  uint8_t minor = (uint8_t)(p_dev_rec->dev_class[2] & BTM_COD_MINOR_CLASS_MASK);
  bool rv = false;

  if ((major == BTM_COD_MAJOR_AUDIO) &&
      ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) ||
       (minor == BTM_COD_MINOR_CAR_AUDIO))) {
    BTM_TRACE_EVENT(
        "%s() Skipping pre-fetch PIN for carkit COD Major: 0x%02x Minor: "
        "0x%02x",
        __func__, major, minor);

    if (btm_cb.security_mode_changed == false) {
      btm_cb.security_mode_changed = true;
#ifdef APPL_AUTH_WRITE_EXCEPTION
      if (!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr))
#endif
        btsnd_hcic_write_auth_enable(true);
    }
  } else {
    btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_PIN);

    /* If we got a PIN, use that, else try to get one */
    if (btm_cb.pin_code_len) {
      BTM_PINCodeReply(p_dev_rec->bd_addr, BTM_SUCCESS, btm_cb.pin_code_len,
                       btm_cb.pin_code, p_dev_rec->trusted_mask);
    } else {
      /* pin was not supplied - pre-fetch pin code now */
      if (btm_cb.api.p_pin_callback &&
          ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0)) {
        BTM_TRACE_DEBUG("%s() PIN code callback called", __func__);
        if (btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR) == NULL)
          btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD;
        (btm_cb.api.p_pin_callback)(
            p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name,
            (p_dev_rec->p_cur_service == NULL)
                ? false
                : (p_dev_rec->p_cur_service->security_flags &
                   BTM_SEC_IN_MIN_16_DIGIT_PIN));
      }
    }

    rv = true;
  }

  return rv;
}

/*******************************************************************************
 *
 * Function         btm_sec_auth_payload_tout
 *
 * Description      Processes the HCI Autheniticated Payload Timeout Event
 *                  indicating that a packet containing a valid MIC on the
 *                  connection handle was not received within the programmed
 *                  timeout value. (Spec Default is 30 secs, but can be
 *                  changed via the BTM_SecSetAuthPayloadTimeout() function.
 *
 ******************************************************************************/
void btm_sec_auth_payload_tout(uint8_t* p, uint16_t hci_evt_len) {
  uint16_t handle;

  STREAM_TO_UINT16(handle, p);
  handle = HCID_GET_HANDLE(handle);

  /* Will be exposed to upper layers in the future if/when determined necessary
   */
  BTM_TRACE_ERROR("%s on handle 0x%02x", __func__, handle);
}

/*******************************************************************************
 *
 * Function         btm_sec_queue_encrypt_request
 *
 * Description      encqueue encryption request when device has active security
 *                  process pending.
 *
 ******************************************************************************/
static bool btm_sec_queue_encrypt_request(BD_ADDR bd_addr,
                                          tBT_TRANSPORT transport,
                                          tBTM_SEC_CALLBACK* p_callback,
                                          void* p_ref_data,
                                          tBTM_BLE_SEC_ACT sec_act) {
  tBTM_SEC_QUEUE_ENTRY* p_e =
      (tBTM_SEC_QUEUE_ENTRY*)osi_malloc(sizeof(tBTM_SEC_QUEUE_ENTRY) + 1);

  p_e->psm = 0; /* if PSM 0, encryption request */
  p_e->p_callback = p_callback;
  p_e->p_ref_data = p_ref_data;
  p_e->transport = transport;
  p_e->sec_act = sec_act;
  memcpy(p_e->bd_addr, bd_addr, BD_ADDR_LEN);
  fixed_queue_enqueue(btm_cb.sec_pending_q, p_e);

  return true;
}

/*******************************************************************************
 *
 * Function         btm_sec_set_peer_sec_caps
 *
 * Description      This function is called to set sm4 and rmt_sec_caps fields
 *                  based on the available peer device features.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_set_peer_sec_caps(tACL_CONN* p_acl_cb,
                               tBTM_SEC_DEV_REC* p_dev_rec) {
  BD_ADDR rem_bd_addr;
  uint8_t* p_rem_bd_addr;

  if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
       btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
       btm_cb.security_mode == BTM_SEC_MODE_SC) &&
      HCI_SSP_HOST_SUPPORTED(p_acl_cb->peer_lmp_feature_pages[1])) {
    p_dev_rec->sm4 = BTM_SM4_TRUE;
    p_dev_rec->remote_supports_secure_connections =
        (HCI_SC_HOST_SUPPORTED(p_acl_cb->peer_lmp_feature_pages[1]));
  } else {
    p_dev_rec->sm4 = BTM_SM4_KNOWN;
    p_dev_rec->remote_supports_secure_connections = false;
  }

  BTM_TRACE_API("%s: sm4: 0x%02x, rmt_support_for_secure_connections %d",
                __func__, p_dev_rec->sm4,
                p_dev_rec->remote_supports_secure_connections);

  if (p_dev_rec->remote_features_needed) {
    BTM_TRACE_EVENT(
        "%s: Now device in SC Only mode, waiting for peer remote features!",
        __func__);
    p_rem_bd_addr = (uint8_t*)rem_bd_addr;
    BDADDR_TO_STREAM(p_rem_bd_addr, p_dev_rec->bd_addr);
    p_rem_bd_addr = (uint8_t*)rem_bd_addr;
    btm_io_capabilities_req(p_rem_bd_addr);
    p_dev_rec->remote_features_needed = false;
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_is_serv_level0
 *
 * Description      This function is called to check if the service
 *                  corresponding to PSM is security mode 4 level 0 service.
 *
 * Returns          true if the service is security mode 4 level 0 service
 *
 ******************************************************************************/
static bool btm_sec_is_serv_level0(uint16_t psm) {
  if (psm == BT_PSM_SDP) {
    BTM_TRACE_DEBUG("%s: PSM: 0x%04x -> mode 4 level 0 service", __func__, psm);
    return true;
  }
  return false;
}

/*******************************************************************************
 *
 * Function         btm_sec_check_pending_enc_req
 *
 * Description      This function is called to send pending encryption callback
 *                  if waiting
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_sec_check_pending_enc_req(tBTM_SEC_DEV_REC* p_dev_rec,
                                          tBT_TRANSPORT transport,
                                          uint8_t encr_enable) {
  if (fixed_queue_is_empty(btm_cb.sec_pending_q)) return;

  uint8_t res = encr_enable ? BTM_SUCCESS : BTM_ERR_PROCESSING;
  list_t* list = fixed_queue_get_list(btm_cb.sec_pending_q);
  for (const list_node_t* node = list_begin(list); node != list_end(list);) {
    tBTM_SEC_QUEUE_ENTRY* p_e = (tBTM_SEC_QUEUE_ENTRY*)list_node(node);
    node = list_next(node);

    if (memcmp(p_e->bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0 &&
        p_e->psm == 0 && p_e->transport == transport) {
      if (encr_enable == 0 || transport == BT_TRANSPORT_BR_EDR ||
          p_e->sec_act == BTM_BLE_SEC_ENCRYPT ||
          p_e->sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM ||
          (p_e->sec_act == BTM_BLE_SEC_ENCRYPT_MITM &&
           p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED)) {
        if (p_e->p_callback)
          (*p_e->p_callback)(p_dev_rec->bd_addr, transport, p_e->p_ref_data,
                             res);
        fixed_queue_try_remove_from_queue(btm_cb.sec_pending_q, (void*)p_e);
      }
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sec_set_serv_level4_flags
 *
 * Description      This function is called to set security mode 4 level 4
 *                  flags.
 *
 * Returns          service security requirements updated to include secure
 *                  connections only mode.
 *
 ******************************************************************************/
static uint16_t btm_sec_set_serv_level4_flags(uint16_t cur_security,
                                              bool is_originator) {
  uint16_t sec_level4_flags =
      is_originator ? BTM_SEC_OUT_LEVEL4_FLAGS : BTM_SEC_IN_LEVEL4_FLAGS;

  return cur_security | sec_level4_flags;
}

/*******************************************************************************
 *
 * Function         btm_sec_clear_ble_keys
 *
 * Description      This function is called to clear out the BLE keys.
 *                  Typically when devices are removed in BTM_SecDeleteDevice,
 *                  or when a new BT Link key is generated.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sec_clear_ble_keys(tBTM_SEC_DEV_REC* p_dev_rec) {
  BTM_TRACE_DEBUG("%s() Clearing BLE Keys", __func__);
  p_dev_rec->ble.key_type = BTM_LE_KEY_NONE;
  memset(&p_dev_rec->ble.keys, 0, sizeof(tBTM_SEC_BLE_KEYS));

#if (BLE_PRIVACY_SPT == TRUE)
  btm_ble_resolving_list_remove_dev(p_dev_rec);
#endif
}

/*******************************************************************************
 *
 * Function         btm_sec_is_a_bonded_dev
 *
 * Description       Is the specified device is a bonded device
 *
 * Returns          true - dev is bonded
 *
 ******************************************************************************/
bool btm_sec_is_a_bonded_dev(BD_ADDR bda) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda);
  bool is_bonded = false;

  if (p_dev_rec && ((p_dev_rec->ble.key_type &&
                     (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN)) ||
                    (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN))) {
    is_bonded = true;
  }
  BTM_TRACE_DEBUG("%s() is_bonded=%d", __func__, is_bonded);
  return (is_bonded);
}

/*******************************************************************************
 *
 * Function         btm_sec_is_le_capable_dev
 *
 * Description       Is the specified device is dual mode or LE only device
 *
 * Returns          true - dev is a dual mode
 *
 ******************************************************************************/
bool btm_sec_is_le_capable_dev(BD_ADDR bda) {
  tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda);
  bool le_capable = false;

  if (p_dev_rec &&
      (p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE)
    le_capable = true;
  return le_capable;
}

/*******************************************************************************
 *
 * Function         btm_sec_use_smp_br_chnl
 *
 * Description      The function checks if SMP BR connection can be used with
 *                  the peer.
 *                  Is called when authentication for dedicated bonding is
 *                  successfully completed.
 *
 * Returns          true - if SMP BR connection can be used (the link key is
 *                         generated from P-256 and the peer supports Security
 *                         Manager over BR).
 *
 ******************************************************************************/
static bool btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC* p_dev_rec) {
  uint32_t ext_feat;
  uint8_t chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE];

  BTM_TRACE_DEBUG("%s() link_key_type = 0x%x", __func__,
                  p_dev_rec->link_key_type);

  if ((p_dev_rec->link_key_type != BTM_LKEY_TYPE_UNAUTH_COMB_P_256) &&
      (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256))
    return false;

  if (!L2CA_GetPeerFeatures(p_dev_rec->bd_addr, &ext_feat, chnl_mask))
    return false;

  if (!(chnl_mask[0] & L2CAP_FIXED_CHNL_SMP_BR_BIT)) return false;

  return true;
}

/*******************************************************************************
 *
 * Function         btm_sec_is_master
 *
 * Description      The function checks if the device is BR/EDR master after
 *                  pairing is completed.
 *
 * Returns          true - if the device is master.
 *
 ******************************************************************************/
static bool btm_sec_is_master(tBTM_SEC_DEV_REC* p_dev_rec) {
  tACL_CONN* p = btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR);
  return (p && (p->link_role == BTM_ROLE_MASTER));
}