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

Commit 1ccc7a6c authored by Myles Watson's avatar Myles Watson Committed by Gerrit Code Review
Browse files

Merge "gd: Implement batch scan"

parents 7124d7eb 310d8d62
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ class LeScanningManagerFacadeService : public LeScanningManagerFacade::Service,
  };
  void OnTrackAdvFoundLost(){};
  void OnBatchScanReports(int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data){};
  void OnBatchScanThresholdCrossed(int client_if){};
  void OnTimeout(){};
  void OnFilterEnable(Enable enable, uint8_t status){};
  void OnFilterParamSetup(uint8_t available_spaces, ApcfAction action, uint8_t status){};
+44 −11
Original line number Diff line number Diff line
@@ -4506,7 +4506,8 @@ enum BatchScanOpcode : 8 {
  READ_RESULT_PARAMETERS = 0x04,
}

packet LeBatchScan : VendorCommand (op_code = LE_BATCH_SCAN) {
// https://source.android.com/devices/bluetooth/hci_requirements#batching-of-scan-results
packet LeBatchScan : LeScanningCommand (op_code = LE_BATCH_SCAN) {
  batch_scan_opcode : BatchScanOpcode,
  _body_,
}
@@ -4551,8 +4552,24 @@ packet LeBatchScanSetScanParameters : LeBatchScan (batch_scan_opcode = SET_SCAN_
packet LeBatchScanSetScanParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_SCAN_PARAMETERS) {
}

packet LeBatchScanReadTruncatedResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  _fixed_ = 0x01 : 8,
enum BatchScanDataRead : 8 {
  TRUNCATED_MODE_DATA = 0x01,
  FULL_MODE_DATA = 0x02,
}

packet LeBatchScanReadResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  batch_scan_data_read : BatchScanDataRead,
}

packet LeBatchScanReadResultParametersCompleteRaw : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  batch_scan_data_read : BatchScanDataRead,
  num_of_records : 8,
  raw_data : 8[],
}

packet LeBatchScanReadResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  batch_scan_data_read : BatchScanDataRead,
  _body_,
}

struct TruncatedResult {
@@ -4563,12 +4580,7 @@ struct TruncatedResult {
  timestamp : 16,
}

packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  _fixed_ = 0x01 : 8,
}

packet LeBatchScanReadFullResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  _fixed_ = 0x02 : 8,
packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = TRUNCATED_MODE_DATA) {
  _count_(results) : 8,
  results : TruncatedResult[],
}
@@ -4585,8 +4597,7 @@ struct FullResult {
  scan_response : 8[],
}

packet LeBatchScanReadFullResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
  _fixed_ = 0x02 : 8,
packet LeBatchScanReadFullResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = FULL_MODE_DATA) {
  _count_(results) : 8,
  results : FullResult[],
}
@@ -5617,6 +5628,28 @@ packet VendorSpecificEvent : Event (event_code = VENDOR_SPECIFIC) {
  _payload_,
}

packet StorageThresholdBreachEvent : VendorSpecificEvent (subevent_code = BLE_THRESHOLD) {
}

packet LEAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = BLE_TRACKING) {
  apcf_filter_index : 8,
  advertiser_state : 8,
  advt_info_present : 8,
  advertiser_address : Address,
  advertiser_address_type : 8,
  _body_,
}

packet LEAdvertisementTrackingWithInfoEvent : LEAdvertisementTrackingEvent {
  tx_power : 8,
  rssi : 8,
  timestamp : 8,
  _size_(adv_packet) : 8,
  adv_packet : 8[],
  _size_(scan_response) : 8,
  scan_response : 8[],
}

enum QualityReportId : 8 {
  MONITOR_MODE = 0x01,
  APPROACH_LSTO = 0x02,
+257 −0
Original line number Diff line number Diff line
@@ -155,6 +155,9 @@ class NullScanningCallback : public ScanningCallback {
  void OnBatchScanReports(int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data) {
    LOG_INFO("OnBatchScanReports in NullScanningCallback");
  }
  void OnBatchScanThresholdCrossed(int client_if) {
    LOG_INFO("OnBatchScanThresholdCrossed in NullScanningCallback");
  }
  void OnTimeout() {
    LOG_INFO("OnTimeout in NullScanningCallback");
  }
@@ -170,6 +173,27 @@ class NullScanningCallback : public ScanningCallback {
  }
};

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;
};

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

@@ -197,11 +221,13 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback
      api_type_ = ScanApiType::LEGACY;
    }
    is_filter_support_ = controller_->IsSupported(OpCode::LE_ADV_FILTER);
    is_batch_scan_support_ = controller->IsSupported(OpCode::LE_BATCH_SCAN);
    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;
    configure_scan();
  }

@@ -803,6 +829,140 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback
        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) {
    if (!is_batch_scan_support_) {
      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));
    }

    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_support_) {
      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_support_) {
      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_support_) {
      LOG_WARN("Batch scan is not supported");
      return;
    }
    AdvertisingAddressType own_address_type = AdvertisingAddressType::PUBLIC_ADDRESS;
    if (own_address_type_ == OwnAddressType::RANDOM_DEVICE_ADDRESS ||
        own_address_type_ == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) {
      own_address_type = AdvertisingAddressType::RANDOM_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_support_) {
      LOG_WARN("Batch scan is not supported");
      int status = static_cast<int>(ErrorCode::UNSUPORTED_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 %d", (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 register_scanning_callback(ScanningCallback* scanning_callbacks) {
    scanning_callbacks_ = scanning_callbacks;
  }
@@ -890,6 +1050,68 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback
    }
  }

  void on_batch_scan_complete(CommandCompleteView view) {
    ASSERT(view.IsValid());
    auto status_view = LeBatchScanCompleteView::Create(view);
    ASSERT(status_view.IsValid());
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      LOG_INFO(
          "Got a Command complete %s, status %s, batch_scan_opcode %s",
          OpCodeText(view.GetCommandOpCode()).c_str(),
          ErrorCodeText(status_view.GetStatus()).c_str(),
          BatchScanOpcodeText(status_view.GetBatchScanOpcode()).c_str());
    }
  }

  void on_batch_scan_enable_complete(CommandCompleteView view) {
    ASSERT(view.IsValid());
    auto status_view = LeBatchScanCompleteView::Create(view);
    ASSERT(status_view.IsValid());
    auto complete_view = LeBatchScanEnableCompleteView::Create(status_view);
    ASSERT(complete_view.IsValid());
    if (status_view.GetStatus() != ErrorCode::SUCCESS) {
      LOG_INFO("Got batch scan enable complete, status %s", ErrorCodeText(status_view.GetStatus()).c_str());
      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) {
    ASSERT(view.IsValid());
    auto status_view = LeBatchScanCompleteView::Create(view);
    ASSERT(status_view.IsValid());
    auto complete_view = LeBatchScanSetScanParametersCompleteView::Create(status_view);
    ASSERT(complete_view.IsValid());
    ASSERT(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) {
    ASSERT(view.IsValid());
    auto status_view = LeBatchScanCompleteView::Create(view);
    ASSERT(status_view.IsValid());
    auto complete_view = LeBatchScanReadResultParametersCompleteRawView::Create(status_view);
    ASSERT(complete_view.IsValid());
    if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
      LOG_INFO("Got batch scan read result complete, status %s", ErrorCodeText(status_view.GetStatus()).c_str());
    }
    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 OnPause() override {
    paused_ = true;
    scan_on_resume_ = is_scanning_;
@@ -926,12 +1148,15 @@ struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback
  bool paused_ = false;
  AdvertisingCache advertising_cache_;
  bool is_filter_support_ = false;
  bool is_batch_scan_support_ = false;

  LeScanType le_scan_type_ = LeScanType::ACTIVE;
  uint32_t interval_ms_{1000};
  uint16_t window_ms_{1000};
  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_;

  static void check_status(CommandCompleteView view) {
    switch (view.GetCommandOpCode()) {
@@ -1020,6 +1245,38 @@ void LeScanningManager::ScanFilterAdd(
  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) {
  CallOn(
      pimpl_.get(),
      &impl::batch_scan_set_storage_parameter,
      batch_scan_full_max,
      batch_scan_truncated_max,
      batch_scan_notify_threshold);
}

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::RegisterScanningCallback(ScanningCallback* scanning_callback) {
  CallOn(pimpl_.get(), &impl::register_scanning_callback, scanning_callback);
}
+19 −0
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@ class ScanningCallback {
  virtual void OnTrackAdvFoundLost() = 0;
  virtual void OnBatchScanReports(
      int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data) = 0;
  virtual void OnBatchScanThresholdCrossed(int client_if) = 0;
  virtual void OnTimeout() = 0;
  virtual void OnFilterEnable(Enable enable, uint8_t status) = 0;
  virtual void OnFilterParamSetup(uint8_t available_spaces, ApcfAction action, uint8_t status) = 0;
@@ -88,6 +89,13 @@ class AdvertisingFilterParameter {
  uint16_t num_of_tracking_entries;
};

enum class BatchScanMode : uint8_t {
  DISABLE = 0,
  TRUNCATED = 1,
  FULL = 2,
  TRUNCATED_AND_FULL = 3,
};

class LeScanningManager : public bluetooth::Module {
 public:
  static constexpr uint8_t kMaxAppNum = 32;
@@ -112,6 +120,17 @@ class LeScanningManager : public bluetooth::Module {

  void ScanFilterAdd(uint8_t filter_index, std::vector<AdvertisingPacketContentFilterCommand> filters);

  /*Batch Scan*/
  void BatchScanConifgStorage(
      uint8_t batch_scan_full_max, uint8_t batch_scan_truncated_max, uint8_t batch_scan_notify_threshold);
  void BatchScanEnable(
      BatchScanMode scan_mode,
      uint32_t duty_cycle_scan_window_slots,
      uint32_t duty_cycle_scan_interval_slots,
      BatchScanDiscardRule batch_scan_discard_rule);
  void BatchScanDisable();
  void BatchScanReadReport(ScannerId scanner_id, BatchScanMode scan_mode);

  void RegisterScanningCallback(ScanningCallback* scanning_callback);

  static const ModuleFactory Factory;
+52 −0
Original line number Diff line number Diff line
@@ -118,11 +118,19 @@ class TestHciLayer : public HciLayer {
    registered_events_[event_code] = event_handler;
  }

  void UnregisterEventHandler(EventCode event_code) override {
    registered_events_.erase(event_code);
  }

  void RegisterLeEventHandler(SubeventCode subevent_code,
                              common::ContextualCallback<void(LeMetaEventView)> event_handler) override {
    registered_le_events_[subevent_code] = event_handler;
  }

  void UnregisterLeEventHandler(SubeventCode subevent_code) override {
    registered_le_events_.erase(subevent_code);
  }

  void IncomingEvent(std::unique_ptr<EventBuilder> event_builder) {
    auto packet = GetPacketView(std::move(event_builder));
    EventView event = EventView::Create(packet);
@@ -235,6 +243,9 @@ class LeScanningManagerTest : public ::testing::Test {
    if (is_filter_support_) {
      test_controller_->AddSupported(OpCode::LE_ADV_FILTER);
    }
    if (is_batch_scan_support_) {
      test_controller_->AddSupported(OpCode::LE_BATCH_SCAN);
    }
    test_acl_manager_ = new TestAclManager;
    fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_);
    fake_registry_.InjectTestModule(&Controller::Factory, test_controller_);
@@ -307,6 +318,7 @@ class LeScanningManagerTest : public ::testing::Test {
        OnBatchScanReports,
        (int client_if, int status, int report_format, int num_records, std::vector<uint8_t> data),
        (override));
    MOCK_METHOD(void, OnBatchScanThresholdCrossed, (int client_if), (override));
    MOCK_METHOD(void, OnTimeout, (), (override));
    MOCK_METHOD(void, OnFilterEnable, (Enable enable, uint8_t status), (override));
    MOCK_METHOD(void, OnFilterParamSetup, (uint8_t available_spaces, ApcfAction action, uint8_t status), (override));
@@ -320,6 +332,7 @@ class LeScanningManagerTest : public ::testing::Test {
  OpCode param_opcode_{OpCode::LE_SET_ADVERTISING_PARAMETERS};
  OpCode enable_opcode_{OpCode::LE_SET_SCAN_ENABLE};
  bool is_filter_support_ = false;
  bool is_batch_scan_support_ = false;
};

class LeAndroidHciScanningManagerTest : public LeScanningManagerTest {
@@ -327,6 +340,7 @@ class LeAndroidHciScanningManagerTest : public LeScanningManagerTest {
  void SetUp() override {
    param_opcode_ = OpCode::LE_EXTENDED_SCAN_PARAMS;
    is_filter_support_ = true;
    is_batch_scan_support_ = true;
    LeScanningManagerTest::SetUp();
    test_controller_->AddSupported(OpCode::LE_ADV_FILTER);
  }
@@ -446,6 +460,44 @@ TEST_F(LeAndroidHciScanningManagerTest, scan_filter_add_test) {
  sync_client_handler();
}

TEST_F(LeAndroidHciScanningManagerTest, read_batch_scan_result) {
  // Enable batch scan feature
  auto next_command_future = test_hci_layer_->GetCommandFuture();
  le_scanning_manager->BatchScanConifgStorage(100, 0, 95);
  auto result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
  ASSERT_EQ(std::future_status::ready, result);
  test_hci_layer_->IncomingEvent(LeBatchScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
  test_hci_layer_->IncomingEvent(
      LeBatchScanSetStorageParametersCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));

  // Enable batch scan
  next_command_future = test_hci_layer_->GetCommandFuture();
  le_scanning_manager->BatchScanEnable(BatchScanMode::FULL, 2400, 2400, BatchScanDiscardRule::OLDEST);
  result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
  ASSERT_EQ(std::future_status::ready, result);
  test_hci_layer_->IncomingEvent(LeBatchScanSetScanParametersCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));

  // Read batch scan data
  next_command_future = test_hci_layer_->GetCommandFuture();
  le_scanning_manager->BatchScanReadReport(0x01, BatchScanMode::FULL);
  result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
  ASSERT_EQ(std::future_status::ready, result);

  EXPECT_CALL(mock_callbacks_, OnBatchScanReports);
  std::vector<uint8_t> raw_data = {0x5c, 0x1f, 0xa2, 0xc3, 0x63, 0x5d, 0x01, 0xf5, 0xb3, 0x5e, 0x00, 0x0c, 0x02,
                                   0x01, 0x02, 0x05, 0x09, 0x6d, 0x76, 0x38, 0x76, 0x02, 0x0a, 0xf5, 0x00};
  next_command_future = test_hci_layer_->GetCommandFuture();
  // We will send read command while num_of_record != 0
  test_hci_layer_->IncomingEvent(LeBatchScanReadResultParametersCompleteRawBuilder::Create(
      uint8_t{1}, ErrorCode::SUCCESS, BatchScanDataRead::FULL_MODE_DATA, 1, raw_data));
  result = next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100)));
  ASSERT_EQ(std::future_status::ready, result);

  // OnBatchScanReports will be trigger when num_of_record == 0
  test_hci_layer_->IncomingEvent(LeBatchScanReadResultParametersCompleteRawBuilder::Create(
      uint8_t{1}, ErrorCode::SUCCESS, BatchScanDataRead::FULL_MODE_DATA, 0, {}));
}

TEST_F(LeExtendedScanningManagerTest, start_scan_test) {
  auto next_command_future = test_hci_layer_->GetCommandFuture();
  le_scanning_manager->Scan(true);
Loading