OSDN Git Service

HCI: Add LeScanningManager
authorMyles Watson <mylesgw@google.com>
Fri, 18 Oct 2019 17:49:46 +0000 (10:49 -0700)
committerMyles Watson <mylesgw@google.com>
Thu, 7 Nov 2019 18:38:25 +0000 (10:38 -0800)
Bug: 139080884
Test: bluetooth_test_gd
Change-Id: Ie0aee4042330e2789727aaffda818279b71e02ca

gd/hci/Android.bp
gd/hci/le_report.h [new file with mode: 0644]
gd/hci/le_scanning_interface.h
gd/hci/le_scanning_manager.cc [new file with mode: 0644]
gd/hci/le_scanning_manager.h [new file with mode: 0644]
gd/hci/le_scanning_manager_test.cc [new file with mode: 0644]

index 675a72e..ecb0f46 100644 (file)
@@ -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 (file)
index 0000000..7dc075f
--- /dev/null
@@ -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 <memory>
+
+#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<GapData> 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
index 4d93f25..97a6766 100644 (file)
@@ -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 (file)
index 0000000..1562dd6
--- /dev/null
@@ -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 <memory>
+#include <mutex>
+#include <set>
+
+#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, LeAdvertisingReport, LeReport>(
+            LeAdvertisingReportView::Create(event));
+        break;
+      case hci::SubeventCode::DIRECTED_ADVERTISING_REPORT:
+        handle_advertising_report<LeDirectedAdvertisingReportView, LeDirectedAdvertisingReport, DirectedLeReport>(
+            LeDirectedAdvertisingReportView::Create(event));
+        break;
+      case hci::SubeventCode::EXTENDED_ADVERTISING_REPORT:
+        handle_advertising_report<LeExtendedAdvertisingReportView, LeExtendedAdvertisingReport, ExtendedLeReport>(
+            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 <class EventType, class ReportStructType, class ReportType>
+  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<ReportStructType> report_vector = event_view.GetAdvertisingReports();
+    if (report_vector.empty()) {
+      LOG_INFO("Zero results in advertising event");
+      return;
+    }
+    std::vector<std::shared_ptr<LeReport>> param;
+    param.reserve(report_vector.size());
+    for (const ReportStructType& report : report_vector) {
+      param.push_back(std::shared_ptr<LeReport>(static_cast<LeReport*>(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<void()> 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<impl>(this);
+}
+
+void LeScanningManager::ListDependencies(ModuleList* list) {
+  list->add<hci::HciLayer>();
+  list->add<hci::Controller>();
+}
+
+void LeScanningManager::Start() {
+  pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>());
+}
+
+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<void()> 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 (file)
index 0000000..1de0f08
--- /dev/null
@@ -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 <memory>
+
+#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<std::shared_ptr<LeReport>>) = 0;
+  virtual void on_timeout() = 0;
+  os::Handler* handler;
+};
+
+class LeScanningManager : public bluetooth::Module {
+ public:
+  LeScanningManager();
+
+  void StartScan(LeScanningManagerCallbacks* callbacks);
+
+  void StopScan(common::Callback<void()> on_stopped);
+
+  static const ModuleFactory Factory;
+
+ protected:
+  void ListDependencies(ModuleList* list) override;
+
+  void Start() override;
+
+  void Stop() override;
+
+ private:
+  struct impl;
+  std::unique_ptr<impl> 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 (file)
index 0000000..e5e461e
--- /dev/null
@@ -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 <algorithm>
+#include <chrono>
+#include <future>
+#include <map>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#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<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(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<OpCode> 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<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandStatusView)> 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<CommandPacketBuilder> command,
+                      common::OnceCallback<void(CommandCompleteView)> 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<void> GetCommandFuture() {
+    ASSERT_LOG(command_promise_ == nullptr, "Promises promises ... Only one at a time");
+    command_promise_ = std::make_unique<std::promise<void>>();
+    return command_promise_->get_future();
+  }
+
+  std::unique_ptr<CommandPacketBuilder> 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<void(EventPacketView)> event_handler,
+                            os::Handler* handler) override {
+    registered_events_[event_code] = event_handler;
+  }
+
+  void RegisterLeEventHandler(SubeventCode subevent_code, common::Callback<void(LeMetaEventView)> event_handler,
+                              os::Handler* handler) override {
+    registered_le_events_[subevent_code] = event_handler;
+  }
+
+  void IncomingEvent(std::unique_ptr<EventPacketBuilder> 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<LeMetaEventBuilder> 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<EventCode, common::Callback<void(EventPacketView)>> registered_events_;
+  std::map<SubeventCode, common::Callback<void(LeMetaEventView)>> registered_le_events_;
+  std::list<base::OnceCallback<void(CommandCompleteView)>> command_complete_callbacks;
+  std::list<base::OnceCallback<void(CommandStatusView)>> command_status_callbacks;
+
+  std::queue<std::unique_ptr<CommandPacketBuilder>> command_queue_;
+  mutable std::mutex mutex_;
+  std::unique_ptr<std::promise<void>> 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<void> config_future = test_hci_layer_->GetCommandFuture();
+    fake_registry_.Start<LeScanningManager>(&thread_);
+    le_scanning_manager =
+        static_cast<LeScanningManager*>(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<std::shared_ptr<LeReport>>), (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<GapData> 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<GapData> 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<GapData> 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