Loading system/gd/hci/facade/le_scanning_manager_facade.cc +1 −0 Original line number Diff line number Diff line Loading @@ -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){}; Loading system/gd/hci/hci_packets.pdl +44 −11 Original line number Diff line number Diff line Loading @@ -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_, } Loading Loading @@ -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 { Loading @@ -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[], } Loading @@ -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[], } Loading Loading @@ -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, Loading system/gd/hci/le_scanning_manager.cc +257 −0 Original line number Diff line number Diff line Loading @@ -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"); } Loading @@ -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) {} Loading Loading @@ -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(); } Loading Loading @@ -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; } Loading Loading @@ -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_; Loading Loading @@ -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()) { Loading Loading @@ -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); } Loading system/gd/hci/le_scanning_manager.h +19 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading system/gd/hci/le_scanning_manager_test.cc +52 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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_); Loading Loading @@ -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)); Loading @@ -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 { Loading @@ -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); } Loading Loading @@ -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 Loading
system/gd/hci/facade/le_scanning_manager_facade.cc +1 −0 Original line number Diff line number Diff line Loading @@ -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){}; Loading
system/gd/hci/hci_packets.pdl +44 −11 Original line number Diff line number Diff line Loading @@ -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_, } Loading Loading @@ -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 { Loading @@ -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[], } Loading @@ -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[], } Loading Loading @@ -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, Loading
system/gd/hci/le_scanning_manager.cc +257 −0 Original line number Diff line number Diff line Loading @@ -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"); } Loading @@ -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) {} Loading Loading @@ -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(); } Loading Loading @@ -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; } Loading Loading @@ -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_; Loading Loading @@ -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()) { Loading Loading @@ -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); } Loading
system/gd/hci/le_scanning_manager.h +19 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading
system/gd/hci/le_scanning_manager_test.cc +52 −0 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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_); Loading Loading @@ -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)); Loading @@ -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 { Loading @@ -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); } Loading Loading @@ -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