From 73ffe7659a6517699cb096b65a8c4d6e287d915e Mon Sep 17 00:00:00 2001 From: Myles Watson Date: Fri, 18 Oct 2019 10:49:46 -0700 Subject: [PATCH] HCI: Add LeScanningManager Bug: 139080884 Test: bluetooth_test_gd Change-Id: Ie0aee4042330e2789727aaffda818279b71e02ca --- gd/hci/Android.bp | 2 + gd/hci/le_report.h | 83 +++++++++ gd/hci/le_scanning_interface.h | 6 + gd/hci/le_scanning_manager.cc | 247 +++++++++++++++++++++++++++ gd/hci/le_scanning_manager.h | 60 +++++++ gd/hci/le_scanning_manager_test.cc | 336 +++++++++++++++++++++++++++++++++++++ 6 files changed, 734 insertions(+) create mode 100644 gd/hci/le_report.h create mode 100644 gd/hci/le_scanning_manager.cc create mode 100644 gd/hci/le_scanning_manager.h create mode 100644 gd/hci/le_scanning_manager_test.cc diff --git a/gd/hci/Android.bp b/gd/hci/Android.bp index 675a72e7b..ecb0f46ce 100644 --- a/gd/hci/Android.bp +++ b/gd/hci/Android.bp @@ -11,6 +11,7 @@ filegroup { "device_database.cc", "hci_layer.cc", "le_advertising_manager.cc", + "le_scanning_manager.cc", ], } @@ -29,6 +30,7 @@ filegroup { "dual_device_test.cc", "hci_layer_test.cc", "hci_packets_test.cc", + "le_scanning_manager_test.cc", ], } diff --git a/gd/hci/le_report.h b/gd/hci/le_report.h new file mode 100644 index 000000000..7dc075f8e --- /dev/null +++ b/gd/hci/le_report.h @@ -0,0 +1,83 @@ +/* + * 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. + */ +#pragma once + +#include + +#include "hci/hci_packets.h" + +namespace bluetooth::hci { + +class LeReport { + public: + explicit LeReport(const LeAdvertisingReport& advertisement) + : report_type_(ReportType::ADVERTISING_EVENT), advertising_event_type_(advertisement.event_type_), + address_(advertisement.address_), address_type_(advertisement.address_type_), rssi_(advertisement.rssi_), + gap_data_(advertisement.advertising_data_) {} + explicit LeReport(const LeDirectedAdvertisingReport& advertisement) + : report_type_(ReportType::DIRECTED_ADVERTISING_EVENT), address_(advertisement.address_), + rssi_(advertisement.rssi_) {} + explicit LeReport(const LeExtendedAdvertisingReport& advertisement) + : report_type_(ReportType::EXTENDED_ADVERTISING_EVENT), address_(advertisement.address_), + rssi_(advertisement.rssi_), gap_data_(advertisement.advertising_data_) {} + virtual ~LeReport() = default; + + enum class ReportType { + ADVERTISING_EVENT = 1, + DIRECTED_ADVERTISING_EVENT = 2, + EXTENDED_ADVERTISING_EVENT = 3, + }; + const ReportType report_type_; + + ReportType GetReportType() const { + return report_type_; + } + + // Advertising Event + const AdvertisingEventType advertising_event_type_{}; + const Address address_{}; + const AddressType address_type_{}; + const uint8_t rssi_; + const std::vector gap_data_{}; +}; + +class DirectedLeReport : public LeReport { + public: + explicit DirectedLeReport(const LeDirectedAdvertisingReport& advertisement) + : LeReport(advertisement), direct_address_type_(advertisement.address_type_) {} + explicit DirectedLeReport(const LeExtendedAdvertisingReport& advertisement) + : LeReport(advertisement), direct_address_type_(advertisement.address_type_) {} + + const DirectAdvertisingAddressType direct_address_type_{}; +}; + +class ExtendedLeReport : public DirectedLeReport { + public: + explicit ExtendedLeReport(const LeExtendedAdvertisingReport& advertisement) + : DirectedLeReport(advertisement), connectable_(advertisement.connectable_), scannable_(advertisement.scannable_), + directed_(advertisement.directed_), scan_response_(advertisement.scan_response_), + complete_(advertisement.data_status_ == DataStatus::COMPLETE), + truncated_(advertisement.data_status_ == DataStatus::TRUNCATED) {} + + // Extended + bool connectable_; + bool scannable_; + bool directed_; + bool scan_response_; + bool complete_; + bool truncated_; +}; +} // namespace bluetooth::hci \ No newline at end of file diff --git a/gd/hci/le_scanning_interface.h b/gd/hci/le_scanning_interface.h index 4d93f251c..97a676625 100644 --- a/gd/hci/le_scanning_interface.h +++ b/gd/hci/le_scanning_interface.h @@ -38,6 +38,12 @@ class LeScanningInterface { static constexpr hci::SubeventCode LeScanningEvents[] = { hci::SubeventCode::SCAN_TIMEOUT, + hci::SubeventCode::ADVERTISING_REPORT, + hci::SubeventCode::DIRECTED_ADVERTISING_REPORT, + hci::SubeventCode::EXTENDED_ADVERTISING_REPORT, + hci::SubeventCode::PERIODIC_ADVERTISING_REPORT, + hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_ESTABLISHED, + hci::SubeventCode::PERIODIC_ADVERTISING_SYNC_LOST, }; }; } // namespace hci diff --git a/gd/hci/le_scanning_manager.cc b/gd/hci/le_scanning_manager.cc new file mode 100644 index 000000000..1562dd626 --- /dev/null +++ b/gd/hci/le_scanning_manager.cc @@ -0,0 +1,247 @@ +/* + * 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 +#include +#include + +#include "hci/controller.h" +#include "hci/hci_layer.h" +#include "hci/hci_packets.h" +#include "hci/le_scanning_interface.h" +#include "hci/le_scanning_manager.h" +#include "module.h" +#include "os/handler.h" +#include "os/log.h" + +namespace bluetooth { +namespace hci { + +const ModuleFactory LeScanningManager::Factory = ModuleFactory([]() { return new LeScanningManager(); }); + +enum class ScanApiType { + LE_4_0 = 1, + ANDROID_HCI = 2, + LE_5_0 = 3, +}; + +struct LeScanningManager::impl { + impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {} + + void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller) { + module_handler_ = handler; + hci_layer_ = hci_layer; + controller_ = controller; + le_scanning_interface_ = hci_layer_->GetLeScanningInterface( + common::Bind(&LeScanningManager::impl::handle_scan_results, common::Unretained(this)), module_handler_); + if (controller_->IsSupported(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS)) { + api_type_ = ScanApiType::LE_5_0; + } else if (controller_->IsSupported(OpCode::LE_EXTENDED_SCAN_PARAMS)) { + api_type_ = ScanApiType::ANDROID_HCI; + } else { + api_type_ = ScanApiType::LE_4_0; + } + configure_scan(); + } + + void handle_scan_results(LeMetaEventView event) { + switch (event.GetSubeventCode()) { + case hci::SubeventCode::ADVERTISING_REPORT: + handle_advertising_report( + LeAdvertisingReportView::Create(event)); + break; + case hci::SubeventCode::DIRECTED_ADVERTISING_REPORT: + handle_advertising_report( + LeDirectedAdvertisingReportView::Create(event)); + break; + case hci::SubeventCode::EXTENDED_ADVERTISING_REPORT: + handle_advertising_report( + LeExtendedAdvertisingReportView::Create(event)); + break; + case hci::SubeventCode::SCAN_TIMEOUT: + if (registered_callback_ != nullptr) { + registered_callback_->handler->Post( + common::BindOnce(&LeScanningManagerCallbacks::on_timeout, common::Unretained(registered_callback_))); + registered_callback_ = nullptr; + } + break; + default: + LOG_ALWAYS_FATAL("Unknown advertising subevent %s", hci::SubeventCodeText(event.GetSubeventCode()).c_str()); + } + } + + template + void handle_advertising_report(EventType event_view) { + if (registered_callback_ == nullptr) { + LOG_INFO("Dropping advertising event (no registered handler)"); + return; + } + if (!event_view.IsValid()) { + LOG_INFO("Dropping invalid advertising event"); + return; + } + std::vector report_vector = event_view.GetAdvertisingReports(); + if (report_vector.empty()) { + LOG_INFO("Zero results in advertising event"); + return; + } + std::vector> param; + param.reserve(report_vector.size()); + for (const ReportStructType& report : report_vector) { + param.push_back(std::shared_ptr(static_cast(new ReportType(report)))); + } + registered_callback_->handler->Post(common::BindOnce(&LeScanningManagerCallbacks::on_advertisements, + common::Unretained(registered_callback_), param)); + } + + void configure_scan() { + switch (api_type_) { + case ScanApiType::LE_5_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetExtendedScanParametersBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, + own_address_type_, filter_policy_), + common::BindOnce(impl::check_status), module_handler_); + break; + case ScanApiType::ANDROID_HCI: + le_scanning_interface_->EnqueueCommand( + hci::LeExtendedScanParamsBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_, + filter_policy_), + common::BindOnce(impl::check_status), module_handler_); + + break; + case ScanApiType::LE_4_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetScanParametersBuilder::Create(LeScanType::ACTIVE, interval_ms_, window_ms_, own_address_type_, + filter_policy_), + common::BindOnce(impl::check_status), module_handler_); + break; + } + } + + void start_scan(LeScanningManagerCallbacks* le_scanning_manager_callbacks) { + registered_callback_ = le_scanning_manager_callbacks; + switch (api_type_) { + case ScanApiType::LE_5_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetExtendedScanEnableBuilder::Create(Enable::ENABLED, + FilterDuplicates::DISABLED /* filter duplicates */, 0, 0), + common::BindOnce(impl::check_status), module_handler_); + break; + case ScanApiType::ANDROID_HCI: + case ScanApiType::LE_4_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetScanEnableBuilder::Create(Enable::ENABLED, Enable::DISABLED /* filter duplicates */), + common::BindOnce(impl::check_status), module_handler_); + break; + } + } + + void stop_scan(common::Callback on_stopped) { + if (registered_callback_ == nullptr) { + return; + } + registered_callback_->handler->Post(std::move(on_stopped)); + switch (api_type_) { + case ScanApiType::LE_5_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetExtendedScanEnableBuilder::Create(Enable::DISABLED, + FilterDuplicates::DISABLED /* filter duplicates */, 0, 0), + common::BindOnce(impl::check_status), module_handler_); + registered_callback_->handler = nullptr; + break; + case ScanApiType::ANDROID_HCI: + case ScanApiType::LE_4_0: + le_scanning_interface_->EnqueueCommand( + hci::LeSetScanEnableBuilder::Create(Enable::DISABLED, Enable::DISABLED /* filter duplicates */), + common::BindOnce(impl::check_status), module_handler_); + registered_callback_->handler = nullptr; + break; + } + } + + ScanApiType api_type_; + + LeScanningManagerCallbacks* registered_callback_; + Module* module_; + os::Handler* module_handler_; + hci::HciLayer* hci_layer_; + hci::Controller* controller_; + hci::LeScanningInterface* le_scanning_interface_; + + uint32_t interval_ms_{1000}; + uint16_t window_ms_{1000}; + AddressType own_address_type_{AddressType::PUBLIC_DEVICE_ADDRESS}; + LeSetScanningFilterPolicy filter_policy_{LeSetScanningFilterPolicy::ACCEPT_ALL}; + + static void check_status(CommandCompleteView view) { + switch (view.GetCommandOpCode()) { + case (OpCode::LE_SET_SCAN_ENABLE): { + auto status_view = LeSetScanEnableCompleteView::Create(view); + ASSERT(status_view.IsValid()); + ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); + } break; + case (OpCode::LE_SET_EXTENDED_SCAN_ENABLE): { + auto status_view = LeSetExtendedScanEnableCompleteView::Create(view); + ASSERT(status_view.IsValid()); + ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); + } break; + case (OpCode::LE_SET_SCAN_PARAMETERS): { + auto status_view = LeSetScanParametersCompleteView::Create(view); + ASSERT(status_view.IsValid()); + ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); + } break; + case (OpCode::LE_EXTENDED_SCAN_PARAMS): { + auto status_view = LeExtendedScanParamsCompleteView::Create(view); + ASSERT(status_view.IsValid()); + ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); + } break; + case (OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS): { + auto status_view = LeSetExtendedScanParametersCompleteView::Create(view); + ASSERT(status_view.IsValid()); + ASSERT(status_view.GetStatus() == ErrorCode::SUCCESS); + } break; + default: + LOG_ALWAYS_FATAL("Unhandled event %s", OpCodeText(view.GetCommandOpCode()).c_str()); + } + } +}; + +LeScanningManager::LeScanningManager() { + pimpl_ = std::make_unique(this); +} + +void LeScanningManager::ListDependencies(ModuleList* list) { + list->add(); + list->add(); +} + +void LeScanningManager::Start() { + pimpl_->start(GetHandler(), GetDependency(), GetDependency()); +} + +void LeScanningManager::Stop() { + pimpl_.reset(); +} + +void LeScanningManager::StartScan(LeScanningManagerCallbacks* callbacks) { + GetHandler()->Post(common::Bind(&impl::start_scan, common::Unretained(pimpl_.get()), callbacks)); +} + +void LeScanningManager::StopScan(common::Callback on_stopped) { + GetHandler()->Post(common::Bind(&impl::stop_scan, common::Unretained(pimpl_.get()), on_stopped)); +} + +} // namespace hci +} // namespace bluetooth \ No newline at end of file diff --git a/gd/hci/le_scanning_manager.h b/gd/hci/le_scanning_manager.h new file mode 100644 index 000000000..1de0f0862 --- /dev/null +++ b/gd/hci/le_scanning_manager.h @@ -0,0 +1,60 @@ +/* + * 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. + */ +#pragma once + +#include + +#include "common/callback.h" +#include "hci/hci_packets.h" +#include "hci/le_report.h" +#include "module.h" + +namespace bluetooth { +namespace hci { + +class LeScanningManagerCallbacks { + public: + virtual ~LeScanningManagerCallbacks() = default; + virtual void on_advertisements(std::vector>) = 0; + virtual void on_timeout() = 0; + os::Handler* handler; +}; + +class LeScanningManager : public bluetooth::Module { + public: + LeScanningManager(); + + void StartScan(LeScanningManagerCallbacks* callbacks); + + void StopScan(common::Callback on_stopped); + + static const ModuleFactory Factory; + + protected: + void ListDependencies(ModuleList* list) override; + + void Start() override; + + void Stop() override; + + private: + struct impl; + std::unique_ptr pimpl_; + DISALLOW_COPY_AND_ASSIGN(LeScanningManager); +}; + +} // namespace hci +} // namespace bluetooth \ No newline at end of file diff --git a/gd/hci/le_scanning_manager_test.cc b/gd/hci/le_scanning_manager_test.cc new file mode 100644 index 000000000..e5e461e9b --- /dev/null +++ b/gd/hci/le_scanning_manager_test.cc @@ -0,0 +1,336 @@ +/* + * 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 +#include +#include +#include + +#include +#include + +#include "common/bind.h" +#include "hci/address.h" +#include "hci/controller.h" +#include "hci/hci_layer.h" +#include "os/thread.h" +#include "packet/raw_builder.h" + +namespace bluetooth { +namespace hci { +namespace { + +using packet::kLittleEndian; +using packet::PacketView; +using packet::RawBuilder; + +PacketView GetPacketView(std::unique_ptr packet) { + auto bytes = std::make_shared>(); + BitInserter i(*bytes); + bytes->reserve(packet->size()); + packet->Serialize(i); + return packet::PacketView(bytes); +} + +class TestController : public Controller { + public: + bool IsSupported(OpCode op_code) const override { + return supported_opcodes_.count(op_code) == 1; + } + + void AddSupported(OpCode op_code) { + supported_opcodes_.insert(op_code); + } + + protected: + void Start() override {} + void Stop() override {} + void ListDependencies(ModuleList* list) override {} + + private: + std::set supported_opcodes_{}; +}; + +class TestHciLayer : public HciLayer { + public: + TestHciLayer() { + RegisterEventHandler(EventCode::COMMAND_COMPLETE, + base::Bind(&TestHciLayer::CommandCompleteCallback, common::Unretained(this)), nullptr); + RegisterEventHandler(EventCode::COMMAND_STATUS, + base::Bind(&TestHciLayer::CommandStatusCallback, common::Unretained(this)), nullptr); + } + + void EnqueueCommand(std::unique_ptr command, + common::OnceCallback on_status, os::Handler* handler) override { + command_queue_.push(std::move(command)); + command_status_callbacks.push_front(std::move(on_status)); + if (command_promise_ != nullptr) { + command_promise_->set_value(); + command_promise_.reset(); + } + } + + void EnqueueCommand(std::unique_ptr command, + common::OnceCallback on_complete, os::Handler* handler) override { + command_queue_.push(std::move(command)); + command_complete_callbacks.push_front(std::move(on_complete)); + if (command_promise_ != nullptr) { + command_promise_->set_value(); + command_promise_.reset(); + } + } + + std::future GetCommandFuture() { + ASSERT_LOG(command_promise_ == nullptr, "Promises promises ... Only one at a time"); + command_promise_ = std::make_unique>(); + return command_promise_->get_future(); + } + + std::unique_ptr GetLastCommand() { + ASSERT(!command_queue_.empty()); + auto last = std::move(command_queue_.front()); + command_queue_.pop(); + return last; + } + + ConnectionManagementCommandView GetCommandPacket(OpCode op_code) { + auto packet_view = GetPacketView(GetLastCommand()); + CommandPacketView command_packet_view = CommandPacketView::Create(packet_view); + ConnectionManagementCommandView command = ConnectionManagementCommandView::Create(command_packet_view); + ASSERT(command.IsValid()); + EXPECT_EQ(command.GetOpCode(), op_code); + + return command; + } + + void RegisterEventHandler(EventCode event_code, common::Callback event_handler, + os::Handler* handler) override { + registered_events_[event_code] = event_handler; + } + + void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback event_handler, + os::Handler* handler) override { + registered_le_events_[subevent_code] = event_handler; + } + + void IncomingEvent(std::unique_ptr event_builder) { + auto packet = GetPacketView(std::move(event_builder)); + EventPacketView event = EventPacketView::Create(packet); + ASSERT_TRUE(event.IsValid()); + EventCode event_code = event.GetEventCode(); + ASSERT_TRUE(registered_events_.find(event_code) != registered_events_.end()) << EventCodeText(event_code); + registered_events_[event_code].Run(event); + } + + void IncomingLeMetaEvent(std::unique_ptr event_builder) { + auto packet = GetPacketView(std::move(event_builder)); + EventPacketView event = EventPacketView::Create(packet); + LeMetaEventView meta_event_view = LeMetaEventView::Create(event); + ASSERT_TRUE(meta_event_view.IsValid()); + SubeventCode subevent_code = meta_event_view.GetSubeventCode(); + ASSERT_TRUE(registered_le_events_.find(subevent_code) != registered_le_events_.end()) + << SubeventCodeText(subevent_code); + registered_le_events_[subevent_code].Run(meta_event_view); + } + + void CommandCompleteCallback(EventPacketView event) { + CommandCompleteView complete_view = CommandCompleteView::Create(event); + ASSERT(complete_view.IsValid()); + std::move(command_complete_callbacks.front()).Run(complete_view); + command_complete_callbacks.pop_front(); + } + + void CommandStatusCallback(EventPacketView event) { + CommandStatusView status_view = CommandStatusView::Create(event); + ASSERT(status_view.IsValid()); + std::move(command_status_callbacks.front()).Run(status_view); + command_status_callbacks.pop_front(); + } + + void ListDependencies(ModuleList* list) override {} + void Start() override {} + void Stop() override {} + + private: + std::map> registered_events_; + std::map> registered_le_events_; + std::list> command_complete_callbacks; + std::list> command_status_callbacks; + + std::queue> command_queue_; + mutable std::mutex mutex_; + std::unique_ptr> command_promise_{}; +}; + +class LeScanningManagerTest : public ::testing::Test { + protected: + void SetUp() override { + test_hci_layer_ = new TestHciLayer; // Ownership is transferred to registry + test_controller_ = new TestController; + test_controller_->AddSupported(param_opcode_); + fake_registry_.InjectTestModule(&HciLayer::Factory, test_hci_layer_); + fake_registry_.InjectTestModule(&Controller::Factory, test_controller_); + client_handler_ = fake_registry_.GetTestModuleHandler(&HciLayer::Factory); + ASSERT_NE(client_handler_, nullptr); + mock_callbacks_.handler = client_handler_; + std::future config_future = test_hci_layer_->GetCommandFuture(); + fake_registry_.Start(&thread_); + le_scanning_manager = + static_cast(fake_registry_.GetModuleUnderTest(&LeScanningManager::Factory)); + config_future.wait_for(std::chrono::duration(std::chrono::milliseconds(1000))); + HandleConfiguration(); + } + + void TearDown() override { + fake_registry_.SynchronizeModuleHandler(&LeScanningManager::Factory, std::chrono::milliseconds(20)); + fake_registry_.StopAll(); + } + + virtual void HandleConfiguration() { + auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_SCAN_PARAMETERS); + test_hci_layer_->IncomingEvent(LeSetScanParametersCompleteBuilder::Create(1, ErrorCode::SUCCESS)); + } + + TestModuleRegistry fake_registry_; + TestHciLayer* test_hci_layer_ = nullptr; + TestController* test_controller_ = nullptr; + os::Thread& thread_ = fake_registry_.GetTestThread(); + LeScanningManager* le_scanning_manager = nullptr; + os::Handler* client_handler_ = nullptr; + + class MockLeScanningManagerCallbacks : public LeScanningManagerCallbacks { + public: + MOCK_METHOD(void, on_advertisements, (std::vector>), (override)); + MOCK_METHOD(void, on_timeout, (), (override)); + } mock_callbacks_; + + OpCode param_opcode_{OpCode::LE_SET_ADVERTISING_PARAMETERS}; +}; + +class LeAndroidHciScanningManagerTest : public LeScanningManagerTest { + protected: + void SetUp() override { + param_opcode_ = OpCode::LE_EXTENDED_SCAN_PARAMS; + LeScanningManagerTest::SetUp(); + } + + void HandleConfiguration() override { + auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_EXTENDED_SCAN_PARAMS); + test_hci_layer_->IncomingEvent(LeExtendedScanParamsCompleteBuilder::Create(1, ErrorCode::SUCCESS)); + } +}; + +class LeExtendedScanningManagerTest : public LeScanningManagerTest { + protected: + void SetUp() override { + param_opcode_ = OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS; + LeScanningManagerTest::SetUp(); + } + + void HandleConfiguration() override { + auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_EXTENDED_SCAN_PARAMETERS); + test_hci_layer_->IncomingEvent(LeSetExtendedScanParametersCompleteBuilder::Create(1, ErrorCode::SUCCESS)); + } +}; + +TEST_F(LeScanningManagerTest, startup_teardown) {} + +TEST_F(LeScanningManagerTest, start_scan_test) { + auto next_command_future = test_hci_layer_->GetCommandFuture(); + le_scanning_manager->StartScan(&mock_callbacks_); + + next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100))); + test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS)); + + LeAdvertisingReport report{}; + report.event_type_ = AdvertisingEventType::ADV_IND; + report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS; + Address::FromString("12:34:56:78:9a:bc", report.address_); + std::vector gap_data{}; + GapData data_item{}; + data_item.data_type_ = GapDataType::FLAGS; + data_item.data_ = {0x34}; + gap_data.push_back(data_item); + data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME; + data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'}; + gap_data.push_back(data_item); + report.advertising_data_ = gap_data; + + EXPECT_CALL(mock_callbacks_, on_advertisements); + + test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report})); +} + +TEST_F(LeAndroidHciScanningManagerTest, start_scan_test) { + auto next_command_future = test_hci_layer_->GetCommandFuture(); + le_scanning_manager->StartScan(&mock_callbacks_); + + next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100))); + test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS)); + + LeAdvertisingReport report{}; + report.event_type_ = AdvertisingEventType::ADV_IND; + report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS; + Address::FromString("12:34:56:78:9a:bc", report.address_); + std::vector gap_data{}; + GapData data_item{}; + data_item.data_type_ = GapDataType::FLAGS; + data_item.data_ = {0x34}; + gap_data.push_back(data_item); + data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME; + data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'}; + gap_data.push_back(data_item); + report.advertising_data_ = gap_data; + + EXPECT_CALL(mock_callbacks_, on_advertisements); + + test_hci_layer_->IncomingLeMetaEvent(LeAdvertisingReportBuilder::Create({report})); +} + +TEST_F(LeExtendedScanningManagerTest, start_scan_test) { + auto next_command_future = test_hci_layer_->GetCommandFuture(); + le_scanning_manager->StartScan(&mock_callbacks_); + + next_command_future.wait_for(std::chrono::duration(std::chrono::milliseconds(100))); + auto packet = test_hci_layer_->GetCommandPacket(OpCode::LE_SET_EXTENDED_SCAN_ENABLE); + + test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS)); + + LeExtendedAdvertisingReport report{}; + report.connectable_ = 1; + report.scannable_ = 1; + report.address_type_ = DirectAdvertisingAddressType::PUBLIC_DEVICE_ADDRESS; + Address::FromString("12:34:56:78:9a:bc", report.address_); + std::vector gap_data{}; + GapData data_item{}; + data_item.data_type_ = GapDataType::FLAGS; + data_item.data_ = {0x34}; + gap_data.push_back(data_item); + data_item.data_type_ = GapDataType::COMPLETE_LOCAL_NAME; + data_item.data_ = {'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'e', 'v', 'i', 'c', 'e'}; + gap_data.push_back(data_item); + report.advertising_data_ = gap_data; + + EXPECT_CALL(mock_callbacks_, on_advertisements); + + test_hci_layer_->IncomingLeMetaEvent(LeExtendedAdvertisingReportBuilder::Create({report})); +} + +} // namespace +} // namespace hci +} // namespace bluetooth -- 2.11.0