/******************************************************************************
 *
 *  Copyright 1999-2014 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 that handle inquiries. These include
 *  setting discoverable mode, controlling the mode of the Baseband, and
 *  maintaining a small database of inquiry responses, with API for people
 *  to browse it.
 *
 ******************************************************************************/

#include "stack/include/btm_inq.h"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#include <mutex>

#include "btif/include/btif_dm.h"
#include "common/time_util.h"
#include "hci/controller_interface.h"
#include "hci/event_checkers.h"
#include "hci/hci_interface.h"
#include "internal_include/bt_target.h"
#include "main/shim/entry.h"
#include "main/shim/helpers.h"
#include "main/shim/shim.h"
#include "osi/include/allocator.h"
#include "osi/include/properties.h"
#include "osi/include/stack_power_telemetry.h"
#include "packet/bit_inserter.h"
#include "stack/btm/btm_eir.h"
#include "stack/btm/btm_int_types.h"
#include "stack/btm/neighbor_inquiry.h"
#include "stack/btm/security_device_record.h"
#include "stack/include/acl_api_types.h"
#include "stack/include/advertise_data_parser.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/bt_lap.h"
#include "stack/include/bt_types.h"
#include "stack/include/bt_uuid16.h"
#include "stack/include/btm_client_interface.h"
#include "stack/include/btm_log_history.h"
#include "stack/include/btm_status.h"
#include "stack/include/hci_error_code.h"
#include "stack/include/hcidefs.h"
#include "stack/include/hcimsgs.h"
#include "stack/include/inq_hci_link_interface.h"
#include "stack/include/main_thread.h"
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"

/* MACRO to set the service bit mask in a bit stream */
#define BTM_EIR_SET_SERVICE(p, service)                              \
  (((uint32_t*)(p))[(((uint32_t)(service)) / BTM_EIR_ARRAY_BITS)] |= \
   ((uint32_t)1 << (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS)))

/* MACRO to clear the service bit mask in a bit stream */
#define BTM_EIR_CLR_SERVICE(p, service)                              \
  (((uint32_t*)(p))[(((uint32_t)(service)) / BTM_EIR_ARRAY_BITS)] &= \
   ~((uint32_t)1 << (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS)))

/* MACRO to check the service bit mask in a bit stream */
#define BTM_EIR_HAS_SERVICE(p, service)                               \
  ((((uint32_t*)(p))[(((uint32_t)(service)) / BTM_EIR_ARRAY_BITS)] &  \
    ((uint32_t)1 << (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS))) >> \
   (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS))

// TODO(b/369381361) Enfore -Wmissing-prototypes
#pragma GCC diagnostic ignored "-Wmissing-prototypes"

namespace {
constexpr char kBtmLogTag[] = "SCAN";

void btm_log_history_scan_mode(uint8_t scan_mode) {
  static uint8_t scan_mode_cached_ = 0xff;
  if (scan_mode_cached_ == scan_mode) {
    return;
  }

  BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic updated",
                 base::StringPrintf("inquiry_scan_enable:%c page_scan_enable:%c",
                                    (scan_mode & HCI_INQUIRY_SCAN_ENABLED) ? 'T' : 'F',
                                    (scan_mode & HCI_PAGE_SCAN_ENABLED) ? 'T' : 'F'));
  scan_mode_cached_ = scan_mode;
}

// Inquiry database lock
std::mutex inq_db_lock_;
// Inquiry database
tINQ_DB_ENT inq_db_[BTM_INQ_DB_SIZE];

// Inquiry bluetooth device database lock
std::mutex bd_db_lock_;
tINQ_BDADDR* p_bd_db_;    /* Pointer to memory that holds bdaddrs */
uint16_t num_bd_entries_; /* Number of entries in database */
uint16_t max_bd_entries_; /* Maximum number of entries that can be stored */

}  // namespace

extern tBTM_CB btm_cb;
void btm_inq_db_set_inq_by_rssi(void);
tBTM_STATUS btm_ble_set_discoverability(uint16_t combined_mode);
tBTM_STATUS btm_ble_set_connectability(uint16_t combined_mode);

tBTM_STATUS btm_ble_start_inquiry(uint8_t duration);
void btm_ble_stop_inquiry(void);

using namespace bluetooth;
using bluetooth::Uuid;
using bluetooth::hci::CommandCompleteView;
using bluetooth::hci::CommandStatusView;
using bluetooth::hci::ErrorCode;
using bluetooth::hci::EventCode;
using bluetooth::hci::EventView;
using bluetooth::hci::ExtendedInquiryResultView;
using bluetooth::hci::GapDataType;
using bluetooth::hci::InquiryBuilder;
using bluetooth::hci::InquiryCancelBuilder;
using bluetooth::hci::InquiryCancelCompleteView;
using bluetooth::hci::InquiryCompleteView;
using bluetooth::hci::InquiryResultView;
using bluetooth::hci::InquiryResultWithRssiView;
using bluetooth::hci::Lap;

/* 3 second timeout waiting for responses */
#define BTM_INQ_REPLY_TIMEOUT_MS (3 * 1000)

/* TRUE to enable DEBUG traces for btm_inq */
#ifndef BTM_INQ_DEBUG
#define BTM_INQ_DEBUG FALSE
#endif

#ifndef PROPERTY_PAGE_SCAN_TYPE
#define PROPERTY_PAGE_SCAN_TYPE "bluetooth.core.classic.page_scan_type"
#endif

#ifndef PROPERTY_PAGE_SCAN_INTERVAL
#define PROPERTY_PAGE_SCAN_INTERVAL "bluetooth.core.classic.page_scan_interval"
#endif

#ifndef PROPERTY_PAGE_SCAN_WINDOW
#define PROPERTY_PAGE_SCAN_WINDOW "bluetooth.core.classic.page_scan_window"
#endif

#ifndef PROPERTY_INQ_SCAN_TYPE
#define PROPERTY_INQ_SCAN_TYPE "bluetooth.core.classic.inq_scan_type"
#endif

#ifndef PROPERTY_INQ_SCAN_INTERVAL
#define PROPERTY_INQ_SCAN_INTERVAL "bluetooth.core.classic.inq_scan_interval"
#endif

#ifndef PROPERTY_INQ_SCAN_WINDOW
#define PROPERTY_INQ_SCAN_WINDOW "bluetooth.core.classic.inq_scan_window"
#endif

#ifndef PROPERTY_INQ_BY_RSSI
#define PROPERTY_INQ_BY_RSSI "persist.bluetooth.inq_by_rssi"
#endif

#define BTIF_DM_DEFAULT_INQ_MAX_DURATION 10

#ifndef PROPERTY_INQ_LENGTH
#define PROPERTY_INQ_LENGTH "bluetooth.core.classic.inq_length"
#endif

/******************************************************************************/
/*               L O C A L    D A T A    D E F I N I T I O N S                */
/******************************************************************************/
static const LAP general_inq_lap = {0x9e, 0x8b, 0x33};
static const LAP limited_inq_lap = {0x9e, 0x8b, 0x00};

const uint16_t BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES] = {
        UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER,
        /*    UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR,   */
        /*    UUID_SERVCLASS_PUBLIC_BROWSE_GROUP,       */
        UUID_SERVCLASS_SERIAL_PORT, UUID_SERVCLASS_LAN_ACCESS_USING_PPP,
        UUID_SERVCLASS_DIALUP_NETWORKING, UUID_SERVCLASS_IRMC_SYNC, UUID_SERVCLASS_OBEX_OBJECT_PUSH,
        UUID_SERVCLASS_OBEX_FILE_TRANSFER, UUID_SERVCLASS_IRMC_SYNC_COMMAND, UUID_SERVCLASS_HEADSET,
        UUID_SERVCLASS_CORDLESS_TELEPHONY, UUID_SERVCLASS_AUDIO_SOURCE, UUID_SERVCLASS_AUDIO_SINK,
        UUID_SERVCLASS_AV_REM_CTRL_TARGET,
        /*    UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION,    */
        UUID_SERVCLASS_AV_REMOTE_CONTROL,
        /*    UUID_SERVCLASS_VIDEO_CONFERENCING,        */
        UUID_SERVCLASS_INTERCOM, UUID_SERVCLASS_FAX, UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY,
        /*    UUID_SERVCLASS_WAP,                       */
        /*    UUID_SERVCLASS_WAP_CLIENT,                */
        UUID_SERVCLASS_PANU, UUID_SERVCLASS_NAP, UUID_SERVCLASS_GN, UUID_SERVCLASS_DIRECT_PRINTING,
        /*    UUID_SERVCLASS_REFERENCE_PRINTING,        */
        UUID_SERVCLASS_IMAGING, UUID_SERVCLASS_IMAGING_RESPONDER,
        UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, UUID_SERVCLASS_IMAGING_REF_OBJECTS,
        UUID_SERVCLASS_HF_HANDSFREE, UUID_SERVCLASS_AG_HANDSFREE,
        UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE,
        /*    UUID_SERVCLASS_REFLECTED_UI,              */
        UUID_SERVCLASS_BASIC_PRINTING, UUID_SERVCLASS_PRINTING_STATUS,
        UUID_SERVCLASS_HUMAN_INTERFACE, UUID_SERVCLASS_CABLE_REPLACEMENT, UUID_SERVCLASS_HCRP_PRINT,
        UUID_SERVCLASS_HCRP_SCAN,
        /*    UUID_SERVCLASS_COMMON_ISDN_ACCESS,        */
        /*    UUID_SERVCLASS_VIDEO_CONFERENCING_GW,     */
        /*    UUID_SERVCLASS_UDI_MT,                    */
        /*    UUID_SERVCLASS_UDI_TA,                    */
        /*    UUID_SERVCLASS_VCP,                       */
        UUID_SERVCLASS_SAP, UUID_SERVCLASS_PBAP_PCE, UUID_SERVCLASS_PBAP_PSE,
        UUID_SERVCLASS_PHONE_ACCESS, UUID_SERVCLASS_HEADSET_HS, UUID_SERVCLASS_PNP_INFORMATION,
        /*    UUID_SERVCLASS_GENERIC_NETWORKING,        */
        /*    UUID_SERVCLASS_GENERIC_FILETRANSFER,      */
        /*    UUID_SERVCLASS_GENERIC_AUDIO,             */
        /*    UUID_SERVCLASS_GENERIC_TELEPHONY,         */
        /*    UUID_SERVCLASS_UPNP_SERVICE,              */
        /*    UUID_SERVCLASS_UPNP_IP_SERVICE,           */
        /*    UUID_SERVCLASS_ESDP_UPNP_IP_PAN,          */
        /*    UUID_SERVCLASS_ESDP_UPNP_IP_LAP,          */
        /*    UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP,        */
        UUID_SERVCLASS_VIDEO_SOURCE, UUID_SERVCLASS_VIDEO_SINK,
        /*    UUID_SERVCLASS_VIDEO_DISTRIBUTION         */
        UUID_SERVCLASS_MESSAGE_ACCESS, UUID_SERVCLASS_MESSAGE_NOTIFICATION,
        UUID_SERVCLASS_HDP_SOURCE, UUID_SERVCLASS_HDP_SINK};

/******************************************************************************/
/*            L O C A L    F U N C T I O N     P R O T O T Y P E S            */
/******************************************************************************/
static void btm_clr_inq_db(const RawAddress* p_bda);
static void btm_init_inq_result_flt(void);
void btm_clr_inq_result_flt(void);

static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16);
void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results);
static const uint8_t* btm_eir_get_uuid_list(const uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
                                            uint8_t* p_num_uuid, uint8_t* p_uuid_list_type);

static void btm_process_cancel_complete(tHCI_STATUS status, uint8_t mode);
static void on_incoming_hci_event(EventView event);
static bool is_inquery_by_rssi() { return osi_property_get_bool(PROPERTY_INQ_BY_RSSI, false); }
/*******************************************************************************
 *
 * Function         BTM_SetDiscoverability
 *
 * Description      This function is called to set the device into or out of
 *                  discoverable mode. Discoverable mode means inquiry
 *                  scans are enabled.  If a value of '0' is entered for window
 *                  or interval, the default values are used.
 *
 * Returns          tBTM_STATUS::BTM_SUCCESS if successful
 *                  tBTM_STATUS::BTM_BUSY if a setting of the filter is already in progress
 *                  tBTM_STATUS::BTM_NO_RESOURCES if couldn't get a memory pool buffer
 *                  tBTM_STATUS::BTM_ILLEGAL_VALUE if a bad parameter was detected
 *                  tBTM_STATUS::BTM_WRONG_MODE if the device is not up.
 *
 ******************************************************************************/
tBTM_STATUS BTM_SetDiscoverability(uint16_t inq_mode) {
  uint8_t scan_mode = 0;
  uint16_t service_class;
  uint8_t major, minor;
  DEV_CLASS cod;
  LAP temp_lap[2];
  bool is_limited;
  bool cod_limited;

  log::verbose("");
  if (bluetooth::shim::GetController()->SupportsBle()) {
    if (btm_ble_set_discoverability((uint16_t)(inq_mode)) == tBTM_STATUS::BTM_SUCCESS) {
      btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_BLE_DISCOVERABLE_MASK);
      btm_cb.btm_inq_vars.discoverable_mode |= (inq_mode & BTM_BLE_DISCOVERABLE_MASK);
    }
  }
  inq_mode &= ~BTM_BLE_DISCOVERABLE_MASK;

  /*** Check mode parameter ***/
  if (inq_mode > BTM_MAX_DISCOVERABLE) {
    return tBTM_STATUS::BTM_ILLEGAL_VALUE;
  }

  /* If the window and/or interval is '0', set to default values */
  log::verbose("mode {} [NonDisc-0, Lim-1, Gen-2]", inq_mode);
  (inq_mode != BTM_NON_DISCOVERABLE) ? power_telemetry::GetInstance().LogInqScanStarted()
                                     : power_telemetry::GetInstance().LogInqScanStopped();

  /* Set the IAC if needed */
  if (inq_mode != BTM_NON_DISCOVERABLE) {
    if (inq_mode & BTM_LIMITED_DISCOVERABLE) {
      /* Use the GIAC and LIAC codes for limited discoverable mode */
      memcpy(temp_lap[0], limited_inq_lap, LAP_LEN);
      memcpy(temp_lap[1], general_inq_lap, LAP_LEN);

      btsnd_hcic_write_cur_iac_lap(2, (LAP* const)temp_lap);
    } else {
      btsnd_hcic_write_cur_iac_lap(1, (LAP* const)&general_inq_lap);
    }

    scan_mode |= HCI_INQUIRY_SCAN_ENABLED;
  }

  const uint16_t window = osi_property_get_int32(PROPERTY_INQ_SCAN_WINDOW, BTM_DEFAULT_DISC_WINDOW);
  const uint16_t interval =
          osi_property_get_int32(PROPERTY_INQ_SCAN_INTERVAL, BTM_DEFAULT_DISC_INTERVAL);

  /* Send down the inquiry scan window and period if changed */
  if ((window != btm_cb.btm_inq_vars.inq_scan_window) ||
      (interval != btm_cb.btm_inq_vars.inq_scan_period)) {
    btsnd_hcic_write_inqscan_cfg(interval, window);
    btm_cb.btm_inq_vars.inq_scan_window = window;
    btm_cb.btm_inq_vars.inq_scan_period = interval;
  }

  if (btm_cb.btm_inq_vars.connectable_mode & BTM_CONNECTABLE_MASK) {
    scan_mode |= HCI_PAGE_SCAN_ENABLED;
  }

  btm_log_history_scan_mode(scan_mode);
  btsnd_hcic_write_scan_enable(scan_mode);
  btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_DISCOVERABLE_MASK);
  btm_cb.btm_inq_vars.discoverable_mode |= inq_mode;

  /* Change the service class bit if mode has changed */
  DEV_CLASS old_cod = BTM_ReadDeviceClass();
  BTM_COD_SERVICE_CLASS(service_class, old_cod);
  is_limited = (inq_mode & BTM_LIMITED_DISCOVERABLE) ? true : false;
  cod_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? true : false;
  if (is_limited ^ cod_limited) {
    BTM_COD_MINOR_CLASS(minor, old_cod);
    BTM_COD_MAJOR_CLASS(major, old_cod);
    if (is_limited) {
      service_class |= BTM_COD_SERVICE_LMTD_DISCOVER;
    } else {
      service_class &= ~BTM_COD_SERVICE_LMTD_DISCOVER;
    }

    FIELDS_TO_COD(cod, minor, major, service_class);
    (void)get_btm_client_interface().local.BTM_SetDeviceClass(cod);
  }

  return tBTM_STATUS::BTM_SUCCESS;
}

void BTM_EnableInterlacedInquiryScan() {
  log::verbose("");

  uint16_t inq_scan_type = osi_property_get_int32(PROPERTY_INQ_SCAN_TYPE, BTM_SCAN_TYPE_INTERLACED);

  if (!bluetooth::shim::GetController()->SupportsInterlacedInquiryScan() ||
      inq_scan_type != BTM_SCAN_TYPE_INTERLACED ||
      btm_cb.btm_inq_vars.inq_scan_type == BTM_SCAN_TYPE_INTERLACED) {
    log::warn(
            "Unable to set interlaced inquiry scan controller_supported:%c "
            "property_supported:%c already_in_mode:%c",
            (bluetooth::shim::GetController()->SupportsInterlacedInquiryScan()) ? 'T' : 'F',
            (inq_scan_type != BTM_SCAN_TYPE_INTERLACED) ? 'T' : 'F',
            (btm_cb.btm_inq_vars.inq_scan_type == BTM_SCAN_TYPE_INTERLACED) ? 'T' : 'F');
    return;
  }

  btsnd_hcic_write_inqscan_type(BTM_SCAN_TYPE_INTERLACED);
  btm_cb.btm_inq_vars.inq_scan_type = BTM_SCAN_TYPE_INTERLACED;
}

void BTM_EnableInterlacedPageScan() {
  log::verbose("");

  uint16_t page_scan_type =
          osi_property_get_int32(PROPERTY_PAGE_SCAN_TYPE, BTM_SCAN_TYPE_INTERLACED);

  if (!bluetooth::shim::GetController()->SupportsInterlacedInquiryScan() ||
      page_scan_type != BTM_SCAN_TYPE_INTERLACED ||
      btm_cb.btm_inq_vars.page_scan_type == BTM_SCAN_TYPE_INTERLACED) {
    log::warn(
            "Unable to set interlaced page scan controller_supported:%c "
            "property_supported:%c already_in_mode:%c",
            (bluetooth::shim::GetController()->SupportsInterlacedInquiryScan()) ? 'T' : 'F',
            (page_scan_type != BTM_SCAN_TYPE_INTERLACED) ? 'T' : 'F',
            (btm_cb.btm_inq_vars.page_scan_type == BTM_SCAN_TYPE_INTERLACED) ? 'T' : 'F');
    return;
  }

  btsnd_hcic_write_pagescan_type(BTM_SCAN_TYPE_INTERLACED);
  btm_cb.btm_inq_vars.page_scan_type = BTM_SCAN_TYPE_INTERLACED;
}

/*******************************************************************************
 *
 * Function         BTM_SetInquiryMode
 *
 * Description      This function is called to set standard or with RSSI
 *                  mode of the inquiry for local device.
 *
 * Output Params:   mode - standard, with RSSI, extended
 *
 * Returns          tBTM_STATUS::BTM_SUCCESS if successful
 *                  tBTM_STATUS::BTM_NO_RESOURCES if couldn't get a memory pool buffer
 *                  tBTM_STATUS::BTM_ILLEGAL_VALUE if a bad parameter was detected
 *                  tBTM_STATUS::BTM_WRONG_MODE if the device is not up.
 *
 ******************************************************************************/
tBTM_STATUS BTM_SetInquiryMode(uint8_t mode) {
  log::verbose("");
  if (mode == BTM_INQ_RESULT_STANDARD) {
    /* mandatory mode */
  } else if (mode == BTM_INQ_RESULT_WITH_RSSI) {
    if (!bluetooth::shim::GetController()->SupportsRssiWithInquiryResults()) {
      return tBTM_STATUS::BTM_MODE_UNSUPPORTED;
    }
  } else if (mode == BTM_INQ_RESULT_EXTENDED) {
    if (!bluetooth::shim::GetController()->SupportsExtendedInquiryResponse()) {
      return tBTM_STATUS::BTM_MODE_UNSUPPORTED;
    }
  } else {
    return tBTM_STATUS::BTM_ILLEGAL_VALUE;
  }

  if (!get_btm_client_interface().local.BTM_IsDeviceUp()) {
    return tBTM_STATUS::BTM_WRONG_MODE;
  }

  btsnd_hcic_write_inquiry_mode(mode);

  return tBTM_STATUS::BTM_SUCCESS;
}

/*******************************************************************************
 *
 * Function         BTM_SetConnectability
 *
 * Description      This function is called to set the device into or out of
 *                  connectable mode. Discoverable mode means page scans are
 *                  enabled.
 *
 * Returns          tBTM_STATUS::BTM_SUCCESS if successful
 *                  tBTM_STATUS::BTM_ILLEGAL_VALUE if a bad parameter is detected
 *                  tBTM_STATUS::BTM_NO_RESOURCES if could not allocate a message buffer
 *                  tBTM_STATUS::BTM_WRONG_MODE if the device is not up.
 *
 ******************************************************************************/
tBTM_STATUS BTM_SetConnectability(uint16_t page_mode) {
  uint8_t scan_mode = 0;

  if (bluetooth::shim::GetController()->SupportsBle()) {
    if (btm_ble_set_connectability(page_mode) != tBTM_STATUS::BTM_SUCCESS) {
      return tBTM_STATUS::BTM_NO_RESOURCES;
    }
    btm_cb.btm_inq_vars.connectable_mode &= (~BTM_BLE_CONNECTABLE_MASK);
    btm_cb.btm_inq_vars.connectable_mode |= (page_mode & BTM_BLE_CONNECTABLE_MASK);
  }
  page_mode &= ~BTM_BLE_CONNECTABLE_MASK;

  /*** Check mode parameter ***/
  if (page_mode != BTM_NON_CONNECTABLE && page_mode != BTM_CONNECTABLE) {
    return tBTM_STATUS::BTM_ILLEGAL_VALUE;
  }

  /*** Only check window and duration if mode is connectable ***/
  if (page_mode == BTM_CONNECTABLE) {
    scan_mode |= HCI_PAGE_SCAN_ENABLED;
  }

  const uint16_t window =
          osi_property_get_int32(PROPERTY_PAGE_SCAN_WINDOW, BTM_DEFAULT_CONN_WINDOW);
  const uint16_t interval =
          osi_property_get_int32(PROPERTY_PAGE_SCAN_INTERVAL, BTM_DEFAULT_CONN_INTERVAL);

  log::verbose("mode={} [NonConn-0, Conn-1], page scan interval=({} * 0.625)ms", page_mode,
               interval);

  if ((window != btm_cb.btm_inq_vars.page_scan_window) ||
      (interval != btm_cb.btm_inq_vars.page_scan_period)) {
    btm_cb.btm_inq_vars.page_scan_window = window;
    btm_cb.btm_inq_vars.page_scan_period = interval;
    btsnd_hcic_write_pagescan_cfg(interval, window);
  }

  /* Keep the inquiry scan as previouosly set */
  if (btm_cb.btm_inq_vars.discoverable_mode & BTM_DISCOVERABLE_MASK) {
    scan_mode |= HCI_INQUIRY_SCAN_ENABLED;
  }

  btm_log_history_scan_mode(scan_mode);
  btsnd_hcic_write_scan_enable(scan_mode);
  btm_cb.btm_inq_vars.connectable_mode &= (~BTM_CONNECTABLE_MASK);
  btm_cb.btm_inq_vars.connectable_mode |= page_mode;
  return tBTM_STATUS::BTM_SUCCESS;
}

/*******************************************************************************
 *
 * Function         BTM_IsInquiryActive
 *
 * Description      This function returns a bit mask of the current inquiry
 *                  state
 *
 * Returns          BTM_INQUIRY_INACTIVE if inactive (0)
 *                  BTM_GENERAL_INQUIRY if a general inquiry is active
 *
 ******************************************************************************/
uint16_t BTM_IsInquiryActive(void) {
  log::verbose("");

  return btm_cb.btm_inq_vars.inq_active;
}

/*******************************************************************************
 *
 * Function         BTM_CancelLeScan
 *
 * Description      This function cancels an le scan if active
 *
 ******************************************************************************/
static void BTM_CancelLeScan() {
#if TARGET_FLOSS
  log::info("Skipping because FLOSS doesn't use this API for LE scans");
  return;
#else
  log::assert_that(get_btm_client_interface().local.BTM_IsDeviceUp(),
                   "assert failed: BTM_IsDeviceUp()");
  if ((btm_cb.btm_inq_vars.inqparms.mode & BTM_BLE_GENERAL_INQUIRY) != 0) {
    btm_ble_stop_inquiry();
  }
#endif
}

/*******************************************************************************
 *
 * Function         BTM_CancelInquiry
 *
 * Description      This function cancels an inquiry if active
 *
 ******************************************************************************/
void BTM_CancelInquiry(void) {
  log::verbose("");

  log::assert_that(get_btm_client_interface().local.BTM_IsDeviceUp(),
                   "assert failed: BTM_IsDeviceUp()");

  btm_cb.neighbor.inquiry_history_->Push({
          .status = tBTM_INQUIRY_CMPL::CANCELED,
          .num_resp = btm_cb.btm_inq_vars.inq_cmpl_info.num_resp,
          .resp_type =
                  {
                          btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD],
                          btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI],
                          btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED],
                  },
          .start_time_ms = btm_cb.neighbor.classic_inquiry.start_time_ms,
  });

  const auto duration_ms = timestamper_in_milliseconds.GetTimestamp() -
                           btm_cb.neighbor.classic_inquiry.start_time_ms;
  BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry canceled",
                 base::StringPrintf(
                         "duration_s:%6.3f results:%lu std:%u rssi:%u ext:%u", duration_ms / 1000.0,
                         (unsigned long)btm_cb.neighbor.classic_inquiry.results,
                         btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD],
                         btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI],
                         btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED]));
  btm_cb.neighbor.classic_inquiry = {};

  /* Only cancel if not in periodic mode, otherwise the caller should call
   * BTM_CancelPeriodicMode */
  if ((btm_cb.btm_inq_vars.inq_active & BTM_INQUIRY_ACTIVE_MASK) != 0) {
    btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
    btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
    btm_cb.btm_inq_vars.p_inq_results_cb = NULL; /* Do not notify caller anymore */
    btm_cb.btm_inq_vars.p_inq_cmpl_cb = NULL;    /* Do not notify caller anymore */

    if ((btm_cb.btm_inq_vars.inqparms.mode & BTM_GENERAL_INQUIRY) != 0) {
      bluetooth::shim::GetHciLayer()->EnqueueCommand(
              InquiryCancelBuilder::Create(),
              get_main_thread()->BindOnce([](CommandCompleteView complete_view) {
                check_complete<InquiryCancelCompleteView>(complete_view);
                btm_process_cancel_complete(HCI_SUCCESS, BTM_GENERAL_INQUIRY);
              }));
    }
    BTM_CancelLeScan();

    btm_cb.btm_inq_vars.inq_counter++;
    btm_clr_inq_result_flt();
  }
}

#if TARGET_FLOSS
static void btm_classic_inquiry_timeout(void* /* data */) {
  // When the Inquiry Complete event is received, the classic inquiry
  // will be marked as completed. Therefore, we only need to mark
  // the BLE inquiry as completed here to stop processing BLE results
  // as inquiry results.
  btm_process_inq_complete(HCI_SUCCESS, BTM_BLE_GENERAL_INQUIRY);
}
#endif

/*******************************************************************************
 *
 * Function         BTM_StartLeScan
 *
 * Description      This function is called to start an LE scan.  Currently
 *                  this is only callable from BTM_StartInquiry.
 *
 * Returns          tBTM_STATUS
 *                  BTM_CMD_STARTED if le scan successfully initiated
 *                  BTM_WRONG_MODE if controller does not support ble
 *
 ******************************************************************************/
static tBTM_STATUS BTM_StartLeScan() {
#if TARGET_FLOSS
  log::info("Skipping because FLOSS doesn't use this API for LE scans");
  return tBTM_STATUS::BTM_WRONG_MODE;
#else
  if (shim::GetController()->SupportsBle()) {
    btm_ble_start_inquiry(btm_cb.btm_inq_vars.inqparms.duration);
    return tBTM_STATUS::BTM_CMD_STARTED;
  }
  log::warn("Trying to do LE scan on a non-LE adapter");
  btm_cb.btm_inq_vars.inqparms.mode &= ~BTM_BLE_GENERAL_INQUIRY;
  return tBTM_STATUS::BTM_WRONG_MODE;
#endif
}

/*******************************************************************************
 *
 * Function         BTM_StartInquiry
 *
 * Description      This function is called to start an inquiry on the
 *                  classic BR/EDR link and start an le scan.  This is an
 *                  Android only API.
 *
 * Parameters:      p_inqparms - pointer to the inquiry information
 *                      mode - GENERAL or LIMITED inquiry, BR/LE bit mask
 *                             separately
 *                      duration - length in 1.28 sec intervals (If '0', the
 *                                 inquiry is CANCELLED)
 *                      filter_cond_type - BTM_CLR_INQUIRY_FILTER,
 *                                         BTM_FILTER_COND_DEVICE_CLASS, or
 *                                         BTM_FILTER_COND_BD_ADDR
 *                      filter_cond - value for the filter (based on
 *                                                          filter_cond_type)
 *
 *                  p_results_cb   - Pointer to the callback routine which gets
 *                                called upon receipt of an inquiry result. If
 *                                this field is NULL, the application is not
 *                                notified.
 *
 *                  p_cmpl_cb   - Pointer to the callback routine which gets
 *                                called upon completion.  If this field is
 *                                NULL, the application is not notified when
 *                                completed.
 * Returns          tBTM_STATUS
 *                  tBTM_STATUS::BTM_CMD_STARTED if successfully initiated
 *                  tBTM_STATUS::BTM_BUSY if already in progress
 *                  tBTM_STATUS::BTM_ILLEGAL_VALUE if parameter(s) are out of range
 *                  tBTM_STATUS::BTM_NO_RESOURCES if could not allocate resources to start
 *                                   the command
 *                  tBTM_STATUS::BTM_WRONG_MODE if the device is not up.
 *
 ******************************************************************************/
tBTM_STATUS BTM_StartInquiry(tBTM_INQ_RESULTS_CB* p_results_cb, tBTM_CMPL_CB* p_cmpl_cb) {
  /* Only one active inquiry is allowed in this implementation.
     Also do not allow an inquiry if the inquiry filter is being updated */
  if (btm_cb.btm_inq_vars.inq_active) {
    log::warn(
            "Active device discovery already in progress inq_active:0x{:02x} "
            "state:{} counter:{}",
            btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state,
            btm_cb.btm_inq_vars.inq_counter);
    btm_cb.neighbor.inquiry_history_->Push({
            .status = tBTM_INQUIRY_CMPL::NOT_STARTED,
    });
    return tBTM_STATUS::BTM_BUSY;
  }

  if (btm_cb.btm_inq_vars.registered_for_hci_events == false) {
    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
            EventCode::INQUIRY_COMPLETE,
            get_main_thread()->Bind([](EventView event) { on_incoming_hci_event(event); }));
    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
            EventCode::INQUIRY_RESULT,
            get_main_thread()->Bind([](EventView event) { on_incoming_hci_event(event); }));
    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
            EventCode::INQUIRY_RESULT_WITH_RSSI,
            get_main_thread()->Bind([](EventView event) { on_incoming_hci_event(event); }));
    bluetooth::shim::GetHciLayer()->RegisterEventHandler(
            EventCode::EXTENDED_INQUIRY_RESULT,
            get_main_thread()->Bind([](EventView event) { on_incoming_hci_event(event); }));

    btm_cb.btm_inq_vars.registered_for_hci_events = true;
  }

  /*** Make sure the device is ready ***/
  if (!get_btm_client_interface().local.BTM_IsDeviceUp()) {
    log::error("adapter is not up");
    btm_cb.neighbor.inquiry_history_->Push({
            .status = tBTM_INQUIRY_CMPL::NOT_STARTED,
    });
    return tBTM_STATUS::BTM_WRONG_MODE;
  }

  BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry started",
                 base::StringPrintf("%s", (btm_cb.neighbor.classic_inquiry.start_time_ms == 0)
                                                  ? ""
                                                  : "ERROR Already in progress"));

  const uint8_t inq_length =
          osi_property_get_int32(PROPERTY_INQ_LENGTH, BTIF_DM_DEFAULT_INQ_MAX_DURATION);

  /* Save the inquiry parameters to be used upon the completion of
   * setting/clearing the inquiry filter */
  btm_cb.btm_inq_vars.inqparms = {
          // tBTM_INQ_PARMS
          .mode = BTM_GENERAL_INQUIRY | BTM_BLE_GENERAL_INQUIRY,
          .duration = inq_length,
  };

  /* Initialize the inquiry variables */
  btm_cb.btm_inq_vars.state = BTM_INQ_ACTIVE_STATE;
  btm_cb.btm_inq_vars.p_inq_cmpl_cb = p_cmpl_cb;
  btm_cb.btm_inq_vars.p_inq_results_cb = p_results_cb;
  btm_cb.btm_inq_vars.inq_cmpl_info = {}; /* Clear the results counter */
  btm_cb.btm_inq_vars.inq_active = btm_cb.btm_inq_vars.inqparms.mode;
  btm_cb.neighbor.classic_inquiry = {
          .start_time_ms = timestamper_in_milliseconds.GetTimestamp(),
          .results = 0,
  };

  log::debug("Starting device discovery inq_active:0x{:02x}", btm_cb.btm_inq_vars.inq_active);

  // Also do BLE scanning here if we aren't limiting discovery to classic only.
  // This path does not play nicely with GD BLE scanning and may cause issues
  // with other scanners.
  BTM_StartLeScan();

  btm_clr_inq_result_flt();

  btm_init_inq_result_flt();

  Lap lap;
  lap.lap_ = general_inq_lap[2];

  // TODO: Register for the inquiry interface and use that
  bluetooth::shim::GetHciLayer()->EnqueueCommand(
          InquiryBuilder::Create(lap, btm_cb.btm_inq_vars.inqparms.duration, 0),
          get_main_thread()->BindOnce([](CommandStatusView status_view) {
            log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
            auto status = status_view.GetStatus();
            if (status == ErrorCode::SUCCESS) {
              BTIF_dm_report_inquiry_status_change(tBTM_INQUIRY_STATE::BTM_INQUIRY_STARTED);
            } else {
              log::info("Inquiry failed to start status: {}", ErrorCodeText(status));
            }
          }));

#if TARGET_FLOSS
  // If we are only doing classic discovery, we should also set a timeout for
  // the inquiry if a duration is set.
  if (btm_cb.btm_inq_vars.inqparms.duration != 0) {
    /* start inquiry timer */
    uint64_t duration_ms = btm_cb.btm_inq_vars.inqparms.duration * 1280;
    alarm_set_on_mloop(btm_cb.btm_inq_vars.classic_inquiry_timer, duration_ms,
                       btm_classic_inquiry_timeout, NULL);
  }
#endif

  return tBTM_STATUS::BTM_CMD_STARTED;
}

/*******************************************************************************
 *
 * Function         BTM_InqDbRead
 *
 * Description      This function looks through the inquiry database for a match
 *                  based on Bluetooth Device Address. This is the application's
 *                  interface to get the inquiry details of a specific BD
 *                  address.
 *
 * Returns          pointer to entry, or NULL if not found
 *
 ******************************************************************************/
tBTM_INQ_INFO* BTM_InqDbRead(const RawAddress& p_bda) {
  tINQ_DB_ENT* p_ent = btm_inq_db_find(p_bda);
  return (p_ent == nullptr) ? nullptr : &p_ent->inq_info;
}

/*******************************************************************************
 *
 * Function         BTM_InqDbFirst
 *
 * Description      This function looks through the inquiry database for the
 *                  first used entry, and returns that. This is used in
 *                  conjunction with
 *                  BTM_InqDbNext by applications as a way to walk through the
 *                  inquiry database.
 *
 * Returns          pointer to first in-use entry, or NULL if DB is empty
 *
 ******************************************************************************/
tBTM_INQ_INFO* BTM_InqDbFirst(void) {
  uint16_t xx;

  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = inq_db_;
  for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) {
    if (p_ent->in_use) {
      return &p_ent->inq_info;
    }
  }

  /* If here, no used entry found */
  return nullptr;
}

/*******************************************************************************
 *
 * Function         BTM_InqDbNext
 *
 * Description      This function looks through the inquiry database for the
 *                  next used entry, and returns that.  If the input parameter
 *                  is NULL, the first entry is returned.
 *
 * Returns          pointer to next in-use entry, or NULL if no more found.
 *
 ******************************************************************************/
tBTM_INQ_INFO* BTM_InqDbNext(tBTM_INQ_INFO* p_cur) {
  uint16_t inx;

  std::lock_guard<std::mutex> lock(inq_db_lock_);

  if (p_cur) {
    tINQ_DB_ENT* p_ent = (tINQ_DB_ENT*)((uint8_t*)p_cur - offsetof(tINQ_DB_ENT, inq_info));
    inx = (uint16_t)((p_ent - inq_db_) + 1);

    for (p_ent = &inq_db_[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) {
      if (p_ent->in_use) {
        return &p_ent->inq_info;
      }
    }

    /* If here, more entries found */
    return nullptr;
  } else {
    return BTM_InqDbFirst();
  }
}

/*******************************************************************************
 *
 * Function         BTM_ClearInqDb
 *
 * Description      This function is called to clear out a device or all devices
 *                  from the inquiry database.
 *
 * Parameter        p_bda - (input) BD_ADDR ->  Address of device to clear
 *                                              (NULL clears all entries)
 *
 * Returns          tBTM_STATUS::BTM_BUSY if an inquiry, get remote name, or event filter
 *                          is active, otherwise tBTM_STATUS::BTM_SUCCESS
 *
 ******************************************************************************/
tBTM_STATUS BTM_ClearInqDb(const RawAddress* p_bda) {
  /* If an inquiry or remote name is in progress return busy */
  if (btm_cb.btm_inq_vars.inq_active != BTM_INQUIRY_INACTIVE) {
    return tBTM_STATUS::BTM_BUSY;
  }

  btm_clr_inq_db(p_bda);

  return tBTM_STATUS::BTM_SUCCESS;
}

/*******************************************************************************
 *
 * Function         btm_clear_all_pending_le_entry
 *
 * Description      This function is called to clear all LE pending entry in
 *                  inquiry database.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_clear_all_pending_le_entry(void) {
  uint16_t xx;
  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = inq_db_;

  for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) {
    /* mark all pending LE entry as unused if an LE only device has scan
     * response outstanding */
    if ((p_ent->in_use) && (p_ent->inq_info.results.device_type == BT_DEVICE_TYPE_BLE) &&
        !p_ent->scan_rsp) {
      p_ent->in_use = false;
    }
  }
}

/*******************************************************************************
 *******************************************************************************
 *                                                                            **
 *                    BTM Internal Inquiry Functions                          **
 *                                                                            **
 *******************************************************************************
 ******************************************************************************/
/*******************************************************************************
 *
 * Function         btm_inq_db_reset
 *
 * Description      This function is called at at reset to clear the inquiry
 *                  database & pending callback.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_inq_db_reset(void) {
  tBTM_REMOTE_DEV_NAME rem_name = {};
  uint8_t num_responses;
  uint8_t temp_inq_active;

  log::debug("Resetting inquiry database");

  /* If an inquiry or periodic inquiry is active, reset the mode to inactive */
  if (btm_cb.btm_inq_vars.inq_active != BTM_INQUIRY_INACTIVE) {
    /* Save so state can change BEFORE callback is called */
    temp_inq_active = btm_cb.btm_inq_vars.inq_active;
    btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;

    /* If not a periodic inquiry, the complete callback must be called to notify
     * caller */
    if (temp_inq_active == BTM_GENERAL_INQUIRY) {
      if (btm_cb.btm_inq_vars.p_inq_cmpl_cb) {
        num_responses = 0;
        (*btm_cb.btm_inq_vars.p_inq_cmpl_cb)(&num_responses);
      }
    }
  }

  /* Cancel a remote name request if active, and notify the caller (if waiting)
   */
  if (btm_cb.rnr.remname_active) {
    alarm_cancel(btm_cb.rnr.remote_name_timer);
    btm_cb.rnr.remname_active = false;
    btm_cb.rnr.remname_bda = RawAddress::kEmpty;
    btm_cb.rnr.remname_dev_type = BT_DEVICE_TYPE_UNKNOWN;

    if (btm_cb.rnr.p_remname_cmpl_cb) {
      rem_name.btm_status = tBTM_STATUS::BTM_DEV_RESET;
      rem_name.hci_status = HCI_SUCCESS;

      (*btm_cb.rnr.p_remname_cmpl_cb)(&rem_name);
      btm_cb.rnr.p_remname_cmpl_cb = NULL;
    }
  }

  btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;
  btm_cb.btm_inq_vars.p_inq_results_cb = NULL;
  btm_clr_inq_db(NULL); /* Clear out all the entries in the database */
  btm_clr_inq_result_flt();

  btm_cb.btm_inq_vars.discoverable_mode = BTM_NON_DISCOVERABLE;
  btm_cb.btm_inq_vars.connectable_mode = BTM_NON_CONNECTABLE;
  btm_cb.btm_inq_vars.page_scan_type = BTM_SCAN_TYPE_STANDARD;
  btm_cb.btm_inq_vars.inq_scan_type = BTM_SCAN_TYPE_STANDARD;

  btm_cb.btm_inq_vars.discoverable_mode |= BTM_BLE_NON_DISCOVERABLE;
  btm_cb.btm_inq_vars.connectable_mode |= BTM_BLE_NON_CONNECTABLE;
  return;
}

/*******************************************************************************
 *
 * Function         btm_clr_inq_db
 *
 * Description      This function is called to clear out a device or all devices
 *                  from the inquiry database.
 *
 * Parameter        p_bda - (input) BD_ADDR ->  Address of device to clear
 *                                              (NULL clears all entries)
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_clr_inq_db(const RawAddress* p_bda) {
  uint16_t xx;

#if (BTM_INQ_DEBUG == TRUE)
  log::verbose("btm_clr_inq_db: inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active,
               btm_cb.btm_inq_vars.state);
#endif
  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = inq_db_;
  for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) {
    if (p_ent->in_use) {
      /* If this is the specified BD_ADDR or clearing all devices */
      if (p_bda == NULL || (p_ent->inq_info.results.remote_bd_addr == *p_bda)) {
        p_ent->in_use = false;
      }
    }
  }
#if (BTM_INQ_DEBUG == TRUE)
  log::verbose("inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active,
               btm_cb.btm_inq_vars.state);
#endif
}

/*******************************************************************************
 *
 * Function         btm_[init|clr]_inq_result_flt
 *
 * Description      These functions initialize and clear the bdaddr
 *                  database for a match based on Bluetooth Device Address
 *
 * Returns          None
 *
 ******************************************************************************/
static void btm_init_inq_result_flt(void) {
  std::lock_guard<std::mutex> lock(bd_db_lock_);

  if (p_bd_db_ != nullptr) {
    log::error("Memory leak with bluetooth device database");
  }

  /* Allocate memory to hold bd_addrs responding */
  p_bd_db_ = (tINQ_BDADDR*)osi_calloc(BT_DEFAULT_BUFFER_SIZE);
  max_bd_entries_ = (uint16_t)(BT_DEFAULT_BUFFER_SIZE / sizeof(tINQ_BDADDR));
}

void btm_clr_inq_result_flt(void) {
  std::lock_guard<std::mutex> lock(bd_db_lock_);
  if (p_bd_db_ == nullptr) {
    log::warn("Memory being reset multiple times");
  }

  osi_free_and_reset((void**)&p_bd_db_);
  num_bd_entries_ = 0;
  max_bd_entries_ = 0;
}

/*******************************************************************************
 *
 * Function         btm_inq_find_bdaddr
 *
 * Description      This function looks through the bdaddr database for a match
 *                  based on Bluetooth Device Address
 *
 * Returns          true if found, else false (new entry)
 *
 ******************************************************************************/
bool btm_inq_find_bdaddr(const RawAddress& p_bda) {
  std::lock_guard<std::mutex> lock(bd_db_lock_);
  tINQ_BDADDR* p_db = p_bd_db_;
  uint16_t xx;

  /* Don't bother searching, database doesn't exist or periodic mode */
  if (!p_db) {
    return false;
  }

  for (xx = 0; xx < num_bd_entries_; xx++, p_db++) {
    if (p_db->bd_addr == p_bda && p_db->inq_count == btm_cb.btm_inq_vars.inq_counter) {
      return true;
    }
  }

  if (xx < max_bd_entries_) {
    p_db->inq_count = btm_cb.btm_inq_vars.inq_counter;
    p_db->bd_addr = p_bda;
    num_bd_entries_++;
  }

  /* If here, New Entry */
  return false;
}

/*******************************************************************************
 *
 * Function         btm_inq_db_find
 *
 * Description      This function looks through the inquiry database for a match
 *                  based on Bluetooth Device Address
 *
 * Returns          pointer to entry, or NULL if not found
 *
 ******************************************************************************/
tINQ_DB_ENT* btm_inq_db_find(const RawAddress& p_bda) {
  uint16_t xx;
  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = inq_db_;

  for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) {
    if (p_ent->in_use && p_ent->inq_info.results.remote_bd_addr == p_bda) {
      return p_ent;
    }
  }

  /* If here, not found */
  return nullptr;
}

/*******************************************************************************
 *
 * Function         btm_inq_db_new
 *
 * Description      This function looks through the inquiry database for an
 *                  unused entry. If no entry is free, it allocates the oldest
 *                  entry.
 *
 * Returns          pointer to entry
 *
 ******************************************************************************/
tINQ_DB_ENT* btm_inq_db_new(const RawAddress& p_bda, bool is_ble) {
  uint16_t xx = 0, yy = 0;
  uint32_t ot = 0xFFFFFFFF;
  int8_t i_rssi = 0;

  if (is_ble) {
    yy = BTM_INQ_DB_SIZE / 2;
  } else {
    yy = 0;
  }

  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = &inq_db_[yy];
  tINQ_DB_ENT* p_old = &inq_db_[yy];

  for (xx = 0; xx < BTM_INQ_DB_SIZE / 2; xx++, p_ent++) {
    if (!p_ent->in_use) {
      memset(p_ent, 0, sizeof(tINQ_DB_ENT));
      p_ent->inq_info.results.remote_bd_addr = p_bda;
      p_ent->in_use = true;

      return p_ent;
    }

    if (is_inquery_by_rssi()) {
      if (p_ent->inq_info.results.rssi < i_rssi) {
        p_old = p_ent;
        i_rssi = p_ent->inq_info.results.rssi;
      }
    } else {
      if (p_ent->time_of_resp < ot) {
        p_old = p_ent;
        ot = p_ent->time_of_resp;
      }
    }
  }

  /* If here, no free entry found. Return the oldest. */

  memset(p_old, 0, sizeof(tINQ_DB_ENT));
  p_old->inq_info.results.remote_bd_addr = p_bda;
  p_old->in_use = true;

  return p_old;
}

/*******************************************************************************
 *
 * Function         btm_process_inq_results_standard
 *
 * Description      This function is called when inquiry results are received
 *                  from the device. It updates the inquiry database. If the
 *                  inquiry database is full, the oldest entry is discarded.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_process_inq_results_standard(EventView event) {
  RawAddress bda;
  tINQ_DB_ENT* p_i;
  tBTM_INQ_RESULTS* p_cur = NULL;
  bool is_new = true;
  tBTM_INQ_RESULTS_CB* p_inq_results_cb = btm_cb.btm_inq_vars.p_inq_results_cb;
  uint8_t page_scan_rep_mode = 0;
  uint8_t page_scan_per_mode = 0;
  uint8_t page_scan_mode = 0;
  DEV_CLASS dc;
  uint16_t clock_offset;
  const uint8_t* p_eir_data = NULL;

  log::debug("Received inquiry result inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active,
             btm_cb.btm_inq_vars.state);

  /* Only process the results if the BR inquiry is still active */
  if (!(btm_cb.btm_inq_vars.inq_active & BTM_GENERAL_INQUIRY)) {
    log::info("Inquiry is inactive so dropping inquiry result");
    return;
  }

  auto standard_view = InquiryResultView::Create(event);
  log::assert_that(standard_view.IsValid(), "assert failed: standard_view.IsValid()");
  auto responses = standard_view.GetResponses();

  btm_cb.neighbor.classic_inquiry.results += responses.size();
  for (const auto& response : responses) {
    /* Extract inquiry results */
    bda = bluetooth::ToRawAddress(response.bd_addr_);
    page_scan_rep_mode = static_cast<uint8_t>(response.page_scan_repetition_mode_);
    page_scan_per_mode = 0;  // reserved
    page_scan_mode = 0;      // reserved

    dc[0] = response.class_of_device_.cod[2];
    dc[1] = response.class_of_device_.cod[1];
    dc[2] = response.class_of_device_.cod[0];

    clock_offset = response.clock_offset_;

    p_i = btm_inq_db_find(bda);

    /* If existing entry, use that, else get a new one (possibly reusing the
     * oldest) */
    if (p_i == NULL) {
      p_i = btm_inq_db_new(bda, false);
      is_new = true;
    } else {
      /* If an entry for the device already exists, overwrite it ONLY if it is
         from a previous inquiry. (Ignore it if it is a duplicate response from
         the same inquiry.
      */
      if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
          (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR)) {
        is_new = false;
      }
    }

    p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI;

    if (is_new) {
      /* Save the info */
      p_cur = &p_i->inq_info.results;
      p_cur->page_scan_rep_mode = page_scan_rep_mode;
      p_cur->page_scan_per_mode = page_scan_per_mode;
      p_cur->page_scan_mode = page_scan_mode;
      p_cur->dev_class[0] = dc[0];
      p_cur->dev_class[1] = dc[1];
      p_cur->dev_class[2] = dc[2];
      p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;

      p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();

      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        /* A new response was found */
        btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
        btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD]++;
      }

      p_cur->inq_result_type |= BT_DEVICE_TYPE_BREDR;
      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        p_cur->device_type = BT_DEVICE_TYPE_BREDR;
        p_i->scan_rsp = false;
      } else {
        p_cur->device_type |= BT_DEVICE_TYPE_BREDR;
      }
      p_i->inq_count = btm_cb.btm_inq_vars.inq_counter; /* Mark entry for current inquiry */

      /* Initialize flag to false. This flag is set/used by application */
      p_i->inq_info.appl_knows_rem_name = false;
    }

    if (is_new) {
      p_eir_data = NULL;

      /* If a callback is registered, call it with the results */
      if (p_inq_results_cb) {
        (p_inq_results_cb)((tBTM_INQ_RESULTS*)p_cur, p_eir_data, HCI_EXT_INQ_RESPONSE_LEN);
      } else {
        log::warn("No callback is registered for inquiry result");
      }
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_process_inq_results_rssi
 *
 * Description      This function is called when inquiry results are received
 *                  from the device. It updates the inquiry database. If the
 *                  inquiry database is full, the oldest entry is discarded.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_process_inq_results_rssi(EventView event) {
  RawAddress bda;
  tINQ_DB_ENT* p_i;
  tBTM_INQ_RESULTS* p_cur = NULL;
  bool is_new = true;
  bool update = false;
  int8_t i_rssi;
  tBTM_INQ_RESULTS_CB* p_inq_results_cb = btm_cb.btm_inq_vars.p_inq_results_cb;
  uint8_t page_scan_rep_mode = 0;
  uint8_t page_scan_per_mode = 0;
  uint8_t page_scan_mode = 0;
  uint8_t rssi = 0;
  DEV_CLASS dc;
  uint16_t clock_offset;
  const uint8_t* p_eir_data = NULL;

  log::debug("Received inquiry result inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active,
             btm_cb.btm_inq_vars.state);

  /* Only process the results if the BR inquiry is still active */
  if (!(btm_cb.btm_inq_vars.inq_active & BTM_GENERAL_INQUIRY)) {
    log::info("Inquiry is inactive so dropping inquiry result");
    return;
  }

  auto rssi_view = InquiryResultWithRssiView::Create(event);
  log::assert_that(rssi_view.IsValid(), "assert failed: rssi_view.IsValid()");
  auto responses = rssi_view.GetResponses();

  btm_cb.neighbor.classic_inquiry.results += responses.size();
  for (const auto& response : responses) {
    update = false;
    /* Extract inquiry results */
    bda = bluetooth::ToRawAddress(response.address_);
    page_scan_rep_mode = static_cast<uint8_t>(response.page_scan_repetition_mode_);
    page_scan_per_mode = 0;  // reserved
    page_scan_mode = 0;      // reserved

    dc[0] = response.class_of_device_.cod[2];
    dc[1] = response.class_of_device_.cod[1];
    dc[2] = response.class_of_device_.cod[0];

    clock_offset = response.clock_offset_;
    rssi = response.rssi_;

    p_i = btm_inq_db_find(bda);

    /* Check if this address has already been processed for this inquiry */
    if (btm_inq_find_bdaddr(bda)) {
      /* By default suppose no update needed */
      i_rssi = (int8_t)rssi;

      /* If this new RSSI is higher than the last one */
      if ((rssi != 0) && p_i &&
          (i_rssi > p_i->inq_info.results.rssi ||
           p_i->inq_info.results.rssi == 0
           /* BR/EDR inquiry information update */
           || (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0)) {
        p_cur = &p_i->inq_info.results;
        log::verbose("update RSSI new:{}, old:{}", i_rssi, p_cur->rssi);
        p_cur->rssi = i_rssi;
        update = true;
      } else {
        /* If no update needed continue with next response (if any) */
        continue;
      }
    }

    /* If existing entry, use that, else get a new one (possibly reusing the
     * oldest) */
    if (p_i == NULL) {
      p_i = btm_inq_db_new(bda, false);
      is_new = true;
    } else {
      /* If an entry for the device already exists, overwrite it ONLY if it is
         from a previous inquiry. (Ignore it if it is a duplicate response from
         the same inquiry.
      */
      if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
          (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR)) {
        is_new = false;
      }
    }

    /* keep updating RSSI to have latest value */
    p_i->inq_info.results.rssi = (int8_t)rssi;

    if (is_new) {
      /* Save the info */
      p_cur = &p_i->inq_info.results;
      p_cur->page_scan_rep_mode = page_scan_rep_mode;
      p_cur->page_scan_per_mode = page_scan_per_mode;
      p_cur->page_scan_mode = page_scan_mode;
      p_cur->dev_class[0] = dc[0];
      p_cur->dev_class[1] = dc[1];
      p_cur->dev_class[2] = dc[2];
      p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;

      p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();

      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        /* A new response was found */
        btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
        btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI]++;
      }

      p_cur->inq_result_type |= BT_DEVICE_TYPE_BREDR;
      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        p_cur->device_type = BT_DEVICE_TYPE_BREDR;
        p_i->scan_rsp = false;
      } else {
        p_cur->device_type |= BT_DEVICE_TYPE_BREDR;
      }
      p_i->inq_count = btm_cb.btm_inq_vars.inq_counter; /* Mark entry for current inquiry */

      /* Initialize flag to false. This flag is set/used by application */
      p_i->inq_info.appl_knows_rem_name = false;
    }

    if (is_new || update) {
      p_eir_data = NULL;

      /* If a callback is registered, call it with the results */
      if (p_inq_results_cb) {
        (p_inq_results_cb)((tBTM_INQ_RESULTS*)p_cur, p_eir_data, HCI_EXT_INQ_RESPONSE_LEN);
      } else {
        log::warn("No callback is registered for inquiry result");
      }
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_process_inq_results_extended
 *
 * Description      This function is called when inquiry results are received
 *                  from the device. It updates the inquiry database. If the
 *                  inquiry database is full, the oldest entry is discarded.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_process_inq_results_extended(EventView event) {
  RawAddress bda;
  tINQ_DB_ENT* p_i;
  tBTM_INQ_RESULTS* p_cur = NULL;
  bool is_new = true;
  bool update = false;
  int8_t i_rssi;
  tBTM_INQ_RESULTS_CB* p_inq_results_cb = btm_cb.btm_inq_vars.p_inq_results_cb;
  uint8_t page_scan_rep_mode = 0;
  uint8_t page_scan_per_mode = 0;
  uint8_t page_scan_mode = 0;
  uint8_t rssi = 0;
  DEV_CLASS dc;
  uint16_t clock_offset;

  log::debug("Received inquiry result inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active,
             btm_cb.btm_inq_vars.state);

  /* Only process the results if the BR inquiry is still active */
  if (!(btm_cb.btm_inq_vars.inq_active & BTM_GENERAL_INQUIRY)) {
    log::info("Inquiry is inactive so dropping inquiry result");
    return;
  }

  auto extended_view = ExtendedInquiryResultView::Create(event);
  log::assert_that(extended_view.IsValid(), "assert failed: extended_view.IsValid()");

  btm_cb.neighbor.classic_inquiry.results++;
  {
    update = false;
    /* Extract inquiry results */
    bda = bluetooth::ToRawAddress(extended_view.GetAddress());
    page_scan_rep_mode = static_cast<uint8_t>(extended_view.GetPageScanRepetitionMode());
    page_scan_per_mode = 0;  // reserved

    dc[0] = extended_view.GetClassOfDevice().cod[2];
    dc[1] = extended_view.GetClassOfDevice().cod[1];
    dc[2] = extended_view.GetClassOfDevice().cod[0];
    clock_offset = extended_view.GetClockOffset();
    rssi = extended_view.GetRssi();

    p_i = btm_inq_db_find(bda);

    /* Check if this address has already been processed for this inquiry */
    if (btm_inq_find_bdaddr(bda)) {
      /* By default suppose no update needed */
      i_rssi = (int8_t)rssi;

      /* If this new RSSI is higher than the last one */
      if ((rssi != 0) && p_i &&
          (i_rssi > p_i->inq_info.results.rssi ||
           p_i->inq_info.results.rssi == 0
           /* BR/EDR inquiry information update */
           || (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0)) {
        p_cur = &p_i->inq_info.results;
        log::verbose("update RSSI new:{}, old:{}", i_rssi, p_cur->rssi);
        p_cur->rssi = i_rssi;
        update = true;
      } else {
        /* If we received a second Extended Inq Event for an already */
        /* discovered device, this is because for the first one EIR was not
           received */
        if (p_i) {
          p_cur = &p_i->inq_info.results;
          update = true;
        } else {
          /* If no update needed continue with next response (if any) */
          return;
        }
      }
    }

    /* If existing entry, use that, else get a new one (possibly reusing the
     * oldest) */
    if (p_i == NULL) {
      p_i = btm_inq_db_new(bda, false);
      is_new = true;
    } else {
      /* If an entry for the device already exists, overwrite it ONLY if it is
         from
         a previous inquiry. (Ignore it if it is a duplicate response from the
         same
         inquiry.
      */
      if (p_i->inq_count == btm_cb.btm_inq_vars.inq_counter &&
          (p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BREDR)) {
        is_new = false;
      }
    }

    /* keep updating RSSI to have latest value */
    p_i->inq_info.results.rssi = (int8_t)rssi;

    if (is_new) {
      /* Save the info */
      p_cur = &p_i->inq_info.results;
      p_cur->page_scan_rep_mode = page_scan_rep_mode;
      p_cur->page_scan_per_mode = page_scan_per_mode;
      p_cur->page_scan_mode = page_scan_mode;
      p_cur->dev_class[0] = dc[0];
      p_cur->dev_class[1] = dc[1];
      p_cur->dev_class[2] = dc[2];
      p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID;

      p_i->time_of_resp = bluetooth::common::time_get_os_boottime_ms();

      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        /* A new response was found */
        btm_cb.btm_inq_vars.inq_cmpl_info.num_resp++;
        btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED]++;
      }

      p_cur->inq_result_type |= BT_DEVICE_TYPE_BREDR;
      if (p_i->inq_count != btm_cb.btm_inq_vars.inq_counter) {
        p_cur->device_type = BT_DEVICE_TYPE_BREDR;
        p_i->scan_rsp = false;
      } else {
        p_cur->device_type |= BT_DEVICE_TYPE_BREDR;
      }
      p_i->inq_count = btm_cb.btm_inq_vars.inq_counter; /* Mark entry for current inquiry */

      /* Initialize flag to false. This flag is set/used by application */
      p_i->inq_info.appl_knows_rem_name = false;
    }

    if (is_new || update) {
      // Create a vector of EIR data and pad it with 0
      auto data = std::vector<uint8_t>();
      data.reserve(HCI_EXT_INQ_RESPONSE_LEN);
      bluetooth::packet::BitInserter bi(data);
      for (const auto& eir : extended_view.GetExtendedInquiryResponse()) {
        if (eir.data_type_ != static_cast<GapDataType>(0)) {
          eir.Serialize(bi);
        }
      }
      while (data.size() < HCI_EXT_INQ_RESPONSE_LEN) {
        data.push_back(0);
      }

      const uint8_t* p_eir_data = data.data();

      {
        memset(p_cur->eir_uuid, 0, BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS / 8));
        /* set bit map of UUID list from received EIR */
        btm_set_eir_uuid(p_eir_data, p_cur);
      }

      /* If a callback is registered, call it with the results */
      if (p_inq_results_cb) {
        (p_inq_results_cb)((tBTM_INQ_RESULTS*)p_cur, p_eir_data, HCI_EXT_INQ_RESPONSE_LEN);
      } else {
        log::warn("No callback is registered for inquiry result");
      }
    }
  }
}

/*******************************************************************************
 *
 * Function         btm_sort_inq_result
 *
 * Description      This function is called when inquiry complete is received
 *                  from the device to sort inquiry results based on rssi.
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_sort_inq_result(void) {
  uint8_t xx, yy, num_resp;
  std::lock_guard<std::mutex> lock(inq_db_lock_);
  tINQ_DB_ENT* p_ent = inq_db_;
  tINQ_DB_ENT* p_next = inq_db_ + 1;
  int size;
  tINQ_DB_ENT* p_tmp = (tINQ_DB_ENT*)osi_malloc(sizeof(tINQ_DB_ENT));

  num_resp = (btm_cb.btm_inq_vars.inq_cmpl_info.num_resp < BTM_INQ_DB_SIZE)
                     ? btm_cb.btm_inq_vars.inq_cmpl_info.num_resp
                     : BTM_INQ_DB_SIZE;

  size = sizeof(tINQ_DB_ENT);
  for (xx = 0; xx < num_resp - 1; xx++, p_ent++) {
    for (yy = xx + 1, p_next = p_ent + 1; yy < num_resp; yy++, p_next++) {
      if (p_ent->inq_info.results.rssi < p_next->inq_info.results.rssi) {
        memcpy(p_tmp, p_next, size);
        memcpy(p_next, p_ent, size);
        memcpy(p_ent, p_tmp, size);
      }
    }
  }

  osi_free(p_tmp);
}

/*******************************************************************************
 *
 * Function         btm_process_inq_complete
 *
 * Description      This function is called when inquiry complete is received
 *                  from the device.  Call the callback if not in periodic
 *                  inquiry mode AND it is not NULL
 *                  (The caller wants the event).
 *
 *                  The callback pass back the status and the number of
 *                  responses
 *
 * Returns          void
 *
 ******************************************************************************/
void btm_process_inq_complete(tHCI_STATUS status, uint8_t mode) {
  btm_cb.btm_inq_vars.inqparms.mode &= ~(mode);
  const auto inq_active = btm_cb.btm_inq_vars.inq_active;

  BTIF_dm_report_inquiry_status_change(tBTM_INQUIRY_STATE::BTM_INQUIRY_COMPLETE);

  if (status != HCI_SUCCESS) {
    log::warn("Received unexpected hci status:{}", hci_error_code_text(status));
  }

  /* Ignore any stray or late complete messages if the inquiry is not active */
  if (btm_cb.btm_inq_vars.inq_active) {
    btm_cb.btm_inq_vars.inq_cmpl_info.hci_status = status;

    /* Notify caller that the inquiry has completed; (periodic inquiries do not
     * send completion events */
    if (btm_cb.btm_inq_vars.inqparms.mode == 0) {
      btm_clear_all_pending_le_entry();
      btm_cb.btm_inq_vars.state = BTM_INQ_INACTIVE_STATE;

      /* Increment so the start of a next inquiry has a new count */
      btm_cb.btm_inq_vars.inq_counter++;

      btm_clr_inq_result_flt();

      if ((status == HCI_SUCCESS) &&
          bluetooth::shim::GetController()->SupportsRssiWithInquiryResults()) {
        btm_sort_inq_result();
      }

      if (btm_cb.btm_inq_vars.p_inq_cmpl_cb) {
        (btm_cb.btm_inq_vars.p_inq_cmpl_cb)((tBTM_INQUIRY_CMPL*)&btm_cb.btm_inq_vars.inq_cmpl_info);
      } else {
        log::warn("No callback to return inquiry result");
      }

      btm_cb.neighbor.inquiry_history_->Push({
              .status = tBTM_INQUIRY_CMPL::TIMER_POPPED,
              .num_resp = btm_cb.btm_inq_vars.inq_cmpl_info.num_resp,
              .resp_type =
                      {
                              btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD],
                              btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI],
                              btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED],
                      },
              .start_time_ms = btm_cb.neighbor.classic_inquiry.start_time_ms,
      });
      const auto end_time_ms = timestamper_in_milliseconds.GetTimestamp();
      BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "Classic inquiry complete",
                     base::StringPrintf(
                             "duration_s:%6.3f results:%lu inq_active:0x%02x std:%u rssi:%u "
                             "ext:%u status:%s",
                             (end_time_ms - btm_cb.neighbor.classic_inquiry.start_time_ms) / 1000.0,
                             (unsigned long)btm_cb.neighbor.classic_inquiry.results, inq_active,
                             btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_STANDARD],
                             btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_WITH_RSSI],
                             btm_cb.btm_inq_vars.inq_cmpl_info.resp_type[BTM_INQ_RESULT_EXTENDED],
                             hci_error_code_text(status).c_str()));

      btm_cb.neighbor.classic_inquiry.start_time_ms = 0;
      /* Clear the results callback if set */
      btm_cb.btm_inq_vars.p_inq_results_cb = NULL;
      btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE;
      btm_cb.btm_inq_vars.p_inq_cmpl_cb = NULL;

    } else {
      log::info("Inquiry params is not clear so not sending callback inq_parms:{}",
                btm_cb.btm_inq_vars.inqparms.mode);
    }
  } else {
    log::error("Received inquiry complete when no inquiry was active");
  }
}

/*******************************************************************************
 *
 * Function         btm_process_cancel_complete
 *
 * Description      This function is called when inquiry cancel complete is
 *                  received from the device. This function will also call the
 *                  btm_process_inq_complete. This function is needed to
 *                  differentiate a cancel_cmpl_evt from the inq_cmpl_evt.
 *
 * Returns          void
 *
 ******************************************************************************/
static void btm_process_cancel_complete(tHCI_STATUS status, uint8_t mode) {
  BTIF_dm_report_inquiry_status_change(tBTM_INQUIRY_STATE::BTM_INQUIRY_CANCELLED);
  btm_process_inq_complete(status, mode);
}

/*******************************************************************************
 *
 * Function         BTM_WriteEIR
 *
 * Description      This function is called to write EIR data to controller.
 *
 * Parameters       p_buff - allocated HCI command buffer including extended
 *                           inquriry response
 *
 * Returns          tBTM_STATUS::BTM_SUCCESS  - if successful
 *                  tBTM_STATUS::BTM_MODE_UNSUPPORTED - if local device cannot support it
 *
 ******************************************************************************/
tBTM_STATUS BTM_WriteEIR(BT_HDR* p_buff) {
  if (bluetooth::shim::GetController()->SupportsExtendedInquiryResponse()) {
    log::verbose("Write Extended Inquiry Response to controller");
    btsnd_hcic_write_ext_inquiry_response(p_buff, TRUE);
    return tBTM_STATUS::BTM_SUCCESS;
  } else {
    osi_free(p_buff);
    return tBTM_STATUS::BTM_MODE_UNSUPPORTED;
  }
}

/*******************************************************************************
 *
 * Function         btm_convert_uuid_to_eir_service
 *
 * Description      This function is called to get the bit position of UUID.
 *
 * Parameters       uuid16 - UUID 16-bit
 *
 * Returns          BTM EIR service ID if found
 *                  BTM_EIR_MAX_SERVICES - if not found
 *
 ******************************************************************************/
static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16) {
  uint8_t xx;

  for (xx = 0; xx < BTM_EIR_MAX_SERVICES; xx++) {
    if (uuid16 == BTM_EIR_UUID_LKUP_TBL[xx]) {
      return xx;
    }
  }
  return BTM_EIR_MAX_SERVICES;
}

/*******************************************************************************
 *
 * Function         BTM_HasEirService
 *
 * Description      This function is called to know if UUID in bit map of UUID.
 *
 * Parameters       p_eir_uuid - bit map of UUID list
 *                  uuid16 - UUID 16-bit
 *
 * Returns          true - if found
 *                  false - if not found
 *
 ******************************************************************************/
bool BTM_HasEirService(const uint32_t* p_eir_uuid, uint16_t uuid16) {
  uint8_t service_id;

  service_id = btm_convert_uuid_to_eir_service(uuid16);
  if (service_id < BTM_EIR_MAX_SERVICES) {
    return BTM_EIR_HAS_SERVICE(p_eir_uuid, service_id);
  } else {
    return false;
  }
}

/*******************************************************************************
 *
 * Function         BTM_AddEirService
 *
 * Description      This function is called to add a service in bit map of UUID
 *                  list.
 *
 * Parameters       p_eir_uuid - bit mask of UUID list for EIR
 *                  uuid16 - UUID 16-bit
 *
 * Returns          None
 *
 ******************************************************************************/
void BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16) {
  uint8_t service_id;

  service_id = btm_convert_uuid_to_eir_service(uuid16);
  if (service_id < BTM_EIR_MAX_SERVICES) {
    BTM_EIR_SET_SERVICE(p_eir_uuid, service_id);
  }
}

/*******************************************************************************
 *
 * Function         BTM_RemoveEirService
 *
 * Description      This function is called to remove a service in bit map of
 *                  UUID list.
 *
 * Parameters       p_eir_uuid - bit mask of UUID list for EIR
 *                  uuid16 - UUID 16-bit
 *
 * Returns          None
 *
 ******************************************************************************/
void BTM_RemoveEirService(uint32_t* p_eir_uuid, uint16_t uuid16) {
  uint8_t service_id;

  service_id = btm_convert_uuid_to_eir_service(uuid16);
  if (service_id < BTM_EIR_MAX_SERVICES) {
    BTM_EIR_CLR_SERVICE(p_eir_uuid, service_id);
  }
}

/*******************************************************************************
 *
 * Function         BTM_GetEirSupportedServices
 *
 * Description      This function is called to get UUID list from bit map of
 *                  UUID list.
 *
 * Parameters       p_eir_uuid - bit mask of UUID list for EIR
 *                  p - reference of current pointer of EIR
 *                  max_num_uuid16 - max number of UUID can be written in EIR
 *                  num_uuid16 - number of UUID have been written in EIR
 *
 * Returns          HCI_EIR_MORE_16BITS_UUID_TYPE, if it has more than max
 *                  HCI_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise
 *
 ******************************************************************************/
uint8_t BTM_GetEirSupportedServices(uint32_t* p_eir_uuid, uint8_t** p, uint8_t max_num_uuid16,
                                    uint8_t* p_num_uuid16) {
  uint8_t service_index;

  *p_num_uuid16 = 0;

  for (service_index = 0; service_index < BTM_EIR_MAX_SERVICES; service_index++) {
    if (BTM_EIR_HAS_SERVICE(p_eir_uuid, service_index)) {
      if (*p_num_uuid16 < max_num_uuid16) {
        UINT16_TO_STREAM(*p, BTM_EIR_UUID_LKUP_TBL[service_index]);
        (*p_num_uuid16)++;
      } else {
        /* if max number of UUIDs are stored and found one more */
        return HCI_EIR_MORE_16BITS_UUID_TYPE;
      }
    }
  }
  return HCI_EIR_COMPLETE_16BITS_UUID_TYPE;
}

/*******************************************************************************
 *
 * Function         BTM_GetEirUuidList
 *
 * Description      This function parses EIR and returns UUID list.
 *
 * Parameters       p_eir - EIR
 *                  eir_len - EIR len
 *                  uuid_size - Uuid::kNumBytes16, Uuid::kNumBytes32,
 *                              Uuid::kNumBytes128
 *                  p_num_uuid - return number of UUID in found list
 *                  p_uuid_list - return UUID list
 *                  max_num_uuid - maximum number of UUID to be returned
 *
 * Returns          0 - if not found
 *                  HCI_EIR_COMPLETE_16BITS_UUID_TYPE
 *                  HCI_EIR_MORE_16BITS_UUID_TYPE
 *                  HCI_EIR_COMPLETE_32BITS_UUID_TYPE
 *                  HCI_EIR_MORE_32BITS_UUID_TYPE
 *                  HCI_EIR_COMPLETE_128BITS_UUID_TYPE
 *                  HCI_EIR_MORE_128BITS_UUID_TYPE
 *
 ******************************************************************************/
uint8_t BTM_GetEirUuidList(const uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
                           uint8_t* p_num_uuid, uint8_t* p_uuid_list, uint8_t max_num_uuid) {
  const uint8_t* p_uuid_data;
  uint8_t type;
  uint8_t yy, xx;
  uint16_t* p_uuid16 = (uint16_t*)p_uuid_list;
  uint32_t* p_uuid32 = (uint32_t*)p_uuid_list;
  char buff[Uuid::kNumBytes128 * 2 + 1];

  p_uuid_data = btm_eir_get_uuid_list(p_eir, eir_len, uuid_size, p_num_uuid, &type);
  if (p_uuid_data == NULL) {
    return 0x00;
  }

  if (*p_num_uuid > max_num_uuid) {
    log::warn("number of uuid in EIR = {}, size of uuid list = {}", *p_num_uuid, max_num_uuid);
    *p_num_uuid = max_num_uuid;
  }

  log::verbose("type = {:02X}, number of uuid = {}", type, *p_num_uuid);

  if (uuid_size == Uuid::kNumBytes16) {
    for (yy = 0; yy < *p_num_uuid; yy++) {
      STREAM_TO_UINT16(*(p_uuid16 + yy), p_uuid_data);
      log::verbose("0x{:04X}", *(p_uuid16 + yy));
    }
  } else if (uuid_size == Uuid::kNumBytes32) {
    for (yy = 0; yy < *p_num_uuid; yy++) {
      STREAM_TO_UINT32(*(p_uuid32 + yy), p_uuid_data);
      log::verbose("0x{:08X}", *(p_uuid32 + yy));
    }
  } else if (uuid_size == Uuid::kNumBytes128) {
    for (yy = 0; yy < *p_num_uuid; yy++) {
      STREAM_TO_ARRAY16(p_uuid_list + yy * Uuid::kNumBytes128, p_uuid_data);
      for (xx = 0; xx < Uuid::kNumBytes128; xx++) {
        snprintf(buff + xx * 2, sizeof(buff) - xx * 2, "%02X",
                 *(p_uuid_list + yy * Uuid::kNumBytes128 + xx));
      }
      log::verbose("0x{}", buff);
    }
  }

  return type;
}

/*******************************************************************************
 *
 * Function         btm_eir_get_uuid_list
 *
 * Description      This function searches UUID list in EIR.
 *
 * Parameters       p_eir - address of EIR
 *                  eir_len - EIR length
 *                  uuid_size - size of UUID to find
 *                  p_num_uuid - number of UUIDs found
 *                  p_uuid_list_type - EIR data type
 *
 * Returns          NULL - if UUID list with uuid_size is not found
 *                  beginning of UUID list in EIR - otherwise
 *
 ******************************************************************************/
static const uint8_t* btm_eir_get_uuid_list(const uint8_t* p_eir, size_t eir_len, uint8_t uuid_size,
                                            uint8_t* p_num_uuid, uint8_t* p_uuid_list_type) {
  const uint8_t* p_uuid_data;
  uint8_t complete_type, more_type;
  uint8_t uuid_len;

  switch (uuid_size) {
    case Uuid::kNumBytes16:
      complete_type = HCI_EIR_COMPLETE_16BITS_UUID_TYPE;
      more_type = HCI_EIR_MORE_16BITS_UUID_TYPE;
      break;
    case Uuid::kNumBytes32:
      complete_type = HCI_EIR_COMPLETE_32BITS_UUID_TYPE;
      more_type = HCI_EIR_MORE_32BITS_UUID_TYPE;
      break;
    case Uuid::kNumBytes128:
      complete_type = HCI_EIR_COMPLETE_128BITS_UUID_TYPE;
      more_type = HCI_EIR_MORE_128BITS_UUID_TYPE;
      break;
    default:
      *p_num_uuid = 0;
      return NULL;
      break;
  }

  p_uuid_data = AdvertiseDataParser::GetFieldByType(p_eir, eir_len, complete_type, &uuid_len);
  if (p_uuid_data == NULL) {
    p_uuid_data = AdvertiseDataParser::GetFieldByType(p_eir, eir_len, more_type, &uuid_len);
    *p_uuid_list_type = more_type;
  } else {
    *p_uuid_list_type = complete_type;
  }

  *p_num_uuid = uuid_len / uuid_size;
  return p_uuid_data;
}

/*******************************************************************************
 *
 * Function         btm_convert_uuid_to_uuid16
 *
 * Description      This function converts UUID to UUID 16-bit.
 *
 * Parameters       p_uuid - address of UUID
 *                  uuid_size - size of UUID
 *
 * Returns          0 - if UUID cannot be converted to UUID 16-bit
 *                  UUID 16-bit - otherwise
 *
 ******************************************************************************/
static uint16_t btm_convert_uuid_to_uuid16(const uint8_t* p_uuid, uint8_t uuid_size) {
  static const uint8_t base_uuid[Uuid::kNumBytes128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00,
                                                        0x00, 0x80, 0x00, 0x10, 0x00, 0x00,
                                                        0x00, 0x00, 0x00, 0x00};
  uint16_t uuid16 = 0;
  uint32_t uuid32;
  bool is_base_uuid;
  uint8_t xx;

  switch (uuid_size) {
    case Uuid::kNumBytes16:
      STREAM_TO_UINT16(uuid16, p_uuid);
      break;
    case Uuid::kNumBytes32:
      STREAM_TO_UINT32(uuid32, p_uuid);
      if (uuid32 < 0x10000) {
        uuid16 = (uint16_t)uuid32;
      }
      break;
    case Uuid::kNumBytes128:
      /* See if we can compress the UUID down to 16 or 32bit UUIDs */
      is_base_uuid = true;
      for (xx = 0; xx < Uuid::kNumBytes128 - 4; xx++) {
        if (p_uuid[xx] != base_uuid[xx]) {
          is_base_uuid = false;
          break;
        }
      }
      if (is_base_uuid) {
        if ((p_uuid[Uuid::kNumBytes128 - 1] == 0) && (p_uuid[Uuid::kNumBytes128 - 2] == 0)) {
          p_uuid += (Uuid::kNumBytes128 - 4);
          STREAM_TO_UINT16(uuid16, p_uuid);
        }
      }
      break;
    default:
      log::warn("btm_convert_uuid_to_uuid16 invalid uuid size");
      break;
  }

  return uuid16;
}

/*******************************************************************************
 *
 * Function         btm_set_eir_uuid
 *
 * Description      This function is called to store received UUID into inquiry
 *                  result.
 *
 * Parameters       p_eir - pointer of EIR significant part
 *                  p_results - pointer of inquiry result
 *
 * Returns          None
 *
 ******************************************************************************/
void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results) {
  const uint8_t* p_uuid_data;
  uint8_t num_uuid;
  uint16_t uuid16;
  uint8_t yy;
  uint8_t type = HCI_EIR_MORE_16BITS_UUID_TYPE;

  p_uuid_data = btm_eir_get_uuid_list(p_eir, HCI_EXT_INQ_RESPONSE_LEN, Uuid::kNumBytes16, &num_uuid,
                                      &type);

  if (type == HCI_EIR_COMPLETE_16BITS_UUID_TYPE) {
    p_results->eir_complete_list = true;
  } else {
    p_results->eir_complete_list = false;
  }

  log::verbose("eir_complete_list=0x{:02X}", p_results->eir_complete_list);

  if (p_uuid_data) {
    for (yy = 0; yy < num_uuid; yy++) {
      STREAM_TO_UINT16(uuid16, p_uuid_data);
      BTM_AddEirService(p_results->eir_uuid, uuid16);
    }
  }

  p_uuid_data = btm_eir_get_uuid_list(p_eir, HCI_EXT_INQ_RESPONSE_LEN, Uuid::kNumBytes32, &num_uuid,
                                      &type);
  if (p_uuid_data) {
    for (yy = 0; yy < num_uuid; yy++) {
      uuid16 = btm_convert_uuid_to_uuid16(p_uuid_data, Uuid::kNumBytes32);
      p_uuid_data += Uuid::kNumBytes32;
      if (uuid16) {
        BTM_AddEirService(p_results->eir_uuid, uuid16);
      }
    }
  }

  p_uuid_data = btm_eir_get_uuid_list(p_eir, HCI_EXT_INQ_RESPONSE_LEN, Uuid::kNumBytes128,
                                      &num_uuid, &type);
  if (p_uuid_data) {
    for (yy = 0; yy < num_uuid; yy++) {
      uuid16 = btm_convert_uuid_to_uuid16(p_uuid_data, Uuid::kNumBytes128);
      p_uuid_data += Uuid::kNumBytes128;
      if (uuid16) {
        BTM_AddEirService(p_results->eir_uuid, uuid16);
      }
    }
  }
}

static void on_inquiry_complete(EventView event) {
  auto complete = InquiryCompleteView::Create(event);
  log::assert_that(complete.IsValid(), "assert failed: complete.IsValid()");
  auto status = to_hci_status_code(static_cast<uint8_t>(complete.GetStatus()));

  btm_process_inq_complete(status, BTM_GENERAL_INQUIRY);
}
/*******************************************************************************
 *
 * Function         on_incoming_hci_event
 *
 * Description      This function is called to process events from the HCI layer
 *
 * Parameters       event - an EventView with the specific event
 *
 * Returns          None
 *
 ******************************************************************************/
static void on_incoming_hci_event(EventView event) {
  log::assert_that(event.IsValid(), "assert failed: event.IsValid()");
  auto event_code = event.GetEventCode();
  switch (event_code) {
    case EventCode::INQUIRY_COMPLETE:
      on_inquiry_complete(event);
      break;
    case EventCode::INQUIRY_RESULT:
      btm_process_inq_results_standard(event);
      break;
    case EventCode::INQUIRY_RESULT_WITH_RSSI:
      btm_process_inq_results_rssi(event);
      break;
    case EventCode::EXTENDED_INQUIRY_RESULT:
      btm_process_inq_results_extended(event);
      break;
    default:
      log::warn("Dropping unhandled event: {}", EventCodeText(event_code));
  }
}

void tBTM_INQUIRY_VAR_ST::Init() {
  alarm_free(classic_inquiry_timer);

  classic_inquiry_timer = alarm_new("btm_inq.classic_inquiry_timer");

  discoverable_mode = BTM_NON_DISCOVERABLE;
  connectable_mode = BTM_NON_CONNECTABLE;

  page_scan_window = HCI_DEF_PAGESCAN_WINDOW;
  page_scan_period = HCI_DEF_PAGESCAN_INTERVAL;
  inq_scan_window = HCI_DEF_INQUIRYSCAN_WINDOW;
  inq_scan_period = HCI_DEF_INQUIRYSCAN_INTERVAL;
  inq_scan_type = BTM_SCAN_TYPE_STANDARD;
  page_scan_type = HCI_DEF_SCAN_TYPE;

  p_inq_cmpl_cb = nullptr;
  p_inq_results_cb = nullptr;

  inq_counter = 0;
  inqparms = {};
  inq_cmpl_info = {};

  per_min_delay = 0;
  per_max_delay = 0;
  state = BTM_INQ_INACTIVE_STATE;
  inq_active = 0;
  registered_for_hci_events = false;
}

void tBTM_INQUIRY_VAR_ST::Free() { alarm_free(classic_inquiry_timer); }

namespace bluetooth {
namespace legacy {
namespace testing {
void btm_clr_inq_db(const RawAddress* p_bda) { ::btm_clr_inq_db(p_bda); }
uint16_t btm_get_num_bd_entries() { return num_bd_entries_; }
}  // namespace testing
}  // namespace legacy
}  // namespace bluetooth
