/*
 * Copyright 2019 The Android Open Source Project
 *
 * 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.
 */
#include "hci/le_scanning_manager.h"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>

#include <memory>
#include <unordered_map>

#include "hci/acl_manager.h"
#include "hci/controller.h"
#include "hci/event_checkers.h"
#include "hci/hci_layer.h"
#include "hci/hci_packets.h"
#include "hci/le_periodic_sync_manager.h"
#include "hci/le_scanning_interface.h"
#include "hci/le_scanning_reassembler.h"
#include "module.h"
#include "os/handler.h"
#include "os/system_properties.h"
#include "storage/storage_module.h"

namespace bluetooth {
namespace hci {

constexpr uint16_t kLeScanWindowMin = 0x0004;
constexpr uint16_t kLeScanWindowMax = 0x4000;
constexpr int64_t kLeScanRssiMin = -127;
constexpr int64_t kLeScanRssiMax = 20;
constexpr int64_t kLeScanRssiUnknown = 127;
constexpr int64_t kLeRxPathLossCompMin = -128;
constexpr int64_t kLeRxPathLossCompMax = 127;
constexpr uint16_t kDefaultLeExtendedScanWindow = 4800;
constexpr uint16_t kLeExtendedScanWindowMax = 0xFFFF;
constexpr uint16_t kLeScanIntervalMin = 0x0004;
constexpr uint16_t kLeScanIntervalMax = 0x4000;
constexpr uint16_t kDefaultLeExtendedScanInterval = 4800;
constexpr uint16_t kLeExtendedScanIntervalMax = 0xFFFF;

constexpr uint8_t kScannableBit = 1;
constexpr uint8_t kDirectedBit = 2;
constexpr uint8_t kScanResponseBit = 3;
constexpr uint8_t kLegacyBit = 4;
constexpr uint8_t kDataStatusBits = 5;

// system properties
const std::string kLeRxPathLossCompProperty = "bluetooth.hardware.radio.le_rx_path_loss_comp_db";

const ModuleFactory LeScanningManager::Factory =
        ModuleFactory([]() { return new LeScanningManager(); });

enum class ScanApiType {
  LEGACY = 1,
  ANDROID_HCI = 2,
  EXTENDED = 3,
};

struct Scanner {
  Uuid app_uuid;
  bool in_use;
};

class NullScanningCallback : public ScanningCallback {
  void OnScannerRegistered(const Uuid /* app_uuid */, ScannerId /* scanner_id */,
                           ScanningStatus /* status */) override {
    log::info("OnScannerRegistered in NullScanningCallback");
  }
  void OnSetScannerParameterComplete(ScannerId /* scanner_id */,
                                     ScanningStatus /* status */) override {
    log::info("OnSetScannerParameterComplete in NullScanningCallback");
  }
  void OnScanResult(uint16_t /* event_type */, uint8_t /* address_type */, Address /* address */,
                    uint8_t /* primary_phy */, uint8_t /* secondary_phy */,
                    uint8_t /* advertising_sid */, int8_t /* tx_power */, int8_t /* rssi */,
                    uint16_t /* periodic_advertising_interval */,
                    std::vector<uint8_t> /* advertising_data */) override {
    log::info("OnScanResult in NullScanningCallback");
  }
  void OnTrackAdvFoundLost(
          AdvertisingFilterOnFoundOnLostInfo /* on_found_on_lost_info */) override {
    log::info("OnTrackAdvFoundLost in NullScanningCallback");
  }
  void OnBatchScanReports(int /* client_if */, int /* status */, int /* report_format */,
                          int /* num_records */, std::vector<uint8_t> /* data */) override {
    log::info("OnBatchScanReports in NullScanningCallback");
  }
  void OnBatchScanThresholdCrossed(int /* client_if */) override {
    log::info("OnBatchScanThresholdCrossed in NullScanningCallback");
  }
  void OnTimeout() override { log::info("OnTimeout in NullScanningCallback"); }
  void OnFilterEnable(Enable /* enable */, uint8_t /* status */) override {
    log::info("OnFilterEnable in NullScanningCallback");
  }
  void OnFilterParamSetup(uint8_t /* available_spaces */, ApcfAction /* action */,
                          uint8_t /* status */) override {
    log::info("OnFilterParamSetup in NullScanningCallback");
  }
  void OnFilterConfigCallback(ApcfFilterType /* filter_type */, uint8_t /* available_spaces */,
                              ApcfAction /* action */, uint8_t /* status */) override {
    log::info("OnFilterConfigCallback in NullScanningCallback");
  }
  void OnPeriodicSyncStarted(int /* reg_id */, uint8_t /* status */, uint16_t /* sync_handle */,
                             uint8_t /* advertising_sid */, AddressWithType /* address_with_type */,
                             uint8_t /* phy */, uint16_t /* interval */) override {
    log::info("OnPeriodicSyncStarted in NullScanningCallback");
  }
  void OnPeriodicSyncReport(uint16_t /* sync_handle */, int8_t /* tx_power */, int8_t /* rssi */,
                            uint8_t /* status */, std::vector<uint8_t> /* data */) override {
    log::info("OnPeriodicSyncReport in NullScanningCallback");
  }
  void OnPeriodicSyncLost(uint16_t /* sync_handle */) override {
    log::info("OnPeriodicSyncLost in NullScanningCallback");
  }
  void OnPeriodicSyncTransferred(int /* pa_source */, uint8_t /* status */,
                                 Address /* address */) override {
    log::info("OnPeriodicSyncTransferred in NullScanningCallback");
  }
  void OnBigInfoReport(uint16_t /* sync_handle */, bool /* encrypted */) {
    log::info("OnBigInfoReport in NullScanningCallback");
  }
};

enum class BatchScanState {
  ERROR_STATE = 0,
  ENABLE_CALLED = 1,
  ENABLED_STATE = 2,
  DISABLE_CALLED = 3,
  DISABLED_STATE = 4,
};

#define BTM_BLE_BATCH_SCAN_MODE_DISABLE 0
#define BTM_BLE_BATCH_SCAN_MODE_PASS 1
#define BTM_BLE_BATCH_SCAN_MODE_ACTI 2
#define BTM_BLE_BATCH_SCAN_MODE_PASS_ACTI 3

struct BatchScanConfig {
  BatchScanState current_state;
  BatchScanMode scan_mode;
  uint32_t scan_interval;
  uint32_t scan_window;
  BatchScanDiscardRule discard_rule;
  ScannerId ref_value;
};

struct LeScanningManager::impl : public LeAddressManagerCallback {
  impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {}

  ~impl() {
    if (address_manager_registered_) {
      le_address_manager_->Unregister(this);
    }
  }

  void start(os::Handler* handler, HciLayer* hci_layer, Controller* controller,
             AclManager* acl_manager, storage::StorageModule* storage_module) {
    module_handler_ = handler;
    hci_layer_ = hci_layer;
    controller_ = controller;
    acl_manager_ = acl_manager;
    storage_module_ = storage_module;
    le_address_manager_ = acl_manager->GetLeAddressManager();
    le_scanning_interface_ = hci_layer_->GetLeScanningInterface(
            module_handler_->BindOn(this, &LeScanningManager::impl::handle_scan_results));
    periodic_sync_manager_.Init(le_scanning_interface_, module_handler_);
    /* Check to see if the opcode is supported and C19 (support for extended advertising). */
    if (controller_->IsSupported(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS) &&
        controller->SupportsBleExtendedAdvertising()) {
      api_type_ = ScanApiType::EXTENDED;
      interval_ms_ = kDefaultLeExtendedScanInterval;
      window_ms_ = kDefaultLeExtendedScanWindow;
      phy_ = static_cast<uint8_t>(PhyType::LE_1M);
    } else if (controller_->IsSupported(OpCode::LE_EXTENDED_SCAN_PARAMS)) {
      api_type_ = ScanApiType::ANDROID_HCI;
    } else {
      api_type_ = ScanApiType::LEGACY;
    }
    is_filter_supported_ = controller_->IsSupported(OpCode::LE_ADV_FILTER);
    if (is_filter_supported_) {
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterReadExtendedFeaturesBuilder::Create(),
              module_handler_->BindOnceOn(this, &impl::on_apcf_read_extended_features_complete));
    }
    is_batch_scan_supported_ = controller->IsSupported(OpCode::LE_BATCH_SCAN);
    is_periodic_advertising_sync_transfer_sender_supported_ =
            controller_->SupportsBlePeriodicAdvertisingSyncTransferSender();
    total_num_of_advt_tracked_ = controller->GetVendorCapabilities().total_num_of_advt_tracked_;
    if (is_batch_scan_supported_) {
      hci_layer_->RegisterVendorSpecificEventHandler(
              VseSubeventCode::BLE_THRESHOLD,
              handler->BindOn(this, &LeScanningManager::impl::on_storage_threshold_breach));
      hci_layer_->RegisterVendorSpecificEventHandler(
              VseSubeventCode::BLE_TRACKING,
              handler->BindOn(this, &LeScanningManager::impl::on_advertisement_tracking));
    }
    scanners_ = std::vector<Scanner>(kMaxAppNum + 1);
    for (size_t i = 0; i < scanners_.size(); i++) {
      scanners_[i].app_uuid = Uuid::kEmpty;
      scanners_[i].in_use = false;
    }
    batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
    batch_scan_config_.ref_value = kInvalidScannerId;
    le_rx_path_loss_comp_ = get_rx_path_loss_compensation();
  }

  void stop() {
    for (auto subevent_code : LeScanningEvents) {
      hci_layer_->UnregisterLeEventHandler(subevent_code);
    }
    if (is_batch_scan_supported_) {
      // TODO implete vse module
      // hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_THRESHOLD);
      // hci_layer_->UnregisterVesEventHandler(VseSubeventCode::BLE_TRACKING);
    }
    batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
    batch_scan_config_.ref_value = kInvalidScannerId;
    scanning_callbacks_ = &null_scanning_callback_;
    periodic_sync_manager_.SetScanningCallback(scanning_callbacks_);
  }

  void handle_scan_results(LeMetaEventView event) {
    switch (event.GetSubeventCode()) {
      case SubeventCode::ADVERTISING_REPORT:
        handle_advertising_report(LeAdvertisingReportRawView::Create(event));
        break;
      case SubeventCode::DIRECTED_ADVERTISING_REPORT:
        handle_directed_advertising_report(LeDirectedAdvertisingReportView::Create(event));
        break;
      case SubeventCode::EXTENDED_ADVERTISING_REPORT:
        handle_extended_advertising_report(LeExtendedAdvertisingReportRawView::Create(event));
        break;
      case SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED:
        LePeriodicAdvertisingSyncEstablishedView::Create(event);
        periodic_sync_manager_.HandleLePeriodicAdvertisingSyncEstablished(
                LePeriodicAdvertisingSyncEstablishedView::Create(event));
        break;
      case SubeventCode::PERIODIC_ADVERTISING_REPORT:
        periodic_sync_manager_.HandleLePeriodicAdvertisingReport(
                LePeriodicAdvertisingReportView::Create(event));
        break;
      case SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST:
        periodic_sync_manager_.HandleLePeriodicAdvertisingSyncLost(
                LePeriodicAdvertisingSyncLostView::Create(event));
        break;
      case SubeventCode::PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED:
        periodic_sync_manager_.HandleLePeriodicAdvertisingSyncTransferReceived(
                LePeriodicAdvertisingSyncTransferReceivedView::Create(event));
        break;
      case SubeventCode::SCAN_TIMEOUT:
        scanning_callbacks_->OnTimeout();
        break;
      case SubeventCode::BIG_INFO_ADVERTISING_REPORT:
        periodic_sync_manager_.HandleLeBigInfoAdvertisingReport(
                LeBigInfoAdvertisingReportView::Create(event));
        break;
      default:
        log::fatal("Unknown advertising subevent {}", SubeventCodeText(event.GetSubeventCode()));
    }
  }

  struct ExtendedEventTypeOptions {
    bool connectable{false};
    bool scannable{false};
    bool directed{false};
    bool scan_response{false};
    bool legacy{false};
    bool continuing{false};
    bool truncated{false};
  };

  int8_t get_rx_path_loss_compensation() {
    int8_t compensation = 0;
    auto compensation_prop = os::GetSystemProperty(kLeRxPathLossCompProperty);
    if (compensation_prop) {
      auto compensation_number = common::Int64FromString(compensation_prop.value());
      if (compensation_number) {
        int64_t number = compensation_number.value();
        if (number < kLeRxPathLossCompMin || number > kLeRxPathLossCompMax) {
          log::error("Invalid number for rx path loss compensation: {}", number);
        } else {
          compensation = number;
        }
      }
    }
    log::info("Rx path loss compensation: {}", compensation);
    return compensation;
  }

  int8_t get_rssi_after_calibration(int8_t rssi) {
    if (le_rx_path_loss_comp_ == 0 || rssi == kLeScanRssiUnknown) {
      return rssi;
    }
    int8_t calibrated_rssi = rssi;
    int64_t number = rssi + le_rx_path_loss_comp_;
    if (number < kLeScanRssiMin || number > kLeScanRssiMax) {
      log::error("Invalid number for calibrated rssi: {}", number);
    } else {
      calibrated_rssi = number;
    }
    return calibrated_rssi;
  }

  uint16_t transform_to_extended_event_type(ExtendedEventTypeOptions o) {
    return (o.connectable ? 0x0001 << 0 : 0) | (o.scannable ? 0x0001 << 1 : 0) |
           (o.directed ? 0x0001 << 2 : 0) | (o.scan_response ? 0x0001 << 3 : 0) |
           (o.legacy ? 0x0001 << 4 : 0) | (o.continuing ? 0x0001 << 5 : 0) |
           (o.truncated ? 0x0001 << 6 : 0);
  }

  void handle_advertising_report(LeAdvertisingReportRawView event_view) {
    if (!event_view.IsValid()) {
      log::info("Dropping invalid advertising event");
      return;
    }
    std::vector<LeAdvertisingResponseRaw> reports = event_view.GetResponses();
    if (reports.empty()) {
      log::info("Zero results in advertising event");
      return;
    }

    for (LeAdvertisingResponseRaw report : reports) {
      uint16_t extended_event_type = 0;
      switch (report.event_type_) {
        case AdvertisingEventType::ADV_IND:
          extended_event_type = transform_to_extended_event_type(
                  {.connectable = true, .scannable = true, .legacy = true});
          break;
        case AdvertisingEventType::ADV_DIRECT_IND:
          extended_event_type = transform_to_extended_event_type(
                  {.connectable = true, .directed = true, .legacy = true});
          break;
        case AdvertisingEventType::ADV_SCAN_IND:
          extended_event_type =
                  transform_to_extended_event_type({.scannable = true, .legacy = true});
          break;
        case AdvertisingEventType::ADV_NONCONN_IND:
          extended_event_type = transform_to_extended_event_type({.legacy = true});
          break;
        case AdvertisingEventType::SCAN_RESPONSE:
          if (com::android::bluetooth::flags::fix_nonconnectable_scannable_advertisement()) {
            // We don't know if the initial advertising report was connectable or not.
            // LeScanningReassembler fixes the connectable field.
            extended_event_type = transform_to_extended_event_type(
                    {.scannable = true, .scan_response = true, .legacy = true});
          } else {
            extended_event_type = transform_to_extended_event_type({.connectable = true,
                                                                    .scannable = true,
                                                                    .scan_response = true,
                                                                    .legacy = true});
          }
          break;
        default:
          log::warn("Unsupported event type:{}", (uint16_t)report.event_type_);
          return;
      }

      process_advertising_package_content(
              extended_event_type, (uint8_t)report.address_type_, report.address_,
              (uint8_t)PrimaryPhyType::LE_1M, (uint8_t)SecondaryPhyType::NO_PACKETS,
              kAdvertisingDataInfoNotPresent, kTxPowerInformationNotPresent, report.rssi_,
              kNotPeriodicAdvertisement, report.advertising_data_);
    }
  }

  void handle_directed_advertising_report(LeDirectedAdvertisingReportView /*event_view*/) {
    log::warn("HCI Directed Advertising Report events are not supported");
  }

  void handle_extended_advertising_report(LeExtendedAdvertisingReportRawView event_view) {
    if (!event_view.IsValid()) {
      log::info("Dropping invalid advertising event");
      return;
    }

    std::vector<LeExtendedAdvertisingResponseRaw> reports = event_view.GetResponses();
    if (reports.empty()) {
      log::info("Zero results in advertising event");
      return;
    }

    for (LeExtendedAdvertisingResponseRaw& report : reports) {
      uint16_t event_type =
              report.connectable_ | (report.scannable_ << kScannableBit) |
              (report.directed_ << kDirectedBit) | (report.scan_response_ << kScanResponseBit) |
              (report.legacy_ << kLegacyBit) | ((uint16_t)report.data_status_ << kDataStatusBits);
      process_advertising_package_content(
              event_type, (uint8_t)report.address_type_, report.address_,
              (uint8_t)report.primary_phy_, (uint8_t)report.secondary_phy_, report.advertising_sid_,
              report.tx_power_, report.rssi_, report.periodic_advertising_interval_,
              report.advertising_data_);
    }
  }

  void process_advertising_package_content(uint16_t event_type, uint8_t address_type,
                                           Address address, uint8_t primary_phy,
                                           uint8_t secondary_phy, uint8_t advertising_sid,
                                           int8_t tx_power, int8_t rssi,
                                           uint16_t periodic_advertising_interval,
                                           const std::vector<uint8_t>& advertising_data) {
    // When using the vendor command Le Set Extended Params to
    // configure a filter accept list based e.g. on the service UUIDs
    // found in the report, we ignore the scan responses as we cannot be
    // certain that they will not be dropped by the filter.
    // TODO(b/275754998): Improve the decision on what to do with scan responses: Only when used
    // with hardware-filtering features should we ignore waiting for scan response, and make sure
    // scan responses are still reported too.
    scanning_reassembler_.SetIgnoreScanResponses(
            le_scan_type_ == LeScanType::PASSIVE ||
            filter_policy_ == LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY);

    std::optional<LeScanningReassembler::CompleteAdvertisingData> processed_report =
            scanning_reassembler_.ProcessAdvertisingReport(event_type, address_type, address,
                                                           advertising_sid, advertising_data);

    if (processed_report.has_value()) {
      switch (address_type) {
        case (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS:
        case (uint8_t)AddressType::PUBLIC_IDENTITY_ADDRESS:
          address_type = (uint8_t)AddressType::PUBLIC_DEVICE_ADDRESS;
          break;
        case (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS:
        case (uint8_t)AddressType::RANDOM_IDENTITY_ADDRESS:
          address_type = (uint8_t)AddressType::RANDOM_DEVICE_ADDRESS;
          break;
      }

      const uint16_t result_event_type =
              com::android::bluetooth::flags::fix_nonconnectable_scannable_advertisement()
                      ? processed_report->extended_event_type
                      : event_type;

      scanning_callbacks_->OnScanResult(
              result_event_type, address_type, address, primary_phy, secondary_phy, advertising_sid,
              tx_power, get_rssi_after_calibration(rssi), periodic_advertising_interval,
              std::move(processed_report->data));
    }
  }

  void configure_scan() {
    std::vector<PhyScanParameters> parameter_vector;
    for (int i = 0; i < 7; i++) {
      if ((phy_ & 1 << i) != 0) {
        PhyScanParameters phy_scan_parameters;
        phy_scan_parameters.le_scan_window_ = window_ms_;
        phy_scan_parameters.le_scan_interval_ = interval_ms_;
        phy_scan_parameters.le_scan_type_ = le_scan_type_;
        parameter_vector.push_back(phy_scan_parameters);
      }
    }
    uint8_t phys_in_use = phy_;

    // The Host shall not issue set scan parameter command when scanning is enabled
    stop_scan();

    if (le_address_manager_->GetAddressPolicy() != LeAddressManager::USE_PUBLIC_ADDRESS) {
      if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
          controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) {
        log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS");
        own_address_type_ = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS;
      } else {
        own_address_type_ = OwnAddressType::RANDOM_DEVICE_ADDRESS;
      }
    } else {
      own_address_type_ = OwnAddressType::PUBLIC_DEVICE_ADDRESS;
    }

    switch (api_type_) {
      case ScanApiType::EXTENDED:
        le_scanning_interface_->EnqueueCommand(
                LeSetExtendedScanParametersBuilder::Create(own_address_type_, filter_policy_,
                                                           phys_in_use, parameter_vector),
                module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));
        break;
      case ScanApiType::ANDROID_HCI:
        le_scanning_interface_->EnqueueCommand(
                LeExtendedScanParamsBuilder::Create(le_scan_type_, interval_ms_, window_ms_,
                                                    own_address_type_, filter_policy_),
                module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));

        break;
      case ScanApiType::LEGACY:
        le_scanning_interface_->EnqueueCommand(

                LeSetScanParametersBuilder::Create(le_scan_type_, interval_ms_, window_ms_,
                                                   own_address_type_, filter_policy_),
                module_handler_->BindOnceOn(this, &impl::on_set_scan_parameter_complete));
        break;
    }
  }

  void register_scanner(const Uuid app_uuid) {
    for (uint8_t i = 1; i <= kMaxAppNum; i++) {
      if (scanners_[i].in_use && scanners_[i].app_uuid == app_uuid) {
        log::error("Application already registered {}", app_uuid.ToString());
        scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00,
                                                 ScanningCallback::ScanningStatus::INTERNAL_ERROR);
        return;
      }
    }

    // valid value of scanner id : 1 ~ kMaxAppNum
    for (uint8_t i = 1; i <= kMaxAppNum; i++) {
      if (!scanners_[i].in_use) {
        scanners_[i].app_uuid = app_uuid;
        scanners_[i].in_use = true;
        scanning_callbacks_->OnScannerRegistered(app_uuid, i,
                                                 ScanningCallback::ScanningStatus::SUCCESS);
        return;
      }
    }

    log::error("Unable to register scanner, max client reached:{}", kMaxAppNum);
    scanning_callbacks_->OnScannerRegistered(app_uuid, 0x00,
                                             ScanningCallback::ScanningStatus::NO_RESOURCES);
  }

  void unregister_scanner(ScannerId scanner_id) {
    if (scanner_id <= 0 || scanner_id > kMaxAppNum) {
      log::warn("Invalid scanner id");
      return;
    }

    if (scanners_[scanner_id].in_use) {
      scanners_[scanner_id].in_use = false;
      scanners_[scanner_id].app_uuid = Uuid::kEmpty;
      log::debug("Unregister scanner successful, scannerId={}", scanner_id);
    } else {
      log::warn("Unregister scanner with unused scanner id");
    }
  }

  void scan(bool start) {
    // On-resume flag should always be reset if there is an explicit start/stop call.
    scan_on_resume_ = false;
    if (start) {
      configure_scan();
      start_scan();
    } else {
      if (address_manager_registered_) {
        le_address_manager_->Unregister(this);
        address_manager_registered_ = false;
        paused_ = false;
      }
      stop_scan();
    }
  }

  void start_scan() {
    // If we receive start_scan during paused, set scan_on_resume_ to true
    if (paused_ && address_manager_registered_) {
      scan_on_resume_ = true;
      return;
    }
    is_scanning_ = true;
    if (!address_manager_registered_) {
      le_address_manager_->Register(this);
      address_manager_registered_ = true;
    }

    switch (api_type_) {
      case ScanApiType::EXTENDED:
        le_scanning_interface_->EnqueueCommand(
                LeSetExtendedScanEnableBuilder::Create(
                        Enable::ENABLED,
#if TARGET_FLOSS
                        FilterDuplicates::ENABLED /* filter duplicates */,
#else
                        FilterDuplicates::DISABLED /* filter duplicates */,
#endif
                        0, 0),
                module_handler_->BindOnce(check_complete<LeSetExtendedScanEnableCompleteView>));
        break;
      case ScanApiType::ANDROID_HCI:
      case ScanApiType::LEGACY:
        le_scanning_interface_->EnqueueCommand(
                LeSetScanEnableBuilder::Create(Enable::ENABLED,
                                               Enable::DISABLED /* filter duplicates */),
                module_handler_->BindOnce(check_complete<LeSetScanEnableCompleteView>));
        break;
    }
  }

  void stop_scan() {
    if (!is_scanning_) {
      log::info("Scanning already stopped, return!");
      return;
    }
    is_scanning_ = false;

    switch (api_type_) {
      case ScanApiType::EXTENDED:
        le_scanning_interface_->EnqueueCommand(
                LeSetExtendedScanEnableBuilder::Create(
                        Enable::DISABLED,
#if TARGET_FLOSS
                        FilterDuplicates::ENABLED /* filter duplicates */,
#else
                        FilterDuplicates::DISABLED /* filter duplicates */,
#endif
                        0, 0),
                module_handler_->BindOnce(check_complete<LeSetExtendedScanEnableCompleteView>));
        break;
      case ScanApiType::ANDROID_HCI:
      case ScanApiType::LEGACY:
        le_scanning_interface_->EnqueueCommand(
                LeSetScanEnableBuilder::Create(Enable::DISABLED,
                                               Enable::DISABLED /* filter duplicates */),
                module_handler_->BindOnce(check_complete<LeSetScanEnableCompleteView>));
        break;
    }
  }

  void set_scan_parameters(ScannerId scanner_id, LeScanType scan_type, uint16_t scan_interval,
                           uint16_t scan_window, uint8_t scan_phy) {
    uint32_t max_scan_interval = kLeScanIntervalMax;
    uint32_t max_scan_window = kLeScanWindowMax;
    if (api_type_ == ScanApiType::EXTENDED) {
      max_scan_interval = kLeExtendedScanIntervalMax;
      max_scan_window = kLeExtendedScanWindowMax;
    }

    if (scan_type != LeScanType::ACTIVE && scan_type != LeScanType::PASSIVE) {
      log::error("Invalid scan type");
      scanning_callbacks_->OnSetScannerParameterComplete(
              scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
      return;
    }
    if (scan_interval > max_scan_interval || scan_interval < kLeScanIntervalMin) {
      log::error("Invalid scan_interval {}", scan_interval);
      scanning_callbacks_->OnSetScannerParameterComplete(
              scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
      return;
    }
    if (scan_window > max_scan_window || scan_window < kLeScanWindowMin) {
      log::error("Invalid scan_window {}", scan_window);
      scanning_callbacks_->OnSetScannerParameterComplete(
              scanner_id, ScanningCallback::ScanningStatus::ILLEGAL_PARAMETER);
      return;
    }
    le_scan_type_ = scan_type;
    interval_ms_ = scan_interval;
    window_ms_ = scan_window;
    if (com::android::bluetooth::flags::phy_to_native()) {
      phy_ = scan_phy;
    }
    scanning_callbacks_->OnSetScannerParameterComplete(scanner_id, ScanningCallback::SUCCESS);
  }

  void set_scan_filter_policy(LeScanningFilterPolicy filter_policy) {
    filter_policy_ = filter_policy;
  }

  void scan_filter_enable(bool enable) {
    if (!is_filter_supported_) {
      log::warn("Advertising filter is not supported");
      return;
    }

    Enable apcf_enable = enable ? Enable::ENABLED : Enable::DISABLED;
    le_scanning_interface_->EnqueueCommand(
            LeAdvFilterEnableBuilder::Create(apcf_enable),
            module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
  }

  bool is_bonded(Address target_address) {
    for (auto device : storage_module_->GetBondedDevices()) {
      if (device.GetAddress() == target_address) {
        log::debug("Addresses match!");
        return true;
      }
    }
    log::debug("Addresse DON'Ts match!");
    return false;
  }

  void scan_filter_parameter_setup(ApcfAction action, uint8_t filter_index,
                                   AdvertisingFilterParameter advertising_filter_parameter) {
    if (!is_filter_supported_) {
      log::warn("Advertising filter is not supported");
      return;
    }

    auto entry = remove_me_later_map_.find(filter_index);
    switch (action) {
      case ApcfAction::ADD:
        le_scanning_interface_->EnqueueCommand(
                LeAdvFilterAddFilteringParametersBuilder::Create(
                        filter_index, advertising_filter_parameter.feature_selection,
                        advertising_filter_parameter.list_logic_type,
                        advertising_filter_parameter.filter_logic_type,
                        advertising_filter_parameter.rssi_high_thresh,
                        advertising_filter_parameter.delivery_mode,
                        advertising_filter_parameter.onfound_timeout,
                        advertising_filter_parameter.onfound_timeout_cnt,
                        advertising_filter_parameter.rssi_low_thresh,
                        advertising_filter_parameter.onlost_timeout,
                        advertising_filter_parameter.num_of_tracking_entries),
                module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
        break;
      case ApcfAction::DELETE:
        tracker_id_map_.erase(filter_index);
        le_scanning_interface_->EnqueueCommand(
                LeAdvFilterDeleteFilteringParametersBuilder::Create(filter_index),
                module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));

        // IRK Scanning
        if (entry != remove_me_later_map_.end()) {
          // Don't want to remove for a bonded device
          if (!is_bonded(entry->second.GetAddress())) {
            le_address_manager_->RemoveDeviceFromResolvingList(
                    static_cast<PeerAddressType>(entry->second.GetAddressType()),
                    entry->second.GetAddress());
          }
          remove_me_later_map_.erase(filter_index);
        }

        break;
      case ApcfAction::CLEAR:
        le_scanning_interface_->EnqueueCommand(
                LeAdvFilterClearFilteringParametersBuilder::Create(),
                module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));

        // IRK Scanning
        if (entry != remove_me_later_map_.end()) {
          // Don't want to remove for a bonded device
          if (!is_bonded(entry->second.GetAddress())) {
            le_address_manager_->RemoveDeviceFromResolvingList(
                    static_cast<PeerAddressType>(entry->second.GetAddressType()),
                    entry->second.GetAddress());
          }
          remove_me_later_map_.erase(filter_index);
        }

        break;
      default:
        log::error("Unknown action type: {}", (uint16_t)action);
        break;
    }
  }

  void scan_filter_add(uint8_t filter_index,
                       std::vector<AdvertisingPacketContentFilterCommand> filters) {
    if (!is_filter_supported_) {
      log::warn("Advertising filter is not supported");
      return;
    }

    ApcfAction apcf_action = ApcfAction::ADD;
    for (auto filter : filters) {
      /* If data is passed, both mask and data have to be the same length */
      if (filter.data.size() != filter.data_mask.size() && filter.data.size() != 0 &&
          filter.data_mask.size() != 0) {
        log::error("data and data_mask are of different size");
        continue;
      }

      switch (filter.filter_type) {
        case ApcfFilterType::BROADCASTER_ADDRESS: {
          update_address_filter(apcf_action, filter_index, filter.address,
                                filter.application_address_type, filter.irk);
          break;
        }
        case ApcfFilterType::SERVICE_UUID:
        case ApcfFilterType::SERVICE_SOLICITATION_UUID: {
          update_uuid_filter(apcf_action, filter_index, filter.filter_type, filter.uuid,
                             filter.uuid_mask);
          break;
        }
        case ApcfFilterType::LOCAL_NAME: {
          update_local_name_filter(apcf_action, filter_index, filter.name);
          break;
        }
        case ApcfFilterType::MANUFACTURER_DATA: {
          update_manufacturer_data_filter(apcf_action, filter_index, filter.company,
                                          filter.company_mask, filter.data, filter.data_mask);
          break;
        }
        case ApcfFilterType::SERVICE_DATA: {
          update_service_data_filter(apcf_action, filter_index, filter.data, filter.data_mask);
          break;
        }
        case ApcfFilterType::TRANSPORT_DISCOVERY_DATA: {
          update_transport_discovery_data_filter(
                  apcf_action, filter_index, filter.org_id, filter.tds_flags, filter.tds_flags_mask,
                  filter.data, filter.data_mask, filter.meta_data_type, filter.meta_data);
          break;
        }
        case ApcfFilterType::AD_TYPE: {
          update_ad_type_filter(apcf_action, filter_index, filter.ad_type, filter.data,
                                filter.data_mask);
          break;
        }
        default:
          log::error("Unknown filter type: {}", (uint16_t)filter.filter_type);
          break;
      }
    }
  }

  std::unordered_map<uint8_t, AddressWithType> remove_me_later_map_;

  void update_address_filter(ApcfAction action, uint8_t filter_index, Address address,
                             ApcfApplicationAddressType address_type, std::array<uint8_t, 16> irk) {
    if (action != ApcfAction::CLEAR) {
      /*
       * The vendor command (APCF Filtering 0x0157) takes Public (0) or Random (1)
       * or Addresses type not applicable (2).
       *
       * Advertising results have four types:
       * ￼    -  Public = 0
       * ￼    -  Random = 1
       * ￼    -  Public ID = 2
       * ￼    -  Random ID = 3
       *
       * e.g. specifying PUBLIC (0) will only return results with a public
       * address. It will ignore resolved addresses, since they return PUBLIC
       * IDENTITY (2). For this, Addresses type not applicable (0x02) must be specified.
       * This should also cover if the RPA is derived from RANDOM STATIC.
       */
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterBroadcasterAddressBuilder::Create(
                      action, filter_index, address, ApcfApplicationAddressType::NOT_APPLICABLE),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
      if (!is_empty_128bit(irk)) {
        // If an entry exists for this filter index, replace data because the filter has been
        // updated.
        auto entry = remove_me_later_map_.find(filter_index);
        // IRK Scanning
        if (entry != remove_me_later_map_.end()) {
          // Don't want to remove for a bonded device
          if (!is_bonded(entry->second.GetAddress())) {
            le_address_manager_->RemoveDeviceFromResolvingList(
                    static_cast<PeerAddressType>(entry->second.GetAddressType()),
                    entry->second.GetAddress());
          }
          remove_me_later_map_.erase(filter_index);
        }

        // Now replace it with a new one
        std::array<uint8_t, 16> empty_irk;
        le_address_manager_->AddDeviceToResolvingList(static_cast<PeerAddressType>(address_type),
                                                      address, irk, empty_irk);
        remove_me_later_map_.emplace(
                filter_index, AddressWithType(address, static_cast<AddressType>(address_type)));
      }
    } else {
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterClearBroadcasterAddressBuilder::Create(filter_index),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
      auto entry = remove_me_later_map_.find(filter_index);
      if (entry != remove_me_later_map_.end()) {
        // TODO(optedoblivion): If not bonded
        le_address_manager_->RemoveDeviceFromResolvingList(
                static_cast<PeerAddressType>(address_type), address);
        remove_me_later_map_.erase(filter_index);
      }
    }
  }

  bool is_empty_128bit(const std::array<uint8_t, 16> data) {
    for (int i = 0; i < 16; i++) {
      if (data[i] != (uint8_t)0) {
        return false;
      }
    }
    return true;
  }

  void update_uuid_filter(ApcfAction action, uint8_t filter_index, ApcfFilterType filter_type,
                          Uuid uuid, Uuid uuid_mask) {
    std::vector<uint8_t> combined_data = {};
    if (action != ApcfAction::CLEAR) {
      uint8_t uuid_len = uuid.GetShortestRepresentationSize();
      if (uuid_len == Uuid::kNumBytes16) {
        uint16_t data = uuid.As16Bit();
        combined_data.push_back((uint8_t)data);
        combined_data.push_back((uint8_t)(data >> 8));
      } else if (uuid_len == Uuid::kNumBytes32) {
        uint32_t data = uuid.As32Bit();
        combined_data.push_back((uint8_t)data);
        combined_data.push_back((uint8_t)(data >> 8));
        combined_data.push_back((uint8_t)(data >> 16));
        combined_data.push_back((uint8_t)(data >> 24));
      } else if (uuid_len == Uuid::kNumBytes128) {
        auto data = uuid.To128BitLE();
        combined_data.insert(combined_data.end(), data.begin(), data.end());
      } else {
        log::error("illegal UUID length: {}", (uint16_t)uuid_len);
        return;
      }

      if (!uuid_mask.IsEmpty()) {
        if (uuid_len == Uuid::kNumBytes16) {
          uint16_t data = uuid_mask.As16Bit();
          combined_data.push_back((uint8_t)data);
          combined_data.push_back((uint8_t)(data >> 8));
        } else if (uuid_len == Uuid::kNumBytes32) {
          uint32_t data = uuid_mask.As32Bit();
          combined_data.push_back((uint8_t)data);
          combined_data.push_back((uint8_t)(data >> 8));
          combined_data.push_back((uint8_t)(data >> 16));
          combined_data.push_back((uint8_t)(data >> 24));
        } else if (uuid_len == Uuid::kNumBytes128) {
          auto data = uuid_mask.To128BitLE();
          combined_data.insert(combined_data.end(), data.begin(), data.end());
        }
      } else {
        std::vector<uint8_t> data(uuid_len, 0xFF);
        combined_data.insert(combined_data.end(), data.begin(), data.end());
      }
    }

    if (filter_type == ApcfFilterType::SERVICE_UUID) {
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterServiceUuidBuilder::Create(action, filter_index, combined_data),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
    } else {
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterSolicitationUuidBuilder::Create(action, filter_index, combined_data),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
    }
  }

  void update_local_name_filter(ApcfAction action, uint8_t filter_index,
                                std::vector<uint8_t> name) {
    le_scanning_interface_->EnqueueCommand(
            LeAdvFilterLocalNameBuilder::Create(action, filter_index, name),
            module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
  }

  void update_manufacturer_data_filter(ApcfAction action, uint8_t filter_index, uint16_t company_id,
                                       uint16_t company_id_mask, std::vector<uint8_t> data,
                                       std::vector<uint8_t> data_mask) {
    if (data.size() != data_mask.size()) {
      log::error("manufacturer data mask should have the same length as manufacturer data");
      return;
    }
    std::vector<uint8_t> combined_data = {};
    if (action != ApcfAction::CLEAR) {
      combined_data.push_back((uint8_t)company_id);
      combined_data.push_back((uint8_t)(company_id >> 8));
      if (data.size() != 0) {
        combined_data.insert(combined_data.end(), data.begin(), data.end());
      }
      if (company_id_mask != 0) {
        combined_data.push_back((uint8_t)company_id_mask);
        combined_data.push_back((uint8_t)(company_id_mask >> 8));
      } else {
        combined_data.push_back(0xFF);
        combined_data.push_back(0xFF);
      }
      if (data_mask.size() != 0) {
        combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
      }
    }

    le_scanning_interface_->EnqueueCommand(
            LeAdvFilterManufacturerDataBuilder::Create(action, filter_index, combined_data),
            module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
  }

  void update_service_data_filter(ApcfAction action, uint8_t filter_index,
                                  std::vector<uint8_t> data, std::vector<uint8_t> data_mask) {
    if (data.size() != data_mask.size()) {
      log::error("service data mask should have the same length as service data");
      return;
    }
    std::vector<uint8_t> combined_data = {};
    if (action != ApcfAction::CLEAR && data.size() != 0) {
      combined_data.insert(combined_data.end(), data.begin(), data.end());
      combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
    }

    le_scanning_interface_->EnqueueCommand(
            LeAdvFilterServiceDataBuilder::Create(action, filter_index, combined_data),
            module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
  }

  void update_transport_discovery_data_filter(ApcfAction action, uint8_t filter_index,
                                              uint8_t org_id, uint8_t tds_flags,
                                              uint8_t tds_flags_mask,
                                              std::vector<uint8_t> transport_data,
                                              std::vector<uint8_t> transport_data_mask,
                                              ApcfMetaDataType meta_data_type,
                                              std::vector<uint8_t> meta_data) {
    LocalVersionInformation local_version_information = controller_->GetLocalVersionInformation();

    // In QTI controller, transport discovery data filter are supported by default. Check is added
    // to keep backward compatibility.
    if (!is_transport_discovery_data_filter_supported_ &&
        !(local_version_information.manufacturer_name_ == LMP_COMPID_QTI)) {
      log::warn("transport discovery data filter isn't supported");
      return;
    }

    log::info(
            "org id: {}, tds_flags: {}, tds_flags_mask: {}, transport_data size: {}, "
            "transport_data_mask size: {}, meta_data_type: {}, meta_data size: {}",
            org_id, tds_flags, tds_flags_mask, transport_data.size(), transport_data_mask.size(),
            (uint8_t)meta_data_type, meta_data.size());

    // 0x02 Wi-Fi Alliance Neighbor Awareness Networking & meta_data_type is 0x01 for NAN Hash.
    if (org_id == 0x02) {
      // meta data contains WIFI NAN hash, reverse it before sending controller.
      switch (meta_data_type) {
        case ApcfMetaDataType::WIFI_NAN_HASH:
          std::reverse(meta_data.begin(), meta_data.end());
          break;
        default:
          break;
      }
    }

    if (is_transport_discovery_data_filter_supported_) {
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterTransportDiscoveryDataBuilder::Create(
                      action, filter_index, org_id, tds_flags, tds_flags_mask, transport_data,
                      transport_data_mask, meta_data_type, meta_data),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
    } else {
      // In QTI controller, transport discovery data filter are supported by default.
      // keeping old version for backward compatibility
      std::vector<uint8_t> combined_data = {};
      if (action != ApcfAction::CLEAR) {
        combined_data.push_back((uint8_t)org_id);
        combined_data.push_back((uint8_t)tds_flags);
        combined_data.push_back((uint8_t)tds_flags_mask);
        if (org_id == 0x02 && meta_data_type == ApcfMetaDataType::WIFI_NAN_HASH) {
          // meta data contains WIFI NAN hash
          combined_data.insert(combined_data.end(), meta_data.begin(), meta_data.end());
        }
      }
      le_scanning_interface_->EnqueueCommand(
              LeAdvFilterTransportDiscoveryDataOldBuilder::Create(action, filter_index,
                                                                  combined_data),
              module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
    }
  }

  void update_ad_type_filter(ApcfAction action, uint8_t filter_index, uint8_t ad_type,
                             std::vector<uint8_t> data, std::vector<uint8_t> data_mask) {
    if (!is_ad_type_filter_supported_) {
      log::error("AD type filter isn't supported");
      return;
    }

    if (data.size() != data_mask.size()) {
      log::error("ad type mask should have the same length as ad type data");
      return;
    }
    std::vector<uint8_t> combined_data = {};
    if (action != ApcfAction::CLEAR) {
      combined_data.push_back((uint8_t)ad_type);
      combined_data.push_back((uint8_t)(data.size()));
      if (data.size() != 0) {
        combined_data.insert(combined_data.end(), data.begin(), data.end());
        combined_data.insert(combined_data.end(), data_mask.begin(), data_mask.end());
      }
    }

    le_scanning_interface_->EnqueueCommand(
            LeAdvFilterADTypeBuilder::Create(action, filter_index, combined_data),
            module_handler_->BindOnceOn(this, &impl::on_advertising_filter_complete));
  }

  void batch_scan_set_storage_parameter(uint8_t batch_scan_full_max,
                                        uint8_t batch_scan_truncated_max,
                                        uint8_t batch_scan_notify_threshold, ScannerId scanner_id) {
    if (!is_batch_scan_supported_) {
      log::warn("Batch scan is not supported");
      return;
    }
    // scanner id for OnBatchScanThresholdCrossed
    batch_scan_config_.ref_value = scanner_id;

    if (batch_scan_config_.current_state == BatchScanState::ERROR_STATE ||
        batch_scan_config_.current_state == BatchScanState::DISABLED_STATE ||
        batch_scan_config_.current_state == BatchScanState::DISABLE_CALLED) {
      batch_scan_config_.current_state = BatchScanState::ENABLE_CALLED;
      le_scanning_interface_->EnqueueCommand(
              LeBatchScanEnableBuilder::Create(Enable::ENABLED),
              module_handler_->BindOnceOn(this, &impl::on_batch_scan_enable_complete));
    }

    le_scanning_interface_->EnqueueCommand(
            LeBatchScanSetStorageParametersBuilder::Create(
                    batch_scan_full_max, batch_scan_truncated_max, batch_scan_notify_threshold),
            module_handler_->BindOnceOn(this, &impl::on_batch_scan_complete));
  }

  void batch_scan_enable(BatchScanMode scan_mode, uint32_t duty_cycle_scan_window_slots,
                         uint32_t duty_cycle_scan_interval_slots,
                         BatchScanDiscardRule batch_scan_discard_rule) {
    if (!is_batch_scan_supported_) {
      log::warn("Batch scan is not supported");
      return;
    }

    if (batch_scan_config_.current_state == BatchScanState::ERROR_STATE ||
        batch_scan_config_.current_state == BatchScanState::DISABLED_STATE ||
        batch_scan_config_.current_state == BatchScanState::DISABLE_CALLED) {
      batch_scan_config_.current_state = BatchScanState::ENABLE_CALLED;
      le_scanning_interface_->EnqueueCommand(
              LeBatchScanEnableBuilder::Create(Enable::ENABLED),
              module_handler_->BindOnceOn(this, &impl::on_batch_scan_enable_complete));
    }

    batch_scan_config_.scan_mode = scan_mode;
    batch_scan_config_.scan_interval = duty_cycle_scan_interval_slots;
    batch_scan_config_.scan_window = duty_cycle_scan_window_slots;
    batch_scan_config_.discard_rule = batch_scan_discard_rule;
    /* This command starts batch scanning, if enabled */
    batch_scan_set_scan_parameter(scan_mode, duty_cycle_scan_window_slots,
                                  duty_cycle_scan_interval_slots, batch_scan_discard_rule);
  }

  void batch_scan_disable() {
    if (!is_batch_scan_supported_) {
      log::warn("Batch scan is not supported");
      return;
    }
    batch_scan_config_.current_state = BatchScanState::DISABLE_CALLED;
    batch_scan_set_scan_parameter(BatchScanMode::DISABLE, batch_scan_config_.scan_window,
                                  batch_scan_config_.scan_interval,
                                  batch_scan_config_.discard_rule);
  }

  void batch_scan_set_scan_parameter(BatchScanMode scan_mode, uint32_t duty_cycle_scan_window_slots,
                                     uint32_t duty_cycle_scan_interval_slots,
                                     BatchScanDiscardRule batch_scan_discard_rule) {
    if (!is_batch_scan_supported_) {
      log::warn("Batch scan is not supported");
      return;
    }
    PeerAddressType own_address_type = PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS;
    if (own_address_type_ == OwnAddressType::RANDOM_DEVICE_ADDRESS ||
        own_address_type_ == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) {
      own_address_type = PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS;
    }
    uint8_t truncated_mode_enabled = 0x00;
    uint8_t full_mode_enabled = 0x00;
    if (scan_mode == BatchScanMode::TRUNCATED || scan_mode == BatchScanMode::TRUNCATED_AND_FULL) {
      truncated_mode_enabled = 0x01;
    }
    if (scan_mode == BatchScanMode::FULL || scan_mode == BatchScanMode::TRUNCATED_AND_FULL) {
      full_mode_enabled = 0x01;
    }

    if (scan_mode == BatchScanMode::DISABLE) {
      le_scanning_interface_->EnqueueCommand(
              LeBatchScanSetScanParametersBuilder::Create(
                      truncated_mode_enabled, full_mode_enabled, duty_cycle_scan_window_slots,
                      duty_cycle_scan_interval_slots, own_address_type, batch_scan_discard_rule),
              module_handler_->BindOnceOn(this, &impl::on_batch_scan_disable_complete));
    } else {
      le_scanning_interface_->EnqueueCommand(
              LeBatchScanSetScanParametersBuilder::Create(
                      truncated_mode_enabled, full_mode_enabled, duty_cycle_scan_window_slots,
                      duty_cycle_scan_interval_slots, own_address_type, batch_scan_discard_rule),
              module_handler_->BindOnceOn(this, &impl::on_batch_scan_complete));
    }
  }

  void batch_scan_read_results(ScannerId scanner_id, uint16_t total_num_of_records,
                               BatchScanMode scan_mode) {
    if (!is_batch_scan_supported_) {
      log::warn("Batch scan is not supported");
      int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
      scanning_callbacks_->OnBatchScanReports(scanner_id, status, 0, 0, {});
      return;
    }

    if (scan_mode != BatchScanMode::FULL && scan_mode != BatchScanMode::TRUNCATED) {
      log::warn("Invalid scan mode {}", (uint16_t)scan_mode);
      int status = static_cast<int>(ErrorCode::INVALID_HCI_COMMAND_PARAMETERS);
      scanning_callbacks_->OnBatchScanReports(scanner_id, status, 0, 0, {});
      return;
    }

    if (batch_scan_result_cache_.find(scanner_id) == batch_scan_result_cache_.end()) {
      std::vector<uint8_t> empty_data = {};
      batch_scan_result_cache_.emplace(scanner_id, empty_data);
    }

    le_scanning_interface_->EnqueueCommand(
            LeBatchScanReadResultParametersBuilder::Create(
                    static_cast<BatchScanDataRead>(scan_mode)),
            module_handler_->BindOnceOn(this, &impl::on_batch_scan_read_result_complete, scanner_id,
                                        total_num_of_records));
  }

  void start_sync(uint8_t sid, const AddressWithType& address_with_type, uint16_t skip,
                  uint16_t timeout, int request_id) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
      scanning_callbacks_->OnPeriodicSyncStarted(request_id, status, -1, sid, address_with_type, 0,
                                                 0);
      return;
    }
    PeriodicSyncStates request{
            .request_id = request_id,
            .advertiser_sid = sid,
            .address_with_type = address_with_type,
            .sync_handle = 0,
            .sync_state = PeriodicSyncState::PERIODIC_SYNC_STATE_IDLE,
    };
    periodic_sync_manager_.StartSync(request, skip, timeout);
  }

  void stop_sync(uint16_t handle) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      return;
    }
    periodic_sync_manager_.StopSync(handle);
  }

  void cancel_create_sync(uint8_t sid, const Address& address) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      return;
    }
    periodic_sync_manager_.CancelCreateSync(sid, address);
  }

  void transfer_sync(const Address& address, uint16_t connection_handle, uint16_t service_data,
                     uint16_t sync_handle, int pa_source) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
      scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
      return;
    }
    if (connection_handle == 0xFFFF) {
      log::error("[PAST]: Invalid connection handle or no LE ACL link");
      int status = static_cast<int>(ErrorCode::UNKNOWN_CONNECTION);
      scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
      return;
    }
    periodic_sync_manager_.TransferSync(address, service_data, sync_handle, pa_source,
                                        connection_handle);
  }

  void transfer_set_info(const Address& address, uint16_t connection_handle, uint16_t service_data,
                         uint8_t adv_handle, int pa_source) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
      scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
      return;
    }
    if (connection_handle == 0xFFFF) {
      log::error("[PAST]:Invalid connection handle or no LE ACL link");
      int status = static_cast<int>(ErrorCode::UNKNOWN_CONNECTION);
      scanning_callbacks_->OnPeriodicSyncTransferred(pa_source, status, address);
      return;
    }
    periodic_sync_manager_.SyncSetInfo(address, service_data, adv_handle, pa_source,
                                       connection_handle);
  }

  void sync_tx_parameters(const Address& address, uint8_t mode, uint16_t skip, uint16_t timeout,
                          int reg_id) {
    if (!is_periodic_advertising_sync_transfer_sender_supported_) {
      log::warn("PAST sender not supported on this device");
      int status = static_cast<int>(ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE);
      AddressWithType address_with_type(address, AddressType::RANDOM_DEVICE_ADDRESS);
      scanning_callbacks_->OnPeriodicSyncStarted(reg_id, status, -1, -1, address_with_type, 0, 0);
      return;
    }
    periodic_sync_manager_.SyncTxParameters(address, mode, skip, timeout, reg_id);
  }

  void track_advertiser(uint8_t filter_index, ScannerId scanner_id) {
    if (total_num_of_advt_tracked_ <= 0) {
      log::warn("advertisement tracking is not supported");
      AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
      on_found_on_lost_info.scanner_id = scanner_id;
      on_found_on_lost_info.advertiser_info_present = AdvtInfoPresent::NO_ADVT_INFO_PRESENT;
      scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
      return;
    } else if (tracker_id_map_.size() >= total_num_of_advt_tracked_) {
      AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
      on_found_on_lost_info.scanner_id = scanner_id;
      on_found_on_lost_info.advertiser_info_present = AdvtInfoPresent::NO_ADVT_INFO_PRESENT;
      scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
      return;
    }
    log::info("track_advertiser scanner_id {}, filter_index {}", (uint16_t)scanner_id,
              (uint16_t)filter_index);
    tracker_id_map_[filter_index] = scanner_id;
  }

  void register_scanning_callback(ScanningCallback* scanning_callbacks) {
    scanning_callbacks_ = scanning_callbacks;
    periodic_sync_manager_.SetScanningCallback(scanning_callbacks_);
  }

  bool is_ad_type_filter_supported() { return is_ad_type_filter_supported_; }

  void on_set_scan_parameter_complete(CommandCompleteView view) {
    switch (view.GetCommandOpCode()) {
      case (OpCode::LE_SET_SCAN_PARAMETERS): {
        auto status_view = LeSetScanParametersCompleteView::Create(view);
        log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
        if (status_view.GetStatus() != ErrorCode::SUCCESS) {
          log::info("Receive set scan parameter complete with error code {}",
                    ErrorCodeText(status_view.GetStatus()));
        }
      } break;
      case (OpCode::LE_EXTENDED_SCAN_PARAMS): {
        auto status_view = LeExtendedScanParamsCompleteView::Create(view);
        log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
        if (status_view.GetStatus() != ErrorCode::SUCCESS) {
          log::info("Receive extended scan parameter complete with error code {}",
                    ErrorCodeText(status_view.GetStatus()));
        }
      } break;
      case (OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS): {
        auto status_view = LeSetExtendedScanParametersCompleteView::Create(view);
        log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
        if (status_view.GetStatus() != ErrorCode::SUCCESS) {
          log::info("Receive set extended scan parameter complete with error code {}",
                    ErrorCodeText(status_view.GetStatus()));
        }
      } break;
      default:
        log::fatal("Unhandled event {}", OpCodeText(view.GetCommandOpCode()));
    }
  }

  void on_advertising_filter_complete(CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeAdvFilterCompleteView::Create(view);
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      log::info("Got a Command complete {}, status {}", OpCodeText(view.GetCommandOpCode()),
                ErrorCodeText(status_view.GetStatus()));
    }

    ApcfOpcode apcf_opcode = status_view.GetApcfOpcode();
    switch (apcf_opcode) {
      case ApcfOpcode::ENABLE: {
        auto complete_view = LeAdvFilterEnableCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterEnable(complete_view.GetApcfEnable(),
                                            (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::SET_FILTERING_PARAMETERS: {
        auto complete_view = LeAdvFilterSetFilteringParametersCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterParamSetup(complete_view.GetApcfAvailableSpaces(),
                                                complete_view.GetApcfAction(),
                                                (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::BROADCASTER_ADDRESS: {
        auto complete_view = LeAdvFilterBroadcasterAddressCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::BROADCASTER_ADDRESS, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::SERVICE_UUID: {
        auto complete_view = LeAdvFilterServiceUuidCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::SERVICE_UUID, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::SERVICE_SOLICITATION_UUID: {
        auto complete_view = LeAdvFilterSolicitationUuidCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::SERVICE_SOLICITATION_UUID, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::LOCAL_NAME: {
        auto complete_view = LeAdvFilterLocalNameCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::LOCAL_NAME, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::MANUFACTURER_DATA: {
        auto complete_view = LeAdvFilterManufacturerDataCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::MANUFACTURER_DATA, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::SERVICE_DATA: {
        auto complete_view = LeAdvFilterServiceDataCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::SERVICE_DATA, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::TRANSPORT_DISCOVERY_DATA: {
        auto complete_view = LeAdvFilterTransportDiscoveryDataCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::TRANSPORT_DISCOVERY_DATA, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      case ApcfOpcode::AD_TYPE: {
        auto complete_view = LeAdvFilterADTypeCompleteView::Create(status_view);
        log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
        scanning_callbacks_->OnFilterConfigCallback(
                ApcfFilterType::AD_TYPE, complete_view.GetApcfAvailableSpaces(),
                complete_view.GetApcfAction(), (uint8_t)complete_view.GetStatus());
      } break;
      default:
        log::warn("Unexpected event type {}", OpCodeText(view.GetCommandOpCode()));
    }
  }

  void on_apcf_read_extended_features_complete(CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeAdvFilterCompleteView::Create(view);
    if (!status_view.IsValid()) {
      log::warn("Can not get valid LeAdvFilterCompleteView, return");
      return;
    }
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      log::warn("Got a Command complete {}, status {}", OpCodeText(view.GetCommandOpCode()),
                ErrorCodeText(status_view.GetStatus()));
      return;
    }
    auto complete_view = LeAdvFilterReadExtendedFeaturesCompleteView::Create(status_view);
    log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
    is_transport_discovery_data_filter_supported_ =
            complete_view.GetTransportDiscoveryDataFilter() == 1;
    is_ad_type_filter_supported_ = complete_view.GetAdTypeFilter() == 1;
    log::info(
            "set is_ad_type_filter_supported_ to {} & "
            "is_transport_discovery_data_filter_supported_ to {}",
            is_ad_type_filter_supported_, is_transport_discovery_data_filter_supported_);
  }

  void on_batch_scan_complete(CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeBatchScanCompleteView::Create(view);
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      log::info("Got a Command complete {}, status {}, batch_scan_opcode {}",
                OpCodeText(view.GetCommandOpCode()), ErrorCodeText(status_view.GetStatus()),
                BatchScanOpcodeText(status_view.GetBatchScanOpcode()));
    }
  }

  void on_batch_scan_enable_complete(CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeBatchScanCompleteView::Create(view);
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    auto complete_view = LeBatchScanEnableCompleteView::Create(status_view);
    log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      log::info("Got batch scan enable complete, status {}",
                ErrorCodeText(status_view.GetStatus()));
      batch_scan_config_.current_state = BatchScanState::ERROR_STATE;
    } else {
      batch_scan_config_.current_state = BatchScanState::ENABLED_STATE;
    }
  }

  void on_batch_scan_disable_complete(CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeBatchScanCompleteView::Create(view);
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    auto complete_view = LeBatchScanSetScanParametersCompleteView::Create(status_view);
    log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
    log::assert_that(status_view.GetStatus() == ErrorCode::SUCCESS,
                     "assert failed: status_view.GetStatus() == ErrorCode::SUCCESS");
    batch_scan_config_.current_state = BatchScanState::DISABLED_STATE;
  }

  void on_batch_scan_read_result_complete(ScannerId scanner_id, uint16_t total_num_of_records,
                                          CommandCompleteView view) {
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    auto status_view = LeBatchScanCompleteView::Create(view);
    log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()");
    auto complete_view = LeBatchScanReadResultParametersCompleteRawView::Create(status_view);
    log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()");
    if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
      log::info("Got batch scan read result complete, status {}",
                ErrorCodeText(status_view.GetStatus()));
    }
    uint8_t num_of_records = complete_view.GetNumOfRecords();
    auto report_format = complete_view.GetBatchScanDataRead();
    if (num_of_records == 0) {
      scanning_callbacks_->OnBatchScanReports(scanner_id, 0x00, (int)report_format,
                                              total_num_of_records,
                                              batch_scan_result_cache_[scanner_id]);
      batch_scan_result_cache_.erase(scanner_id);
    } else {
      auto raw_data = complete_view.GetRawData();
      batch_scan_result_cache_[scanner_id].insert(batch_scan_result_cache_[scanner_id].end(),
                                                  raw_data.begin(), raw_data.end());
      total_num_of_records += num_of_records;
      batch_scan_read_results(scanner_id, total_num_of_records,
                              static_cast<BatchScanMode>(report_format));
    }
  }

  void on_storage_threshold_breach(VendorSpecificEventView /* event */) {
    if (batch_scan_config_.ref_value == kInvalidScannerId) {
      log::warn("storage threshold was not set !!");
      return;
    }
    scanning_callbacks_->OnBatchScanThresholdCrossed(
            static_cast<int>(batch_scan_config_.ref_value));
  }

  void on_advertisement_tracking(VendorSpecificEventView event) {
    auto view = LEAdvertisementTrackingEventView::Create(event);
    log::assert_that(view.IsValid(), "assert failed: view.IsValid()");
    uint8_t filter_index = view.GetApcfFilterIndex();
    if (tracker_id_map_.find(filter_index) == tracker_id_map_.end()) {
      log::warn("Advertisement track for filter_index {} is not register", (uint16_t)filter_index);
      return;
    }
    AdvertisingFilterOnFoundOnLostInfo on_found_on_lost_info = {};
    on_found_on_lost_info.scanner_id = tracker_id_map_[filter_index];
    on_found_on_lost_info.filter_index = filter_index;
    on_found_on_lost_info.advertiser_state = view.GetAdvertiserState();
    on_found_on_lost_info.advertiser_address = view.GetAdvertiserAddress();
    on_found_on_lost_info.advertiser_address_type = view.GetAdvertiserAddressType();
    on_found_on_lost_info.advertiser_info_present = view.GetAdvtInfoPresent();
    /* Extract the adv info details */
    if (on_found_on_lost_info.advertiser_info_present == AdvtInfoPresent::ADVT_INFO_PRESENT) {
      auto info_view = LEAdvertisementTrackingWithInfoEventView::Create(view);
      log::assert_that(info_view.IsValid(), "assert failed: info_view.IsValid()");
      on_found_on_lost_info.tx_power = info_view.GetTxPower();
      on_found_on_lost_info.rssi = info_view.GetRssi();
      on_found_on_lost_info.time_stamp = info_view.GetTimestamp();
      auto adv_data = info_view.GetAdvPacket();
      on_found_on_lost_info.adv_packet.reserve(adv_data.size());
      on_found_on_lost_info.adv_packet.insert(on_found_on_lost_info.adv_packet.end(),
                                              adv_data.begin(), adv_data.end());
      auto scan_rsp_data = info_view.GetScanResponse();
      on_found_on_lost_info.scan_response.reserve(scan_rsp_data.size());
      on_found_on_lost_info.scan_response.insert(on_found_on_lost_info.scan_response.end(),
                                                 scan_rsp_data.begin(), scan_rsp_data.end());
    }
    scanning_callbacks_->OnTrackAdvFoundLost(on_found_on_lost_info);
  }

  void OnPause() override {
    if (!address_manager_registered_) {
      log::warn("Unregistered!");
      return;
    }
    paused_ = true;
    scan_on_resume_ = is_scanning_;
    stop_scan();
    ack_pause();
  }

  void ack_pause() { le_address_manager_->AckPause(this); }

  void OnResume() override {
    if (!address_manager_registered_) {
      log::warn("Unregistered!");
      return;
    }
    paused_ = false;
    if (scan_on_resume_ == true) {
      scan_on_resume_ = false;
      start_scan();
    }
    le_address_manager_->AckResume(this);
  }

  ScanApiType api_type_;

  Module* module_;
  os::Handler* module_handler_;
  HciLayer* hci_layer_;
  Controller* controller_;
  AclManager* acl_manager_;
  storage::StorageModule* storage_module_;
  LeScanningInterface* le_scanning_interface_;
  LeAddressManager* le_address_manager_;
  bool address_manager_registered_ = false;
  NullScanningCallback null_scanning_callback_;
  ScanningCallback* scanning_callbacks_ = &null_scanning_callback_;
  PeriodicSyncManager periodic_sync_manager_{&null_scanning_callback_};
  std::vector<Scanner> scanners_;
  bool is_scanning_ = false;
  bool scan_on_resume_ = false;
  bool paused_ = false;
  LeScanningReassembler scanning_reassembler_;
  bool is_filter_supported_ = false;
  bool is_ad_type_filter_supported_ = false;
  bool is_batch_scan_supported_ = false;
  bool is_periodic_advertising_sync_transfer_sender_supported_ = false;
  bool is_transport_discovery_data_filter_supported_ = false;

  LeScanType le_scan_type_ = LeScanType::ACTIVE;
  uint32_t interval_ms_{1000};
  uint16_t window_ms_{1000};
  uint8_t phy_{(uint8_t)PhyType::LE_1M};
  OwnAddressType own_address_type_{OwnAddressType::PUBLIC_DEVICE_ADDRESS};
  LeScanningFilterPolicy filter_policy_{LeScanningFilterPolicy::ACCEPT_ALL};
  BatchScanConfig batch_scan_config_;
  std::map<ScannerId, std::vector<uint8_t>> batch_scan_result_cache_;
  std::unordered_map<uint8_t, ScannerId> tracker_id_map_;
  uint16_t total_num_of_advt_tracked_ = 0x00;
  int8_t le_rx_path_loss_comp_ = 0;
};

LeScanningManager::LeScanningManager() { pimpl_ = std::make_unique<impl>(this); }

void LeScanningManager::ListDependencies(ModuleList* list) const {
  list->add<HciLayer>();
  list->add<Controller>();
  list->add<AclManager>();
  list->add<storage::StorageModule>();
}

void LeScanningManager::Start() {
  pimpl_->start(GetHandler(), GetDependency<HciLayer>(), GetDependency<Controller>(),
                GetDependency<AclManager>(), GetDependency<storage::StorageModule>());
}

void LeScanningManager::Stop() {
  pimpl_->stop();
  pimpl_.reset();
}

std::string LeScanningManager::ToString() const { return "Le Scanning Manager"; }

void LeScanningManager::RegisterScanner(Uuid app_uuid) {
  CallOn(pimpl_.get(), &impl::register_scanner, app_uuid);
}

void LeScanningManager::Unregister(ScannerId scanner_id) {
  CallOn(pimpl_.get(), &impl::unregister_scanner, scanner_id);
}

void LeScanningManager::Scan(bool start) { CallOn(pimpl_.get(), &impl::scan, start); }

void LeScanningManager::SetScanParameters(ScannerId scanner_id, LeScanType scan_type,
                                          uint16_t scan_interval, uint16_t scan_window,
                                          uint8_t scan_phy) {
  CallOn(pimpl_.get(), &impl::set_scan_parameters, scanner_id, scan_type, scan_interval,
         scan_window, scan_phy);
}

void LeScanningManager::SetScanFilterPolicy(LeScanningFilterPolicy filter_policy) {
  CallOn(pimpl_.get(), &impl::set_scan_filter_policy, filter_policy);
}

void LeScanningManager::ScanFilterEnable(bool enable) {
  CallOn(pimpl_.get(), &impl::scan_filter_enable, enable);
}

void LeScanningManager::ScanFilterParameterSetup(
        ApcfAction action, uint8_t filter_index,
        AdvertisingFilterParameter advertising_filter_parameter) {
  CallOn(pimpl_.get(), &impl::scan_filter_parameter_setup, action, filter_index,
         advertising_filter_parameter);
}

void LeScanningManager::ScanFilterAdd(uint8_t filter_index,
                                      std::vector<AdvertisingPacketContentFilterCommand> filters) {
  CallOn(pimpl_.get(), &impl::scan_filter_add, filter_index, filters);
}

void LeScanningManager::BatchScanConifgStorage(uint8_t batch_scan_full_max,
                                               uint8_t batch_scan_truncated_max,
                                               uint8_t batch_scan_notify_threshold,
                                               ScannerId scanner_id) {
  CallOn(pimpl_.get(), &impl::batch_scan_set_storage_parameter, batch_scan_full_max,
         batch_scan_truncated_max, batch_scan_notify_threshold, scanner_id);
}

void LeScanningManager::BatchScanEnable(BatchScanMode scan_mode,
                                        uint32_t duty_cycle_scan_window_slots,
                                        uint32_t duty_cycle_scan_interval_slots,
                                        BatchScanDiscardRule batch_scan_discard_rule) {
  CallOn(pimpl_.get(), &impl::batch_scan_enable, scan_mode, duty_cycle_scan_window_slots,
         duty_cycle_scan_interval_slots, batch_scan_discard_rule);
}

void LeScanningManager::BatchScanDisable() { CallOn(pimpl_.get(), &impl::batch_scan_disable); }

void LeScanningManager::BatchScanReadReport(ScannerId scanner_id, BatchScanMode scan_mode) {
  CallOn(pimpl_.get(), &impl::batch_scan_read_results, scanner_id, 0, scan_mode);
}

void LeScanningManager::StartSync(uint8_t sid, const AddressWithType& address_with_type,
                                  uint16_t skip, uint16_t timeout, int reg_id) {
  CallOn(pimpl_.get(), &impl::start_sync, sid, address_with_type, skip, timeout, reg_id);
}

void LeScanningManager::StopSync(uint16_t handle) {
  CallOn(pimpl_.get(), &impl::stop_sync, handle);
}

void LeScanningManager::CancelCreateSync(uint8_t sid, const Address& address) {
  CallOn(pimpl_.get(), &impl::cancel_create_sync, sid, address);
}

void LeScanningManager::TransferSync(const Address& address, uint16_t handle, uint16_t service_data,
                                     uint16_t sync_handle, int pa_source) {
  CallOn(pimpl_.get(), &impl::transfer_sync, address, handle, service_data, sync_handle, pa_source);
}

void LeScanningManager::TransferSetInfo(const Address& address, uint16_t handle,
                                        uint16_t service_data, uint8_t adv_handle, int pa_source) {
  CallOn(pimpl_.get(), &impl::transfer_set_info, address, handle, service_data, adv_handle,
         pa_source);
}

void LeScanningManager::SyncTxParameters(const Address& address, uint8_t mode, uint16_t skip,
                                         uint16_t timeout, int reg_id) {
  CallOn(pimpl_.get(), &impl::sync_tx_parameters, address, mode, skip, timeout, reg_id);
}

void LeScanningManager::TrackAdvertiser(uint8_t filter_index, ScannerId scanner_id) {
  CallOn(pimpl_.get(), &impl::track_advertiser, filter_index, scanner_id);
}

void LeScanningManager::RegisterScanningCallback(ScanningCallback* scanning_callback) {
  CallOn(pimpl_.get(), &impl::register_scanning_callback, scanning_callback);
}

bool LeScanningManager::IsAdTypeFilterSupported() const {
  return pimpl_->is_ad_type_filter_supported();
}

}  // namespace hci
}  // namespace bluetooth
