Loading android/pandora/mmi2grpc/mmi2grpc/hfp.py +2 −1 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ class HFPProxy(ProfileProxy): th.start() def test_started(self, test: str, pts_addr: bytes, **kwargs): if test not in ("HFP/AG/SLC/BV-02-C", "HFP/AG/SLC/BV-04-C"): self.asyncWaitConnection(pts_addr) return "OK" Loading tools/rootcanal/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -316,6 +316,7 @@ python_test_host { "test/LL/DDI/ADV/*.py", "test/LL/DDI/SCN/*.py", "test/LMP/LIH/*.py", "test/LMP/*.py", "test/main.py", ], data: [ Loading tools/rootcanal/model/controller/acl_connection_handler.cc +15 −4 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ uint16_t AclConnectionHandler::GetUnusedHandle() { bool AclConnectionHandler::CreatePendingConnection(Address addr, bool authenticate_on_connect, bool allow_role_switch) { if (classic_connection_pending_) { if (classic_connection_pending_ || GetAclConnectionHandle(addr).has_value()) { return false; } classic_connection_pending_ = true; Loading Loading @@ -132,9 +132,9 @@ bool AclConnectionHandler::CancelPendingLeConnection(AddressWithType addr) { return true; } uint16_t AclConnectionHandler::CreateConnection(Address addr, Address own_addr) { if (CancelPendingConnection(addr)) { uint16_t AclConnectionHandler::CreateConnection(Address addr, Address own_addr, bool pending) { if (!pending || CancelPendingConnection(addr)) { uint16_t handle = GetUnusedHandle(); acl_connections_.emplace( handle, Loading Loading @@ -199,6 +199,17 @@ uint16_t AclConnectionHandler::GetHandleOnlyAddress( return kReservedHandle; } std::optional<uint16_t> AclConnectionHandler::GetAclConnectionHandle( bluetooth::hci::Address bd_addr) const { for (auto const& [handle, connection] : acl_connections_) { if (connection.GetAddress().GetAddress() == bd_addr && connection.GetPhyType() == Phy::Type::BR_EDR) { return handle; } } return {}; } AclConnection& AclConnectionHandler::GetAclConnection(uint16_t handle) { ASSERT_LOG(HasHandle(handle), "Unknown handle %d", handle); return acl_connections_.at(handle); Loading tools/rootcanal/model/controller/acl_connection_handler.h +9 −1 Original line number Diff line number Diff line Loading @@ -75,8 +75,11 @@ class AclConnectionHandler { bool HasPendingLeConnection(bluetooth::hci::AddressWithType addr) const; bool CancelPendingLeConnection(bluetooth::hci::AddressWithType addr); // \p pending is true if the connection is expected to be // in pending state. uint16_t CreateConnection(bluetooth::hci::Address addr, bluetooth::hci::Address own_addr); bluetooth::hci::Address own_addr, bool pending = true); uint16_t CreateLeConnection(bluetooth::hci::AddressWithType addr, bluetooth::hci::AddressWithType own_addr, bluetooth::hci::Role role); Loading @@ -84,6 +87,11 @@ class AclConnectionHandler { bool HasHandle(uint16_t handle) const; bool HasScoHandle(uint16_t handle) const; // Return the connection handle for a classic ACL connection only. // \p bd_addr is the peer address. std::optional<uint16_t> GetAclConnectionHandle( bluetooth::hci::Address bd_addr) const; uint16_t GetHandle(bluetooth::hci::AddressWithType addr) const; uint16_t GetHandleOnlyAddress(bluetooth::hci::Address addr) const; bluetooth::hci::AddressWithType GetAddress(uint16_t handle) const; Loading tools/rootcanal/model/controller/link_layer_controller.cc +155 −73 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ using TaskId = rootcanal::LinkLayerController::TaskId; namespace rootcanal { constexpr milliseconds kNoDelayMs(0); constexpr milliseconds kPageInterval(1000); const Address& LinkLayerController::GetAddress() const { return address_; } Loading Loading @@ -5021,75 +5022,87 @@ void LinkLayerController::LeSynchronization() { void LinkLayerController::IncomingPagePacket( model::packets::LinkLayerPacketView incoming) { auto bd_addr = incoming.GetSourceAddress(); auto page = model::packets::PageView::Create(incoming); ASSERT(page.IsValid()); INFO(id_, "from {}", incoming.GetSourceAddress()); // Cannot establish two BR-EDR connections with the same peer. if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { return; } bool allow_role_switch = page.GetAllowRoleSwitch(); if (!connections_.CreatePendingConnection( incoming.GetSourceAddress(), authentication_enable_ == AuthenticationEnable::REQUIRED, bd_addr, authentication_enable_ == AuthenticationEnable::REQUIRED, allow_role_switch)) { // Send a response to indicate that we're busy, or drop the packet? WARNING(id_, "Failed to create a pending connection for {}", incoming.GetSourceAddress()); // Will be triggered when multiple hosts are paging simultaneously; // only one connection will be accepted. WARNING(id_, "Failed to create a pending connection for {}", bd_addr); return; } bluetooth::hci::Address source_address{}; bluetooth::hci::Address::FromString(page.GetSourceAddress().ToString(), source_address); if (IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( source_address, page.GetClassOfDevice(), bd_addr, page.GetClassOfDevice(), bluetooth::hci::ConnectionRequestLinkType::ACL)); } } void LinkLayerController::IncomingPageRejectPacket( model::packets::LinkLayerPacketView incoming) { INFO(id_, "{}", incoming.GetSourceAddress()); auto bd_addr = incoming.GetSourceAddress(); auto reject = model::packets::PageRejectView::Create(incoming); ASSERT(reject.IsValid()); INFO(id_, "Sending CreateConnectionComplete"); if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "ignoring Page Reject packet received when not in Page state," " or paging to a different address"); return; } INFO(id_, "Received Page Reject packet from {}", bd_addr); page_ = {}; if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( static_cast<ErrorCode>(reject.GetReason()), 0x0eff, incoming.GetSourceAddress(), bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); static_cast<ErrorCode>(reject.GetReason()), 0, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } void LinkLayerController::IncomingPageResponsePacket( model::packets::LinkLayerPacketView incoming) { Address peer = incoming.GetSourceAddress(); INFO(id_, "{}", peer); uint16_t handle = connections_.CreateConnection(peer, incoming.GetDestinationAddress()); if (handle == kReservedHandle) { WARNING(id_, "No free handles"); auto bd_addr = incoming.GetSourceAddress(); auto response = model::packets::PageResponseView::Create(incoming); ASSERT(response.IsValid()); if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "ignoring Page Response packet received when not in Page state," " or paging to a different address"); return; } CancelScheduledTask(page_timeout_task_id_); ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(peer.data()))); CheckExpiringConnection(handle); INFO(id_, "Received Page Response packet from {}", bd_addr); AclConnection& connection = connections_.GetAclConnection(handle); auto bd_addr = incoming.GetSourceAddress(); auto response = model::packets::PageResponseView::Create(incoming); ASSERT(response.IsValid()); uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress(), false); ASSERT(connection_handle != kReservedHandle); bluetooth::hci::Role role = connections_.IsRoleSwitchAllowedForPendingConnection() && response.GetTryRoleSwitch() page_->allow_role_switch && response.GetTryRoleSwitch() ? bluetooth::hci::Role::PERIPHERAL : bluetooth::hci::Role::CENTRAL; AclConnection& connection = connections_.GetAclConnection(connection_handle); CheckExpiringConnection(connection_handle); connection.SetLinkPolicySettings(default_link_policy_settings_); connection.SetRole(role); page_ = {}; ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); // Role change event before connection complete generates an HCI Role Change // event on the initiator side if accepted; the event is sent before the Loading @@ -5102,13 +5115,15 @@ void LinkLayerController::IncomingPageResponsePacket( if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } void LinkLayerController::Tick() { RunPendingTasks(); Paging(); if (inquiry_timer_task_id_ != kInvalidTaskId) { Inquiry(); } Loading Loading @@ -5222,11 +5237,13 @@ ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, link_parameters.extended)); // Schedule HCI Connection Complete event. if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { ScheduleTask(kNoDelayMs, [this, status, sco_handle, bd_addr]() { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode(status), sco_handle, bd_addr, bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED)); ErrorCode(status), sco_handle, bd_addr, bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED)); }); } return ErrorCode::SUCCESS; } Loading @@ -5237,10 +5254,6 @@ ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, bool try_role_switch) { INFO(id_, "Sending page response to {}", bd_addr); SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( GetAddress(), bd_addr, try_role_switch)); uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); if (connection_handle == kReservedHandle) { Loading @@ -5248,21 +5261,19 @@ void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, return; } ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); CheckExpiringConnection(connection_handle); bluetooth::hci::Role role = try_role_switch && connections_.IsRoleSwitchAllowedForPendingConnection() ? bluetooth::hci::Role::CENTRAL : bluetooth::hci::Role::PERIPHERAL; AclConnection& connection = connections_.GetAclConnection(connection_handle); CheckExpiringConnection(connection_handle); connection.SetLinkPolicySettings(default_link_policy_settings_); connection.SetRole(role); ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); // Role change event before connection complete generates an HCI Role Change // event on the acceptor side if accepted; the event is sent before the // HCI Connection Complete event. Loading @@ -5273,12 +5284,33 @@ void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, bd_addr, role)); } INFO(id_, "CreateConnection returned handle 0x{:x}", connection_handle); if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } // If the current Host was initiating a connection to the same bd_addr, // send a connection complete event for the pending Create Connection // command and cancel the paging. if (page_.has_value() && page_->bd_addr == bd_addr) { // TODO: the core specification is very unclear as to what behavior // is expected when two connections are established simultaneously. // This implementation considers that an HCI Connection Complete // event is expected for both the HCI Create Connection and HCI Accept // Connection Request commands. Both events are sent with the status // for success. if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } page_ = {}; } INFO(id_, "Sending page response to {}", bd_addr.ToString()); SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( GetAddress(), bd_addr, try_role_switch)); } ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr, Loading Loading @@ -5308,36 +5340,61 @@ void LinkLayerController::RejectPeripheralConnection(const Address& addr, } } ErrorCode LinkLayerController::CreateConnection(const Address& addr, ErrorCode LinkLayerController::CreateConnection(const Address& bd_addr, uint16_t /* packet_type */, uint8_t /* page_scan_mode */, uint16_t /* clock_offset */, uint8_t allow_role_switch) { if (!connections_.CreatePendingConnection( addr, authentication_enable_ == AuthenticationEnable::REQUIRED, allow_role_switch)) { return ErrorCode::CONTROLLER_BUSY; // RootCanal only accepts one pending outgoing connection at any time. if (page_.has_value()) { INFO(id_, "Create Connection command is already pending"); return ErrorCode::COMMAND_DISALLOWED; } page_timeout_task_id_ = ScheduleTask( duration_cast<milliseconds>(page_timeout_ * microseconds(625)), [this, addr] { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::PAGE_TIMEOUT, 0xeff, addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); }); // Reject the command if a connection or pending connection already exists // for the selected peer address. if (connections_.HasPendingConnection(bd_addr) || connections_.GetAclConnectionHandle(bd_addr).has_value()) { INFO(id_, "Connection with {} already exists", bd_addr.ToString()); return ErrorCode::CONNECTION_ALREADY_EXISTS; } SendLinkLayerPacket(model::packets::PageBuilder::Create( GetAddress(), addr, class_of_device_, allow_role_switch)); auto now = std::chrono::steady_clock::now(); page_ = Page{ .bd_addr = bd_addr, .allow_role_switch = allow_role_switch, .next_page_event = now + kPageInterval, .page_timeout = now + slots(page_timeout_), }; return ErrorCode::SUCCESS; } ErrorCode LinkLayerController::CreateConnectionCancel(const Address& addr) { if (!connections_.CancelPendingConnection(addr)) { ErrorCode LinkLayerController::CreateConnectionCancel(const Address& bd_addr) { // If the HCI_Create_Connection_Cancel command is sent to the Controller // without a preceding HCI_Create_Connection command to the same device, // the BR/EDR Controller shall return an HCI_Command_Complete event with // the error code Unknown Connection Identifier (0x02) if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "no pending connection to {}", bd_addr.ToString()); return ErrorCode::UNKNOWN_CONNECTION; } CancelScheduledTask(page_timeout_task_id_); // The HCI_Connection_Complete event for the corresponding HCI_Create_- // Connection command shall always be sent. The HCI_Connection_Complete // event shall be sent after the HCI_Command_Complete event for the // HCI_Create_Connection_Cancel command. If the cancellation was successful, // the HCI_Connection_Complete event will be generated with the error code // Unknown Connection Identifier (0x02). if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { ScheduleTask(kNoDelayMs, [this, bd_addr]() { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::UNKNOWN_CONNECTION, 0, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); }); } page_ = {}; return ErrorCode::SUCCESS; } Loading Loading @@ -5972,20 +6029,45 @@ void LinkLayerController::Reset() { current_iac_lap_list_.clear(); current_iac_lap_list_.emplace_back(general_iac); page_ = {}; if (inquiry_timer_task_id_ != kInvalidTaskId) { CancelScheduledTask(inquiry_timer_task_id_); inquiry_timer_task_id_ = kInvalidTaskId; } if (page_timeout_task_id_ != kInvalidTaskId) { CancelScheduledTask(page_timeout_task_id_); page_timeout_task_id_ = kInvalidTaskId; } lm_.reset(link_manager_create(controller_ops_)); ll_.reset(link_layer_create(controller_ops_)); } /// Drive the logic for the Page controller substate. void LinkLayerController::Paging() { auto now = std::chrono::steady_clock::now(); if (page_.has_value() && now >= page_->page_timeout) { INFO("page timeout triggered for connection with {}", page_->bd_addr.ToString()); send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::PAGE_TIMEOUT, 0, page_->bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); page_ = {}; return; } // Send a Page packet to the peer when a paging interval has passed. // Paging is suppressed while a pending connection with the same peer is // being established (i.e. two hosts initiated a connection simultaneously). if (page_.has_value() && now >= page_->next_page_event && !connections_.HasPendingConnection(page_->bd_addr)) { SendLinkLayerPacket(model::packets::PageBuilder::Create( GetAddress(), page_->bd_addr, class_of_device_, page_->allow_role_switch)); page_->next_page_event = now + kPageInterval; } } void LinkLayerController::StartInquiry(milliseconds timeout) { inquiry_timer_task_id_ = ScheduleTask(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); Loading Loading
android/pandora/mmi2grpc/mmi2grpc/hfp.py +2 −1 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ class HFPProxy(ProfileProxy): th.start() def test_started(self, test: str, pts_addr: bytes, **kwargs): if test not in ("HFP/AG/SLC/BV-02-C", "HFP/AG/SLC/BV-04-C"): self.asyncWaitConnection(pts_addr) return "OK" Loading
tools/rootcanal/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -316,6 +316,7 @@ python_test_host { "test/LL/DDI/ADV/*.py", "test/LL/DDI/SCN/*.py", "test/LMP/LIH/*.py", "test/LMP/*.py", "test/main.py", ], data: [ Loading
tools/rootcanal/model/controller/acl_connection_handler.cc +15 −4 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ uint16_t AclConnectionHandler::GetUnusedHandle() { bool AclConnectionHandler::CreatePendingConnection(Address addr, bool authenticate_on_connect, bool allow_role_switch) { if (classic_connection_pending_) { if (classic_connection_pending_ || GetAclConnectionHandle(addr).has_value()) { return false; } classic_connection_pending_ = true; Loading Loading @@ -132,9 +132,9 @@ bool AclConnectionHandler::CancelPendingLeConnection(AddressWithType addr) { return true; } uint16_t AclConnectionHandler::CreateConnection(Address addr, Address own_addr) { if (CancelPendingConnection(addr)) { uint16_t AclConnectionHandler::CreateConnection(Address addr, Address own_addr, bool pending) { if (!pending || CancelPendingConnection(addr)) { uint16_t handle = GetUnusedHandle(); acl_connections_.emplace( handle, Loading Loading @@ -199,6 +199,17 @@ uint16_t AclConnectionHandler::GetHandleOnlyAddress( return kReservedHandle; } std::optional<uint16_t> AclConnectionHandler::GetAclConnectionHandle( bluetooth::hci::Address bd_addr) const { for (auto const& [handle, connection] : acl_connections_) { if (connection.GetAddress().GetAddress() == bd_addr && connection.GetPhyType() == Phy::Type::BR_EDR) { return handle; } } return {}; } AclConnection& AclConnectionHandler::GetAclConnection(uint16_t handle) { ASSERT_LOG(HasHandle(handle), "Unknown handle %d", handle); return acl_connections_.at(handle); Loading
tools/rootcanal/model/controller/acl_connection_handler.h +9 −1 Original line number Diff line number Diff line Loading @@ -75,8 +75,11 @@ class AclConnectionHandler { bool HasPendingLeConnection(bluetooth::hci::AddressWithType addr) const; bool CancelPendingLeConnection(bluetooth::hci::AddressWithType addr); // \p pending is true if the connection is expected to be // in pending state. uint16_t CreateConnection(bluetooth::hci::Address addr, bluetooth::hci::Address own_addr); bluetooth::hci::Address own_addr, bool pending = true); uint16_t CreateLeConnection(bluetooth::hci::AddressWithType addr, bluetooth::hci::AddressWithType own_addr, bluetooth::hci::Role role); Loading @@ -84,6 +87,11 @@ class AclConnectionHandler { bool HasHandle(uint16_t handle) const; bool HasScoHandle(uint16_t handle) const; // Return the connection handle for a classic ACL connection only. // \p bd_addr is the peer address. std::optional<uint16_t> GetAclConnectionHandle( bluetooth::hci::Address bd_addr) const; uint16_t GetHandle(bluetooth::hci::AddressWithType addr) const; uint16_t GetHandleOnlyAddress(bluetooth::hci::Address addr) const; bluetooth::hci::AddressWithType GetAddress(uint16_t handle) const; Loading
tools/rootcanal/model/controller/link_layer_controller.cc +155 −73 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ using TaskId = rootcanal::LinkLayerController::TaskId; namespace rootcanal { constexpr milliseconds kNoDelayMs(0); constexpr milliseconds kPageInterval(1000); const Address& LinkLayerController::GetAddress() const { return address_; } Loading Loading @@ -5021,75 +5022,87 @@ void LinkLayerController::LeSynchronization() { void LinkLayerController::IncomingPagePacket( model::packets::LinkLayerPacketView incoming) { auto bd_addr = incoming.GetSourceAddress(); auto page = model::packets::PageView::Create(incoming); ASSERT(page.IsValid()); INFO(id_, "from {}", incoming.GetSourceAddress()); // Cannot establish two BR-EDR connections with the same peer. if (connections_.GetAclConnectionHandle(bd_addr).has_value()) { return; } bool allow_role_switch = page.GetAllowRoleSwitch(); if (!connections_.CreatePendingConnection( incoming.GetSourceAddress(), authentication_enable_ == AuthenticationEnable::REQUIRED, bd_addr, authentication_enable_ == AuthenticationEnable::REQUIRED, allow_role_switch)) { // Send a response to indicate that we're busy, or drop the packet? WARNING(id_, "Failed to create a pending connection for {}", incoming.GetSourceAddress()); // Will be triggered when multiple hosts are paging simultaneously; // only one connection will be accepted. WARNING(id_, "Failed to create a pending connection for {}", bd_addr); return; } bluetooth::hci::Address source_address{}; bluetooth::hci::Address::FromString(page.GetSourceAddress().ToString(), source_address); if (IsEventUnmasked(EventCode::CONNECTION_REQUEST)) { send_event_(bluetooth::hci::ConnectionRequestBuilder::Create( source_address, page.GetClassOfDevice(), bd_addr, page.GetClassOfDevice(), bluetooth::hci::ConnectionRequestLinkType::ACL)); } } void LinkLayerController::IncomingPageRejectPacket( model::packets::LinkLayerPacketView incoming) { INFO(id_, "{}", incoming.GetSourceAddress()); auto bd_addr = incoming.GetSourceAddress(); auto reject = model::packets::PageRejectView::Create(incoming); ASSERT(reject.IsValid()); INFO(id_, "Sending CreateConnectionComplete"); if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "ignoring Page Reject packet received when not in Page state," " or paging to a different address"); return; } INFO(id_, "Received Page Reject packet from {}", bd_addr); page_ = {}; if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( static_cast<ErrorCode>(reject.GetReason()), 0x0eff, incoming.GetSourceAddress(), bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); static_cast<ErrorCode>(reject.GetReason()), 0, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } void LinkLayerController::IncomingPageResponsePacket( model::packets::LinkLayerPacketView incoming) { Address peer = incoming.GetSourceAddress(); INFO(id_, "{}", peer); uint16_t handle = connections_.CreateConnection(peer, incoming.GetDestinationAddress()); if (handle == kReservedHandle) { WARNING(id_, "No free handles"); auto bd_addr = incoming.GetSourceAddress(); auto response = model::packets::PageResponseView::Create(incoming); ASSERT(response.IsValid()); if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "ignoring Page Response packet received when not in Page state," " or paging to a different address"); return; } CancelScheduledTask(page_timeout_task_id_); ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(peer.data()))); CheckExpiringConnection(handle); INFO(id_, "Received Page Response packet from {}", bd_addr); AclConnection& connection = connections_.GetAclConnection(handle); auto bd_addr = incoming.GetSourceAddress(); auto response = model::packets::PageResponseView::Create(incoming); ASSERT(response.IsValid()); uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress(), false); ASSERT(connection_handle != kReservedHandle); bluetooth::hci::Role role = connections_.IsRoleSwitchAllowedForPendingConnection() && response.GetTryRoleSwitch() page_->allow_role_switch && response.GetTryRoleSwitch() ? bluetooth::hci::Role::PERIPHERAL : bluetooth::hci::Role::CENTRAL; AclConnection& connection = connections_.GetAclConnection(connection_handle); CheckExpiringConnection(connection_handle); connection.SetLinkPolicySettings(default_link_policy_settings_); connection.SetRole(role); page_ = {}; ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); // Role change event before connection complete generates an HCI Role Change // event on the initiator side if accepted; the event is sent before the Loading @@ -5102,13 +5115,15 @@ void LinkLayerController::IncomingPageResponsePacket( if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } } void LinkLayerController::Tick() { RunPendingTasks(); Paging(); if (inquiry_timer_task_id_ != kInvalidTaskId) { Inquiry(); } Loading Loading @@ -5222,11 +5237,13 @@ ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, link_parameters.extended)); // Schedule HCI Connection Complete event. if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { ScheduleTask(kNoDelayMs, [this, status, sco_handle, bd_addr]() { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode(status), sco_handle, bd_addr, bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED)); ErrorCode(status), sco_handle, bd_addr, bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED)); }); } return ErrorCode::SUCCESS; } Loading @@ -5237,10 +5254,6 @@ ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr, void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, bool try_role_switch) { INFO(id_, "Sending page response to {}", bd_addr); SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( GetAddress(), bd_addr, try_role_switch)); uint16_t connection_handle = connections_.CreateConnection(bd_addr, GetAddress()); if (connection_handle == kReservedHandle) { Loading @@ -5248,21 +5261,19 @@ void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, return; } ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); CheckExpiringConnection(connection_handle); bluetooth::hci::Role role = try_role_switch && connections_.IsRoleSwitchAllowedForPendingConnection() ? bluetooth::hci::Role::CENTRAL : bluetooth::hci::Role::PERIPHERAL; AclConnection& connection = connections_.GetAclConnection(connection_handle); CheckExpiringConnection(connection_handle); connection.SetLinkPolicySettings(default_link_policy_settings_); connection.SetRole(role); ASSERT(link_manager_add_link( lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(bd_addr.data()))); // Role change event before connection complete generates an HCI Role Change // event on the acceptor side if accepted; the event is sent before the // HCI Connection Complete event. Loading @@ -5273,12 +5284,33 @@ void LinkLayerController::MakePeripheralConnection(const Address& bd_addr, bd_addr, role)); } INFO(id_, "CreateConnection returned handle 0x{:x}", connection_handle); if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } // If the current Host was initiating a connection to the same bd_addr, // send a connection complete event for the pending Create Connection // command and cancel the paging. if (page_.has_value() && page_->bd_addr == bd_addr) { // TODO: the core specification is very unclear as to what behavior // is expected when two connections are established simultaneously. // This implementation considers that an HCI Connection Complete // event is expected for both the HCI Create Connection and HCI Accept // Connection Request commands. Both events are sent with the status // for success. if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::SUCCESS, connection_handle, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); } page_ = {}; } INFO(id_, "Sending page response to {}", bd_addr.ToString()); SendLinkLayerPacket(model::packets::PageResponseBuilder::Create( GetAddress(), bd_addr, try_role_switch)); } ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr, Loading Loading @@ -5308,36 +5340,61 @@ void LinkLayerController::RejectPeripheralConnection(const Address& addr, } } ErrorCode LinkLayerController::CreateConnection(const Address& addr, ErrorCode LinkLayerController::CreateConnection(const Address& bd_addr, uint16_t /* packet_type */, uint8_t /* page_scan_mode */, uint16_t /* clock_offset */, uint8_t allow_role_switch) { if (!connections_.CreatePendingConnection( addr, authentication_enable_ == AuthenticationEnable::REQUIRED, allow_role_switch)) { return ErrorCode::CONTROLLER_BUSY; // RootCanal only accepts one pending outgoing connection at any time. if (page_.has_value()) { INFO(id_, "Create Connection command is already pending"); return ErrorCode::COMMAND_DISALLOWED; } page_timeout_task_id_ = ScheduleTask( duration_cast<milliseconds>(page_timeout_ * microseconds(625)), [this, addr] { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::PAGE_TIMEOUT, 0xeff, addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); }); // Reject the command if a connection or pending connection already exists // for the selected peer address. if (connections_.HasPendingConnection(bd_addr) || connections_.GetAclConnectionHandle(bd_addr).has_value()) { INFO(id_, "Connection with {} already exists", bd_addr.ToString()); return ErrorCode::CONNECTION_ALREADY_EXISTS; } SendLinkLayerPacket(model::packets::PageBuilder::Create( GetAddress(), addr, class_of_device_, allow_role_switch)); auto now = std::chrono::steady_clock::now(); page_ = Page{ .bd_addr = bd_addr, .allow_role_switch = allow_role_switch, .next_page_event = now + kPageInterval, .page_timeout = now + slots(page_timeout_), }; return ErrorCode::SUCCESS; } ErrorCode LinkLayerController::CreateConnectionCancel(const Address& addr) { if (!connections_.CancelPendingConnection(addr)) { ErrorCode LinkLayerController::CreateConnectionCancel(const Address& bd_addr) { // If the HCI_Create_Connection_Cancel command is sent to the Controller // without a preceding HCI_Create_Connection command to the same device, // the BR/EDR Controller shall return an HCI_Command_Complete event with // the error code Unknown Connection Identifier (0x02) if (!page_.has_value() || page_->bd_addr != bd_addr) { INFO(id_, "no pending connection to {}", bd_addr.ToString()); return ErrorCode::UNKNOWN_CONNECTION; } CancelScheduledTask(page_timeout_task_id_); // The HCI_Connection_Complete event for the corresponding HCI_Create_- // Connection command shall always be sent. The HCI_Connection_Complete // event shall be sent after the HCI_Command_Complete event for the // HCI_Create_Connection_Cancel command. If the cancellation was successful, // the HCI_Connection_Complete event will be generated with the error code // Unknown Connection Identifier (0x02). if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) { ScheduleTask(kNoDelayMs, [this, bd_addr]() { send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::UNKNOWN_CONNECTION, 0, bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); }); } page_ = {}; return ErrorCode::SUCCESS; } Loading Loading @@ -5972,20 +6029,45 @@ void LinkLayerController::Reset() { current_iac_lap_list_.clear(); current_iac_lap_list_.emplace_back(general_iac); page_ = {}; if (inquiry_timer_task_id_ != kInvalidTaskId) { CancelScheduledTask(inquiry_timer_task_id_); inquiry_timer_task_id_ = kInvalidTaskId; } if (page_timeout_task_id_ != kInvalidTaskId) { CancelScheduledTask(page_timeout_task_id_); page_timeout_task_id_ = kInvalidTaskId; } lm_.reset(link_manager_create(controller_ops_)); ll_.reset(link_layer_create(controller_ops_)); } /// Drive the logic for the Page controller substate. void LinkLayerController::Paging() { auto now = std::chrono::steady_clock::now(); if (page_.has_value() && now >= page_->page_timeout) { INFO("page timeout triggered for connection with {}", page_->bd_addr.ToString()); send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create( ErrorCode::PAGE_TIMEOUT, 0, page_->bd_addr, bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED)); page_ = {}; return; } // Send a Page packet to the peer when a paging interval has passed. // Paging is suppressed while a pending connection with the same peer is // being established (i.e. two hosts initiated a connection simultaneously). if (page_.has_value() && now >= page_->next_page_event && !connections_.HasPendingConnection(page_->bd_addr)) { SendLinkLayerPacket(model::packets::PageBuilder::Create( GetAddress(), page_->bd_addr, class_of_device_, page_->allow_role_switch)); page_->next_page_event = now + kPageInterval; } } void LinkLayerController::StartInquiry(milliseconds timeout) { inquiry_timer_task_id_ = ScheduleTask(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); Loading