packages/modules/Bluetooth/tools/rootcanal/model/controller/link_layer_controller.cc

6425 lines
250 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright 2017 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 "link_layer_controller.h"
#include <hci/hci_packets.h>
#ifdef ROOTCANAL_LMP
#include <lmp.h>
#endif /* ROOTCANAL_LMP */
#include "crypto_toolbox/crypto_toolbox.h"
#include "log.h"
#include "packet/raw_builder.h"
using std::vector;
using namespace std::chrono;
using bluetooth::hci::Address;
using bluetooth::hci::AddressType;
using bluetooth::hci::AddressWithType;
using bluetooth::hci::DirectAdvertisingAddressType;
using bluetooth::hci::EventCode;
using bluetooth::hci::LLFeaturesBits;
using bluetooth::hci::SubeventCode;
using namespace model::packets;
using model::packets::PacketType;
using namespace std::literals;
// Temporay define, to be replaced when verbose log level is implemented.
#define LOG_VERB(...) LOG_INFO(__VA_ARGS__)
namespace rootcanal {
constexpr uint16_t kNumCommandPackets = 0x01;
constexpr milliseconds kNoDelayMs(0);
// TODO: Model Rssi?
static uint8_t GetRssi() {
static uint8_t rssi = 0;
rssi += 5;
if (rssi > 128) {
rssi = rssi % 7;
}
return -(rssi);
}
const Address& LinkLayerController::GetAddress() const { return address_; }
AddressWithType PeerDeviceAddress(Address address,
PeerAddressType peer_address_type) {
switch (peer_address_type) {
case PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
return AddressWithType(address, AddressType::PUBLIC_DEVICE_ADDRESS);
case PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
return AddressWithType(address, AddressType::RANDOM_DEVICE_ADDRESS);
}
}
AddressWithType PeerIdentityAddress(Address address,
PeerAddressType peer_address_type) {
switch (peer_address_type) {
case PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS:
return AddressWithType(address, AddressType::PUBLIC_IDENTITY_ADDRESS);
case PeerAddressType::RANDOM_DEVICE_OR_IDENTITY_ADDRESS:
return AddressWithType(address, AddressType::RANDOM_IDENTITY_ADDRESS);
}
}
bool LinkLayerController::IsEventUnmasked(EventCode event) const {
uint64_t bit = UINT64_C(1) << (static_cast<uint8_t>(event) - 1);
return (event_mask_ & bit) != 0;
}
bool LinkLayerController::IsLeEventUnmasked(SubeventCode subevent) const {
uint64_t bit = UINT64_C(1) << (static_cast<uint8_t>(subevent) - 1);
return IsEventUnmasked(EventCode::LE_META_EVENT) &&
(le_event_mask_ & bit) != 0;
}
bool LinkLayerController::FilterAcceptListBusy() {
// Filter Accept List cannot be modified when
// • any advertising filter policy uses the Filter Accept List and
// advertising is enabled,
if (legacy_advertiser_.IsEnabled() &&
legacy_advertiser_.advertising_filter_policy !=
bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES) {
return true;
}
for (auto const& [_, advertiser] : extended_advertisers_) {
if (advertiser.IsEnabled() &&
advertiser.advertising_filter_policy !=
bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES) {
return true;
}
}
// • the scanning filter policy uses the Filter Accept List and scanning
// is enabled,
if (scanner_.IsEnabled() &&
(scanner_.scan_filter_policy ==
bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY ||
scanner_.scan_filter_policy ==
bluetooth::hci::LeScanningFilterPolicy::
FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY)) {
return true;
}
// • the initiator filter policy uses the Filter Accept List and an
// HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection
// command is pending.
if (initiator_.IsEnabled() &&
initiator_.initiator_filter_policy ==
bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST) {
return true;
}
return false;
}
bool LinkLayerController::LeFilterAcceptListContainsDevice(
FilterAcceptListAddressType address_type, Address address) {
for (auto const& entry : le_filter_accept_list_) {
if (entry.address_type == address_type &&
(address_type == FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS ||
entry.address == address)) {
return true;
}
}
return false;
}
bool LinkLayerController::LeFilterAcceptListContainsDevice(
AddressWithType address) {
FilterAcceptListAddressType address_type;
switch (address.GetAddressType()) {
case AddressType::PUBLIC_DEVICE_ADDRESS:
case AddressType::PUBLIC_IDENTITY_ADDRESS:
address_type = FilterAcceptListAddressType::PUBLIC;
break;
case AddressType::RANDOM_DEVICE_ADDRESS:
case AddressType::RANDOM_IDENTITY_ADDRESS:
address_type = FilterAcceptListAddressType::RANDOM;
break;
}
return LeFilterAcceptListContainsDevice(address_type, address.GetAddress());
}
bool LinkLayerController::ResolvingListBusy() {
// The resolving list cannot be modified when
// • Advertising (other than periodic advertising) is enabled,
if (legacy_advertiser_.IsEnabled()) {
return true;
}
for (auto const& [_, advertiser] : extended_advertisers_) {
if (advertiser.IsEnabled()) {
return true;
}
}
// • Scanning is enabled,
if (scanner_.IsEnabled()) {
return true;
}
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (initiator_.IsEnabled()) {
return true;
}
return false;
}
std::optional<AddressWithType> LinkLayerController::ResolvePrivateAddress(
AddressWithType address, IrkSelection irk) {
if (!address.IsRpa()) {
return address;
}
if (!le_resolving_list_enabled_) {
return {};
}
for (auto const& entry : le_resolving_list_) {
std::array<uint8_t, LinkLayerController::kIrkSize> const& used_irk =
irk == IrkSelection::Local ? entry.local_irk : entry.peer_irk;
if (address.IsRpaThatMatchesIrk(used_irk)) {
return PeerIdentityAddress(entry.peer_identity_address,
entry.peer_identity_address_type);
}
}
return {};
}
static Address generate_rpa(
std::array<uint8_t, LinkLayerController::kIrkSize> irk);
std::optional<AddressWithType>
LinkLayerController::GenerateResolvablePrivateAddress(AddressWithType address,
IrkSelection irk) {
if (!le_resolving_list_enabled_) {
return {};
}
for (auto const& entry : le_resolving_list_) {
if (address.GetAddress() == entry.peer_identity_address &&
address.ToPeerAddressType() == entry.peer_identity_address_type) {
std::array<uint8_t, LinkLayerController::kIrkSize> const& used_irk =
irk == IrkSelection::Local ? entry.local_irk : entry.peer_irk;
return AddressWithType{generate_rpa(used_irk),
AddressType::RANDOM_DEVICE_ADDRESS};
}
}
return {};
}
// =============================================================================
// General LE Commands
// =============================================================================
// HCI LE Set Random Address command (Vol 4, Part E § 7.8.4).
ErrorCode LinkLayerController::LeSetRandomAddress(Address random_address) {
// If the Host issues this command when any of advertising (created using
// legacy advertising commands), scanning, or initiating are enabled,
// the Controller shall return the error code Command Disallowed (0x0C).
if (legacy_advertiser_.IsEnabled() || scanner_.IsEnabled() ||
initiator_.IsEnabled()) {
LOG_INFO("advertising, scanning or initiating are currently active");
return ErrorCode::COMMAND_DISALLOWED;
}
if (random_address == Address::kEmpty) {
LOG_INFO("the random address may not be set to 00:00:00:00:00:00");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
LOG_INFO("device random address configured to %s",
random_address.ToString().c_str());
random_address_ = random_address;
return ErrorCode::SUCCESS;
}
// HCI LE Set Host Feature command (Vol 4, Part E § 7.8.45).
ErrorCode LinkLayerController::LeSetResolvablePrivateAddressTimeout(
uint16_t rpa_timeout) {
// Note: no documented status code for this case.
if (rpa_timeout < 0x1 || rpa_timeout > 0x0e10) {
LOG_INFO(
"rpa_timeout (0x%04x) is outside the range of supported values "
" 0x1 - 0x0e10",
rpa_timeout);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
resolvable_private_address_timeout_ = seconds(rpa_timeout);
return ErrorCode::SUCCESS;
}
// HCI LE Set Host Feature command (Vol 4, Part E § 7.8.115).
ErrorCode LinkLayerController::LeSetHostFeature(uint8_t bit_number,
uint8_t bit_value) {
if (bit_number >= 64 || bit_value > 1) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If Bit_Value is set to 0x01 and Bit_Number specifies a feature bit that
// requires support of a feature that the Controller does not support,
// the Controller shall return the error code Unsupported Feature or
// Parameter Value (0x11).
// TODO
// If the Host issues this command while the Controller has a connection to
// another device, the Controller shall return the error code
// Command Disallowed (0x0C).
if (HasAclConnection()) {
return ErrorCode::COMMAND_DISALLOWED;
}
uint64_t bit_mask = UINT64_C(1) << bit_number;
if (bit_mask ==
static_cast<uint64_t>(
LLFeaturesBits::CONNECTED_ISOCHRONOUS_STREAM_HOST_SUPPORT)) {
connected_isochronous_stream_host_support_ = bit_value != 0;
} else if (bit_mask ==
static_cast<uint64_t>(
LLFeaturesBits::CONNECTION_SUBRATING_HOST_SUPPORT)) {
connection_subrating_host_support_ = bit_value != 0;
}
// If Bit_Number specifies a feature bit that is not controlled by the Host,
// the Controller shall return the error code Unsupported Feature or
// Parameter Value (0x11).
else {
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
if (bit_value != 0) {
le_host_supported_features_ |= bit_mask;
} else {
le_host_supported_features_ &= ~bit_mask;
}
return ErrorCode::SUCCESS;
}
// =============================================================================
// LE Resolving List
// =============================================================================
// HCI command LE_Add_Device_To_Resolving_List (Vol 4, Part E § 7.8.38).
ErrorCode LinkLayerController::LeAddDeviceToResolvingList(
PeerAddressType peer_identity_address_type, Address peer_identity_address,
std::array<uint8_t, kIrkSize> peer_irk,
std::array<uint8_t, kIrkSize> local_irk) {
// This command shall not be used when address resolution is enabled in the
// Controller and:
// • Advertising (other than periodic advertising) is enabled,
// • Scanning is enabled, or
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (le_resolving_list_enabled_ && ResolvingListBusy()) {
LOG_INFO(
"device is currently advertising, scanning, or establishing an"
" LE connection");
return ErrorCode::COMMAND_DISALLOWED;
}
// When a Controller cannot add a device to the list because there is no space
// available, it shall return the error code Memory Capacity Exceeded (0x07).
if (le_resolving_list_.size() >= properties_.le_resolving_list_size) {
LOG_INFO("resolving list is full");
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
// If there is an existing entry in the resolving list with the same
// Peer_Identity_Address and Peer_Identity_Address_Type, or with the same
// Peer_IRK, the Controller should return the error code Invalid HCI Command
// Parameters (0x12).
for (auto const& entry : le_resolving_list_) {
if ((entry.peer_identity_address_type == peer_identity_address_type &&
entry.peer_identity_address == peer_identity_address) ||
entry.peer_irk == peer_irk) {
LOG_INFO("device is already present in the resolving list");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
}
le_resolving_list_.emplace_back(
ResolvingListEntry{peer_identity_address_type, peer_identity_address,
peer_irk, local_irk, PrivacyMode::NETWORK});
return ErrorCode::SUCCESS;
}
// HCI command LE_Remove_Device_From_Resolving_List (Vol 4, Part E § 7.8.39).
ErrorCode LinkLayerController::LeRemoveDeviceFromResolvingList(
PeerAddressType peer_identity_address_type, Address peer_identity_address) {
// This command shall not be used when address resolution is enabled in the
// Controller and:
// • Advertising (other than periodic advertising) is enabled,
// • Scanning is enabled, or
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (le_resolving_list_enabled_ && ResolvingListBusy()) {
LOG_INFO(
"device is currently advertising, scanning, or establishing an"
" LE connection");
return ErrorCode::COMMAND_DISALLOWED;
}
for (auto it = le_resolving_list_.begin(); it != le_resolving_list_.end();
it++) {
if (it->peer_identity_address_type == peer_identity_address_type &&
it->peer_identity_address == peer_identity_address) {
le_resolving_list_.erase(it);
return ErrorCode::SUCCESS;
}
}
// When a Controller cannot remove a device from the resolving list because
// it is not found, it shall return the error code
// Unknown Connection Identifier (0x02).
LOG_INFO("peer address not found in the resolving list");
return ErrorCode::UNKNOWN_CONNECTION;
}
// HCI command LE_Clear_Resolving_List (Vol 4, Part E § 7.8.40).
ErrorCode LinkLayerController::LeClearResolvingList() {
// This command shall not be used when address resolution is enabled in the
// Controller and:
// • Advertising (other than periodic advertising) is enabled,
// • Scanning is enabled, or
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (le_resolving_list_enabled_ && ResolvingListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection");
return ErrorCode::COMMAND_DISALLOWED;
}
le_resolving_list_.clear();
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Address_Resolution_Enable (Vol 4, Part E § 7.8.44).
ErrorCode LinkLayerController::LeSetAddressResolutionEnable(bool enable) {
// This command shall not be used when:
// • Advertising (other than periodic advertising) is enabled,
// • Scanning is enabled, or
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (ResolvingListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection");
return ErrorCode::COMMAND_DISALLOWED;
}
le_resolving_list_enabled_ = enable;
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Privacy_Mode (Vol 4, Part E § 7.8.77).
ErrorCode LinkLayerController::LeSetPrivacyMode(
PeerAddressType peer_identity_address_type, Address peer_identity_address,
bluetooth::hci::PrivacyMode privacy_mode) {
// This command shall not be used when address resolution is enabled in the
// Controller and:
// • Advertising (other than periodic advertising) is enabled,
// • Scanning is enabled, or
// • an HCI_LE_Create_Connection, HCI_LE_Extended_Create_Connection, or
// HCI_LE_Periodic_Advertising_Create_Sync command is pending.
if (le_resolving_list_enabled_ && ResolvingListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection");
return ErrorCode::COMMAND_DISALLOWED;
}
for (auto& entry : le_resolving_list_) {
if (entry.peer_identity_address_type == peer_identity_address_type &&
entry.peer_identity_address == peer_identity_address) {
entry.privacy_mode = privacy_mode;
return ErrorCode::SUCCESS;
}
}
// If the device is not on the resolving list, the Controller shall return
// the error code Unknown Connection Identifier (0x02).
LOG_INFO("peer address not found in the resolving list");
return ErrorCode::UNKNOWN_CONNECTION;
}
// =============================================================================
// LE Filter Accept List
// =============================================================================
// HCI command LE_Clear_Filter_Accept_List (Vol 4, Part E § 7.8.15).
ErrorCode LinkLayerController::LeClearFilterAcceptList() {
// This command shall not be used when:
// • any advertising filter policy uses the Filter Accept List and
// advertising is enabled,
// • the scanning filter policy uses the Filter Accept List and scanning
// is enabled, or
// • the initiator filter policy uses the Filter Accept List and an
// HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection
// command is pending.
if (FilterAcceptListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection using the filter accept list");
return ErrorCode::COMMAND_DISALLOWED;
}
le_filter_accept_list_.clear();
return ErrorCode::SUCCESS;
}
// HCI command LE_Add_Device_To_Filter_Accept_List (Vol 4, Part E § 7.8.16).
ErrorCode LinkLayerController::LeAddDeviceToFilterAcceptList(
FilterAcceptListAddressType address_type, Address address) {
// This command shall not be used when:
// • any advertising filter policy uses the Filter Accept List and
// advertising is enabled,
// • the scanning filter policy uses the Filter Accept List and scanning
// is enabled, or
// • the initiator filter policy uses the Filter Accept List and an
// HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection
// command is pending.
if (FilterAcceptListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection using the filter accept list");
return ErrorCode::COMMAND_DISALLOWED;
}
// When a Controller cannot add a device to the Filter Accept List
// because there is no space available, it shall return the error code
// Memory Capacity Exceeded (0x07).
if (le_filter_accept_list_.size() >= properties_.le_filter_accept_list_size) {
LOG_INFO("filter accept list is full");
return ErrorCode::MEMORY_CAPACITY_EXCEEDED;
}
le_filter_accept_list_.emplace_back(
FilterAcceptListEntry{address_type, address});
return ErrorCode::SUCCESS;
}
// HCI command LE_Remove_Device_From_Filter_Accept_List (Vol 4, Part E
// § 7.8.17).
ErrorCode LinkLayerController::LeRemoveDeviceFromFilterAcceptList(
FilterAcceptListAddressType address_type, Address address) {
// This command shall not be used when:
// • any advertising filter policy uses the Filter Accept List and
// advertising is enabled,
// • the scanning filter policy uses the Filter Accept List and scanning
// is enabled, or
// • the initiator filter policy uses the Filter Accept List and an
// HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection
// command is pending.
if (FilterAcceptListBusy()) {
LOG_INFO(
"device is currently advertising, scanning,"
" or establishing an LE connection using the filter accept list");
return ErrorCode::COMMAND_DISALLOWED;
}
for (auto it = le_filter_accept_list_.begin();
it != le_filter_accept_list_.end(); it++) {
// Address shall be ignored when Address_Type is set to 0xFF.
if (it->address_type == address_type &&
(address_type == FilterAcceptListAddressType::ANONYMOUS_ADVERTISERS ||
it->address == address)) {
le_filter_accept_list_.erase(it);
return ErrorCode::SUCCESS;
}
}
// Note: this case is not documented.
LOG_INFO("address not found in the filter accept list");
return ErrorCode::SUCCESS;
}
// =============================================================================
// LE Legacy Scanning
// =============================================================================
// HCI command LE_Set_Scan_Parameters (Vol 4, Part E § 7.8.10).
ErrorCode LinkLayerController::LeSetScanParameters(
bluetooth::hci::LeScanType scan_type, uint16_t scan_interval,
uint16_t scan_window, bluetooth::hci::OwnAddressType own_address_type,
bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
LOG_INFO(
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// The Host shall not issue this command when scanning is enabled in the
// Controller; if it is the Command Disallowed error code shall be used.
if (scanner_.IsEnabled()) {
LOG_INFO("scanning is currently enabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// Note: no explicit error code stated for invalid interval and window
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (scan_interval < 0x4 || scan_interval > 0x4000 || scan_window < 0x4 ||
scan_window > 0x4000) {
LOG_INFO(
"le_scan_interval (0x%04x) and/or"
" le_scan_window (0x%04x) are outside the range"
" of supported values (0x0004 - 0x4000)",
scan_interval, scan_window);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The LE_Scan_Window parameter shall always be set to a value smaller
// or equal to the value set for the LE_Scan_Interval parameter.
if (scan_window > scan_interval) {
LOG_INFO("le_scan_window (0x%04x) is larger than le_scan_interval (0x%04x)",
scan_window, scan_interval);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
scanner_.le_1m_phy.enabled = true;
scanner_.le_coded_phy.enabled = false;
scanner_.le_1m_phy.scan_type = scan_type;
scanner_.le_1m_phy.scan_interval = scan_interval;
scanner_.le_1m_phy.scan_window = scan_window;
scanner_.own_address_type = own_address_type;
scanner_.scan_filter_policy = scanning_filter_policy;
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Scan_Enable (Vol 4, Part E § 7.8.11).
ErrorCode LinkLayerController::LeSetScanEnable(bool enable,
bool filter_duplicates) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
LOG_INFO(
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
if (!enable) {
scanner_.scan_enable = false;
scanner_.history.clear();
return ErrorCode::SUCCESS;
}
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
// If LE_Scan_Enable is set to 0x01, the scanning parameters' Own_Address_Type
// parameter is set to 0x01 or 0x03, and the random address for the device
// has not been initialized using the HCI_LE_Set_Random_Address command,
// the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if ((scanner_.own_address_type ==
bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS ||
scanner_.own_address_type ==
bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Random_Device_Address or"
" Resolvable_or_Random_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
scanner_.scan_enable = true;
scanner_.history.clear();
scanner_.timeout = {};
scanner_.periodical_timeout = {};
scanner_.filter_duplicates = filter_duplicates
? bluetooth::hci::FilterDuplicates::ENABLED
: bluetooth::hci::FilterDuplicates::DISABLED;
return ErrorCode::SUCCESS;
}
// =============================================================================
// LE Extended Scanning
// =============================================================================
// HCI command LE_Set_Extended_Scan_Parameters (Vol 4, Part E § 7.8.64).
ErrorCode LinkLayerController::LeSetExtendedScanParameters(
bluetooth::hci::OwnAddressType own_address_type,
bluetooth::hci::LeScanningFilterPolicy scanning_filter_policy,
uint8_t scanning_phys,
std::vector<bluetooth::hci::PhyScanParameters> scanning_phy_parameters) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
LOG_INFO(
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host issues this command when scanning is enabled in the Controller,
// the Controller shall return the error code Command Disallowed (0x0C).
if (scanner_.IsEnabled()) {
LOG_INFO("scanning is currently enabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host specifies a PHY that is not supported by the Controller,
// including a bit that is reserved for future use, it should return the
// error code Unsupported Feature or Parameter Value (0x11).
if ((scanning_phys & 0xfa) != 0) {
LOG_INFO(
"scanning_phys (%02x) enables PHYs that are not supported by"
" the controller",
scanning_phys);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// TODO(c++20) std::popcount
if (__builtin_popcount(scanning_phys) !=
int(scanning_phy_parameters.size())) {
LOG_INFO(
"scanning_phy_parameters (%zu)"
" does not match scanning_phys (%02x)",
scanning_phy_parameters.size(), scanning_phys);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for empty scanning_phys
// but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on HCI Extended LE Create Connecton command.
if (scanning_phys == 0) {
LOG_INFO("scanning_phys is empty");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
for (auto const& parameter : scanning_phy_parameters) {
// If the requested scan cannot be supported by the implementation,
// the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (parameter.le_scan_interval_ < 0x4 || parameter.le_scan_window_ < 0x4) {
LOG_INFO(
"le_scan_interval (0x%04x) and/or"
" le_scan_window (0x%04x) are outside the range"
" of supported values (0x0004 - 0xffff)",
parameter.le_scan_interval_, parameter.le_scan_window_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
if (parameter.le_scan_window_ > parameter.le_scan_interval_) {
LOG_INFO(
"le_scan_window (0x%04x) is larger than le_scan_interval (0x%04x)",
parameter.le_scan_window_, parameter.le_scan_interval_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
}
scanner_.own_address_type = own_address_type;
scanner_.scan_filter_policy = scanning_filter_policy;
scanner_.le_1m_phy.enabled = false;
scanner_.le_coded_phy.enabled = false;
int offset = 0;
if (scanning_phys & 0x1) {
scanner_.le_1m_phy = Scanner::PhyParameters{
.enabled = true,
.scan_type = scanning_phy_parameters[offset].le_scan_type_,
.scan_interval = scanning_phy_parameters[offset].le_scan_interval_,
.scan_window = scanning_phy_parameters[offset].le_scan_window_,
};
offset++;
}
if (scanning_phys & 0x4) {
scanner_.le_coded_phy = Scanner::PhyParameters{
.enabled = true,
.scan_type = scanning_phy_parameters[offset].le_scan_type_,
.scan_interval = scanning_phy_parameters[offset].le_scan_interval_,
.scan_window = scanning_phy_parameters[offset].le_scan_window_,
};
offset++;
}
return ErrorCode::SUCCESS;
}
// HCI command LE_Set_Extended_Scan_Enable (Vol 4, Part E § 7.8.65).
ErrorCode LinkLayerController::LeSetExtendedScanEnable(
bool enable, bluetooth::hci::FilterDuplicates filter_duplicates,
uint16_t duration, uint16_t period) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
LOG_INFO(
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
if (!enable) {
scanner_.scan_enable = false;
scanner_.history.clear();
return ErrorCode::SUCCESS;
}
// The Period parameter shall be ignored when the Duration parameter is zero.
if (duration == 0) {
period = 0;
}
// If Filter_Duplicates is set to 0x02 and either Period or Duration to zero,
// the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (filter_duplicates ==
bluetooth::hci::FilterDuplicates::RESET_EACH_PERIOD &&
(period == 0 || duration == 0)) {
LOG_INFO(
"filter_duplicates is Reset_Each_Period but either"
" the period or duration is 0");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
auto duration_ms = std::chrono::milliseconds(10 * duration);
auto period_ms = std::chrono::milliseconds(1280 * period);
// If both the Duration and Period parameters are non-zero and the Duration is
// greater than or equal to the Period, the Controller shall return the
// error code Invalid HCI Command Parameters (0x12).
if (period != 0 && duration != 0 && duration_ms >= period_ms) {
LOG_INFO("the period is greater than or equal to the duration");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
// If LE_Scan_Enable is set to 0x01, the scanning parameters' Own_Address_Type
// parameter is set to 0x01 or 0x03, and the random address for the device
// has not been initialized using the HCI_LE_Set_Random_Address command,
// the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if ((scanner_.own_address_type ==
bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS ||
scanner_.own_address_type ==
bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Random_Device_Address or"
" Resolvable_or_Random_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
scanner_.scan_enable = true;
scanner_.history.clear();
scanner_.timeout = {};
scanner_.periodical_timeout = {};
scanner_.filter_duplicates = filter_duplicates;
scanner_.duration = duration_ms;
scanner_.period = period_ms;
auto now = std::chrono::steady_clock::now();
// At the end of a single scan (Duration non-zero but Period zero), an
// HCI_LE_Scan_Timeout event shall be generated.
if (duration != 0) {
scanner_.timeout = now + scanner_.duration;
}
if (period != 0) {
scanner_.periodical_timeout = now + scanner_.period;
}
return ErrorCode::SUCCESS;
}
// =============================================================================
// LE Legacy Connection
// =============================================================================
// HCI LE Create Connection command (Vol 4, Part E § 7.8.12).
ErrorCode LinkLayerController::LeCreateConnection(
uint16_t scan_interval, uint16_t scan_window,
bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy,
AddressWithType peer_address,
bluetooth::hci::OwnAddressType own_address_type,
uint16_t connection_interval_min, uint16_t connection_interval_max,
uint16_t max_latency, uint16_t supervision_timeout, uint16_t min_ce_length,
uint16_t max_ce_length) {
// Legacy advertising commands are disallowed when extended advertising
// commands were used since the last reset.
if (!SelectLegacyAdvertising()) {
LOG_INFO(
"legacy advertising command rejected because extended advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host issues this command when another HCI_LE_Create_Connection
// command is pending in the Controller, the Controller shall return the
// error code Command Disallowed (0x0C).
if (initiator_.IsEnabled()) {
LOG_INFO("initiator is currently enabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// Note: no explicit error code stated for invalid interval and window
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (scan_interval < 0x4 || scan_interval > 0x4000 || scan_window < 0x4 ||
scan_window > 0x4000) {
LOG_INFO(
"scan_interval (0x%04x) and/or "
"scan_window (0x%04x) are outside the range"
" of supported values (0x4 - 0x4000)",
scan_interval, scan_window);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The LE_Scan_Window parameter shall be set to a value smaller or equal to
// the value set for the LE_Scan_Interval parameter.
if (scan_interval < scan_window) {
LOG_INFO("scan_window (0x%04x) is larger than scan_interval (0x%04x)",
scan_window, scan_interval);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for invalid connection interval
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (connection_interval_min < 0x6 || connection_interval_min > 0x0c80 ||
connection_interval_max < 0x6 || connection_interval_max > 0x0c80) {
LOG_INFO(
"connection_interval_min (0x%04x) and/or "
"connection_interval_max (0x%04x) are outside the range"
" of supported values (0x6 - 0x0c80)",
connection_interval_min, connection_interval_max);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Connection_Interval_Min parameter shall not be greater than the
// Connection_Interval_Max parameter.
if (connection_interval_max < connection_interval_min) {
LOG_INFO(
"connection_interval_min (0x%04x) is larger than"
" connection_interval_max (0x%04x)",
connection_interval_min, connection_interval_max);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for invalid max_latency
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (max_latency > 0x01f3) {
LOG_INFO(
"max_latency (0x%04x) is outside the range"
" of supported values (0x0 - 0x01f3)",
max_latency);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// Note: no explicit error code stated for invalid supervision timeout
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (supervision_timeout < 0xa || supervision_timeout > 0x0c80) {
LOG_INFO(
"supervision_timeout (0x%04x) is outside the range"
" of supported values (0xa - 0x0c80)",
supervision_timeout);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Supervision_Timeout in milliseconds shall be larger than
// (1 + Max_Latency) * Connection_Interval_Max * 2, where
// Connection_Interval_Max is given in milliseconds.
milliseconds min_supervision_timeout = duration_cast<milliseconds>(
(1 + max_latency) * slots(2 * connection_interval_max) * 2);
if (supervision_timeout * 10ms < min_supervision_timeout) {
LOG_INFO(
"supervision_timeout (%d ms) is smaller that the minimal supervision "
"timeout allowed by connection_interval_max and max_latency (%u ms)",
supervision_timeout * 10,
static_cast<unsigned>(min_supervision_timeout / 1ms));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
// If the Own_Address_Type parameter is set to 0x01 and the random
// address for the device has not been initialized using the
// HCI_LE_Set_Random_Address command, the Controller shall return the
// error code Invalid HCI Command Parameters (0x12).
if (own_address_type == OwnAddressType::RANDOM_DEVICE_ADDRESS &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Random_Device_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the Own_Address_Type parameter is set to 0x03, the
// Initiator_Filter_Policy parameter is set to 0x00, the controller's
// resolving list did not contain matching entry, and the random address for
// the device has not been initialized using the HCI_LE_Set_Random_Address
// command, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (own_address_type == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS &&
initiator_filter_policy == InitiatorFilterPolicy::USE_PEER_ADDRESS &&
!GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local) &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Resolvable_Or_Random_Address but the"
" Resolving_List does not contain a matching entry and the"
" Random_Address is not initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
initiator_.connect_enable = true;
initiator_.initiator_filter_policy = initiator_filter_policy;
initiator_.peer_address = peer_address;
initiator_.own_address_type = own_address_type;
initiator_.le_1m_phy.enabled = true;
initiator_.le_1m_phy.scan_interval = scan_interval;
initiator_.le_1m_phy.scan_window = scan_window;
initiator_.le_1m_phy.connection_interval_min = connection_interval_min;
initiator_.le_1m_phy.connection_interval_max = connection_interval_max;
initiator_.le_1m_phy.max_latency = max_latency;
initiator_.le_1m_phy.supervision_timeout = supervision_timeout;
initiator_.le_1m_phy.min_ce_length = min_ce_length;
initiator_.le_1m_phy.max_ce_length = max_ce_length;
initiator_.le_2m_phy.enabled = false;
initiator_.le_coded_phy.enabled = false;
initiator_.pending_connect_request = {};
return ErrorCode::SUCCESS;
}
// HCI LE Create Connection Cancel command (Vol 4, Part E § 7.8.12).
ErrorCode LinkLayerController::LeCreateConnectionCancel() {
// If no HCI_LE_Create_Connection or HCI_LE_Extended_Create_Connection
// command is pending, then the Controller shall return the error code
// Command Disallowed (0x0C).
if (!initiator_.IsEnabled()) {
LOG_INFO("initiator is currently disabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the cancellation was successful then, after the HCI_Command_Complete
// event for the HCI_LE_Create_Connection_Cancel command, either an LE
// Connection Complete or an HCI_LE_Enhanced_Connection_Complete event
// shall be generated. In either case, the event shall be sent with the error
// code Unknown Connection Identifier (0x02).
if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
ScheduleTask(0ms, [this] {
send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
ErrorCode::UNKNOWN_CONNECTION, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), Address(), Address(),
0, 0, 0, bluetooth::hci::ClockAccuracy::PPM_500));
});
} else if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
ScheduleTask(0ms, [this] {
send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
ErrorCode::UNKNOWN_CONNECTION, 0, Role::CENTRAL,
AddressType::PUBLIC_DEVICE_ADDRESS, Address(), 0, 0, 0,
bluetooth::hci::ClockAccuracy::PPM_500));
});
}
initiator_.Disable();
return ErrorCode::SUCCESS;
}
// =============================================================================
// LE Extended Connection
// =============================================================================
// HCI LE Extended Create Connection command (Vol 4, Part E § 7.8.66).
ErrorCode LinkLayerController::LeExtendedCreateConnection(
bluetooth::hci::InitiatorFilterPolicy initiator_filter_policy,
bluetooth::hci::OwnAddressType own_address_type,
AddressWithType peer_address, uint8_t initiating_phys,
std::vector<bluetooth::hci::LeCreateConnPhyScanParameters>
initiating_phy_parameters) {
// Extended advertising commands are disallowed when legacy advertising
// commands were used since the last reset.
if (!SelectExtendedAdvertising()) {
LOG_INFO(
"extended advertising command rejected because legacy advertising"
" is being used");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host issues this command when another
// HCI_LE_Extended_Create_Connection command is pending in the Controller,
// the Controller shall return the error code Command Disallowed (0x0C).
if (initiator_.IsEnabled()) {
LOG_INFO("initiator is currently enabled");
return ErrorCode::COMMAND_DISALLOWED;
}
// If the Host specifies a PHY that is not supported by the Controller,
// including a bit that is reserved for future use, the latter should return
// the error code Unsupported Feature or Parameter Value (0x11).
if ((initiating_phys & 0xf8) != 0) {
LOG_INFO(
"initiating_phys (%02x) enables PHYs that are not supported by"
" the controller",
initiating_phys);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// TODO(c++20) std::popcount
if (__builtin_popcount(initiating_phys) !=
int(initiating_phy_parameters.size())) {
LOG_INFO(
"initiating_phy_parameters (%zu)"
" does not match initiating_phys (%02x)",
initiating_phy_parameters.size(), initiating_phys);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the Initiating_PHYs parameter does not have at least one bit set for a
// PHY allowed for scanning on the primary advertising physical channel, the
// Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (initiating_phys == 0) {
LOG_INFO("initiating_phys is empty");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
for (auto const& parameter : initiating_phy_parameters) {
// Note: no explicit error code stated for invalid interval and window
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (parameter.scan_interval_ < 0x4 || parameter.scan_interval_ > 0x4000 ||
parameter.scan_window_ < 0x4 || parameter.scan_window_ > 0x4000) {
LOG_INFO(
"scan_interval (0x%04x) and/or "
"scan_window (0x%04x) are outside the range"
" of supported values (0x4 - 0x4000)",
parameter.scan_interval_, parameter.scan_window_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The LE_Scan_Window parameter shall be set to a value smaller or equal to
// the value set for the LE_Scan_Interval parameter.
if (parameter.scan_interval_ < parameter.scan_window_) {
LOG_INFO("scan_window (0x%04x) is larger than scan_interval (0x%04x)",
parameter.scan_window_, parameter.scan_interval_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for invalid connection interval
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (parameter.conn_interval_min_ < 0x6 ||
parameter.conn_interval_min_ > 0x0c80 ||
parameter.conn_interval_max_ < 0x6 ||
parameter.conn_interval_max_ > 0x0c80) {
LOG_INFO(
"connection_interval_min (0x%04x) and/or "
"connection_interval_max (0x%04x) are outside the range"
" of supported values (0x6 - 0x0c80)",
parameter.conn_interval_min_, parameter.conn_interval_max_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Connection_Interval_Min parameter shall not be greater than the
// Connection_Interval_Max parameter.
if (parameter.conn_interval_max_ < parameter.conn_interval_min_) {
LOG_INFO(
"connection_interval_min (0x%04x) is larger than"
" connection_interval_max (0x%04x)",
parameter.conn_interval_min_, parameter.conn_interval_max_);
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// Note: no explicit error code stated for invalid max_latency
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (parameter.conn_latency_ > 0x01f3) {
LOG_INFO(
"max_latency (0x%04x) is outside the range"
" of supported values (0x0 - 0x01f3)",
parameter.conn_latency_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// Note: no explicit error code stated for invalid supervision timeout
// values but assuming Unsupported Feature or Parameter Value (0x11)
// error code based on similar advertising command.
if (parameter.supervision_timeout_ < 0xa ||
parameter.supervision_timeout_ > 0x0c80) {
LOG_INFO(
"supervision_timeout (0x%04x) is outside the range"
" of supported values (0xa - 0x0c80)",
parameter.supervision_timeout_);
return ErrorCode::UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE;
}
// The Supervision_Timeout in milliseconds shall be larger than
// (1 + Max_Latency) * Connection_Interval_Max * 2, where
// Connection_Interval_Max is given in milliseconds.
milliseconds min_supervision_timeout = duration_cast<milliseconds>(
(1 + parameter.conn_latency_) *
slots(2 * parameter.conn_interval_max_) * 2);
if (parameter.supervision_timeout_ * 10ms < min_supervision_timeout) {
LOG_INFO(
"supervision_timeout (%d ms) is smaller that the minimal supervision "
"timeout allowed by connection_interval_max and max_latency (%u ms)",
parameter.supervision_timeout_ * 10,
static_cast<unsigned>(min_supervision_timeout / 1ms));
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
}
// TODO: additional checks would apply in the case of a LE only Controller
// with no configured public device address.
// If the Own_Address_Type parameter is set to 0x01 and the random
// address for the device has not been initialized using the
// HCI_LE_Set_Random_Address command, the Controller shall return the
// error code Invalid HCI Command Parameters (0x12).
if (own_address_type == OwnAddressType::RANDOM_DEVICE_ADDRESS &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Random_Device_Address but the Random_Address"
" has not been initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// If the Own_Address_Type parameter is set to 0x03, the
// Initiator_Filter_Policy parameter is set to 0x00, the controller's
// resolving list did not contain matching entry, and the random address for
// the device has not been initialized using the HCI_LE_Set_Random_Address
// command, the Controller shall return the error code
// Invalid HCI Command Parameters (0x12).
if (own_address_type == OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS &&
initiator_filter_policy == InitiatorFilterPolicy::USE_PEER_ADDRESS &&
!GenerateResolvablePrivateAddress(peer_address, IrkSelection::Local) &&
random_address_ == Address::kEmpty) {
LOG_INFO(
"own_address_type is Resolvable_Or_Random_Address but the"
" Resolving_List does not contain a matching entry and the"
" Random_Address is not initialized");
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
initiator_.connect_enable = true;
initiator_.initiator_filter_policy = initiator_filter_policy;
initiator_.peer_address = peer_address;
initiator_.own_address_type = own_address_type;
initiator_.pending_connect_request = {};
initiator_.le_1m_phy.enabled = false;
initiator_.le_2m_phy.enabled = false;
initiator_.le_coded_phy.enabled = false;
int offset = 0;
if (initiating_phys & 0x1) {
initiator_.le_1m_phy = Initiator::PhyParameters{
.enabled = true,
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
initiating_phy_parameters[offset].conn_interval_min_,
.connection_interval_max =
initiating_phy_parameters[offset].conn_interval_max_,
.max_latency = initiating_phy_parameters[offset].conn_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
.max_ce_length = initiating_phy_parameters[offset].max_ce_length_,
};
offset++;
}
if (initiating_phys & 0x2) {
initiator_.le_2m_phy = Initiator::PhyParameters{
.enabled = true,
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
initiating_phy_parameters[offset].conn_interval_min_,
.connection_interval_max =
initiating_phy_parameters[offset].conn_interval_max_,
.max_latency = initiating_phy_parameters[offset].conn_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
.max_ce_length = initiating_phy_parameters[offset].max_ce_length_,
};
offset++;
}
if (initiating_phys & 0x4) {
initiator_.le_coded_phy = Initiator::PhyParameters{
.enabled = true,
.scan_interval = initiating_phy_parameters[offset].scan_interval_,
.scan_window = initiating_phy_parameters[offset].scan_window_,
.connection_interval_min =
initiating_phy_parameters[offset].conn_interval_min_,
.connection_interval_max =
initiating_phy_parameters[offset].conn_interval_max_,
.max_latency = initiating_phy_parameters[offset].conn_latency_,
.supervision_timeout =
initiating_phy_parameters[offset].supervision_timeout_,
.min_ce_length = initiating_phy_parameters[offset].min_ce_length_,
.max_ce_length = initiating_phy_parameters[offset].max_ce_length_,
};
offset++;
}
return ErrorCode::SUCCESS;
}
void LinkLayerController::SetSecureSimplePairingSupport(bool enable) {
uint64_t bit = 0x1;
secure_simple_pairing_host_support_ = enable;
if (enable) {
host_supported_features_ |= bit;
} else {
host_supported_features_ &= ~bit;
}
}
void LinkLayerController::SetLeHostSupport(bool enable) {
// TODO: Vol 2, Part C § 3.5 Feature requirements.
// (65) LE Supported (Host) implies
// (38) LE Supported (Controller)
uint64_t bit = 0x2;
le_host_support_ = enable;
if (enable) {
host_supported_features_ |= bit;
} else {
host_supported_features_ &= ~bit;
}
}
void LinkLayerController::SetSecureConnectionsSupport(bool enable) {
// TODO: Vol 2, Part C § 3.5 Feature requirements.
// (67) Secure Connections (Host Support) implies
// (64) Secure Simple Pairing (Host Support) and
// (136) Secure Connections (Controller Support)
uint64_t bit = 0x8;
secure_connections_host_support_ = enable;
if (enable) {
host_supported_features_ |= bit;
} else {
host_supported_features_ &= ~bit;
}
}
void LinkLayerController::SetLocalName(
std::array<uint8_t, 248> const& local_name) {
std::copy(local_name.begin(), local_name.end(), local_name_.begin());
}
void LinkLayerController::SetLocalName(std::vector<uint8_t> const& local_name) {
ASSERT(local_name.size() <= local_name_.size());
local_name_.fill(0);
std::copy(local_name.begin(), local_name.end(), local_name_.begin());
}
void LinkLayerController::SetExtendedInquiryResponse(
std::vector<uint8_t> const& extended_inquiry_response) {
ASSERT(extended_inquiry_response.size() <= extended_inquiry_response_.size());
extended_inquiry_response_.fill(0);
std::copy(extended_inquiry_response.begin(), extended_inquiry_response.end(),
extended_inquiry_response_.begin());
}
#ifdef ROOTCANAL_LMP
LinkLayerController::LinkLayerController(const Address& address,
const ControllerProperties& properties)
: address_(address),
properties_(properties),
lm_(nullptr, link_manager_destroy) {
ops_ = {
.user_pointer = this,
.get_handle =
[](void* user, const uint8_t(*address)[6]) {
auto controller = static_cast<LinkLayerController*>(user);
return controller->connections_.GetHandleOnlyAddress(
Address(*address));
},
.get_address =
[](void* user, uint16_t handle, uint8_t(*result)[6]) {
auto controller = static_cast<LinkLayerController*>(user);
auto address =
controller->connections_.GetAddress(handle).GetAddress();
std::copy(address.data(), address.data() + 6,
reinterpret_cast<uint8_t*>(result));
},
.extended_features =
[](void* user, uint8_t features_page) {
auto controller = static_cast<LinkLayerController*>(user);
return controller->GetLmpFeatures(features_page);
},
.send_hci_event =
[](void* user, const uint8_t* data, uintptr_t len) {
auto controller = static_cast<LinkLayerController*>(user);
auto event_code = static_cast<EventCode>(data[0]);
auto payload = std::make_unique<bluetooth::packet::RawBuilder>(
std::vector(data + 2, data + len));
controller->send_event_(bluetooth::hci::EventBuilder::Create(
event_code, std::move(payload)));
},
.send_lmp_packet =
[](void* user, const uint8_t(*to)[6], const uint8_t* data,
uintptr_t len) {
auto controller = static_cast<LinkLayerController*>(user);
auto payload = std::make_unique<bluetooth::packet::RawBuilder>(
std::vector(data, data + len));
Address source = controller->GetAddress();
Address dest(*to);
controller->SendLinkLayerPacket(model::packets::LmpBuilder::Create(
source, dest, std::move(payload)));
}};
lm_.reset(link_manager_create(ops_));
}
#else
LinkLayerController::LinkLayerController(const Address& address,
const ControllerProperties& properties)
: address_(address), properties_(properties) {}
#endif
void LinkLayerController::SendLeLinkLayerPacket(
std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet) {
std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet =
std::move(packet);
ScheduleTask(kNoDelayMs, [this, shared_packet]() {
send_to_remote_(shared_packet, Phy::Type::LOW_ENERGY);
});
}
void LinkLayerController::SendLinkLayerPacket(
std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet) {
std::shared_ptr<model::packets::LinkLayerPacketBuilder> shared_packet =
std::move(packet);
ScheduleTask(kNoDelayMs, [this, shared_packet]() {
send_to_remote_(shared_packet, Phy::Type::BR_EDR);
});
}
void LinkLayerController::SendLeLinkLayerPacketWithRssi(
Address source_address, Address destination_address, uint8_t rssi,
std::unique_ptr<model::packets::LinkLayerPacketBuilder> packet) {
std::shared_ptr<model::packets::RssiWrapperBuilder> shared_packet =
model::packets::RssiWrapperBuilder::Create(
source_address, destination_address, rssi, std::move(packet));
ScheduleTask(kNoDelayMs, [this, shared_packet]() {
send_to_remote_(shared_packet, Phy::Type::LOW_ENERGY);
});
}
ErrorCode LinkLayerController::SendLeCommandToRemoteByAddress(
OpCode opcode, const Address& own_address, const Address& peer_address) {
switch (opcode) {
case (OpCode::LE_READ_REMOTE_FEATURES):
SendLeLinkLayerPacket(model::packets::LeReadRemoteFeaturesBuilder::Create(
own_address, peer_address));
break;
default:
LOG_INFO("Dropping unhandled command 0x%04x",
static_cast<uint16_t>(opcode));
return ErrorCode::UNKNOWN_HCI_COMMAND;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SendCommandToRemoteByAddress(
OpCode opcode, bluetooth::packet::PacketView<true> args,
const Address& own_address, const Address& peer_address) {
switch (opcode) {
case (OpCode::REMOTE_NAME_REQUEST):
// LMP features get requested with remote name requests.
SendLinkLayerPacket(model::packets::ReadRemoteLmpFeaturesBuilder::Create(
own_address, peer_address));
SendLinkLayerPacket(model::packets::RemoteNameRequestBuilder::Create(
own_address, peer_address));
break;
case (OpCode::READ_REMOTE_SUPPORTED_FEATURES):
SendLinkLayerPacket(
model::packets::ReadRemoteSupportedFeaturesBuilder::Create(
own_address, peer_address));
break;
case (OpCode::READ_REMOTE_EXTENDED_FEATURES): {
uint8_t page_number =
(args.begin() + 2).extract<uint8_t>(); // skip the handle
SendLinkLayerPacket(
model::packets::ReadRemoteExtendedFeaturesBuilder::Create(
own_address, peer_address, page_number));
} break;
case (OpCode::READ_REMOTE_VERSION_INFORMATION):
SendLinkLayerPacket(
model::packets::ReadRemoteVersionInformationBuilder::Create(
own_address, peer_address));
break;
case (OpCode::READ_CLOCK_OFFSET):
SendLinkLayerPacket(model::packets::ReadClockOffsetBuilder::Create(
own_address, peer_address));
break;
default:
LOG_INFO("Dropping unhandled command 0x%04x",
static_cast<uint16_t>(opcode));
return ErrorCode::UNKNOWN_HCI_COMMAND;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SendCommandToRemoteByHandle(
OpCode opcode, bluetooth::packet::PacketView<true> args, uint16_t handle) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
switch (opcode) {
case (OpCode::LE_READ_REMOTE_FEATURES):
return SendLeCommandToRemoteByAddress(
opcode, connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress());
default:
return SendCommandToRemoteByAddress(
opcode, args, connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress());
}
}
ErrorCode LinkLayerController::SendAclToRemote(
bluetooth::hci::AclView acl_packet) {
uint16_t handle = acl_packet.GetHandle();
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
AddressWithType my_address = connections_.GetOwnAddress(handle);
AddressWithType destination = connections_.GetAddress(handle);
Phy::Type phy = connections_.GetPhyType(handle);
ScheduleTask(kNoDelayMs, [this, handle]() {
std::vector<bluetooth::hci::CompletedPackets> completed_packets;
bluetooth::hci::CompletedPackets cp;
cp.connection_handle_ = handle;
cp.host_num_of_completed_packets_ = kNumCommandPackets;
completed_packets.push_back(cp);
send_event_(bluetooth::hci::NumberOfCompletedPacketsBuilder::Create(
completed_packets));
});
auto acl_payload = acl_packet.GetPayload();
std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
std::make_unique<bluetooth::packet::RawBuilder>();
std::vector<uint8_t> payload_bytes(acl_payload.begin(), acl_payload.end());
uint16_t first_two_bytes =
static_cast<uint16_t>(acl_packet.GetHandle()) +
(static_cast<uint16_t>(acl_packet.GetPacketBoundaryFlag()) << 12) +
(static_cast<uint16_t>(acl_packet.GetBroadcastFlag()) << 14);
raw_builder_ptr->AddOctets2(first_two_bytes);
raw_builder_ptr->AddOctets2(static_cast<uint16_t>(payload_bytes.size()));
raw_builder_ptr->AddOctets(payload_bytes);
auto acl = model::packets::AclBuilder::Create(my_address.GetAddress(),
destination.GetAddress(),
std::move(raw_builder_ptr));
switch (phy) {
case Phy::Type::BR_EDR:
SendLinkLayerPacket(std::move(acl));
break;
case Phy::Type::LOW_ENERGY:
SendLeLinkLayerPacket(std::move(acl));
break;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SendScoToRemote(
bluetooth::hci::ScoView sco_packet) {
uint16_t handle = sco_packet.GetHandle();
if (!connections_.HasScoHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
// TODO: SCO flow control
Address source = GetAddress();
Address destination = connections_.GetScoAddress(handle);
auto sco_data = sco_packet.GetData();
std::vector<uint8_t> sco_data_bytes(sco_data.begin(), sco_data.end());
SendLinkLayerPacket(model::packets::ScoBuilder::Create(
source, destination,
std::make_unique<bluetooth::packet::RawBuilder>(sco_data_bytes)));
return ErrorCode::SUCCESS;
}
void LinkLayerController::IncomingPacket(
model::packets::LinkLayerPacketView incoming) {
ASSERT(incoming.IsValid());
if (incoming.GetType() == PacketType::RSSI_WRAPPER) {
auto rssi_wrapper = model::packets::RssiWrapperView::Create(incoming);
ASSERT(rssi_wrapper.IsValid());
auto wrapped =
model::packets::LinkLayerPacketView::Create(rssi_wrapper.GetPayload());
IncomingPacketWithRssi(wrapped, rssi_wrapper.GetRssi());
} else {
IncomingPacketWithRssi(incoming, GetRssi());
}
}
void LinkLayerController::IncomingPacketWithRssi(
model::packets::LinkLayerPacketView incoming, uint8_t rssi) {
ASSERT(incoming.IsValid());
auto destination_address = incoming.GetDestinationAddress();
// Match broadcasts
bool address_matches = (destination_address == Address::kEmpty);
// Address match is performed in specific handlers for these PDU types.
switch (incoming.GetType()) {
case model::packets::PacketType::LE_SCAN:
case model::packets::PacketType::LE_SCAN_RESPONSE:
case model::packets::PacketType::LE_LEGACY_ADVERTISING_PDU:
case model::packets::PacketType::LE_EXTENDED_ADVERTISING_PDU:
case model::packets::PacketType::LE_CONNECT:
address_matches = true;
break;
default:
break;
}
// Check public address
if (destination_address == address_ ||
destination_address == random_address_) {
address_matches = true;
}
// Check current connection address
if (destination_address == initiator_.initiating_address) {
address_matches = true;
}
// Check connection addresses
auto source_address = incoming.GetSourceAddress();
auto handle = connections_.GetHandleOnlyAddress(source_address);
if (handle != kReservedHandle) {
if (connections_.GetOwnAddress(handle).GetAddress() ==
destination_address) {
address_matches = true;
// Update link timeout for valid ACL connections
connections_.ResetLinkTimer(handle);
}
}
// Drop packets not addressed to me
if (!address_matches) {
LOG_INFO("%s | Dropping packet not addressed to me %s->%s (type 0x%x)",
address_.ToString().c_str(), source_address.ToString().c_str(),
destination_address.ToString().c_str(),
static_cast<int>(incoming.GetType()));
return;
}
switch (incoming.GetType()) {
case model::packets::PacketType::ACL:
IncomingAclPacket(incoming);
break;
case model::packets::PacketType::SCO:
IncomingScoPacket(incoming);
break;
case model::packets::PacketType::DISCONNECT:
IncomingDisconnectPacket(incoming);
break;
#ifdef ROOTCANAL_LMP
case model::packets::PacketType::LMP:
IncomingLmpPacket(incoming);
break;
#else
case model::packets::PacketType::ENCRYPT_CONNECTION:
IncomingEncryptConnection(incoming);
break;
case model::packets::PacketType::ENCRYPT_CONNECTION_RESPONSE:
IncomingEncryptConnectionResponse(incoming);
break;
case model::packets::PacketType::IO_CAPABILITY_REQUEST:
IncomingIoCapabilityRequestPacket(incoming);
break;
case model::packets::PacketType::IO_CAPABILITY_RESPONSE:
IncomingIoCapabilityResponsePacket(incoming);
break;
case model::packets::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE:
IncomingIoCapabilityNegativeResponsePacket(incoming);
break;
case PacketType::KEYPRESS_NOTIFICATION:
IncomingKeypressNotificationPacket(incoming);
break;
case (model::packets::PacketType::PASSKEY):
IncomingPasskeyPacket(incoming);
break;
case (model::packets::PacketType::PASSKEY_FAILED):
IncomingPasskeyFailedPacket(incoming);
break;
case (model::packets::PacketType::PIN_REQUEST):
IncomingPinRequestPacket(incoming);
break;
case (model::packets::PacketType::PIN_RESPONSE):
IncomingPinResponsePacket(incoming);
break;
#endif /* ROOTCANAL_LMP */
case model::packets::PacketType::INQUIRY:
if (inquiry_scan_enable_) {
IncomingInquiryPacket(incoming, rssi);
}
break;
case model::packets::PacketType::INQUIRY_RESPONSE:
IncomingInquiryResponsePacket(incoming);
break;
case PacketType::ISO:
IncomingIsoPacket(incoming);
break;
case PacketType::ISO_CONNECTION_REQUEST:
IncomingIsoConnectionRequestPacket(incoming);
break;
case PacketType::ISO_CONNECTION_RESPONSE:
IncomingIsoConnectionResponsePacket(incoming);
break;
case model::packets::PacketType::LE_LEGACY_ADVERTISING_PDU:
IncomingLeLegacyAdvertisingPdu(incoming, rssi);
return;
case model::packets::PacketType::LE_EXTENDED_ADVERTISING_PDU:
IncomingLeExtendedAdvertisingPdu(incoming, rssi);
return;
case model::packets::PacketType::LE_CONNECT:
IncomingLeConnectPacket(incoming);
break;
case model::packets::PacketType::LE_CONNECT_COMPLETE:
IncomingLeConnectCompletePacket(incoming);
break;
case model::packets::PacketType::LE_CONNECTION_PARAMETER_REQUEST:
IncomingLeConnectionParameterRequest(incoming);
break;
case model::packets::PacketType::LE_CONNECTION_PARAMETER_UPDATE:
IncomingLeConnectionParameterUpdate(incoming);
break;
case model::packets::PacketType::LE_ENCRYPT_CONNECTION:
IncomingLeEncryptConnection(incoming);
break;
case model::packets::PacketType::LE_ENCRYPT_CONNECTION_RESPONSE:
IncomingLeEncryptConnectionResponse(incoming);
break;
case (model::packets::PacketType::LE_READ_REMOTE_FEATURES):
IncomingLeReadRemoteFeatures(incoming);
break;
case (model::packets::PacketType::LE_READ_REMOTE_FEATURES_RESPONSE):
IncomingLeReadRemoteFeaturesResponse(incoming);
break;
case model::packets::PacketType::LE_SCAN:
IncomingLeScanPacket(incoming);
break;
case model::packets::PacketType::LE_SCAN_RESPONSE:
IncomingLeScanResponsePacket(incoming, rssi);
break;
case model::packets::PacketType::PAGE:
if (page_scan_enable_) {
IncomingPagePacket(incoming);
}
break;
case model::packets::PacketType::PAGE_RESPONSE:
IncomingPageResponsePacket(incoming);
break;
case model::packets::PacketType::PAGE_REJECT:
IncomingPageRejectPacket(incoming);
break;
case (model::packets::PacketType::REMOTE_NAME_REQUEST):
IncomingRemoteNameRequest(incoming);
break;
case (model::packets::PacketType::REMOTE_NAME_REQUEST_RESPONSE):
IncomingRemoteNameRequestResponse(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES):
IncomingReadRemoteSupportedFeatures(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_SUPPORTED_FEATURES_RESPONSE):
IncomingReadRemoteSupportedFeaturesResponse(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES):
IncomingReadRemoteLmpFeatures(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_LMP_FEATURES_RESPONSE):
IncomingReadRemoteLmpFeaturesResponse(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES):
IncomingReadRemoteExtendedFeatures(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_EXTENDED_FEATURES_RESPONSE):
IncomingReadRemoteExtendedFeaturesResponse(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION):
IncomingReadRemoteVersion(incoming);
break;
case (model::packets::PacketType::READ_REMOTE_VERSION_INFORMATION_RESPONSE):
IncomingReadRemoteVersionResponse(incoming);
break;
case (model::packets::PacketType::READ_CLOCK_OFFSET):
IncomingReadClockOffset(incoming);
break;
case (model::packets::PacketType::READ_CLOCK_OFFSET_RESPONSE):
IncomingReadClockOffsetResponse(incoming);
break;
case (model::packets::PacketType::RSSI_WRAPPER):
LOG_ERROR("Dropping double-wrapped RSSI packet");
break;
case model::packets::PacketType::SCO_CONNECTION_REQUEST:
IncomingScoConnectionRequest(incoming);
break;
case model::packets::PacketType::SCO_CONNECTION_RESPONSE:
IncomingScoConnectionResponse(incoming);
break;
case model::packets::PacketType::SCO_DISCONNECT:
IncomingScoDisconnect(incoming);
break;
case model::packets::PacketType::PING_REQUEST:
IncomingPingRequest(incoming);
break;
case model::packets::PacketType::PING_RESPONSE:
// ping responses require no action
break;
default:
LOG_WARN("Dropping unhandled packet of type %s",
model::packets::PacketTypeText(incoming.GetType()).c_str());
}
}
void LinkLayerController::IncomingAclPacket(
model::packets::LinkLayerPacketView incoming) {
auto acl = model::packets::AclView::Create(incoming);
ASSERT(acl.IsValid());
auto payload = acl.GetPayload();
std::shared_ptr<std::vector<uint8_t>> payload_bytes =
std::make_shared<std::vector<uint8_t>>(payload.begin(), payload.end());
LOG_INFO("Acl Packet [%d] %s -> %s", static_cast<int>(payload_bytes->size()),
incoming.GetSourceAddress().ToString().c_str(),
incoming.GetDestinationAddress().ToString().c_str());
bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian> raw_packet(
payload_bytes);
auto acl_view = bluetooth::hci::AclView::Create(raw_packet);
ASSERT(acl_view.IsValid());
uint16_t local_handle =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
std::vector<uint8_t> payload_data(acl_view.GetPayload().begin(),
acl_view.GetPayload().end());
uint16_t acl_buffer_size = properties_.acl_data_packet_length;
int num_packets =
(payload_data.size() + acl_buffer_size - 1) / acl_buffer_size;
auto pb_flag_controller_to_host = acl_view.GetPacketBoundaryFlag();
if (pb_flag_controller_to_host ==
bluetooth::hci::PacketBoundaryFlag::FIRST_NON_AUTOMATICALLY_FLUSHABLE) {
pb_flag_controller_to_host =
bluetooth::hci::PacketBoundaryFlag::FIRST_AUTOMATICALLY_FLUSHABLE;
}
for (int i = 0; i < num_packets; i++) {
size_t start_index = acl_buffer_size * i;
size_t end_index =
std::min(start_index + acl_buffer_size, payload_data.size());
std::vector<uint8_t> fragment(&payload_data[start_index],
&payload_data[end_index]);
std::unique_ptr<bluetooth::packet::RawBuilder> raw_builder_ptr =
std::make_unique<bluetooth::packet::RawBuilder>(fragment);
auto acl_packet = bluetooth::hci::AclBuilder::Create(
local_handle, pb_flag_controller_to_host, acl_view.GetBroadcastFlag(),
std::move(raw_builder_ptr));
pb_flag_controller_to_host =
bluetooth::hci::PacketBoundaryFlag::CONTINUING_FRAGMENT;
send_acl_(std::move(acl_packet));
}
}
void LinkLayerController::IncomingScoPacket(
model::packets::LinkLayerPacketView incoming) {
Address source = incoming.GetSourceAddress();
uint16_t sco_handle = connections_.GetScoHandle(source);
if (!connections_.HasScoHandle(sco_handle)) {
LOG_INFO("Spurious SCO packet from %s", source.ToString().c_str());
return;
}
auto sco = model::packets::ScoView::Create(incoming);
ASSERT(sco.IsValid());
auto sco_data = sco.GetPayload();
std::vector<uint8_t> sco_data_bytes(sco_data.begin(), sco_data.end());
LOG_INFO("Sco Packet [%d] %s -> %s", static_cast<int>(sco_data_bytes.size()),
incoming.GetSourceAddress().ToString().c_str(),
incoming.GetDestinationAddress().ToString().c_str());
send_sco_(bluetooth::hci::ScoBuilder::Create(
sco_handle, bluetooth::hci::PacketStatusFlag::CORRECTLY_RECEIVED,
sco_data_bytes));
}
void LinkLayerController::IncomingRemoteNameRequest(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::RemoteNameRequestView::Create(packet);
ASSERT(view.IsValid());
SendLinkLayerPacket(model::packets::RemoteNameRequestResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(), local_name_));
}
void LinkLayerController::IncomingRemoteNameRequestResponse(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::RemoteNameRequestResponseView::Create(packet);
ASSERT(view.IsValid());
if (IsEventUnmasked(EventCode::REMOTE_NAME_REQUEST_COMPLETE)) {
send_event_(bluetooth::hci::RemoteNameRequestCompleteBuilder::Create(
ErrorCode::SUCCESS, packet.GetSourceAddress(), view.GetName()));
}
}
void LinkLayerController::IncomingReadRemoteLmpFeatures(
model::packets::LinkLayerPacketView packet) {
SendLinkLayerPacket(
model::packets::ReadRemoteLmpFeaturesResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(),
host_supported_features_));
}
void LinkLayerController::IncomingReadRemoteLmpFeaturesResponse(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::ReadRemoteLmpFeaturesResponseView::Create(packet);
ASSERT(view.IsValid());
if (IsEventUnmasked(EventCode::REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION)) {
send_event_(
bluetooth::hci::RemoteHostSupportedFeaturesNotificationBuilder::Create(
packet.GetSourceAddress(), view.GetFeatures()));
}
}
void LinkLayerController::IncomingReadRemoteSupportedFeatures(
model::packets::LinkLayerPacketView packet) {
SendLinkLayerPacket(
model::packets::ReadRemoteSupportedFeaturesResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(),
properties_.lmp_features[0]));
}
void LinkLayerController::IncomingReadRemoteSupportedFeaturesResponse(
model::packets::LinkLayerPacketView packet) {
auto view =
model::packets::ReadRemoteSupportedFeaturesResponseView::Create(packet);
ASSERT(view.IsValid());
Address source = packet.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(source);
if (handle == kReservedHandle) {
LOG_INFO("Discarding response from a disconnected device %s",
source.ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::READ_REMOTE_SUPPORTED_FEATURES_COMPLETE)) {
send_event_(
bluetooth::hci::ReadRemoteSupportedFeaturesCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, view.GetFeatures()));
}
}
void LinkLayerController::IncomingReadRemoteExtendedFeatures(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::ReadRemoteExtendedFeaturesView::Create(packet);
ASSERT(view.IsValid());
uint8_t page_number = view.GetPageNumber();
uint8_t error_code = static_cast<uint8_t>(ErrorCode::SUCCESS);
if (page_number >= properties_.lmp_features.size()) {
error_code = static_cast<uint8_t>(ErrorCode::INVALID_LMP_OR_LL_PARAMETERS);
}
SendLinkLayerPacket(
model::packets::ReadRemoteExtendedFeaturesResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(), error_code,
page_number, GetMaxLmpFeaturesPageNumber(),
GetLmpFeatures(page_number)));
}
void LinkLayerController::IncomingReadRemoteExtendedFeaturesResponse(
model::packets::LinkLayerPacketView packet) {
auto view =
model::packets::ReadRemoteExtendedFeaturesResponseView::Create(packet);
ASSERT(view.IsValid());
Address source = packet.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(source);
if (handle == kReservedHandle) {
LOG_INFO("Discarding response from a disconnected device %s",
source.ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::READ_REMOTE_EXTENDED_FEATURES_COMPLETE)) {
send_event_(
bluetooth::hci::ReadRemoteExtendedFeaturesCompleteBuilder::Create(
static_cast<ErrorCode>(view.GetStatus()), handle,
view.GetPageNumber(), view.GetMaxPageNumber(), view.GetFeatures()));
}
}
void LinkLayerController::IncomingReadRemoteVersion(
model::packets::LinkLayerPacketView packet) {
SendLinkLayerPacket(
model::packets::ReadRemoteVersionInformationResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(),
static_cast<uint8_t>(properties_.lmp_version),
static_cast<uint16_t>(properties_.lmp_subversion),
properties_.company_identifier));
}
void LinkLayerController::IncomingReadRemoteVersionResponse(
model::packets::LinkLayerPacketView packet) {
auto view =
model::packets::ReadRemoteVersionInformationResponseView::Create(packet);
ASSERT(view.IsValid());
Address source = packet.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(source);
if (handle == kReservedHandle) {
LOG_INFO("Discarding response from a disconnected device %s",
source.ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::READ_REMOTE_VERSION_INFORMATION_COMPLETE)) {
send_event_(
bluetooth::hci::ReadRemoteVersionInformationCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, view.GetLmpVersion(),
view.GetManufacturerName(), view.GetLmpSubversion()));
}
}
void LinkLayerController::IncomingReadClockOffset(
model::packets::LinkLayerPacketView packet) {
SendLinkLayerPacket(model::packets::ReadClockOffsetResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress(),
GetClockOffset()));
}
void LinkLayerController::IncomingReadClockOffsetResponse(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::ReadClockOffsetResponseView::Create(packet);
ASSERT(view.IsValid());
Address source = packet.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(source);
if (handle == kReservedHandle) {
LOG_INFO("Discarding response from a disconnected device %s",
source.ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::READ_CLOCK_OFFSET_COMPLETE)) {
send_event_(bluetooth::hci::ReadClockOffsetCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, view.GetOffset()));
}
}
void LinkLayerController::IncomingDisconnectPacket(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("Disconnect Packet");
auto disconnect = model::packets::DisconnectView::Create(incoming);
ASSERT(disconnect.IsValid());
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(peer);
if (handle == kReservedHandle) {
LOG_INFO("Discarding disconnect from a disconnected device %s",
peer.ToString().c_str());
return;
}
#ifdef ROOTCANAL_LMP
auto is_br_edr = connections_.GetPhyType(handle) == Phy::Type::BR_EDR;
#endif
ASSERT_LOG(connections_.Disconnect(handle, cancel_task_),
"GetHandle() returned invalid handle %hx", handle);
uint8_t reason = disconnect.GetReason();
SendDisconnectionCompleteEvent(handle, ErrorCode(reason));
#ifdef ROOTCANAL_LMP
if (is_br_edr) {
ASSERT(link_manager_remove_link(
lm_.get(), reinterpret_cast<uint8_t(*)[6]>(peer.data())));
}
#endif
}
#ifndef ROOTCANAL_LMP
void LinkLayerController::IncomingEncryptConnection(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("IncomingEncryptConnection");
// TODO: Check keys
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(peer);
if (handle == kReservedHandle) {
LOG_INFO("Unknown connection @%s", peer.ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) {
send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON));
}
uint16_t count = security_manager_.ReadKey(peer);
if (count == 0) {
LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
return;
}
auto array = security_manager_.GetKey(peer);
std::vector<uint8_t> key_vec{array.begin(), array.end()};
SendLinkLayerPacket(model::packets::EncryptConnectionResponseBuilder::Create(
GetAddress(), peer, key_vec));
}
void LinkLayerController::IncomingEncryptConnectionResponse(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("IncomingEncryptConnectionResponse");
// TODO: Check keys
uint16_t handle =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
if (handle == kReservedHandle) {
LOG_INFO("Unknown connection @%s",
incoming.GetSourceAddress().ToString().c_str());
return;
}
if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) {
send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON));
}
}
#endif /* !ROOTCANAL_LMP */
void LinkLayerController::IncomingInquiryPacket(
model::packets::LinkLayerPacketView incoming, uint8_t rssi) {
auto inquiry = model::packets::InquiryView::Create(incoming);
ASSERT(inquiry.IsValid());
Address peer = incoming.GetSourceAddress();
uint8_t lap = inquiry.GetLap();
// Filter out inquiry packets with IAC not present in the
// list Current_IAC_LAP.
if (std::none_of(current_iac_lap_list_.cbegin(), current_iac_lap_list_.cend(),
[lap](auto iac_lap) { return iac_lap.lap_ == lap; })) {
return;
}
switch (inquiry.GetInquiryType()) {
case (model::packets::InquiryType::STANDARD): {
SendLinkLayerPacket(model::packets::InquiryResponseBuilder::Create(
GetAddress(), peer, static_cast<uint8_t>(GetPageScanRepetitionMode()),
class_of_device_, GetClockOffset()));
} break;
case (model::packets::InquiryType::RSSI): {
SendLinkLayerPacket(
model::packets::InquiryResponseWithRssiBuilder::Create(
GetAddress(), peer,
static_cast<uint8_t>(GetPageScanRepetitionMode()),
class_of_device_, GetClockOffset(), rssi));
} break;
case (model::packets::InquiryType::EXTENDED): {
SendLinkLayerPacket(
model::packets::ExtendedInquiryResponseBuilder::Create(
GetAddress(), peer,
static_cast<uint8_t>(GetPageScanRepetitionMode()),
class_of_device_, GetClockOffset(), rssi,
extended_inquiry_response_));
} break;
default:
LOG_WARN("Unhandled Incoming Inquiry of type %d",
static_cast<int>(inquiry.GetType()));
return;
}
// TODO: Send an Inquiry Response Notification Event 7.7.74
}
void LinkLayerController::IncomingInquiryResponsePacket(
model::packets::LinkLayerPacketView incoming) {
auto basic_inquiry_response =
model::packets::BasicInquiryResponseView::Create(incoming);
ASSERT(basic_inquiry_response.IsValid());
std::vector<uint8_t> eir;
switch (basic_inquiry_response.GetInquiryType()) {
case (model::packets::InquiryType::STANDARD): {
// TODO: Support multiple inquiries in the same packet.
auto inquiry_response =
model::packets::InquiryResponseView::Create(basic_inquiry_response);
ASSERT(inquiry_response.IsValid());
auto page_scan_repetition_mode =
(bluetooth::hci::PageScanRepetitionMode)
inquiry_response.GetPageScanRepetitionMode();
std::vector<bluetooth::hci::InquiryResponse> responses;
responses.emplace_back();
responses.back().bd_addr_ = inquiry_response.GetSourceAddress();
responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
responses.back().clock_offset_ = inquiry_response.GetClockOffset();
if (IsEventUnmasked(EventCode::INQUIRY_RESULT)) {
send_event_(bluetooth::hci::InquiryResultBuilder::Create(responses));
}
} break;
case (model::packets::InquiryType::RSSI): {
auto inquiry_response =
model::packets::InquiryResponseWithRssiView::Create(
basic_inquiry_response);
ASSERT(inquiry_response.IsValid());
auto page_scan_repetition_mode =
(bluetooth::hci::PageScanRepetitionMode)
inquiry_response.GetPageScanRepetitionMode();
std::vector<bluetooth::hci::InquiryResponseWithRssi> responses;
responses.emplace_back();
responses.back().address_ = inquiry_response.GetSourceAddress();
responses.back().page_scan_repetition_mode_ = page_scan_repetition_mode;
responses.back().class_of_device_ = inquiry_response.GetClassOfDevice();
responses.back().clock_offset_ = inquiry_response.GetClockOffset();
responses.back().rssi_ = inquiry_response.GetRssi();
if (IsEventUnmasked(EventCode::INQUIRY_RESULT_WITH_RSSI)) {
send_event_(
bluetooth::hci::InquiryResultWithRssiBuilder::Create(responses));
}
} break;
case (model::packets::InquiryType::EXTENDED): {
auto inquiry_response =
model::packets::ExtendedInquiryResponseView::Create(
basic_inquiry_response);
ASSERT(inquiry_response.IsValid());
send_event_(bluetooth::hci::ExtendedInquiryResultRawBuilder::Create(
inquiry_response.GetSourceAddress(),
static_cast<bluetooth::hci::PageScanRepetitionMode>(
inquiry_response.GetPageScanRepetitionMode()),
inquiry_response.GetClassOfDevice(),
inquiry_response.GetClockOffset(), inquiry_response.GetRssi(),
std::vector<uint8_t>(extended_inquiry_response_.begin(),
extended_inquiry_response_.end())));
} break;
default:
LOG_WARN("Unhandled Incoming Inquiry Response of type %d",
static_cast<int>(basic_inquiry_response.GetInquiryType()));
}
}
#ifndef ROOTCANAL_LMP
void LinkLayerController::IncomingIoCapabilityRequestPacket(
model::packets::LinkLayerPacketView incoming) {
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandle(AddressWithType(
peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS));
if (handle == kReservedHandle) {
LOG_INFO("Device not connected %s", peer.ToString().c_str());
return;
}
if (!secure_simple_pairing_host_support_) {
LOG_WARN("Trying PIN pairing for %s",
incoming.GetDestinationAddress().ToString().c_str());
SendLinkLayerPacket(
model::packets::IoCapabilityNegativeResponseBuilder::Create(
incoming.GetDestinationAddress(), incoming.GetSourceAddress(),
static_cast<uint8_t>(
ErrorCode::UNSUPPORTED_REMOTE_OR_LMP_FEATURE)));
if (!security_manager_.AuthenticationInProgress()) {
security_manager_.AuthenticationRequest(incoming.GetSourceAddress(),
handle, false);
}
security_manager_.SetPinRequested(peer);
if (IsEventUnmasked(EventCode::PIN_CODE_REQUEST)) {
send_event_(bluetooth::hci::PinCodeRequestBuilder::Create(
incoming.GetSourceAddress()));
}
return;
}
auto request = model::packets::IoCapabilityRequestView::Create(incoming);
ASSERT(request.IsValid());
uint8_t io_capability = request.GetIoCapability();
uint8_t oob_data_present = request.GetOobDataPresent();
uint8_t authentication_requirements = request.GetAuthenticationRequirements();
if (IsEventUnmasked(EventCode::IO_CAPABILITY_RESPONSE)) {
send_event_(bluetooth::hci::IoCapabilityResponseBuilder::Create(
peer, static_cast<bluetooth::hci::IoCapability>(io_capability),
static_cast<bluetooth::hci::OobDataPresent>(oob_data_present),
static_cast<bluetooth::hci::AuthenticationRequirements>(
authentication_requirements)));
}
bool pairing_started = security_manager_.AuthenticationInProgress();
if (!pairing_started) {
security_manager_.AuthenticationRequest(peer, handle, false);
StartSimplePairing(peer);
}
security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
authentication_requirements);
if (pairing_started) {
PairingType pairing_type = security_manager_.GetSimplePairingType();
if (pairing_type != PairingType::INVALID) {
ScheduleTask(kNoDelayMs, [this, peer, pairing_type]() {
AuthenticateRemoteStage1(peer, pairing_type);
});
} else {
LOG_INFO("Security Manager returned INVALID");
}
}
}
void LinkLayerController::IncomingIoCapabilityResponsePacket(
model::packets::LinkLayerPacketView incoming) {
auto response = model::packets::IoCapabilityResponseView::Create(incoming);
ASSERT(response.IsValid());
if (!secure_simple_pairing_host_support_) {
LOG_WARN("Only simple pairing mode is implemented");
SendLinkLayerPacket(
model::packets::IoCapabilityNegativeResponseBuilder::Create(
incoming.GetDestinationAddress(), incoming.GetSourceAddress(),
static_cast<uint8_t>(
ErrorCode::UNSUPPORTED_REMOTE_OR_LMP_FEATURE)));
return;
}
Address peer = incoming.GetSourceAddress();
uint8_t io_capability = response.GetIoCapability();
uint8_t oob_data_present = response.GetOobDataPresent();
uint8_t authentication_requirements =
response.GetAuthenticationRequirements();
security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present,
authentication_requirements);
if (IsEventUnmasked(EventCode::IO_CAPABILITY_RESPONSE)) {
send_event_(bluetooth::hci::IoCapabilityResponseBuilder::Create(
peer, static_cast<bluetooth::hci::IoCapability>(io_capability),
static_cast<bluetooth::hci::OobDataPresent>(oob_data_present),
static_cast<bluetooth::hci::AuthenticationRequirements>(
authentication_requirements)));
}
PairingType pairing_type = security_manager_.GetSimplePairingType();
if (pairing_type != PairingType::INVALID) {
ScheduleTask(kNoDelayMs, [this, peer, pairing_type]() {
AuthenticateRemoteStage1(peer, pairing_type);
});
} else {
LOG_INFO("Security Manager returned INVALID");
}
}
void LinkLayerController::IncomingIoCapabilityNegativeResponsePacket(
model::packets::LinkLayerPacketView incoming) {
Address peer = incoming.GetSourceAddress();
ASSERT(security_manager_.GetAuthenticationAddress() == peer);
security_manager_.InvalidateIoCapabilities();
LOG_INFO("%s doesn't support SSP, try PIN",
incoming.GetSourceAddress().ToString().c_str());
security_manager_.SetPinRequested(peer);
if (IsEventUnmasked(EventCode::PIN_CODE_REQUEST)) {
send_event_(bluetooth::hci::PinCodeRequestBuilder::Create(
incoming.GetSourceAddress()));
}
}
#endif /* !ROOTCANAL_LMP */
void LinkLayerController::IncomingIsoPacket(LinkLayerPacketView incoming) {
auto iso = IsoDataPacketView::Create(incoming);
ASSERT(iso.IsValid());
uint16_t cis_handle = iso.GetHandle();
if (!connections_.HasCisHandle(cis_handle)) {
LOG_INFO("Dropping ISO packet to unknown handle 0x%hx", cis_handle);
return;
}
if (!connections_.HasConnectedCis(cis_handle)) {
LOG_INFO("Dropping ISO packet to a disconnected handle 0x%hx", cis_handle);
return;
}
auto sc = iso.GetSc();
switch (sc) {
case StartContinuation::START: {
auto iso_start = IsoStartView::Create(iso);
ASSERT(iso_start.IsValid());
if (iso.GetCmplt() == Complete::COMPLETE) {
send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create(
cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU,
0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID,
std::make_unique<bluetooth::packet::RawBuilder>(
std::vector<uint8_t>(iso_start.GetPayload().begin(),
iso_start.GetPayload().end()))));
} else {
send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create(
cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT,
0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID,
std::make_unique<bluetooth::packet::RawBuilder>(
std::vector<uint8_t>(iso_start.GetPayload().begin(),
iso_start.GetPayload().end()))));
}
} break;
case StartContinuation::CONTINUATION: {
auto continuation = IsoContinuationView::Create(iso);
ASSERT(continuation.IsValid());
if (iso.GetCmplt() == Complete::COMPLETE) {
send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create(
cis_handle, bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT,
0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID,
std::make_unique<bluetooth::packet::RawBuilder>(
std::vector<uint8_t>(continuation.GetPayload().begin(),
continuation.GetPayload().end()))));
} else {
send_iso_(bluetooth::hci::IsoWithoutTimestampBuilder::Create(
cis_handle,
bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT,
0 /* seq num */, bluetooth::hci::IsoPacketStatusFlag::VALID,
std::make_unique<bluetooth::packet::RawBuilder>(
std::vector<uint8_t>(continuation.GetPayload().begin(),
continuation.GetPayload().end()))));
}
} break;
}
}
void LinkLayerController::HandleIso(bluetooth::hci::IsoView iso) {
auto cis_handle = iso.GetConnectionHandle();
if (!connections_.HasCisHandle(cis_handle)) {
LOG_INFO("Dropping ISO packet to unknown handle 0x%hx", cis_handle);
return;
}
if (!connections_.HasConnectedCis(cis_handle)) {
LOG_INFO("Dropping ISO packet to disconnected handle 0x%hx", cis_handle);
return;
}
auto acl_handle = connections_.GetAclHandleForCisHandle(cis_handle);
uint16_t remote_handle =
connections_.GetRemoteCisHandleForCisHandle(cis_handle);
model::packets::StartContinuation start_flag =
model::packets::StartContinuation::START;
model::packets::Complete complete_flag = model::packets::Complete::COMPLETE;
switch (iso.GetPbFlag()) {
case bluetooth::hci::IsoPacketBoundaryFlag::COMPLETE_SDU:
start_flag = model::packets::StartContinuation::START;
complete_flag = model::packets::Complete::COMPLETE;
break;
case bluetooth::hci::IsoPacketBoundaryFlag::CONTINUATION_FRAGMENT:
start_flag = model::packets::StartContinuation::CONTINUATION;
complete_flag = model::packets::Complete::INCOMPLETE;
break;
case bluetooth::hci::IsoPacketBoundaryFlag::FIRST_FRAGMENT:
start_flag = model::packets::StartContinuation::START;
complete_flag = model::packets::Complete::INCOMPLETE;
break;
case bluetooth::hci::IsoPacketBoundaryFlag::LAST_FRAGMENT:
start_flag = model::packets::StartContinuation::CONTINUATION;
complete_flag = model::packets::Complete::INCOMPLETE;
break;
}
if (start_flag == model::packets::StartContinuation::START) {
if (iso.GetTsFlag() == bluetooth::hci::TimeStampFlag::PRESENT) {
auto timestamped = bluetooth::hci::IsoWithTimestampView::Create(iso);
ASSERT(timestamped.IsValid());
uint32_t timestamp = timestamped.GetTimeStamp();
std::unique_ptr<bluetooth::packet::RawBuilder> payload =
std::make_unique<bluetooth::packet::RawBuilder>();
for (const auto it : timestamped.GetPayload()) {
payload->AddOctets1(it);
}
SendLeLinkLayerPacket(model::packets::IsoStartBuilder::Create(
connections_.GetOwnAddress(acl_handle).GetAddress(),
connections_.GetAddress(acl_handle).GetAddress(), remote_handle,
complete_flag, timestamp, std::move(payload)));
} else {
auto pkt = bluetooth::hci::IsoWithoutTimestampView::Create(iso);
ASSERT(pkt.IsValid());
auto payload =
std::make_unique<bluetooth::packet::RawBuilder>(std::vector<uint8_t>(
pkt.GetPayload().begin(), pkt.GetPayload().end()));
SendLeLinkLayerPacket(model::packets::IsoStartBuilder::Create(
connections_.GetOwnAddress(acl_handle).GetAddress(),
connections_.GetAddress(acl_handle).GetAddress(), remote_handle,
complete_flag, 0, std::move(payload)));
}
} else {
auto pkt = bluetooth::hci::IsoWithoutTimestampView::Create(iso);
ASSERT(pkt.IsValid());
std::unique_ptr<bluetooth::packet::RawBuilder> payload =
std::make_unique<bluetooth::packet::RawBuilder>(std::vector<uint8_t>(
pkt.GetPayload().begin(), pkt.GetPayload().end()));
SendLeLinkLayerPacket(model::packets::IsoContinuationBuilder::Create(
connections_.GetOwnAddress(acl_handle).GetAddress(),
connections_.GetAddress(acl_handle).GetAddress(), remote_handle,
complete_flag, std::move(payload)));
}
}
void LinkLayerController::IncomingIsoConnectionRequestPacket(
LinkLayerPacketView incoming) {
auto req = IsoConnectionRequestView::Create(incoming);
ASSERT(req.IsValid());
std::vector<bluetooth::hci::CisParametersConfig> stream_configs;
bluetooth::hci::CisParametersConfig stream_config;
stream_config.max_sdu_m_to_s_ = req.GetMaxSduMToS();
stream_config.max_sdu_s_to_m_ = req.GetMaxSduSToM();
stream_configs.push_back(stream_config);
uint8_t group_id = req.GetCigId();
/* CIG should be created by the local host before use */
bluetooth::hci::CreateCisConfig config;
config.cis_connection_handle_ = req.GetRequesterCisHandle();
config.acl_connection_handle_ =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
connections_.CreatePendingCis(config);
connections_.SetRemoteCisHandle(config.cis_connection_handle_,
req.GetRequesterCisHandle());
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeCisRequestBuilder::Create(
config.acl_connection_handle_, config.cis_connection_handle_, group_id,
req.GetId()));
}
}
void LinkLayerController::IncomingIsoConnectionResponsePacket(
LinkLayerPacketView incoming) {
auto response = IsoConnectionResponseView::Create(incoming);
ASSERT(response.IsValid());
bluetooth::hci::CreateCisConfig config;
config.acl_connection_handle_ = response.GetRequesterAclHandle();
config.cis_connection_handle_ = response.GetRequesterCisHandle();
if (!connections_.HasPendingCisConnection(config.cis_connection_handle_)) {
LOG_INFO("Ignoring connection response with unknown CIS handle 0x%04hx",
config.cis_connection_handle_);
return;
}
ErrorCode status = static_cast<ErrorCode>(response.GetStatus());
if (status != ErrorCode::SUCCESS) {
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create(
status, config.cis_connection_handle_, 0, 0, 0, 0,
bluetooth::hci::SecondaryPhyType::NO_PACKETS,
bluetooth::hci::SecondaryPhyType::NO_PACKETS, 0, 0, 0, 0, 0, 0, 0,
0));
}
return;
}
connections_.SetRemoteCisHandle(config.cis_connection_handle_,
response.GetResponderCisHandle());
connections_.ConnectCis(config.cis_connection_handle_);
auto stream_parameters =
connections_.GetStreamParameters(config.cis_connection_handle_);
auto group_parameters =
connections_.GetGroupParameters(stream_parameters.group_id);
// TODO: Which of these are important enough to fake?
uint32_t cig_sync_delay = 0x100;
uint32_t cis_sync_delay = 0x200;
uint32_t latency_m_to_s = group_parameters.max_transport_latency_m_to_s;
uint32_t latency_s_to_m = group_parameters.max_transport_latency_s_to_m;
uint8_t nse = 1;
uint8_t bn_m_to_s = 0;
uint8_t bn_s_to_m = 0;
uint8_t ft_m_to_s = 0;
uint8_t ft_s_to_m = 0;
uint8_t max_pdu_m_to_s = 0x40;
uint8_t max_pdu_s_to_m = 0x40;
uint16_t iso_interval = 0x100;
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create(
status, config.cis_connection_handle_, cig_sync_delay, cis_sync_delay,
latency_m_to_s, latency_s_to_m,
bluetooth::hci::SecondaryPhyType::NO_PACKETS,
bluetooth::hci::SecondaryPhyType::NO_PACKETS, nse, bn_m_to_s, bn_s_to_m,
ft_m_to_s, ft_s_to_m, max_pdu_m_to_s, max_pdu_s_to_m, iso_interval));
}
}
#ifndef ROOTCANAL_LMP
void LinkLayerController::IncomingKeypressNotificationPacket(
model::packets::LinkLayerPacketView incoming) {
auto keypress = model::packets::KeypressNotificationView::Create(incoming);
ASSERT(keypress.IsValid());
auto notification_type = keypress.GetNotificationType();
if (notification_type >
model::packets::PasskeyNotificationType::ENTRY_COMPLETED) {
LOG_WARN("Dropping unknown notification type %d",
static_cast<int>(notification_type));
return;
}
if (IsEventUnmasked(EventCode::KEYPRESS_NOTIFICATION)) {
send_event_(bluetooth::hci::KeypressNotificationBuilder::Create(
incoming.GetSourceAddress(),
static_cast<bluetooth::hci::KeypressNotificationType>(
notification_type)));
}
}
#endif /* !ROOTCANAL_LMP */
static Address generate_rpa(
std::array<uint8_t, LinkLayerController::kIrkSize> irk) {
// most significant bit, bit7, bit6 is 01 to be resolvable random
// Bits of the random part of prand shall not be all 1 or all 0
std::array<uint8_t, 3> prand;
prand[0] = std::rand();
prand[1] = std::rand();
prand[2] = std::rand();
constexpr uint8_t BLE_RESOLVE_ADDR_MSB = 0x40;
prand[2] &= ~0xC0; // BLE Address mask
if ((prand[0] == 0x00 && prand[1] == 0x00 && prand[2] == 0x00) ||
(prand[0] == 0xFF && prand[1] == 0xFF && prand[2] == 0x3F)) {
prand[0] = (uint8_t)(std::rand() % 0xFE + 1);
}
prand[2] |= BLE_RESOLVE_ADDR_MSB;
Address rpa;
rpa.address[3] = prand[0];
rpa.address[4] = prand[1];
rpa.address[5] = prand[2];
/* encrypt with IRK */
bluetooth::crypto_toolbox::Octet16 p =
bluetooth::crypto_toolbox::aes_128(irk, prand.data(), 3);
/* set hash to be LSB of rpAddress */
rpa.address[0] = p[0];
rpa.address[1] = p[1];
rpa.address[2] = p[2];
LOG_INFO("RPA %s", rpa.ToString().c_str());
return rpa;
}
// Handle legacy advertising PDUs while in the Scanning state.
void LinkLayerController::ScanIncomingLeLegacyAdvertisingPdu(
model::packets::LeLegacyAdvertisingPduView& pdu, uint8_t rssi) {
if (!scanner_.IsEnabled()) {
return;
}
auto advertising_type = pdu.GetAdvertisingType();
std::vector<uint8_t> advertising_data = pdu.GetAdvertisingData();
AddressWithType advertising_address{
pdu.GetSourceAddress(),
static_cast<AddressType>(pdu.GetAdvertisingAddressType())};
AddressWithType target_address{
pdu.GetDestinationAddress(),
static_cast<AddressType>(pdu.GetTargetAddressType())};
bool scannable_advertising =
advertising_type == model::packets::LegacyAdvertisingType::ADV_IND ||
advertising_type == model::packets::LegacyAdvertisingType::ADV_SCAN_IND;
bool directed_advertising =
advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
bool connectable_advertising =
advertising_type == model::packets::LegacyAdvertisingType::ADV_IND ||
advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
// TODO: check originating PHY, compare against active scanning PHYs
// (scanner_.le_1m_phy or scanner_.le_coded_phy).
// When a scanner receives an advertising packet that contains a resolvable
// private address for the advertisers device address (AdvA field) and
// address resolution is enabled, the Link Layer shall resolve the private
// address. The scanners filter policy shall then determine if the scanner
// responds with a scan request.
AddressWithType resolved_advertising_address =
ResolvePrivateAddress(advertising_address, IrkSelection::Peer)
.value_or(advertising_address);
std::optional<AddressWithType> resolved_target_address =
ResolvePrivateAddress(target_address, IrkSelection::Peer);
if (resolved_advertising_address != advertising_address) {
LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
}
// Vol 6, Part B § 4.3.3 Scanner filter policy
switch (scanner_.scan_filter_policy) {
case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL:
case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY:
break;
case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY:
case bluetooth::hci::LeScanningFilterPolicy::
FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY:
if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) {
LOG_VERB(
"Legacy advertising ignored by scanner because the advertising "
"address %s(%hhx) is not in the filter accept list",
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
return;
}
break;
}
// When LE_Set_Scan_Enable is used:
//
// When the Scanning_Filter_Policy is set to 0x02 or 0x03 (see Section 7.8.10)
// and a directed advertisement was received where the advertiser used a
// resolvable private address which the Controller is unable to resolve, an
// HCI_LE_Directed_Advertising_Report event shall be generated instead of an
// HCI_LE_Advertising_Report event.
bool should_send_directed_advertising_report = false;
if (directed_advertising) {
switch (scanner_.scan_filter_policy) {
// In both basic scanner filter policy modes, a directed advertising PDU
// shall be ignored unless either:
// • the TargetA field is identical to the scanner's device address, or
// • the TargetA field is a resolvable private address, address
// resolution is
// enabled, and the address is resolved successfully
case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL:
case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY:
if (!IsLocalPublicOrRandomAddress(target_address) &&
!(target_address.IsRpa() && resolved_target_address)) {
LOG_VERB(
"Legacy advertising ignored by scanner because the directed "
"address %s(%hhx) does not match the current device or cannot be "
"resolved",
target_address.ToString().c_str(),
target_address.GetAddressType());
return;
}
break;
// These are identical to the basic modes except
// that a directed advertising PDU shall be ignored unless either:
// • the TargetA field is identical to the scanner's device address, or
// • the TargetA field is a resolvable private address.
case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY:
case bluetooth::hci::LeScanningFilterPolicy::
FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY:
if (!IsLocalPublicOrRandomAddress(target_address) &&
!target_address.IsRpa()) {
LOG_VERB(
"Legacy advertising ignored by scanner because the directed "
"address %s(%hhx) does not match the current device or is not a "
"resovable private address",
target_address.ToString().c_str(),
target_address.GetAddressType());
return;
}
should_send_directed_advertising_report =
target_address.IsRpa() && !resolved_target_address;
break;
}
}
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
if (scanner_.IsPacketInHistory(pdu)) {
should_send_advertising_report = false;
} else {
scanner_.AddPacketToHistory(pdu);
}
}
// Legacy scanning, directed advertising.
if (LegacyAdvertising() && should_send_advertising_report &&
should_send_directed_advertising_report &&
IsLeEventUnmasked(SubeventCode::DIRECTED_ADVERTISING_REPORT)) {
bluetooth::hci::LeDirectedAdvertisingResponse response;
response.event_type_ =
bluetooth::hci::DirectAdvertisingEventType::ADV_DIRECT_IND;
response.address_type_ =
static_cast<bluetooth::hci::DirectAdvertisingAddressType>(
resolved_advertising_address.GetAddressType());
response.address_ = resolved_advertising_address.GetAddress();
response.direct_address_type_ =
bluetooth::hci::DirectAddressType::RANDOM_DEVICE_ADDRESS;
response.direct_address_ = target_address.GetAddress();
response.rssi_ = rssi;
send_event_(
bluetooth::hci::LeDirectedAdvertisingReportBuilder::Create({response}));
}
// Legacy scanning, un-directed advertising.
if (LegacyAdvertising() && should_send_advertising_report &&
!should_send_directed_advertising_report &&
IsLeEventUnmasked(SubeventCode::ADVERTISING_REPORT)) {
bluetooth::hci::LeAdvertisingResponseRaw response;
response.address_type_ = resolved_advertising_address.GetAddressType();
response.address_ = resolved_advertising_address.GetAddress();
response.advertising_data_ = advertising_data;
response.rssi_ = rssi;
switch (advertising_type) {
case model::packets::LegacyAdvertisingType::ADV_IND:
response.event_type_ = bluetooth::hci::AdvertisingEventType::ADV_IND;
break;
case model::packets::LegacyAdvertisingType::ADV_DIRECT_IND:
response.event_type_ =
bluetooth::hci::AdvertisingEventType::ADV_DIRECT_IND;
break;
case model::packets::LegacyAdvertisingType::ADV_SCAN_IND:
response.event_type_ =
bluetooth::hci::AdvertisingEventType::ADV_SCAN_IND;
break;
case model::packets::LegacyAdvertisingType::ADV_NONCONN_IND:
response.event_type_ =
bluetooth::hci::AdvertisingEventType::ADV_NONCONN_IND;
break;
}
send_event_(
bluetooth::hci::LeAdvertisingReportRawBuilder::Create({response}));
}
// Extended scanning.
if (ExtendedAdvertising() && should_send_advertising_report &&
IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) {
bluetooth::hci::LeExtendedAdvertisingResponseRaw response;
response.connectable_ = connectable_advertising;
response.scannable_ = scannable_advertising;
response.directed_ = directed_advertising;
response.scan_response_ = false;
response.legacy_ = true;
response.data_status_ = bluetooth::hci::DataStatus::COMPLETE;
response.address_type_ =
static_cast<bluetooth::hci::DirectAdvertisingAddressType>(
resolved_advertising_address.GetAddressType());
response.address_ = resolved_advertising_address.GetAddress();
response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M;
response.secondary_phy_ = bluetooth::hci::SecondaryPhyType::NO_PACKETS;
response.advertising_sid_ = 0xff; // Not ADI field provided.
response.tx_power_ = 0x7f; // TX power information not available.
response.rssi_ = rssi;
response.periodic_advertising_interval_ = 0; // No periodic advertising.
response.direct_address_type_ =
bluetooth::hci::DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED;
response.direct_address_ = Address::kEmpty;
response.advertising_data_ = advertising_data;
send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create(
{response}));
}
// Did the user enable Active scanning ?
bool active_scanning =
(scanner_.le_1m_phy.enabled &&
scanner_.le_1m_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE) ||
(scanner_.le_coded_phy.enabled &&
scanner_.le_coded_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE);
// Active scanning.
// Note: only send SCAN requests in response to scannable advertising
// events (ADV_IND, ADV_SCAN_IND).
if (!scannable_advertising) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"it is not scannable",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (!active_scanning) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"the scanner is passive",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (scanner_.pending_scan_request) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"an LE Scan request is already pending",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (!should_send_advertising_report) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"the advertising message was filtered",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else {
// TODO: apply privacy mode in resolving list.
// Scan requests with public or random device addresses must be ignored
// when the peer has network privacy mode.
AddressWithType public_address{address_,
AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{random_address_,
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_scanning_address =
GenerateResolvablePrivateAddress(resolved_advertising_address,
IrkSelection::Local);
// The ScanA field of the scanning PDU is generated using the
// Resolving Lists Local IRK value and the Resolvable Private Address
// Generation procedure (see Section 1.3.2.2), or the address is provided
// by the Host.
AddressWithType scanning_address;
switch (scanner_.own_address_type) {
case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
scanning_address = public_address;
break;
case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
// The random address is checked in Le_Set_Scan_Enable or
// Le_Set_Extended_Scan_Enable.
ASSERT(random_address_ != Address::kEmpty);
scanning_address = random_address;
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
scanning_address = resolvable_scanning_address.value_or(public_address);
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// The random address is checked in Le_Set_Scan_Enable or
// Le_Set_Extended_Scan_Enable.
ASSERT(random_address_ != Address::kEmpty);
scanning_address = resolvable_scanning_address.value_or(random_address);
break;
}
// Save the original advertising type to report if the advertising
// is connectable in the scan response report.
scanner_.connectable_scan_response = connectable_advertising;
scanner_.pending_scan_request = advertising_address;
LOG_INFO(
"Sending LE Scan request to advertising address %s(%hhx) with scanning "
"address %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
scanning_address.ToString().c_str(), scanning_address.GetAddressType());
// The advertisers device address (AdvA field) in the scan request PDU
// shall be the same as the advertisers device address (AdvA field)
// received in the advertising PDU to which the scanner is responding.
SendLeLinkLayerPacket(model::packets::LeScanBuilder::Create(
scanning_address.GetAddress(), advertising_address.GetAddress(),
static_cast<model::packets::AddressType>(
scanning_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType())));
}
}
void LinkLayerController::ConnectIncomingLeLegacyAdvertisingPdu(
model::packets::LeLegacyAdvertisingPduView& pdu) {
if (!initiator_.IsEnabled()) {
return;
}
auto advertising_type = pdu.GetAdvertisingType();
bool connectable_advertising =
advertising_type == model::packets::LegacyAdvertisingType::ADV_IND ||
advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
bool directed_advertising =
advertising_type == model::packets::LegacyAdvertisingType::ADV_DIRECT_IND;
// Connection.
// Note: only send CONNECT requests in response to connectable advertising
// events (ADV_IND, ADV_DIRECT_IND).
if (!connectable_advertising) {
LOG_VERB(
"Legacy advertising ignored by initiator because it is not "
"connectable");
return;
}
if (initiator_.pending_connect_request) {
LOG_VERB(
"Legacy advertising ignored because an LE Connect request is already "
"pending");
return;
}
AddressWithType advertising_address{
pdu.GetSourceAddress(),
static_cast<AddressType>(pdu.GetAdvertisingAddressType())};
AddressWithType target_address{
pdu.GetDestinationAddress(),
static_cast<AddressType>(pdu.GetTargetAddressType())};
AddressWithType resolved_advertising_address =
ResolvePrivateAddress(advertising_address, IrkSelection::Peer)
.value_or(advertising_address);
AddressWithType resolved_target_address =
ResolvePrivateAddress(target_address, IrkSelection::Peer)
.value_or(target_address);
// Vol 6, Part B § 4.3.5 Initiator filter policy.
switch (initiator_.initiator_filter_policy) {
case bluetooth::hci::InitiatorFilterPolicy::USE_PEER_ADDRESS:
if (resolved_advertising_address != initiator_.peer_address) {
LOG_VERB(
"Legacy advertising ignored by initiator because the "
"advertising address %s does not match the peer address %s",
resolved_advertising_address.ToString().c_str(),
initiator_.peer_address.ToString().c_str());
return;
}
break;
case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST:
if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) {
LOG_VERB(
"Legacy advertising ignored by initiator because the "
"advertising address %s is not in the filter accept list",
resolved_advertising_address.ToString().c_str());
return;
}
break;
}
// When an initiator receives a directed connectable advertising event that
// contains a resolvable private address for the targets address
// (TargetA field) and address resolution is enabled, the Link Layer shall
// resolve the private address using the resolving lists Local IRK values.
// An initiator that has been instructed by the Host to use Resolvable Private
// Addresses shall not respond to directed connectable advertising events that
// contain Public or Static addresses for the targets address (TargetA
// field).
if (directed_advertising) {
if (!IsLocalPublicOrRandomAddress(resolved_target_address)) {
LOG_VERB(
"Directed legacy advertising ignored by initiator because the "
"target address %s does not match the current device addresses",
resolved_advertising_address.ToString().c_str());
return;
}
if (resolved_target_address == target_address &&
(initiator_.own_address_type ==
OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS ||
initiator_.own_address_type ==
OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS)) {
LOG_VERB(
"Directed legacy advertising ignored by initiator because the "
"target address %s is static or public and the initiator is "
"configured to use resolvable addresses",
resolved_advertising_address.ToString().c_str());
return;
}
}
AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{random_address_,
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_initiating_address =
GenerateResolvablePrivateAddress(resolved_advertising_address,
IrkSelection::Local);
// The Link Layer shall use resolvable private addresses for the initiators
// device address (InitA field) when initiating connection establishment with
// an associated device that exists in the Resolving List.
AddressWithType initiating_address;
switch (initiator_.own_address_type) {
case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
initiating_address = public_address;
break;
case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
// The random address is checked in Le_Create_Connection or
// Le_Extended_Create_Connection.
ASSERT(random_address_ != Address::kEmpty);
initiating_address = random_address;
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
initiating_address =
resolvable_initiating_address.value_or(public_address);
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// The random address is checked in Le_Create_Connection or
// Le_Extended_Create_Connection.
ASSERT(random_address_ != Address::kEmpty);
initiating_address =
resolvable_initiating_address.value_or(random_address);
break;
}
if (!connections_.CreatePendingLeConnection(
advertising_address,
resolved_advertising_address != advertising_address
? resolved_advertising_address
: AddressWithType{},
initiating_address)) {
LOG_WARN("CreatePendingLeConnection failed for connection to %s",
advertising_address.ToString().c_str());
}
initiator_.pending_connect_request = advertising_address;
LOG_INFO("Sending LE Connect request to %s with initiating address %s",
resolved_advertising_address.ToString().c_str(),
initiating_address.ToString().c_str());
// The advertisers device address (AdvA field) in the initiating PDU
// shall be the same as the advertisers device address (AdvA field)
// received in the advertising event PDU to which the initiator is
// responding.
SendLeLinkLayerPacket(model::packets::LeConnectBuilder::Create(
initiating_address.GetAddress(), advertising_address.GetAddress(),
static_cast<model::packets::AddressType>(
initiating_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
initiator_.le_1m_phy.connection_interval_min,
initiator_.le_1m_phy.connection_interval_max,
initiator_.le_1m_phy.max_latency,
initiator_.le_1m_phy.supervision_timeout));
}
void LinkLayerController::IncomingLeLegacyAdvertisingPdu(
model::packets::LinkLayerPacketView incoming, uint8_t rssi) {
auto pdu = model::packets::LeLegacyAdvertisingPduView::Create(incoming);
ASSERT(pdu.IsValid());
ScanIncomingLeLegacyAdvertisingPdu(pdu, rssi);
ConnectIncomingLeLegacyAdvertisingPdu(pdu);
}
// Handle legacy advertising PDUs while in the Scanning state.
void LinkLayerController::ScanIncomingLeExtendedAdvertisingPdu(
model::packets::LeExtendedAdvertisingPduView& pdu, uint8_t rssi) {
if (!scanner_.IsEnabled()) {
return;
}
if (!ExtendedAdvertising()) {
LOG_VERB("Extended advertising ignored because the scanner is legacy");
return;
}
std::vector<uint8_t> advertising_data = pdu.GetAdvertisingData();
AddressWithType advertising_address{
pdu.GetSourceAddress(),
static_cast<AddressType>(pdu.GetAdvertisingAddressType())};
AddressWithType target_address{
pdu.GetDestinationAddress(),
static_cast<AddressType>(pdu.GetTargetAddressType())};
bool scannable_advertising = pdu.GetScannable();
bool connectable_advertising = pdu.GetConnectable();
bool directed_advertising = pdu.GetDirected();
// TODO: check originating PHY, compare against active scanning PHYs
// (scanner_.le_1m_phy or scanner_.le_coded_phy).
// When a scanner receives an advertising packet that contains a resolvable
// private address for the advertisers device address (AdvA field) and
// address resolution is enabled, the Link Layer shall resolve the private
// address. The scanners filter policy shall then determine if the scanner
// responds with a scan request.
AddressWithType resolved_advertising_address =
ResolvePrivateAddress(advertising_address, IrkSelection::Peer)
.value_or(advertising_address);
std::optional<AddressWithType> resolved_target_address =
ResolvePrivateAddress(target_address, IrkSelection::Peer);
if (resolved_advertising_address != advertising_address) {
LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
}
// Vol 6, Part B § 4.3.3 Scanner filter policy
switch (scanner_.scan_filter_policy) {
case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL:
case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY:
break;
case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY:
case bluetooth::hci::LeScanningFilterPolicy::
FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY:
if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) {
LOG_VERB(
"Extended advertising ignored by scanner because the advertising "
"address %s(%hhx) is not in the filter accept list",
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
return;
}
break;
}
if (directed_advertising) {
switch (scanner_.scan_filter_policy) {
// In both basic scanner filter policy modes, a directed advertising PDU
// shall be ignored unless either:
// • the TargetA field is identical to the scanner's device address, or
// • the TargetA field is a resolvable private address, address
// resolution is enabled, and the address is resolved successfully
case bluetooth::hci::LeScanningFilterPolicy::ACCEPT_ALL:
case bluetooth::hci::LeScanningFilterPolicy::FILTER_ACCEPT_LIST_ONLY:
if (!IsLocalPublicOrRandomAddress(target_address) &&
!(target_address.IsRpa() && resolved_target_address)) {
LOG_VERB(
"Extended advertising ignored by scanner because the directed "
"address %s(%hhx) does not match the current device or cannot be "
"resolved",
target_address.ToString().c_str(),
target_address.GetAddressType());
return;
}
break;
// These are identical to the basic modes except
// that a directed advertising PDU shall be ignored unless either:
// • the TargetA field is identical to the scanner's device address, or
// • the TargetA field is a resolvable private address.
case bluetooth::hci::LeScanningFilterPolicy::CHECK_INITIATORS_IDENTITY:
case bluetooth::hci::LeScanningFilterPolicy::
FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY:
if (!IsLocalPublicOrRandomAddress(target_address) &&
!target_address.IsRpa()) {
LOG_VERB(
"Extended advertising ignored by scanner because the directed "
"address %s(%hhx) does not match the current device or is not a "
"resovable private address",
target_address.ToString().c_str(),
target_address.GetAddressType());
return;
}
break;
}
}
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
if (scanner_.IsPacketInHistory(pdu)) {
should_send_advertising_report = false;
} else {
scanner_.AddPacketToHistory(pdu);
}
}
if (should_send_advertising_report &&
IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) {
bluetooth::hci::LeExtendedAdvertisingResponseRaw response;
response.connectable_ = connectable_advertising;
response.scannable_ = scannable_advertising;
response.directed_ = directed_advertising;
response.scan_response_ = false;
response.legacy_ = false;
response.data_status_ = bluetooth::hci::DataStatus::COMPLETE;
response.address_type_ =
static_cast<bluetooth::hci::DirectAdvertisingAddressType>(
resolved_advertising_address.GetAddressType());
response.address_ = resolved_advertising_address.GetAddress();
response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M;
response.secondary_phy_ = bluetooth::hci::SecondaryPhyType::NO_PACKETS;
response.advertising_sid_ = 0xff; // Not ADI field provided.
response.tx_power_ = 0x7f; // TX power information not available.
response.rssi_ = rssi;
response.periodic_advertising_interval_ = 0; // No periodic advertising.
response.direct_address_type_ =
bluetooth::hci::DirectAdvertisingAddressType::NO_ADDRESS_PROVIDED;
response.direct_address_ = Address::kEmpty;
response.advertising_data_ = advertising_data;
send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create(
{response}));
}
// Did the user enable Active scanning ?
bool active_scanning =
(scanner_.le_1m_phy.enabled &&
scanner_.le_1m_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE) ||
(scanner_.le_coded_phy.enabled &&
scanner_.le_coded_phy.scan_type == bluetooth::hci::LeScanType::ACTIVE);
// Active scanning.
// Note: only send SCAN requests in response to scannable advertising
// events (ADV_IND, ADV_SCAN_IND).
if (!scannable_advertising) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"it is not scannable",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (!active_scanning) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"the scanner is passive",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (scanner_.pending_scan_request) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"an LE Scan request is already pending",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else if (!should_send_advertising_report) {
LOG_VERB(
"Not sending LE Scan request to advertising address %s(%hhx) because "
"the advertising message was filtered",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
} else {
// TODO: apply privacy mode in resolving list.
// Scan requests with public or random device addresses must be ignored
// when the peer has network privacy mode.
AddressWithType public_address{address_,
AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{random_address_,
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_address =
GenerateResolvablePrivateAddress(resolved_advertising_address,
IrkSelection::Local);
// The ScanA field of the scanning PDU is generated using the
// Resolving Lists Local IRK value and the Resolvable Private Address
// Generation procedure (see Section 1.3.2.2), or the address is provided
// by the Host.
AddressWithType scanning_address;
std::optional<AddressWithType> resolvable_scanning_address;
switch (scanner_.own_address_type) {
case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
scanning_address = public_address;
break;
case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
// The random address is checked in Le_Set_Scan_Enable or
// Le_Set_Extended_Scan_Enable.
ASSERT(random_address_ != Address::kEmpty);
scanning_address = random_address;
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
scanning_address = resolvable_address.value_or(public_address);
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// The random address is checked in Le_Set_Scan_Enable or
// Le_Set_Extended_Scan_Enable.
ASSERT(random_address_ != Address::kEmpty);
scanning_address = resolvable_address.value_or(random_address);
break;
}
// Save the original advertising type to report if the advertising
// is connectable in the scan response report.
scanner_.connectable_scan_response = connectable_advertising;
scanner_.pending_scan_request = advertising_address;
LOG_INFO(
"Sending LE Scan request to advertising address %s(%hhx) with scanning "
"address %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
scanning_address.ToString().c_str(), scanning_address.GetAddressType());
// The advertisers device address (AdvA field) in the scan request PDU
// shall be the same as the advertisers device address (AdvA field)
// received in the advertising PDU to which the scanner is responding.
SendLeLinkLayerPacket(model::packets::LeScanBuilder::Create(
scanning_address.GetAddress(), advertising_address.GetAddress(),
static_cast<model::packets::AddressType>(
scanning_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType())));
}
}
void LinkLayerController::ConnectIncomingLeExtendedAdvertisingPdu(
model::packets::LeExtendedAdvertisingPduView& pdu) {
if (!initiator_.IsEnabled()) {
return;
}
if (!ExtendedAdvertising()) {
LOG_VERB("Extended advertising ignored because the initiator is legacy");
return;
}
// Connection.
// Note: only send CONNECT requests in response to connectable advertising
// events (ADV_IND, ADV_DIRECT_IND).
if (!pdu.GetConnectable()) {
LOG_VERB(
"Extended advertising ignored by initiator because it is not "
"connectable");
return;
}
if (initiator_.pending_connect_request) {
LOG_VERB(
"Extended advertising ignored because an LE Connect request is already "
"pending");
return;
}
AddressWithType advertising_address{
pdu.GetSourceAddress(),
static_cast<AddressType>(pdu.GetAdvertisingAddressType())};
AddressWithType target_address{
pdu.GetDestinationAddress(),
static_cast<AddressType>(pdu.GetTargetAddressType())};
AddressWithType resolved_advertising_address =
ResolvePrivateAddress(advertising_address, IrkSelection::Peer)
.value_or(advertising_address);
AddressWithType resolved_target_address =
ResolvePrivateAddress(target_address, IrkSelection::Peer)
.value_or(target_address);
// Vol 6, Part B § 4.3.5 Initiator filter policy.
switch (initiator_.initiator_filter_policy) {
case bluetooth::hci::InitiatorFilterPolicy::USE_PEER_ADDRESS:
if (resolved_advertising_address != initiator_.peer_address) {
LOG_VERB(
"Extended advertising ignored by initiator because the "
"advertising address %s does not match the peer address %s",
resolved_advertising_address.ToString().c_str(),
initiator_.peer_address.ToString().c_str());
return;
}
break;
case bluetooth::hci::InitiatorFilterPolicy::USE_FILTER_ACCEPT_LIST:
if (!LeFilterAcceptListContainsDevice(resolved_advertising_address)) {
LOG_VERB(
"Extended advertising ignored by initiator because the "
"advertising address %s is not in the filter accept list",
resolved_advertising_address.ToString().c_str());
return;
}
break;
}
// When an initiator receives a directed connectable advertising event that
// contains a resolvable private address for the targets address
// (TargetA field) and address resolution is enabled, the Link Layer shall
// resolve the private address using the resolving lists Local IRK values.
// An initiator that has been instructed by the Host to use Resolvable Private
// Addresses shall not respond to directed connectable advertising events that
// contain Public or Static addresses for the targets address (TargetA
// field).
if (pdu.GetDirected()) {
if (!IsLocalPublicOrRandomAddress(resolved_target_address)) {
LOG_VERB(
"Directed extended advertising ignored by initiator because the "
"target address %s does not match the current device addresses",
resolved_advertising_address.ToString().c_str());
return;
}
if (resolved_target_address == target_address &&
(initiator_.own_address_type ==
OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS ||
initiator_.own_address_type ==
OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS)) {
LOG_VERB(
"Directed extended advertising ignored by initiator because the "
"target address %s is static or public and the initiator is "
"configured to use resolvable addresses",
resolved_advertising_address.ToString().c_str());
return;
}
}
AddressWithType public_address{address_, AddressType::PUBLIC_DEVICE_ADDRESS};
AddressWithType random_address{random_address_,
AddressType::RANDOM_DEVICE_ADDRESS};
std::optional<AddressWithType> resolvable_initiating_address =
GenerateResolvablePrivateAddress(resolved_advertising_address,
IrkSelection::Local);
// The Link Layer shall use resolvable private addresses for the initiators
// device address (InitA field) when initiating connection establishment with
// an associated device that exists in the Resolving List.
AddressWithType initiating_address;
switch (initiator_.own_address_type) {
case bluetooth::hci::OwnAddressType::PUBLIC_DEVICE_ADDRESS:
initiating_address = public_address;
break;
case bluetooth::hci::OwnAddressType::RANDOM_DEVICE_ADDRESS:
// The random address is checked in Le_Create_Connection or
// Le_Extended_Create_Connection.
ASSERT(random_address_ != Address::kEmpty);
initiating_address = random_address;
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS:
initiating_address =
resolvable_initiating_address.value_or(public_address);
break;
case bluetooth::hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS:
// The random address is checked in Le_Create_Connection or
// Le_Extended_Create_Connection.
ASSERT(random_address_ != Address::kEmpty);
initiating_address =
resolvable_initiating_address.value_or(random_address);
break;
}
if (!connections_.CreatePendingLeConnection(
advertising_address,
resolved_advertising_address != advertising_address
? resolved_advertising_address
: AddressWithType{},
initiating_address)) {
LOG_WARN("CreatePendingLeConnection failed for connection to %s",
advertising_address.ToString().c_str());
}
initiator_.pending_connect_request = advertising_address;
LOG_INFO("Sending LE Connect request to %s with initiating address %s",
resolved_advertising_address.ToString().c_str(),
initiating_address.ToString().c_str());
// The advertisers device address (AdvA field) in the initiating PDU
// shall be the same as the advertisers device address (AdvA field)
// received in the advertising event PDU to which the initiator is
// responding.
SendLeLinkLayerPacket(model::packets::LeConnectBuilder::Create(
initiating_address.GetAddress(), advertising_address.GetAddress(),
static_cast<model::packets::AddressType>(
initiating_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
initiator_.le_1m_phy.connection_interval_min,
initiator_.le_1m_phy.connection_interval_max,
initiator_.le_1m_phy.max_latency,
initiator_.le_1m_phy.supervision_timeout));
}
void LinkLayerController::IncomingLeExtendedAdvertisingPdu(
model::packets::LinkLayerPacketView incoming, uint8_t rssi) {
auto pdu = model::packets::LeExtendedAdvertisingPduView::Create(incoming);
ASSERT(pdu.IsValid());
ScanIncomingLeExtendedAdvertisingPdu(pdu, rssi);
ConnectIncomingLeExtendedAdvertisingPdu(pdu);
}
void LinkLayerController::IncomingScoConnectionRequest(
model::packets::LinkLayerPacketView incoming) {
Address address = incoming.GetSourceAddress();
auto request = model::packets::ScoConnectionRequestView::Create(incoming);
ASSERT(request.IsValid());
LOG_INFO("Received eSCO connection request from %s",
address.ToString().c_str());
// Automatically reject if connection request was already sent
// from the current device.
if (connections_.HasPendingScoConnection(address)) {
LOG_INFO(
"Rejecting eSCO connection request from %s, "
"an eSCO connection already exist with this device",
address.ToString().c_str());
SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create(
GetAddress(), address,
(uint8_t)ErrorCode::SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED, 0, 0, 0, 0,
0, 0));
return;
}
// Create local connection context.
ScoConnectionParameters connection_parameters = {
request.GetTransmitBandwidth(), request.GetReceiveBandwidth(),
request.GetMaxLatency(), request.GetVoiceSetting(),
request.GetRetransmissionEffort(), request.GetPacketType()};
bool extended = connection_parameters.IsExtended();
connections_.CreateScoConnection(
address, connection_parameters,
extended ? ScoState::SCO_STATE_SENT_ESCO_CONNECTION_REQUEST
: ScoState::SCO_STATE_SENT_SCO_CONNECTION_REQUEST,
ScoDatapath::NORMAL);
// Send connection request event and wait for Accept or Reject command.
send_event_(bluetooth::hci::ConnectionRequestBuilder::Create(
address, request.GetClassOfDevice(),
extended ? bluetooth::hci::ConnectionRequestLinkType::ESCO
: bluetooth::hci::ConnectionRequestLinkType::SCO));
}
void LinkLayerController::IncomingScoConnectionResponse(
model::packets::LinkLayerPacketView incoming) {
Address address = incoming.GetSourceAddress();
auto response = model::packets::ScoConnectionResponseView::Create(incoming);
ASSERT(response.IsValid());
auto status = ErrorCode(response.GetStatus());
bool is_legacy = connections_.IsLegacyScoConnection(address);
LOG_INFO("Received eSCO connection response with status 0x%02x from %s",
static_cast<unsigned>(status),
incoming.GetSourceAddress().ToString().c_str());
if (status == ErrorCode::SUCCESS) {
bool extended = response.GetExtended();
ScoLinkParameters link_parameters = {
response.GetTransmissionInterval(),
response.GetRetransmissionWindow(),
response.GetRxPacketLength(),
response.GetTxPacketLength(),
response.GetAirMode(),
extended,
};
connections_.AcceptPendingScoConnection(
address, link_parameters, [this, address] {
return LinkLayerController::StartScoStream(address);
});
if (is_legacy) {
send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, connections_.GetScoHandle(address), address,
bluetooth::hci::LinkType::SCO, bluetooth::hci::Enable::DISABLED));
} else {
send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, connections_.GetScoHandle(address), address,
extended ? bluetooth::hci::ScoLinkType::ESCO
: bluetooth::hci::ScoLinkType::SCO,
extended ? response.GetTransmissionInterval() : 0,
extended ? response.GetRetransmissionWindow() : 0,
extended ? response.GetRxPacketLength() : 0,
extended ? response.GetTxPacketLength() : 0,
bluetooth::hci::ScoAirMode(response.GetAirMode())));
}
} else {
connections_.CancelPendingScoConnection(address);
if (is_legacy) {
send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create(
status, 0, address, bluetooth::hci::LinkType::SCO,
bluetooth::hci::Enable::DISABLED));
} else {
ScoConnectionParameters connection_parameters =
connections_.GetScoConnectionParameters(address);
send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
status, 0, address,
connection_parameters.IsExtended() ? bluetooth::hci::ScoLinkType::ESCO
: bluetooth::hci::ScoLinkType::SCO,
0, 0, 0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT));
}
}
}
void LinkLayerController::IncomingScoDisconnect(
model::packets::LinkLayerPacketView incoming) {
Address address = incoming.GetSourceAddress();
auto request = model::packets::ScoDisconnectView::Create(incoming);
ASSERT(request.IsValid());
auto reason = request.GetReason();
uint16_t handle = connections_.GetScoHandle(address);
LOG_INFO(
"Received eSCO disconnection request with"
" reason 0x%02x from %s",
static_cast<unsigned>(reason),
incoming.GetSourceAddress().ToString().c_str());
if (handle != kReservedHandle) {
connections_.Disconnect(handle, cancel_task_);
SendDisconnectionCompleteEvent(handle, ErrorCode(reason));
}
}
#ifdef ROOTCANAL_LMP
void LinkLayerController::IncomingLmpPacket(
model::packets::LinkLayerPacketView incoming) {
Address address = incoming.GetSourceAddress();
auto request = model::packets::LmpView::Create(incoming);
ASSERT(request.IsValid());
auto payload = request.GetPayload();
auto packet = std::vector(payload.begin(), payload.end());
ASSERT(link_manager_ingest_lmp(
lm_.get(), reinterpret_cast<uint8_t(*)[6]>(address.data()), packet.data(),
packet.size()));
}
#endif /* ROOTCANAL_LMP */
uint16_t LinkLayerController::HandleLeConnection(
AddressWithType address, AddressWithType own_address,
bluetooth::hci::Role role, uint16_t connection_interval,
uint16_t connection_latency, uint16_t supervision_timeout,
bool send_le_channel_selection_algorithm_event) {
// Note: the HCI_LE_Connection_Complete event is not sent if the
// HCI_LE_Enhanced_Connection_Complete event (see Section 7.7.65.10) is
// unmasked.
uint16_t handle = connections_.CreateLeConnection(address, own_address, role);
if (handle == kReservedHandle) {
LOG_WARN("No pending connection for connection from %s",
address.ToString().c_str());
return kReservedHandle;
}
if (IsLeEventUnmasked(SubeventCode::ENHANCED_CONNECTION_COMPLETE)) {
AddressWithType peer_resolved_address =
connections_.GetResolvedAddress(handle);
Address peer_resolvable_private_address;
Address connection_address = address.GetAddress();
AddressType peer_address_type = address.GetAddressType();
if (peer_resolved_address != AddressWithType()) {
peer_resolvable_private_address = address.GetAddress();
if (peer_resolved_address.GetAddressType() ==
AddressType::PUBLIC_DEVICE_ADDRESS) {
peer_address_type = AddressType::PUBLIC_IDENTITY_ADDRESS;
} else if (peer_resolved_address.GetAddressType() ==
AddressType::RANDOM_DEVICE_ADDRESS) {
peer_address_type = AddressType::RANDOM_IDENTITY_ADDRESS;
} else {
LOG_WARN("Unhandled resolved address type %s -> %s",
address.ToString().c_str(),
peer_resolved_address.ToString().c_str());
}
connection_address = peer_resolved_address.GetAddress();
}
Address local_resolved_address = own_address.GetAddress();
if (local_resolved_address == GetAddress() ||
local_resolved_address == random_address_) {
local_resolved_address = Address::kEmpty;
}
send_event_(bluetooth::hci::LeEnhancedConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, role, peer_address_type, connection_address,
local_resolved_address, peer_resolvable_private_address,
connection_interval, connection_latency, supervision_timeout,
static_cast<bluetooth::hci::ClockAccuracy>(0x00)));
} else if (IsLeEventUnmasked(SubeventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, role, address.GetAddressType(),
address.GetAddress(), connection_interval, connection_latency,
supervision_timeout, static_cast<bluetooth::hci::ClockAccuracy>(0x00)));
}
// Note: the HCI_LE_Connection_Complete event is immediately followed by
// an HCI_LE_Channel_Selection_Algorithm event if the connection is created
// using the LE_Extended_Create_Connection command (see Section 7.7.8.66).
if (send_le_channel_selection_algorithm_event &&
IsLeEventUnmasked(SubeventCode::CHANNEL_SELECTION_ALGORITHM)) {
// The selection channel algorithm probably will have no impact
// on emulation.
send_event_(bluetooth::hci::LeChannelSelectionAlgorithmBuilder::Create(
handle, bluetooth::hci::ChannelSelectionAlgorithm::ALGORITHM_1));
}
if (own_address.GetAddress() == initiator_.initiating_address) {
initiator_.initiating_address = Address::kEmpty;
}
return handle;
}
// Handle CONNECT_IND PDUs for the legacy advertiser.
bool LinkLayerController::ProcessIncomingLegacyConnectRequest(
model::packets::LeConnectView const& connect_ind) {
if (!legacy_advertiser_.IsEnabled()) {
return false;
}
if (!legacy_advertiser_.IsConnectable()) {
LOG_VERB(
"LE Connect request ignored by legacy advertiser because it is not "
"connectable");
return false;
}
AddressWithType advertising_address{
connect_ind.GetDestinationAddress(),
static_cast<AddressType>(connect_ind.GetAdvertisingAddressType()),
};
AddressWithType initiating_address{
connect_ind.GetSourceAddress(),
static_cast<AddressType>(connect_ind.GetInitiatingAddressType()),
};
if (legacy_advertiser_.GetAdvertisingAddress() != advertising_address) {
LOG_VERB(
"LE Connect request ignored by legacy advertiser because the "
"advertising address %s(%hhx) does not match %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
legacy_advertiser_.GetAdvertisingAddress().ToString().c_str(),
legacy_advertiser_.GetAdvertisingAddress().GetAddressType());
return false;
}
// When an advertiser receives a connection request that contains a resolvable
// private address for the initiators address (InitA field) and address
// resolution is enabled, the Link Layer shall resolve the private address.
// The advertising filter policy shall then determine if the
// advertiser establishes a connection.
AddressWithType resolved_initiating_address =
ResolvePrivateAddress(initiating_address, IrkSelection::Peer)
.value_or(initiating_address);
if (resolved_initiating_address != initiating_address) {
LOG_VERB("Resolved the initiating address %s(%hhx) to %s(%hhx)",
initiating_address.ToString().c_str(),
initiating_address.GetAddressType(),
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
}
// When the Link Layer is [...] connectable directed advertising events the
// advertising filter policy shall be ignored.
if (legacy_advertiser_.IsDirected()) {
if (legacy_advertiser_.GetTargetAddress() != resolved_initiating_address) {
LOG_VERB(
"LE Connect request ignored by legacy advertiser because the "
"initiating address %s(%hhx) does not match the target address "
"%s(%hhx)",
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType(),
legacy_advertiser_.GetTargetAddress().ToString().c_str(),
legacy_advertiser_.GetTargetAddress().GetAddressType());
return false;
}
} else {
// Check if initiator address is in the filter accept list
// for this advertiser.
switch (legacy_advertiser_.advertising_filter_policy) {
case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN:
break;
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT:
if (!LeFilterAcceptListContainsDevice(resolved_initiating_address)) {
LOG_VERB(
"LE Connect request ignored by legacy advertiser because the "
"initiating address %s(%hhx) is not in the filter accept list",
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
return false;
}
break;
}
}
LOG_INFO(
"Accepting LE Connect request to legacy advertiser from initiating "
"address %s(%hhx)",
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
if (!connections_.CreatePendingLeConnection(
initiating_address,
resolved_initiating_address != initiating_address
? resolved_initiating_address
: AddressWithType{},
advertising_address)) {
LOG_WARN(
"CreatePendingLeConnection failed for connection from %s (type %hhx)",
initiating_address.GetAddress().ToString().c_str(),
initiating_address.GetAddressType());
return false;
}
(void)HandleLeConnection(
initiating_address, advertising_address, bluetooth::hci::Role::PERIPHERAL,
connect_ind.GetLeConnectionIntervalMax(),
connect_ind.GetLeConnectionLatency(),
connect_ind.GetLeConnectionSupervisionTimeout(), false);
SendLeLinkLayerPacket(model::packets::LeConnectCompleteBuilder::Create(
advertising_address.GetAddress(), initiating_address.GetAddress(),
static_cast<model::packets::AddressType>(
initiating_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
connect_ind.GetLeConnectionIntervalMax(),
connect_ind.GetLeConnectionLatency(),
connect_ind.GetLeConnectionSupervisionTimeout()));
legacy_advertiser_.Disable();
return true;
}
// Handle CONNECT_IND PDUs for the selected extended advertiser.
bool LinkLayerController::ProcessIncomingExtendedConnectRequest(
ExtendedAdvertiser& advertiser,
model::packets::LeConnectView const& connect_ind) {
if (!advertiser.IsEnabled()) {
return false;
}
if (!advertiser.IsConnectable()) {
LOG_VERB(
"LE Connect request ignored by extended advertiser %d because it is "
"not connectable",
advertiser.advertising_handle);
return false;
}
AddressWithType advertising_address{
connect_ind.GetDestinationAddress(),
static_cast<AddressType>(connect_ind.GetAdvertisingAddressType()),
};
AddressWithType initiating_address{
connect_ind.GetSourceAddress(),
static_cast<AddressType>(connect_ind.GetInitiatingAddressType()),
};
if (advertiser.GetAdvertisingAddress() != advertising_address) {
LOG_VERB(
"LE Connect request ignored by extended advertiser %d because the "
"advertising address %s(%hhx) does not match %s(%hhx)",
advertiser.advertising_handle, advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
advertiser.GetAdvertisingAddress().ToString().c_str(),
advertiser.GetAdvertisingAddress().GetAddressType());
return false;
}
// When an advertiser receives a connection request that contains a resolvable
// private address for the initiators address (InitA field) and address
// resolution is enabled, the Link Layer shall resolve the private address.
// The advertising filter policy shall then determine if the
// advertiser establishes a connection.
AddressWithType resolved_initiating_address =
ResolvePrivateAddress(initiating_address, IrkSelection::Peer)
.value_or(initiating_address);
if (resolved_initiating_address != initiating_address) {
LOG_VERB("Resolved the initiating address %s(%hhx) to %s(%hhx)",
initiating_address.ToString().c_str(),
initiating_address.GetAddressType(),
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
}
// When the Link Layer is [...] connectable directed advertising events the
// advertising filter policy shall be ignored.
if (advertiser.IsDirected()) {
if (advertiser.GetTargetAddress() != resolved_initiating_address) {
LOG_VERB(
"LE Connect request ignored by extended advertiser %d because the "
"initiating address %s(%hhx) does not match the target address "
"%s(%hhx)",
advertiser.advertising_handle,
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType(),
advertiser.GetTargetAddress().ToString().c_str(),
advertiser.GetTargetAddress().GetAddressType());
return false;
}
} else {
// Check if initiator address is in the filter accept list
// for this advertiser.
switch (advertiser.advertising_filter_policy) {
case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN:
break;
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT:
if (!LeFilterAcceptListContainsDevice(resolved_initiating_address)) {
LOG_VERB(
"LE Connect request ignored by extended advertiser %d because "
"the initiating address %s(%hhx) is not in the filter accept "
"list",
advertiser.advertising_handle,
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
return false;
}
break;
}
}
LOG_INFO(
"Accepting LE Connect request to extended advertiser %d from initiating "
"address %s(%hhx)",
advertiser.advertising_handle,
resolved_initiating_address.ToString().c_str(),
resolved_initiating_address.GetAddressType());
if (!connections_.CreatePendingLeConnection(
initiating_address,
resolved_initiating_address != initiating_address
? resolved_initiating_address
: AddressWithType{},
advertising_address)) {
LOG_WARN(
"CreatePendingLeConnection failed for connection from %s (type %hhx)",
initiating_address.GetAddress().ToString().c_str(),
initiating_address.GetAddressType());
return false;
}
advertiser.Disable();
uint16_t connection_handle = HandleLeConnection(
initiating_address, advertising_address, bluetooth::hci::Role::PERIPHERAL,
connect_ind.GetLeConnectionIntervalMax(),
connect_ind.GetLeConnectionLatency(),
connect_ind.GetLeConnectionSupervisionTimeout(), false);
SendLeLinkLayerPacket(model::packets::LeConnectCompleteBuilder::Create(
advertising_address.GetAddress(), initiating_address.GetAddress(),
static_cast<model::packets::AddressType>(
initiating_address.GetAddressType()),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
connect_ind.GetLeConnectionIntervalMax(),
connect_ind.GetLeConnectionLatency(),
connect_ind.GetLeConnectionSupervisionTimeout()));
// If the advertising set is connectable and a connection gets created, an
// HCI_LE_Connection_Complete or HCI_LE_Enhanced_Connection_Complete
// event shall be generated followed by an HCI_LE_Advertising_Set_Terminated
// event with the Status parameter set to 0x00. The Controller should not send
// any other events in between these two events
if (IsLeEventUnmasked(SubeventCode::ADVERTISING_SET_TERMINATED)) {
send_event_(bluetooth::hci::LeAdvertisingSetTerminatedBuilder::Create(
ErrorCode::SUCCESS, advertiser.advertising_handle, connection_handle,
advertiser.num_completed_extended_advertising_events));
}
return true;
}
void LinkLayerController::IncomingLeConnectPacket(
model::packets::LinkLayerPacketView incoming) {
model::packets::LeConnectView connect =
model::packets::LeConnectView::Create(incoming);
ASSERT(connect.IsValid());
if (ProcessIncomingLegacyConnectRequest(connect)) {
return;
}
for (auto& [_, advertiser] : extended_advertisers_) {
if (ProcessIncomingExtendedConnectRequest(advertiser, connect)) {
return;
}
}
}
void LinkLayerController::IncomingLeConnectCompletePacket(
model::packets::LinkLayerPacketView incoming) {
auto complete = model::packets::LeConnectCompleteView::Create(incoming);
ASSERT(complete.IsValid());
AddressWithType advertising_address{
incoming.GetSourceAddress(), static_cast<bluetooth::hci::AddressType>(
complete.GetAdvertisingAddressType())};
LOG_INFO(
"Received LE Connect complete response with advertising address %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType());
HandleLeConnection(
advertising_address,
AddressWithType(incoming.GetDestinationAddress(),
static_cast<bluetooth::hci::AddressType>(
complete.GetInitiatingAddressType())),
bluetooth::hci::Role::CENTRAL, complete.GetLeConnectionInterval(),
complete.GetLeConnectionLatency(),
complete.GetLeConnectionSupervisionTimeout(), ExtendedAdvertising());
initiator_.pending_connect_request = {};
initiator_.Disable();
}
void LinkLayerController::IncomingLeConnectionParameterRequest(
model::packets::LinkLayerPacketView incoming) {
auto request =
model::packets::LeConnectionParameterRequestView::Create(incoming);
ASSERT(request.IsValid());
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(peer);
if (handle == kReservedHandle) {
LOG_INFO("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
peer.ToString().c_str());
return;
}
if (IsLeEventUnmasked(SubeventCode::REMOTE_CONNECTION_PARAMETER_REQUEST)) {
send_event_(
bluetooth::hci::LeRemoteConnectionParameterRequestBuilder::Create(
handle, request.GetIntervalMin(), request.GetIntervalMax(),
request.GetLatency(), request.GetTimeout()));
} else {
// If the request is being indicated to the Host and the event to the Host
// is masked, then the Link Layer shall issue an LL_REJECT_EXT_IND PDU with
// the ErrorCode set to Unsupported Remote Feature (0x1A).
SendLeLinkLayerPacket(
model::packets::LeConnectionParameterUpdateBuilder::Create(
request.GetDestinationAddress(), request.GetSourceAddress(),
static_cast<uint8_t>(ErrorCode::UNSUPPORTED_REMOTE_OR_LMP_FEATURE),
0, 0, 0));
}
}
void LinkLayerController::IncomingLeConnectionParameterUpdate(
model::packets::LinkLayerPacketView incoming) {
auto update =
model::packets::LeConnectionParameterUpdateView::Create(incoming);
ASSERT(update.IsValid());
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(peer);
if (handle == kReservedHandle) {
LOG_INFO("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
peer.ToString().c_str());
return;
}
if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
static_cast<ErrorCode>(update.GetStatus()), handle,
update.GetInterval(), update.GetLatency(), update.GetTimeout()));
}
}
void LinkLayerController::IncomingLeEncryptConnection(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("IncomingLeEncryptConnection");
Address peer = incoming.GetSourceAddress();
uint16_t handle = connections_.GetHandleOnlyAddress(peer);
if (handle == kReservedHandle) {
LOG_INFO("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
peer.ToString().c_str());
return;
}
auto le_encrypt = model::packets::LeEncryptConnectionView::Create(incoming);
ASSERT(le_encrypt.IsValid());
// TODO: Save keys to check
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeLongTermKeyRequestBuilder::Create(
handle, le_encrypt.GetRand(), le_encrypt.GetEdiv()));
}
}
void LinkLayerController::IncomingLeEncryptConnectionResponse(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("IncomingLeEncryptConnectionResponse");
// TODO: Check keys
uint16_t handle =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
if (handle == kReservedHandle) {
LOG_INFO("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
incoming.GetSourceAddress().ToString().c_str());
return;
}
ErrorCode status = ErrorCode::SUCCESS;
auto response =
model::packets::LeEncryptConnectionResponseView::Create(incoming);
ASSERT(response.IsValid());
// Zero LTK is a rejection
if (response.GetLtk() == std::array<uint8_t, 16>()) {
status = ErrorCode::AUTHENTICATION_FAILURE;
}
if (connections_.IsEncrypted(handle)) {
if (IsEventUnmasked(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE)) {
send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
status, handle));
}
} else {
connections_.Encrypt(handle);
if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) {
send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
status, handle, bluetooth::hci::EncryptionEnabled::ON));
}
}
}
void LinkLayerController::IncomingLeReadRemoteFeatures(
model::packets::LinkLayerPacketView incoming) {
uint16_t handle =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
ErrorCode status = ErrorCode::SUCCESS;
if (handle == kReservedHandle) {
LOG_WARN("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
incoming.GetSourceAddress().ToString().c_str());
}
SendLeLinkLayerPacket(
model::packets::LeReadRemoteFeaturesResponseBuilder::Create(
incoming.GetDestinationAddress(), incoming.GetSourceAddress(),
GetLeSupportedFeatures(), static_cast<uint8_t>(status)));
}
void LinkLayerController::IncomingLeReadRemoteFeaturesResponse(
model::packets::LinkLayerPacketView incoming) {
uint16_t handle =
connections_.GetHandleOnlyAddress(incoming.GetSourceAddress());
ErrorCode status = ErrorCode::SUCCESS;
auto response =
model::packets::LeReadRemoteFeaturesResponseView::Create(incoming);
ASSERT(response.IsValid());
if (handle == kReservedHandle) {
LOG_INFO("@%s: Unknown connection @%s",
incoming.GetDestinationAddress().ToString().c_str(),
incoming.GetSourceAddress().ToString().c_str());
status = ErrorCode::UNKNOWN_CONNECTION;
} else {
status = static_cast<ErrorCode>(response.GetStatus());
}
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeReadRemoteFeaturesCompleteBuilder::Create(
status, handle, response.GetFeatures()));
}
}
void LinkLayerController::ProcessIncomingLegacyScanRequest(
AddressWithType scanning_address, AddressWithType resolved_scanning_address,
AddressWithType advertising_address) {
// Check if the advertising addresses matches the legacy
// advertising address.
if (!legacy_advertiser_.IsEnabled()) {
return;
}
if (!legacy_advertiser_.IsScannable()) {
LOG_VERB(
"LE Scan request ignored by legacy advertiser because it is not "
"scannable");
return;
}
if (advertising_address != legacy_advertiser_.advertising_address) {
LOG_VERB(
"LE Scan request ignored by legacy advertiser because the advertising "
"address %s(%hhx) does not match %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
legacy_advertiser_.GetAdvertisingAddress().ToString().c_str(),
legacy_advertiser_.GetAdvertisingAddress().GetAddressType());
return;
}
// Check if scanner address is in the filter accept list
// for this advertiser.
switch (legacy_advertiser_.advertising_filter_policy) {
case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT:
break;
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT:
if (!LeFilterAcceptListContainsDevice(resolved_scanning_address)) {
LOG_VERB(
"LE Scan request ignored by legacy advertiser because the scanning "
"address %s(%hhx) is not in the filter accept list",
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType());
return;
}
break;
}
LOG_INFO(
"Accepting LE Scan request to legacy advertiser from scanning address "
"%s(%hhx)",
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType());
// Generate the SCAN_RSP packet.
// Note: If the advertiser processes the scan request, the advertisers
// device address (AdvA field) in the SCAN_RSP PDU shall be the same as
// the advertisers device address (AdvA field) in the SCAN_REQ PDU to
// which it is responding.
SendLeLinkLayerPacketWithRssi(
advertising_address.GetAddress(), scanning_address.GetAddress(),
properties_.le_advertising_physical_channel_tx_power,
model::packets::LeScanResponseBuilder::Create(
advertising_address.GetAddress(), scanning_address.GetAddress(),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
legacy_advertiser_.scan_response_data));
}
void LinkLayerController::ProcessIncomingExtendedScanRequest(
ExtendedAdvertiser const& advertiser, AddressWithType scanning_address,
AddressWithType resolved_scanning_address,
AddressWithType advertising_address) {
// Check if the advertising addresses matches the legacy
// advertising address.
if (!advertiser.IsEnabled()) {
return;
}
if (!advertiser.IsScannable()) {
LOG_VERB(
"LE Scan request ignored by extended advertiser %d because it is not "
"scannable",
advertiser.advertising_handle);
return;
}
if (advertising_address != advertiser.advertising_address) {
LOG_VERB(
"LE Scan request ignored by extended advertiser %d because the "
"advertising address %s(%hhx) does not match %s(%hhx)",
advertiser.advertising_handle, advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
advertiser.GetAdvertisingAddress().ToString().c_str(),
advertiser.GetAdvertisingAddress().GetAddressType());
return;
}
// Check if scanner address is in the filter accept list
// for this advertiser.
switch (advertiser.advertising_filter_policy) {
case bluetooth::hci::AdvertisingFilterPolicy::ALL_DEVICES:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_CONNECT:
break;
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN:
case bluetooth::hci::AdvertisingFilterPolicy::LISTED_SCAN_AND_CONNECT:
if (!LeFilterAcceptListContainsDevice(resolved_scanning_address)) {
LOG_VERB(
"LE Scan request ignored by extended advertiser %d because the "
"scanning address %s(%hhx) is not in the filter accept list",
advertiser.advertising_handle,
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType());
return;
}
break;
}
// Check if the scanner address is the target address in the case of
// scannable directed event types.
if (advertiser.IsDirected() &&
advertiser.target_address != resolved_scanning_address) {
LOG_VERB(
"LE Scan request ignored by extended advertiser %d because the "
"scanning address %s(%hhx) does not match the target address %s(%hhx)",
advertiser.advertising_handle,
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType(),
advertiser.GetTargetAddress().ToString().c_str(),
advertiser.GetTargetAddress().GetAddressType());
return;
}
LOG_INFO(
"Accepting LE Scan request to extended advertiser %d from scanning "
"address %s(%hhx)",
advertiser.advertising_handle,
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType());
// Generate the SCAN_RSP packet.
// Note: If the advertiser processes the scan request, the advertisers
// device address (AdvA field) in the SCAN_RSP PDU shall be the same as
// the advertisers device address (AdvA field) in the SCAN_REQ PDU to
// which it is responding.
SendLeLinkLayerPacketWithRssi(
advertising_address.GetAddress(), scanning_address.GetAddress(),
advertiser.advertising_tx_power,
model::packets::LeScanResponseBuilder::Create(
advertising_address.GetAddress(), scanning_address.GetAddress(),
static_cast<model::packets::AddressType>(
advertising_address.GetAddressType()),
advertiser.scan_response_data));
}
void LinkLayerController::IncomingLeScanPacket(
model::packets::LinkLayerPacketView incoming) {
auto scan_request = model::packets::LeScanView::Create(incoming);
ASSERT(scan_request.IsValid());
AddressWithType scanning_address{
scan_request.GetSourceAddress(),
static_cast<AddressType>(scan_request.GetScanningAddressType())};
AddressWithType advertising_address{
scan_request.GetDestinationAddress(),
static_cast<AddressType>(scan_request.GetAdvertisingAddressType())};
// Note: Vol 6, Part B § 6.2 Privacy in the Advertising State.
//
// When an advertiser receives a scan request that contains a resolvable
// private address for the scanners device address (ScanA field) and
// address resolution is enabled, the Link Layer shall resolve the private
// address. The advertising filter policy shall then determine if
// the advertiser processes the scan request.
AddressWithType resolved_scanning_address =
ResolvePrivateAddress(scanning_address, IrkSelection::Peer)
.value_or(scanning_address);
if (resolved_scanning_address != scanning_address) {
LOG_VERB("Resolved the scanning address %s(%hhx) to %s(%hhx)",
scanning_address.ToString().c_str(),
scanning_address.GetAddressType(),
resolved_scanning_address.ToString().c_str(),
resolved_scanning_address.GetAddressType());
}
ProcessIncomingLegacyScanRequest(scanning_address, resolved_scanning_address,
advertising_address);
for (auto& [_, advertiser] : extended_advertisers_) {
ProcessIncomingExtendedScanRequest(advertiser, scanning_address,
resolved_scanning_address,
advertising_address);
}
}
void LinkLayerController::IncomingLeScanResponsePacket(
model::packets::LinkLayerPacketView incoming, uint8_t rssi) {
auto scan_response = model::packets::LeScanResponseView::Create(incoming);
ASSERT(scan_response.IsValid());
if (!scanner_.IsEnabled()) {
return;
}
if (!scanner_.pending_scan_request) {
LOG_VERB(
"LE Scan response ignored by scanner because no request is currently "
"pending");
return;
}
AddressWithType advertising_address{
scan_response.GetSourceAddress(),
static_cast<AddressType>(scan_response.GetAdvertisingAddressType())};
// If the advertiser processes the scan request, the advertisers device
// address (AdvA field) in the scan response PDU shall be the same as the
// advertisers device address (AdvA field) in the scan request PDU to which
// it is responding.
if (advertising_address != scanner_.pending_scan_request) {
LOG_VERB(
"LE Scan response ignored by scanner because the advertising address "
"%s(%hhx) does not match the pending request %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
scanner_.pending_scan_request.value().ToString().c_str(),
scanner_.pending_scan_request.value().GetAddressType());
return;
}
AddressWithType resolved_advertising_address =
ResolvePrivateAddress(advertising_address, IrkSelection::Peer)
.value_or(advertising_address);
if (advertising_address != resolved_advertising_address) {
LOG_VERB("Resolved the advertising address %s(%hhx) to %s(%hhx)",
advertising_address.ToString().c_str(),
advertising_address.GetAddressType(),
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
return;
}
LOG_INFO("Accepting LE Scan response from advertising address %s(%hhx)",
resolved_advertising_address.ToString().c_str(),
resolved_advertising_address.GetAddressType());
scanner_.pending_scan_request = {};
bool should_send_advertising_report = true;
if (scanner_.filter_duplicates !=
bluetooth::hci::FilterDuplicates::DISABLED) {
if (scanner_.IsPacketInHistory(incoming)) {
should_send_advertising_report = false;
} else {
scanner_.AddPacketToHistory(incoming);
}
}
if (LegacyAdvertising() && should_send_advertising_report &&
IsLeEventUnmasked(SubeventCode::ADVERTISING_REPORT)) {
bluetooth::hci::LeAdvertisingResponseRaw response;
response.event_type_ = bluetooth::hci::AdvertisingEventType::SCAN_RESPONSE;
response.address_ = resolved_advertising_address.GetAddress();
response.address_type_ = resolved_advertising_address.GetAddressType();
response.advertising_data_ = scan_response.GetScanResponseData();
response.rssi_ = rssi;
send_event_(
bluetooth::hci::LeAdvertisingReportRawBuilder::Create({response}));
}
if (ExtendedAdvertising() && should_send_advertising_report &&
IsLeEventUnmasked(SubeventCode::EXTENDED_ADVERTISING_REPORT)) {
bluetooth::hci::LeExtendedAdvertisingResponseRaw response;
response.address_ = resolved_advertising_address.GetAddress();
response.address_type_ =
static_cast<bluetooth::hci::DirectAdvertisingAddressType>(
resolved_advertising_address.GetAddressType());
response.connectable_ = scanner_.connectable_scan_response;
response.scannable_ = true;
response.legacy_ = true;
response.scan_response_ = true;
response.primary_phy_ = bluetooth::hci::PrimaryPhyType::LE_1M;
response.advertising_sid_ = 0xFF;
response.tx_power_ = 0x7F;
response.advertising_data_ = scan_response.GetScanResponseData();
response.rssi_ = rssi;
send_event_(bluetooth::hci::LeExtendedAdvertisingReportRawBuilder::Create(
{response}));
}
}
void LinkLayerController::LeScanning() {
if (!scanner_.IsEnabled()) {
return;
}
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
// Extended Scanning Timeout
// Generate HCI Connection Complete or Enhanced HCI Connection Complete
// events with Advertising Timeout error code when the advertising
// type is ADV_DIRECT_IND and the connection failed to be established.
if (scanner_.timeout.has_value() &&
!scanner_.periodical_timeout.has_value() &&
now >= scanner_.timeout.value()) {
// At the end of a single scan (Duration non-zero but Period zero),
// an HCI_LE_Scan_Timeout event shall be generated.
LOG_INFO("Extended Scan Timeout");
scanner_.scan_enable = false;
scanner_.history.clear();
if (IsLeEventUnmasked(SubeventCode::SCAN_TIMEOUT)) {
send_event_(bluetooth::hci::LeScanTimeoutBuilder::Create());
}
}
// End of duration with scan enabled
if (scanner_.timeout.has_value() && scanner_.periodical_timeout.has_value() &&
now >= scanner_.timeout.value()) {
scanner_.timeout = {};
}
// End of period
if (!scanner_.timeout.has_value() &&
scanner_.periodical_timeout.has_value() &&
now >= scanner_.periodical_timeout.value()) {
if (scanner_.filter_duplicates == FilterDuplicates::RESET_EACH_PERIOD) {
scanner_.history.clear();
}
scanner_.timeout = now + scanner_.duration;
scanner_.periodical_timeout = now + scanner_.period;
}
}
#ifndef ROOTCANAL_LMP
void LinkLayerController::IncomingPasskeyPacket(
model::packets::LinkLayerPacketView incoming) {
auto passkey = model::packets::PasskeyView::Create(incoming);
ASSERT(passkey.IsValid());
SaveKeyAndAuthenticate('P', incoming.GetSourceAddress());
}
void LinkLayerController::IncomingPasskeyFailedPacket(
model::packets::LinkLayerPacketView incoming) {
auto failed = model::packets::PasskeyFailedView::Create(incoming);
ASSERT(failed.IsValid());
auto current_peer = incoming.GetSourceAddress();
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
}
void LinkLayerController::IncomingPinRequestPacket(
model::packets::LinkLayerPacketView incoming) {
auto request = model::packets::PinRequestView::Create(incoming);
ASSERT(request.IsValid());
auto peer = incoming.GetSourceAddress();
auto handle = connections_.GetHandle(AddressWithType(
peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS));
if (handle == kReservedHandle) {
LOG_INFO("Dropping %s request (no connection)", peer.ToString().c_str());
auto wrong_pin = request.GetPinCode();
wrong_pin[0] = wrong_pin[0]++;
SendLinkLayerPacket(model::packets::PinResponseBuilder::Create(
GetAddress(), peer, wrong_pin));
return;
}
if (security_manager_.AuthenticationInProgress()) {
auto current_peer = security_manager_.GetAuthenticationAddress();
if (current_peer != peer) {
LOG_INFO("Dropping %s request (%s in progress)", peer.ToString().c_str(),
current_peer.ToString().c_str());
auto wrong_pin = request.GetPinCode();
wrong_pin[0] = wrong_pin[0]++;
SendLinkLayerPacket(model::packets::PinResponseBuilder::Create(
GetAddress(), peer, wrong_pin));
return;
}
} else {
LOG_INFO("Incoming authentication request %s", peer.ToString().c_str());
security_manager_.AuthenticationRequest(peer, handle, false);
}
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.SetRemotePin(peer, request.GetPinCode());
if (security_manager_.GetPinRequested(peer)) {
if (security_manager_.GetLocalPinResponseReceived(peer)) {
SendLinkLayerPacket(model::packets::PinResponseBuilder::Create(
GetAddress(), peer, request.GetPinCode()));
if (security_manager_.PinCompare()) {
LOG_INFO("Authenticating %s", peer.ToString().c_str());
SaveKeyAndAuthenticate('L', peer); // Legacy
} else {
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, peer));
}
});
}
}
} else {
LOG_INFO("PIN pairing %s", GetAddress().ToString().c_str());
ScheduleTask(kNoDelayMs, [this, peer]() {
security_manager_.SetPinRequested(peer);
if (IsEventUnmasked(EventCode::PIN_CODE_REQUEST)) {
send_event_(bluetooth::hci::PinCodeRequestBuilder::Create(peer));
}
});
}
}
void LinkLayerController::IncomingPinResponsePacket(
model::packets::LinkLayerPacketView incoming) {
auto request = model::packets::PinResponseView::Create(incoming);
ASSERT(request.IsValid());
auto peer = incoming.GetSourceAddress();
auto handle = connections_.GetHandle(AddressWithType(
peer, bluetooth::hci::AddressType::PUBLIC_DEVICE_ADDRESS));
if (handle == kReservedHandle) {
LOG_INFO("Dropping %s request (no connection)", peer.ToString().c_str());
return;
}
if (security_manager_.AuthenticationInProgress()) {
auto current_peer = security_manager_.GetAuthenticationAddress();
if (current_peer != peer) {
LOG_INFO("Dropping %s request (%s in progress)", peer.ToString().c_str(),
current_peer.ToString().c_str());
return;
}
} else {
LOG_INFO("Dropping response without authentication request %s",
peer.ToString().c_str());
return;
}
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.SetRemotePin(peer, request.GetPinCode());
if (security_manager_.GetPinRequested(peer)) {
if (security_manager_.GetLocalPinResponseReceived(peer)) {
SendLinkLayerPacket(model::packets::PinResponseBuilder::Create(
GetAddress(), peer, request.GetPinCode()));
if (security_manager_.PinCompare()) {
LOG_INFO("Authenticating %s", peer.ToString().c_str());
SaveKeyAndAuthenticate('L', peer); // Legacy
} else {
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, peer));
}
});
}
}
} else {
LOG_INFO("PIN pairing %s", GetAddress().ToString().c_str());
ScheduleTask(kNoDelayMs, [this, peer]() {
security_manager_.SetPinRequested(peer);
if (IsEventUnmasked(EventCode::PIN_CODE_REQUEST)) {
send_event_(bluetooth::hci::PinCodeRequestBuilder::Create(peer));
}
});
}
}
#endif /* !ROOTCANAL_LMP */
void LinkLayerController::IncomingPagePacket(
model::packets::LinkLayerPacketView incoming) {
auto page = model::packets::PageView::Create(incoming);
ASSERT(page.IsValid());
LOG_INFO("from %s", incoming.GetSourceAddress().ToString().c_str());
if (!connections_.CreatePendingConnection(
incoming.GetSourceAddress(),
authentication_enable_ == AuthenticationEnable::REQUIRED)) {
// Send a response to indicate that we're busy, or drop the packet?
LOG_WARN("Failed to create a pending connection for %s",
incoming.GetSourceAddress().ToString().c_str());
}
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(),
bluetooth::hci::ConnectionRequestLinkType::ACL));
}
}
void LinkLayerController::IncomingPageRejectPacket(
model::packets::LinkLayerPacketView incoming) {
LOG_INFO("%s", incoming.GetSourceAddress().ToString().c_str());
auto reject = model::packets::PageRejectView::Create(incoming);
ASSERT(reject.IsValid());
LOG_INFO("Sending CreateConnectionComplete");
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));
}
}
void LinkLayerController::IncomingPageResponsePacket(
model::packets::LinkLayerPacketView incoming) {
Address peer = incoming.GetSourceAddress();
LOG_INFO("%s", peer.ToString().c_str());
#ifndef ROOTCANAL_LMP
bool awaiting_authentication = connections_.AuthenticatePendingConnection();
#endif /* !ROOTCANAL_LMP */
uint16_t handle =
connections_.CreateConnection(peer, incoming.GetDestinationAddress());
if (handle == kReservedHandle) {
LOG_WARN("No free handles");
return;
}
CancelScheduledTask(page_timeout_task_id_);
#ifdef ROOTCANAL_LMP
ASSERT(link_manager_add_link(
lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(peer.data())));
#endif /* ROOTCANAL_LMP */
CheckExpiringConnection(handle);
if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, incoming.GetSourceAddress(),
bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED));
}
#ifndef ROOTCANAL_LMP
if (awaiting_authentication) {
ScheduleTask(kNoDelayMs, [this, peer, handle]() {
HandleAuthenticationRequest(peer, handle);
});
}
#endif /* !ROOTCANAL_LMP */
}
void LinkLayerController::TimerTick() {
if (inquiry_timer_task_id_ != kInvalidTaskId) Inquiry();
LeAdvertising();
LeScanning();
#ifdef ROOTCANAL_LMP
link_manager_tick(lm_.get());
#endif /* ROOTCANAL_LMP */
}
void LinkLayerController::Close() {
for (auto handle : connections_.GetAclHandles()) {
Disconnect(handle, ErrorCode::CONNECTION_TIMEOUT);
}
}
void LinkLayerController::RegisterEventChannel(
const std::function<void(std::shared_ptr<bluetooth::hci::EventBuilder>)>&
callback) {
send_event_ = callback;
}
void LinkLayerController::RegisterAclChannel(
const std::function<void(std::shared_ptr<bluetooth::hci::AclBuilder>)>&
callback) {
send_acl_ = callback;
}
void LinkLayerController::RegisterScoChannel(
const std::function<void(std::shared_ptr<bluetooth::hci::ScoBuilder>)>&
callback) {
send_sco_ = callback;
}
void LinkLayerController::RegisterIsoChannel(
const std::function<void(std::shared_ptr<bluetooth::hci::IsoBuilder>)>&
callback) {
send_iso_ = callback;
}
void LinkLayerController::RegisterRemoteChannel(
const std::function<void(
std::shared_ptr<model::packets::LinkLayerPacketBuilder>, Phy::Type)>&
callback) {
send_to_remote_ = callback;
}
void LinkLayerController::RegisterTaskScheduler(
std::function<AsyncTaskId(milliseconds, const TaskCallback&)>
event_scheduler) {
schedule_task_ = event_scheduler;
}
AsyncTaskId LinkLayerController::ScheduleTask(milliseconds delay_ms,
const TaskCallback& callback) {
if (schedule_task_) {
return schedule_task_(delay_ms, callback);
} else if (delay_ms == milliseconds::zero()) {
callback();
return 0;
} else {
LOG_ERROR("Unable to schedule task on delay");
return 0;
}
}
AsyncTaskId LinkLayerController::SchedulePeriodicTask(
milliseconds delay_ms, milliseconds period_ms,
const TaskCallback& callback) {
if (schedule_periodic_task_) {
return schedule_periodic_task_(delay_ms, period_ms, callback);
} else {
LOG_ERROR("Unable to schedule task on delay");
return 0;
}
}
void LinkLayerController::RegisterPeriodicTaskScheduler(
std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)>
periodic_event_scheduler) {
schedule_periodic_task_ = periodic_event_scheduler;
}
void LinkLayerController::CancelScheduledTask(AsyncTaskId task_id) {
if (schedule_task_ && cancel_task_) {
cancel_task_(task_id);
}
}
void LinkLayerController::RegisterTaskCancel(
std::function<void(AsyncTaskId)> task_cancel) {
cancel_task_ = task_cancel;
}
#ifdef ROOTCANAL_LMP
void LinkLayerController::ForwardToLm(bluetooth::hci::CommandView command) {
auto packet = std::vector(command.begin(), command.end());
ASSERT(link_manager_ingest_hci(lm_.get(), packet.data(), packet.size()));
}
#else
void LinkLayerController::StartSimplePairing(const Address& address) {
// IO Capability Exchange (See the Diagram in the Spec)
if (IsEventUnmasked(EventCode::IO_CAPABILITY_REQUEST)) {
send_event_(bluetooth::hci::IoCapabilityRequestBuilder::Create(address));
}
// Get a Key, then authenticate
// PublicKeyExchange(address);
// AuthenticateRemoteStage1(address);
// AuthenticateRemoteStage2(address);
}
void LinkLayerController::AuthenticateRemoteStage1(const Address& peer,
PairingType pairing_type) {
ASSERT(security_manager_.GetAuthenticationAddress() == peer);
// TODO: Public key exchange first?
switch (pairing_type) {
case PairingType::AUTO_CONFIRMATION:
if (IsEventUnmasked(EventCode::USER_CONFIRMATION_REQUEST)) {
send_event_(bluetooth::hci::UserConfirmationRequestBuilder::Create(
peer, 123456));
}
break;
case PairingType::CONFIRM_Y_N:
if (IsEventUnmasked(EventCode::USER_CONFIRMATION_REQUEST)) {
send_event_(bluetooth::hci::UserConfirmationRequestBuilder::Create(
peer, 123456));
}
break;
case PairingType::DISPLAY_PIN:
if (IsEventUnmasked(EventCode::USER_PASSKEY_NOTIFICATION)) {
send_event_(bluetooth::hci::UserPasskeyNotificationBuilder::Create(
peer, 123456));
}
break;
case PairingType::DISPLAY_AND_CONFIRM:
if (IsEventUnmasked(EventCode::USER_CONFIRMATION_REQUEST)) {
send_event_(bluetooth::hci::UserConfirmationRequestBuilder::Create(
peer, 123456));
}
break;
case PairingType::INPUT_PIN:
if (IsEventUnmasked(EventCode::USER_PASSKEY_REQUEST)) {
send_event_(bluetooth::hci::UserPasskeyRequestBuilder::Create(peer));
}
break;
case PairingType::OUT_OF_BAND:
LOG_INFO("Oob data request for %s", peer.ToString().c_str());
if (IsEventUnmasked(EventCode::REMOTE_OOB_DATA_REQUEST)) {
send_event_(bluetooth::hci::RemoteOobDataRequestBuilder::Create(peer));
}
break;
case PairingType::PEER_HAS_OUT_OF_BAND:
LOG_INFO("Trusting that %s has OOB data", peer.ToString().c_str());
SaveKeyAndAuthenticate('P', peer);
break;
default:
LOG_ALWAYS_FATAL("Invalid PairingType %d",
static_cast<int>(pairing_type));
}
}
void LinkLayerController::AuthenticateRemoteStage2(const Address& peer) {
uint16_t handle = security_manager_.GetAuthenticationHandle();
ASSERT(security_manager_.GetAuthenticationAddress() == peer);
// Check key in security_manager_ ?
if (security_manager_.IsInitiator()) {
if (IsEventUnmasked(EventCode::AUTHENTICATION_COMPLETE)) {
send_event_(bluetooth::hci::AuthenticationCompleteBuilder::Create(
ErrorCode::SUCCESS, handle));
}
}
}
ErrorCode LinkLayerController::LinkKeyRequestReply(
const Address& peer, const std::array<uint8_t, 16>& key) {
security_manager_.WriteKey(peer, key);
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, peer]() { AuthenticateRemoteStage2(peer); });
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LinkKeyRequestNegativeReply(
const Address& address) {
security_manager_.DeleteKey(address);
// Simple pairing to get a key
uint16_t handle = connections_.GetHandleOnlyAddress(address);
if (handle == kReservedHandle) {
LOG_INFO("Device not connected %s", address.ToString().c_str());
return ErrorCode::UNKNOWN_CONNECTION;
}
if (secure_simple_pairing_host_support_) {
if (!security_manager_.AuthenticationInProgress()) {
security_manager_.AuthenticationRequest(address, handle, false);
}
ScheduleTask(kNoDelayMs,
[this, address]() { StartSimplePairing(address); });
} else {
LOG_INFO("PIN pairing %s", GetAddress().ToString().c_str());
ScheduleTask(kNoDelayMs, [this, address]() {
security_manager_.SetPinRequested(address);
if (IsEventUnmasked(EventCode::PIN_CODE_REQUEST)) {
send_event_(bluetooth::hci::PinCodeRequestBuilder::Create(address));
}
});
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::IoCapabilityRequestReply(
const Address& peer, uint8_t io_capability, uint8_t oob_data_present_flag,
uint8_t authentication_requirements) {
security_manager_.SetLocalIoCapability(
peer, io_capability, oob_data_present_flag, authentication_requirements);
PairingType pairing_type = security_manager_.GetSimplePairingType();
if (pairing_type != PairingType::INVALID) {
ScheduleTask(kNoDelayMs, [this, peer, pairing_type]() {
AuthenticateRemoteStage1(peer, pairing_type);
});
SendLinkLayerPacket(model::packets::IoCapabilityResponseBuilder::Create(
GetAddress(), peer, io_capability, oob_data_present_flag,
authentication_requirements));
} else {
LOG_INFO("Requesting remote capability");
SendLinkLayerPacket(model::packets::IoCapabilityRequestBuilder::Create(
GetAddress(), peer, io_capability, oob_data_present_flag,
authentication_requirements));
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::IoCapabilityRequestNegativeReply(
const Address& peer, ErrorCode reason) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return ErrorCode::AUTHENTICATION_FAILURE;
}
security_manager_.InvalidateIoCapabilities();
SendLinkLayerPacket(
model::packets::IoCapabilityNegativeResponseBuilder::Create(
GetAddress(), peer, static_cast<uint8_t>(reason)));
return ErrorCode::SUCCESS;
}
void LinkLayerController::SaveKeyAndAuthenticate(uint8_t key_type,
const Address& peer) {
std::array<uint8_t, 16> key_vec{'k',
'e',
'y',
' ',
key_type,
5,
6,
7,
8,
9,
10,
11,
12,
13,
static_cast<uint8_t>(key_id_ >> 8u),
static_cast<uint8_t>(key_id_)};
key_id_ += 1;
security_manager_.WriteKey(peer, key_vec);
security_manager_.AuthenticationRequestFinished();
if (key_type == 'L') {
// Legacy
ScheduleTask(kNoDelayMs, [this, peer, key_vec]() {
if (IsEventUnmasked(EventCode::LINK_KEY_NOTIFICATION)) {
send_event_(bluetooth::hci::LinkKeyNotificationBuilder::Create(
peer, key_vec, bluetooth::hci::KeyType::AUTHENTICATED_P192));
}
});
} else {
ScheduleTask(kNoDelayMs, [this, peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::SUCCESS, peer));
}
});
ScheduleTask(kNoDelayMs, [this, peer, key_vec]() {
if (IsEventUnmasked(EventCode::LINK_KEY_NOTIFICATION)) {
send_event_(bluetooth::hci::LinkKeyNotificationBuilder::Create(
peer, key_vec, bluetooth::hci::KeyType::AUTHENTICATED_P256));
}
});
}
ScheduleTask(kNoDelayMs, [this, peer]() { AuthenticateRemoteStage2(peer); });
}
ErrorCode LinkLayerController::PinCodeRequestReply(const Address& peer,
std::vector<uint8_t> pin) {
LOG_INFO("%s", GetAddress().ToString().c_str());
auto current_peer = security_manager_.GetAuthenticationAddress();
if (peer != current_peer) {
LOG_INFO("%s: %s != %s", GetAddress().ToString().c_str(),
peer.ToString().c_str(), current_peer.ToString().c_str());
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
return ErrorCode::UNKNOWN_CONNECTION;
}
if (!security_manager_.GetPinRequested(peer)) {
LOG_INFO("No Pin Requested for %s", peer.ToString().c_str());
return ErrorCode::COMMAND_DISALLOWED;
}
security_manager_.SetLocalPin(peer, pin);
if (security_manager_.GetRemotePinResponseReceived(peer)) {
if (security_manager_.PinCompare()) {
LOG_INFO("Authenticating %s", peer.ToString().c_str());
SaveKeyAndAuthenticate('L', peer); // Legacy
} else {
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, peer));
}
});
}
} else {
SendLinkLayerPacket(
model::packets::PinRequestBuilder::Create(GetAddress(), peer, pin));
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::PinCodeRequestNegativeReply(
const Address& peer) {
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
if (peer != current_peer) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (!security_manager_.GetPinRequested(peer)) {
LOG_INFO("No Pin Requested for %s", peer.ToString().c_str());
return ErrorCode::COMMAND_DISALLOWED;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::UserConfirmationRequestReply(
const Address& peer) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return ErrorCode::AUTHENTICATION_FAILURE;
}
SaveKeyAndAuthenticate('U', peer);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::UserConfirmationRequestNegativeReply(
const Address& peer) {
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
if (peer != current_peer) {
return ErrorCode::UNKNOWN_CONNECTION;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::UserPasskeyRequestReply(const Address& peer,
uint32_t numeric_value) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return ErrorCode::AUTHENTICATION_FAILURE;
}
SendLinkLayerPacket(model::packets::PasskeyBuilder::Create(GetAddress(), peer,
numeric_value));
SaveKeyAndAuthenticate('P', peer);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::UserPasskeyRequestNegativeReply(
const Address& peer) {
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
if (peer != current_peer) {
return ErrorCode::UNKNOWN_CONNECTION;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::RemoteOobDataRequestReply(
const Address& peer, const std::array<uint8_t, 16>& c,
const std::array<uint8_t, 16>& r) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return ErrorCode::AUTHENTICATION_FAILURE;
}
LOG_INFO("TODO:Do something with the OOB data c=%d r=%d", c[0], r[0]);
SaveKeyAndAuthenticate('o', peer);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::RemoteOobDataRequestNegativeReply(
const Address& peer) {
auto current_peer = security_manager_.GetAuthenticationAddress();
security_manager_.AuthenticationRequestFinished();
ScheduleTask(kNoDelayMs, [this, current_peer]() {
if (IsEventUnmasked(EventCode::SIMPLE_PAIRING_COMPLETE)) {
send_event_(bluetooth::hci::SimplePairingCompleteBuilder::Create(
ErrorCode::AUTHENTICATION_FAILURE, current_peer));
}
});
if (peer != current_peer) {
return ErrorCode::UNKNOWN_CONNECTION;
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::RemoteOobExtendedDataRequestReply(
const Address& peer, const std::array<uint8_t, 16>& c192,
const std::array<uint8_t, 16>& r192, const std::array<uint8_t, 16>& c256,
const std::array<uint8_t, 16>& r256) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return ErrorCode::AUTHENTICATION_FAILURE;
}
LOG_INFO(
"TODO:Do something with the OOB data c192=%d r192=%d c256=%d r256=%d",
c192[0], r192[0], c256[0], r256[0]);
SaveKeyAndAuthenticate('O', peer);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SendKeypressNotification(
const Address& peer,
bluetooth::hci::KeypressNotificationType notification_type) {
if (notification_type >
bluetooth::hci::KeypressNotificationType::ENTRY_COMPLETED) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
SendLinkLayerPacket(model::packets::KeypressNotificationBuilder::Create(
GetAddress(), peer,
static_cast<model::packets::PasskeyNotificationType>(notification_type)));
return ErrorCode::SUCCESS;
}
void LinkLayerController::HandleAuthenticationRequest(const Address& address,
uint16_t handle) {
security_manager_.AuthenticationRequest(address, handle, true);
if (IsEventUnmasked(EventCode::LINK_KEY_REQUEST)) {
send_event_(bluetooth::hci::LinkKeyRequestBuilder::Create(address));
}
}
ErrorCode LinkLayerController::AuthenticationRequested(uint16_t handle) {
if (!connections_.HasHandle(handle)) {
LOG_INFO("Authentication Requested for unknown handle %04x", handle);
return ErrorCode::UNKNOWN_CONNECTION;
}
AddressWithType remote = connections_.GetAddress(handle);
ScheduleTask(kNoDelayMs, [this, remote, handle]() {
HandleAuthenticationRequest(remote.GetAddress(), handle);
});
return ErrorCode::SUCCESS;
}
void LinkLayerController::HandleSetConnectionEncryption(
const Address& peer, uint16_t handle, uint8_t encryption_enable) {
// TODO: Block ACL traffic or at least guard against it
if (connections_.IsEncrypted(handle) && encryption_enable) {
if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) {
send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
ErrorCode::SUCCESS, handle,
static_cast<bluetooth::hci::EncryptionEnabled>(encryption_enable)));
}
return;
}
uint16_t count = security_manager_.ReadKey(peer);
if (count == 0) {
LOG_ERROR("NO KEY HERE for %s", peer.ToString().c_str());
return;
}
auto array = security_manager_.GetKey(peer);
std::vector<uint8_t> key_vec{array.begin(), array.end()};
SendLinkLayerPacket(model::packets::EncryptConnectionBuilder::Create(
GetAddress(), peer, key_vec));
}
ErrorCode LinkLayerController::SetConnectionEncryption(
uint16_t handle, uint8_t encryption_enable) {
if (!connections_.HasHandle(handle)) {
LOG_INFO("Set Connection Encryption for unknown handle %04x", handle);
return ErrorCode::UNKNOWN_CONNECTION;
}
if (connections_.IsEncrypted(handle) && !encryption_enable) {
return ErrorCode::ENCRYPTION_MODE_NOT_ACCEPTABLE;
}
AddressWithType remote = connections_.GetAddress(handle);
if (security_manager_.ReadKey(remote.GetAddress()) == 0) {
return ErrorCode::PIN_OR_KEY_MISSING;
}
ScheduleTask(kNoDelayMs, [this, remote, handle, encryption_enable]() {
HandleSetConnectionEncryption(remote.GetAddress(), handle,
encryption_enable);
});
return ErrorCode::SUCCESS;
}
#endif /* ROOTCANAL_LMP */
std::vector<bluetooth::hci::Lap> const& LinkLayerController::ReadCurrentIacLap()
const {
return current_iac_lap_list_;
}
void LinkLayerController::WriteCurrentIacLap(
std::vector<bluetooth::hci::Lap> iac_lap) {
current_iac_lap_list_.swap(iac_lap);
// If Num_Current_IAC is greater than Num_Supported_IAC then only the first
// Num_Supported_IAC shall be stored in the Controller
if (current_iac_lap_list_.size() > properties_.num_supported_iac) {
current_iac_lap_list_.resize(properties_.num_supported_iac);
}
}
ErrorCode LinkLayerController::AcceptConnectionRequest(const Address& bd_addr,
bool try_role_switch) {
if (connections_.HasPendingConnection(bd_addr)) {
LOG_INFO("Accepting connection request from %s",
bd_addr.ToString().c_str());
ScheduleTask(kNoDelayMs, [this, bd_addr, try_role_switch]() {
LOG_INFO("Accepted connection from %s", bd_addr.ToString().c_str());
MakePeripheralConnection(bd_addr, try_role_switch);
});
return ErrorCode::SUCCESS;
}
// The HCI command Accept Connection may be used to accept incoming SCO
// connection requests.
if (connections_.HasPendingScoConnection(bd_addr)) {
ErrorCode status = ErrorCode::SUCCESS;
uint16_t sco_handle = 0;
ScoLinkParameters link_parameters = {};
ScoConnectionParameters connection_parameters =
connections_.GetScoConnectionParameters(bd_addr);
if (!connections_.AcceptPendingScoConnection(
bd_addr, connection_parameters, [this, bd_addr] {
return LinkLayerController::StartScoStream(bd_addr);
})) {
connections_.CancelPendingScoConnection(bd_addr);
status = ErrorCode::SCO_INTERVAL_REJECTED; // TODO: proper status code
} else {
sco_handle = connections_.GetScoHandle(bd_addr);
link_parameters = connections_.GetScoLinkParameters(bd_addr);
}
// Send eSCO connection response to peer.
SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create(
GetAddress(), bd_addr, (uint8_t)status,
link_parameters.transmission_interval,
link_parameters.retransmission_window, link_parameters.rx_packet_length,
link_parameters.tx_packet_length, link_parameters.air_mode,
link_parameters.extended));
// Schedule HCI Connection Complete event.
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));
});
return ErrorCode::SUCCESS;
}
LOG_INFO("No pending connection for %s", bd_addr.ToString().c_str());
return ErrorCode::UNKNOWN_CONNECTION;
}
void LinkLayerController::MakePeripheralConnection(const Address& addr,
bool try_role_switch) {
LOG_INFO("Sending page response to %s", addr.ToString().c_str());
SendLinkLayerPacket(model::packets::PageResponseBuilder::Create(
GetAddress(), addr, try_role_switch));
uint16_t handle = connections_.CreateConnection(addr, GetAddress());
if (handle == kReservedHandle) {
LOG_INFO("CreateConnection failed");
return;
}
#ifdef ROOTCANAL_LMP
ASSERT(link_manager_add_link(
lm_.get(), reinterpret_cast<const uint8_t(*)[6]>(addr.data())));
#endif /* ROOTCANAL_LMP */
CheckExpiringConnection(handle);
LOG_INFO("CreateConnection returned handle 0x%x", handle);
if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, addr, bluetooth::hci::LinkType::ACL,
bluetooth::hci::Enable::DISABLED));
}
}
ErrorCode LinkLayerController::RejectConnectionRequest(const Address& addr,
uint8_t reason) {
if (!connections_.HasPendingConnection(addr)) {
LOG_INFO("No pending connection for %s", addr.ToString().c_str());
return ErrorCode::UNKNOWN_CONNECTION;
}
ScheduleTask(kNoDelayMs, [this, addr, reason]() {
RejectPeripheralConnection(addr, reason);
});
return ErrorCode::SUCCESS;
}
void LinkLayerController::RejectPeripheralConnection(const Address& addr,
uint8_t reason) {
LOG_INFO("Sending page reject to %s (reason 0x%02hhx)",
addr.ToString().c_str(), reason);
SendLinkLayerPacket(
model::packets::PageRejectBuilder::Create(GetAddress(), addr, reason));
if (IsEventUnmasked(EventCode::CONNECTION_COMPLETE)) {
send_event_(bluetooth::hci::ConnectionCompleteBuilder::Create(
static_cast<ErrorCode>(reason), 0xeff, addr,
bluetooth::hci::LinkType::ACL, bluetooth::hci::Enable::DISABLED));
}
}
ErrorCode LinkLayerController::CreateConnection(const Address& addr, uint16_t,
uint8_t, uint16_t,
uint8_t allow_role_switch) {
if (!connections_.CreatePendingConnection(
addr, authentication_enable_ == AuthenticationEnable::REQUIRED)) {
return ErrorCode::CONTROLLER_BUSY;
}
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));
});
SendLinkLayerPacket(model::packets::PageBuilder::Create(
GetAddress(), addr, class_of_device_, allow_role_switch));
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::CreateConnectionCancel(const Address& addr) {
if (!connections_.CancelPendingConnection(addr)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
CancelScheduledTask(page_timeout_task_id_);
return ErrorCode::SUCCESS;
}
void LinkLayerController::SendDisconnectionCompleteEvent(uint16_t handle,
ErrorCode reason) {
if (IsEventUnmasked(EventCode::DISCONNECTION_COMPLETE)) {
ScheduleTask(kNoDelayMs, [this, handle, reason]() {
send_event_(bluetooth::hci::DisconnectionCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, reason));
});
}
}
ErrorCode LinkLayerController::Disconnect(uint16_t handle, ErrorCode reason) {
if (connections_.HasScoHandle(handle)) {
const Address remote = connections_.GetScoAddress(handle);
LOG_INFO("Disconnecting eSCO connection with %s",
remote.ToString().c_str());
SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create(
GetAddress(), remote, static_cast<uint8_t>(reason)));
connections_.Disconnect(handle, cancel_task_);
SendDisconnectionCompleteEvent(handle, reason);
return ErrorCode::SUCCESS;
}
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
const AddressWithType remote = connections_.GetAddress(handle);
auto is_br_edr = connections_.GetPhyType(handle) == Phy::Type::BR_EDR;
if (is_br_edr) {
LOG_INFO("Disconnecting ACL connection with %s", remote.ToString().c_str());
uint16_t sco_handle = connections_.GetScoHandle(remote.GetAddress());
if (sco_handle != kReservedHandle) {
SendLinkLayerPacket(model::packets::ScoDisconnectBuilder::Create(
GetAddress(), remote.GetAddress(), static_cast<uint8_t>(reason)));
connections_.Disconnect(sco_handle, cancel_task_);
SendDisconnectionCompleteEvent(sco_handle, reason);
}
SendLinkLayerPacket(model::packets::DisconnectBuilder::Create(
GetAddress(), remote.GetAddress(), static_cast<uint8_t>(reason)));
} else {
LOG_INFO("Disconnecting LE connection with %s", remote.ToString().c_str());
SendLeLinkLayerPacket(model::packets::DisconnectBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(), remote.GetAddress(),
static_cast<uint8_t>(reason)));
}
connections_.Disconnect(handle, cancel_task_);
SendDisconnectionCompleteEvent(handle, ErrorCode(reason));
#ifdef ROOTCANAL_LMP
if (is_br_edr) {
ASSERT(link_manager_remove_link(
lm_.get(),
reinterpret_cast<uint8_t(*)[6]>(remote.GetAddress().data())));
}
#endif
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::ChangeConnectionPacketType(uint16_t handle,
uint16_t types) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
ScheduleTask(kNoDelayMs, [this, handle, types]() {
if (IsEventUnmasked(EventCode::CONNECTION_PACKET_TYPE_CHANGED)) {
send_event_(bluetooth::hci::ConnectionPacketTypeChangedBuilder::Create(
ErrorCode::SUCCESS, handle, types));
}
});
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::ChangeConnectionLinkKey(uint16_t handle) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::CentralLinkKey(uint8_t /* key_flag */) {
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::HoldMode(uint16_t handle,
uint16_t hold_mode_max_interval,
uint16_t hold_mode_min_interval) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (hold_mode_max_interval < hold_mode_min_interval) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::SniffMode(uint16_t handle,
uint16_t sniff_max_interval,
uint16_t sniff_min_interval,
uint16_t sniff_attempt,
uint16_t sniff_timeout) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (sniff_max_interval < sniff_min_interval || sniff_attempt < 0x0001 ||
sniff_attempt > 0x7FFF || sniff_timeout > 0x7FFF) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::ExitSniffMode(uint16_t handle) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::QosSetup(uint16_t handle, uint8_t service_type,
uint32_t /* token_rate */,
uint32_t /* peak_bandwidth */,
uint32_t /* latency */,
uint32_t /* delay_variation */) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (service_type > 0x02) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::RoleDiscovery(uint16_t handle,
bluetooth::hci::Role* role) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
*role = connections_.GetAclRole(handle);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SwitchRole(Address addr,
bluetooth::hci::Role role) {
auto handle = connections_.GetHandleOnlyAddress(addr);
if (handle == rootcanal::kReservedHandle) {
return ErrorCode::UNKNOWN_CONNECTION;
}
connections_.SetAclRole(handle, role);
ScheduleTask(kNoDelayMs, [this, addr, role]() {
send_event_(bluetooth::hci::RoleChangeBuilder::Create(ErrorCode::SUCCESS,
addr, role));
});
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::ReadLinkPolicySettings(uint16_t handle,
uint16_t* settings) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
*settings = connections_.GetAclLinkPolicySettings(handle);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::WriteLinkPolicySettings(uint16_t handle,
uint16_t settings) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (settings > 7 /* Sniff + Hold + Role switch */) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
connections_.SetAclLinkPolicySettings(handle, settings);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::WriteDefaultLinkPolicySettings(
uint16_t settings) {
if (settings > 7 /* Sniff + Hold + Role switch */) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
default_link_policy_settings_ = settings;
return ErrorCode::SUCCESS;
}
uint16_t LinkLayerController::ReadDefaultLinkPolicySettings() {
return default_link_policy_settings_;
}
void LinkLayerController::ReadLocalOobData() {
std::array<uint8_t, 16> c_array(
{'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
std::array<uint8_t, 16> r_array(
{'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '0', '0', '0', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
send_event_(bluetooth::hci::ReadLocalOobDataCompleteBuilder::Create(
1, ErrorCode::SUCCESS, c_array, r_array));
oob_id_ += 1;
}
void LinkLayerController::ReadLocalOobExtendedData() {
std::array<uint8_t, 16> c_192_array(
{'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
std::array<uint8_t, 16> r_192_array(
{'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '1', '9', '2', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
std::array<uint8_t, 16> c_256_array(
{'c', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
std::array<uint8_t, 16> r_256_array(
{'r', ' ', 'a', 'r', 'r', 'a', 'y', ' ', '2', '5', '6', '0', '0', '0',
static_cast<uint8_t>((oob_id_ % 0x10000) >> 8u),
static_cast<uint8_t>(oob_id_ % 0x100)});
send_event_(bluetooth::hci::ReadLocalOobExtendedDataCompleteBuilder::Create(
1, ErrorCode::SUCCESS, c_192_array, r_192_array, c_256_array,
r_256_array));
oob_id_ += 1;
}
ErrorCode LinkLayerController::FlowSpecification(
uint16_t handle, uint8_t flow_direction, uint8_t service_type,
uint32_t /* token_rate */, uint32_t /* token_bucket_size */,
uint32_t /* peak_bandwidth */, uint32_t /* access_latency */) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if (flow_direction > 0x01 || service_type > 0x02) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
// TODO: implement real logic
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode LinkLayerController::WriteLinkSupervisionTimeout(uint16_t handle,
uint16_t) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
return ErrorCode::SUCCESS;
}
void LinkLayerController::LeConnectionUpdateComplete(
uint16_t handle, uint16_t interval_min, uint16_t interval_max,
uint16_t latency, uint16_t supervision_timeout) {
ErrorCode status = ErrorCode::SUCCESS;
if (!connections_.HasHandle(handle)) {
status = ErrorCode::UNKNOWN_CONNECTION;
}
if (interval_min < 6 || interval_max > 0xC80 || interval_min > interval_max ||
interval_max < interval_min || latency > 0x1F3 ||
supervision_timeout < 0xA || supervision_timeout > 0xC80 ||
// The Supervision_Timeout in milliseconds (*10) shall be larger than (1 +
// Connection_Latency) * Connection_Interval_Max (* 5/4) * 2
supervision_timeout <= ((((1 + latency) * interval_max * 10) / 4) / 10)) {
status = ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
uint16_t interval = (interval_min + interval_max) / 2;
SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(),
static_cast<uint8_t>(ErrorCode::SUCCESS), interval, latency,
supervision_timeout));
if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
status, handle, interval, latency, supervision_timeout));
}
}
ErrorCode LinkLayerController::LeConnectionUpdate(
uint16_t handle, uint16_t interval_min, uint16_t interval_max,
uint16_t latency, uint16_t supervision_timeout) {
if (!connections_.HasHandle(handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
bluetooth::hci::Role role = connections_.GetAclRole(handle);
if (role == bluetooth::hci::Role::CENTRAL) {
// As Central, it is allowed to directly send
// LL_CONNECTION_PARAM_UPDATE_IND to update the parameters.
SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(),
static_cast<uint8_t>(ErrorCode::SUCCESS), interval_max, latency,
supervision_timeout));
if (IsLeEventUnmasked(SubeventCode::CONNECTION_UPDATE_COMPLETE)) {
send_event_(bluetooth::hci::LeConnectionUpdateCompleteBuilder::Create(
ErrorCode::SUCCESS, handle, interval_max, latency,
supervision_timeout));
}
} else {
// Send LL_CONNECTION_PARAM_REQ and wait for LL_CONNECTION_PARAM_RSP
// in return.
SendLeLinkLayerPacket(LeConnectionParameterRequestBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(), interval_min,
interval_max, latency, supervision_timeout));
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestReply(
uint16_t connection_handle, uint16_t interval_min, uint16_t interval_max,
uint16_t timeout, uint16_t latency, uint16_t minimum_ce_length,
uint16_t maximum_ce_length) {
if (!connections_.HasHandle(connection_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
if ((interval_min > interval_max) ||
(minimum_ce_length > maximum_ce_length)) {
return ErrorCode::INVALID_HCI_COMMAND_PARAMETERS;
}
ScheduleTask(kNoDelayMs, [this, connection_handle, interval_min, interval_max,
latency, timeout]() {
LeConnectionUpdateComplete(connection_handle, interval_min, interval_max,
latency, timeout);
});
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeRemoteConnectionParameterRequestNegativeReply(
uint16_t connection_handle, bluetooth::hci::ErrorCode reason) {
if (!connections_.HasHandle(connection_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
uint16_t interval = 0;
uint16_t latency = 0;
uint16_t timeout = 0;
SendLeLinkLayerPacket(LeConnectionParameterUpdateBuilder::Create(
connections_.GetOwnAddress(connection_handle).GetAddress(),
connections_.GetAddress(connection_handle).GetAddress(),
static_cast<uint8_t>(reason), interval, latency, timeout));
return ErrorCode::SUCCESS;
}
bool LinkLayerController::HasAclConnection() {
return (connections_.GetAclHandles().size() > 0);
}
void LinkLayerController::LeReadIsoTxSync(uint16_t /* handle */) {}
void LinkLayerController::LeSetCigParameters(
uint8_t cig_id, uint32_t sdu_interval_m_to_s, uint32_t sdu_interval_s_to_m,
bluetooth::hci::ClockAccuracy clock_accuracy,
bluetooth::hci::Packing packing, bluetooth::hci::Enable framing,
uint16_t max_transport_latency_m_to_s,
uint16_t max_transport_latency_s_to_m,
std::vector<bluetooth::hci::CisParametersConfig> cis_config) {
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(connections_.SetCigParameters(
cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, clock_accuracy,
packing, framing, max_transport_latency_m_to_s,
max_transport_latency_s_to_m, cis_config));
}
}
ErrorCode LinkLayerController::LeCreateCis(
std::vector<bluetooth::hci::CreateCisConfig> cis_config) {
if (connections_.HasPendingCis()) {
return ErrorCode::COMMAND_DISALLOWED;
}
for (auto& config : cis_config) {
if (!connections_.HasHandle(config.acl_connection_handle_)) {
LOG_INFO("Unknown ACL handle %04x", config.acl_connection_handle_);
return ErrorCode::UNKNOWN_CONNECTION;
}
if (!connections_.HasCisHandle(config.cis_connection_handle_)) {
LOG_INFO("Unknown CIS handle %04x", config.cis_connection_handle_);
return ErrorCode::UNKNOWN_CONNECTION;
}
}
for (auto& config : cis_config) {
connections_.CreatePendingCis(config);
auto own_address =
connections_.GetOwnAddress(config.acl_connection_handle_);
auto peer_address = connections_.GetAddress(config.acl_connection_handle_);
StreamParameters stream_parameters =
connections_.GetStreamParameters(config.cis_connection_handle_);
GroupParameters group_parameters =
connections_.GetGroupParameters(stream_parameters.group_id);
SendLeLinkLayerPacket(model::packets::IsoConnectionRequestBuilder::Create(
own_address.GetAddress(), peer_address.GetAddress(),
stream_parameters.group_id, group_parameters.sdu_interval_m_to_s,
group_parameters.sdu_interval_s_to_m, group_parameters.interleaved,
group_parameters.framed, group_parameters.max_transport_latency_m_to_s,
group_parameters.max_transport_latency_s_to_m,
stream_parameters.stream_id, stream_parameters.max_sdu_m_to_s,
stream_parameters.max_sdu_s_to_m, config.cis_connection_handle_,
config.acl_connection_handle_));
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeRemoveCig(uint8_t cig_id) {
return connections_.RemoveCig(cig_id);
}
ErrorCode LinkLayerController::LeAcceptCisRequest(uint16_t cis_handle) {
if (!connections_.HasPendingCisConnection(cis_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
auto acl_handle = connections_.GetPendingAclHandle(cis_handle);
connections_.ConnectCis(cis_handle);
SendLeLinkLayerPacket(model::packets::IsoConnectionResponseBuilder::Create(
connections_.GetOwnAddress(acl_handle).GetAddress(),
connections_.GetAddress(acl_handle).GetAddress(),
static_cast<uint8_t>(ErrorCode::SUCCESS), cis_handle, acl_handle,
connections_.GetRemoteCisHandleForCisHandle(cis_handle)));
// Both sides have to send LeCisEstablished event
uint32_t cig_sync_delay = 0x100;
uint32_t cis_sync_delay = 0x200;
uint32_t latency_m_to_s = 0x200;
uint32_t latency_s_to_m = 0x200;
uint8_t nse = 1;
uint8_t bn_m_to_s = 0;
uint8_t bn_s_to_m = 0;
uint8_t ft_m_to_s = 0;
uint8_t ft_s_to_m = 0;
uint8_t max_pdu_m_to_s = 0x40;
uint8_t max_pdu_s_to_m = 0x40;
uint16_t iso_interval = 0x100;
if (IsEventUnmasked(EventCode::LE_META_EVENT)) {
send_event_(bluetooth::hci::LeCisEstablishedBuilder::Create(
ErrorCode::SUCCESS, cis_handle, cig_sync_delay, cis_sync_delay,
latency_m_to_s, latency_s_to_m,
bluetooth::hci::SecondaryPhyType::NO_PACKETS,
bluetooth::hci::SecondaryPhyType::NO_PACKETS, nse, bn_m_to_s, bn_s_to_m,
ft_m_to_s, ft_s_to_m, max_pdu_m_to_s, max_pdu_s_to_m, iso_interval));
}
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeRejectCisRequest(uint16_t cis_handle,
ErrorCode reason) {
if (!connections_.HasPendingCisConnection(cis_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
auto acl_handle = connections_.GetPendingAclHandle(cis_handle);
SendLeLinkLayerPacket(model::packets::IsoConnectionResponseBuilder::Create(
connections_.GetOwnAddress(acl_handle).GetAddress(),
connections_.GetAddress(acl_handle).GetAddress(),
static_cast<uint8_t>(reason), acl_handle, cis_handle, kReservedHandle));
connections_.RejectCis(cis_handle);
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeCreateBig(
uint8_t /* big_handle */, uint8_t /* advertising_handle */,
uint8_t /* num_bis */, uint32_t /* sdu_interval */, uint16_t /* max_sdu */,
uint16_t /* max_transport_latency */, uint8_t /* rtn */,
bluetooth::hci::SecondaryPhyType /* phy */,
bluetooth::hci::Packing /* packing */, bluetooth::hci::Enable /* framing */,
bluetooth::hci::Enable /* encryption */,
std::array<uint8_t, 16> /* broadcast_code */) {
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeTerminateBig(uint8_t /* big_handle */,
ErrorCode /* reason */) {
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeBigCreateSync(
uint8_t /* big_handle */, uint16_t /* sync_handle */,
bluetooth::hci::Enable /* encryption */,
std::array<uint8_t, 16> /* broadcast_code */, uint8_t /* mse */,
uint16_t /* big_sync_timeout */, std::vector<uint8_t> /* bis */) {
return ErrorCode::SUCCESS;
}
void LinkLayerController::LeBigTerminateSync(uint8_t /* big_handle */) {}
ErrorCode LinkLayerController::LeRequestPeerSca(uint16_t /* request_handle */) {
return ErrorCode::SUCCESS;
}
void LinkLayerController::LeSetupIsoDataPath(
uint16_t /* connection_handle */,
bluetooth::hci::DataPathDirection /* data_path_direction */,
uint8_t /* data_path_id */, uint64_t /* codec_id */,
uint32_t /* controller_Delay */,
std::vector<uint8_t> /* codec_configuration */) {}
void LinkLayerController::LeRemoveIsoDataPath(
uint16_t /* connection_handle */,
bluetooth::hci::RemoveDataPathDirection /* remove_data_path_direction */) {}
void LinkLayerController::HandleLeEnableEncryption(
uint16_t handle, std::array<uint8_t, 8> rand, uint16_t ediv,
std::array<uint8_t, 16> ltk) {
// TODO: Check keys
// TODO: Block ACL traffic or at least guard against it
if (!connections_.HasHandle(handle)) {
return;
}
SendLeLinkLayerPacket(model::packets::LeEncryptConnectionBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(), rand, ediv, ltk));
}
ErrorCode LinkLayerController::LeEnableEncryption(uint16_t handle,
std::array<uint8_t, 8> rand,
uint16_t ediv,
std::array<uint8_t, 16> ltk) {
if (!connections_.HasHandle(handle)) {
LOG_INFO("Unknown handle %04x", handle);
return ErrorCode::UNKNOWN_CONNECTION;
}
ScheduleTask(kNoDelayMs, [this, handle, rand, ediv, ltk]() {
HandleLeEnableEncryption(handle, rand, ediv, ltk);
});
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeLongTermKeyRequestReply(
uint16_t handle, std::array<uint8_t, 16> ltk) {
if (!connections_.HasHandle(handle)) {
LOG_INFO("Unknown handle %04x", handle);
return ErrorCode::UNKNOWN_CONNECTION;
}
// TODO: Check keys
if (connections_.IsEncrypted(handle)) {
if (IsEventUnmasked(EventCode::ENCRYPTION_KEY_REFRESH_COMPLETE)) {
send_event_(bluetooth::hci::EncryptionKeyRefreshCompleteBuilder::Create(
ErrorCode::SUCCESS, handle));
}
} else {
connections_.Encrypt(handle);
if (IsEventUnmasked(EventCode::ENCRYPTION_CHANGE)) {
send_event_(bluetooth::hci::EncryptionChangeBuilder::Create(
ErrorCode::SUCCESS, handle, bluetooth::hci::EncryptionEnabled::ON));
}
}
SendLeLinkLayerPacket(
model::packets::LeEncryptConnectionResponseBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(),
std::array<uint8_t, 8>(), uint16_t(), ltk));
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::LeLongTermKeyRequestNegativeReply(
uint16_t handle) {
if (!connections_.HasHandle(handle)) {
LOG_INFO("Unknown handle %04x", handle);
return ErrorCode::UNKNOWN_CONNECTION;
}
SendLeLinkLayerPacket(
model::packets::LeEncryptConnectionResponseBuilder::Create(
connections_.GetOwnAddress(handle).GetAddress(),
connections_.GetAddress(handle).GetAddress(),
std::array<uint8_t, 8>(), uint16_t(), std::array<uint8_t, 16>()));
return ErrorCode::SUCCESS;
}
void LinkLayerController::Reset() {
host_supported_features_ = 0;
le_host_support_ = false;
secure_simple_pairing_host_support_ = false;
secure_connections_host_support_ = false;
le_host_supported_features_ = 0;
connected_isochronous_stream_host_support_ = false;
connection_subrating_host_support_ = false;
random_address_ = Address::kEmpty;
page_scan_enable_ = false;
inquiry_scan_enable_ = false;
inquiry_scan_interval_ = 0x1000;
inquiry_scan_window_ = 0x0012;
page_timeout_ = 0x2000;
connection_accept_timeout_ = 0x1FA0;
page_scan_interval_ = 0x0800;
page_scan_window_ = 0x0012;
voice_setting_ = 0x0060;
authentication_enable_ = AuthenticationEnable::NOT_REQUIRED;
default_link_policy_settings_ = 0x0000;
sco_flow_control_enable_ = false;
local_name_.fill(0);
extended_inquiry_response_.fill(0);
class_of_device_ = ClassOfDevice({0, 0, 0});
min_encryption_key_size_ = 16;
event_mask_ = 0x00001fffffffffff;
event_mask_page_2_ = 0x0;
le_event_mask_ = 0x01f;
le_suggested_max_tx_octets_ = 0x001b;
le_suggested_max_tx_time_ = 0x0148;
resolvable_private_address_timeout_ = std::chrono::seconds(0x0384);
page_scan_repetition_mode_ = PageScanRepetitionMode::R0;
connections_ = AclConnectionHandler();
oob_id_ = 1;
key_id_ = 1;
le_filter_accept_list_.clear();
le_resolving_list_.clear();
le_resolving_list_enabled_ = false;
legacy_advertising_in_use_ = false;
extended_advertising_in_use_ = false;
legacy_advertiser_ = LegacyAdvertiser{};
extended_advertisers_.clear();
scanner_ = Scanner{};
initiator_ = Initiator{};
last_inquiry_ = steady_clock::now();
inquiry_mode_ = InquiryType::STANDARD;
inquiry_lap_ = 0;
inquiry_max_responses_ = 0;
bluetooth::hci::Lap general_iac;
general_iac.lap_ = 0x33; // 0x9E8B33
current_iac_lap_list_.clear();
current_iac_lap_list_.emplace_back(general_iac);
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;
}
#ifdef ROOTCANAL_LMP
lm_.reset(link_manager_create(ops_));
#else
security_manager_ = SecurityManager(10);
#endif
}
void LinkLayerController::StartInquiry(milliseconds timeout) {
inquiry_timer_task_id_ = ScheduleTask(milliseconds(timeout), [this]() {
LinkLayerController::InquiryTimeout();
});
}
void LinkLayerController::InquiryCancel() {
ASSERT(inquiry_timer_task_id_ != kInvalidTaskId);
CancelScheduledTask(inquiry_timer_task_id_);
inquiry_timer_task_id_ = kInvalidTaskId;
}
void LinkLayerController::InquiryTimeout() {
if (inquiry_timer_task_id_ != kInvalidTaskId) {
inquiry_timer_task_id_ = kInvalidTaskId;
if (IsEventUnmasked(EventCode::INQUIRY_COMPLETE)) {
send_event_(
bluetooth::hci::InquiryCompleteBuilder::Create(ErrorCode::SUCCESS));
}
}
}
void LinkLayerController::SetInquiryMode(uint8_t mode) {
inquiry_mode_ = static_cast<model::packets::InquiryType>(mode);
}
void LinkLayerController::SetInquiryLAP(uint64_t lap) { inquiry_lap_ = lap; }
void LinkLayerController::SetInquiryMaxResponses(uint8_t max) {
inquiry_max_responses_ = max;
}
void LinkLayerController::Inquiry() {
steady_clock::time_point now = steady_clock::now();
if (duration_cast<milliseconds>(now - last_inquiry_) < milliseconds(2000)) {
return;
}
SendLinkLayerPacket(model::packets::InquiryBuilder::Create(
GetAddress(), Address::kEmpty, inquiry_mode_, inquiry_lap_));
last_inquiry_ = now;
}
void LinkLayerController::SetInquiryScanEnable(bool enable) {
inquiry_scan_enable_ = enable;
}
void LinkLayerController::SetPageScanEnable(bool enable) {
page_scan_enable_ = enable;
}
uint16_t LinkLayerController::GetPageTimeout() { return page_timeout_; }
void LinkLayerController::SetPageTimeout(uint16_t page_timeout) {
page_timeout_ = page_timeout;
}
ErrorCode LinkLayerController::AddScoConnection(uint16_t connection_handle,
uint16_t packet_type,
ScoDatapath datapath) {
if (!connections_.HasHandle(connection_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
Address bd_addr = connections_.GetAddress(connection_handle).GetAddress();
if (connections_.HasPendingScoConnection(bd_addr)) {
return ErrorCode::COMMAND_DISALLOWED;
}
LOG_INFO("Creating SCO connection with %s", bd_addr.ToString().c_str());
// Save connection parameters.
ScoConnectionParameters connection_parameters = {
8000,
8000,
0xffff,
0x60 /* 16bit CVSD */,
(uint8_t)bluetooth::hci::RetransmissionEffort::NO_RETRANSMISSION,
(uint16_t)((uint16_t)((packet_type >> 5) & 0x7u) |
(uint16_t)bluetooth::hci::SynchronousPacketTypeBits::
NO_2_EV3_ALLOWED |
(uint16_t)bluetooth::hci::SynchronousPacketTypeBits::
NO_3_EV3_ALLOWED |
(uint16_t)bluetooth::hci::SynchronousPacketTypeBits::
NO_2_EV5_ALLOWED |
(uint16_t)bluetooth::hci::SynchronousPacketTypeBits::
NO_3_EV5_ALLOWED)};
connections_.CreateScoConnection(
connections_.GetAddress(connection_handle).GetAddress(),
connection_parameters, SCO_STATE_PENDING, datapath, true);
// Send SCO connection request to peer.
SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create(
GetAddress(), bd_addr, connection_parameters.transmit_bandwidth,
connection_parameters.receive_bandwidth,
connection_parameters.max_latency, connection_parameters.voice_setting,
connection_parameters.retransmission_effort,
connection_parameters.packet_type, class_of_device_));
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::SetupSynchronousConnection(
uint16_t connection_handle, uint32_t transmit_bandwidth,
uint32_t receive_bandwidth, uint16_t max_latency, uint16_t voice_setting,
uint8_t retransmission_effort, uint16_t packet_types,
ScoDatapath datapath) {
if (!connections_.HasHandle(connection_handle)) {
return ErrorCode::UNKNOWN_CONNECTION;
}
Address bd_addr = connections_.GetAddress(connection_handle).GetAddress();
if (connections_.HasPendingScoConnection(bd_addr)) {
// This command may be used to modify an exising eSCO link.
// Skip for now. TODO: should return an event
// HCI_Synchronous_Connection_Changed on both sides.
return ErrorCode::COMMAND_DISALLOWED;
}
LOG_INFO("Creating eSCO connection with %s", bd_addr.ToString().c_str());
// Save connection parameters.
ScoConnectionParameters connection_parameters = {
transmit_bandwidth, receive_bandwidth, max_latency,
voice_setting, retransmission_effort, packet_types};
connections_.CreateScoConnection(
connections_.GetAddress(connection_handle).GetAddress(),
connection_parameters, SCO_STATE_PENDING, datapath);
// Send eSCO connection request to peer.
SendLinkLayerPacket(model::packets::ScoConnectionRequestBuilder::Create(
GetAddress(), bd_addr, transmit_bandwidth, receive_bandwidth, max_latency,
voice_setting, retransmission_effort, packet_types, class_of_device_));
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::AcceptSynchronousConnection(
Address bd_addr, uint32_t transmit_bandwidth, uint32_t receive_bandwidth,
uint16_t max_latency, uint16_t voice_setting, uint8_t retransmission_effort,
uint16_t packet_types) {
LOG_INFO("Accepting eSCO connection request from %s",
bd_addr.ToString().c_str());
if (!connections_.HasPendingScoConnection(bd_addr)) {
LOG_INFO("No pending eSCO connection for %s", bd_addr.ToString().c_str());
return ErrorCode::COMMAND_DISALLOWED;
}
ErrorCode status = ErrorCode::SUCCESS;
uint16_t sco_handle = 0;
ScoLinkParameters link_parameters = {};
ScoConnectionParameters connection_parameters = {
transmit_bandwidth, receive_bandwidth, max_latency,
voice_setting, retransmission_effort, packet_types};
if (!connections_.AcceptPendingScoConnection(
bd_addr, connection_parameters, [this, bd_addr] {
return LinkLayerController::StartScoStream(bd_addr);
})) {
connections_.CancelPendingScoConnection(bd_addr);
status = ErrorCode::STATUS_UNKNOWN; // TODO: proper status code
} else {
sco_handle = connections_.GetScoHandle(bd_addr);
link_parameters = connections_.GetScoLinkParameters(bd_addr);
}
// Send eSCO connection response to peer.
SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create(
GetAddress(), bd_addr, (uint8_t)status,
link_parameters.transmission_interval,
link_parameters.retransmission_window, link_parameters.rx_packet_length,
link_parameters.tx_packet_length, link_parameters.air_mode,
link_parameters.extended));
// Schedule HCI Synchronous Connection Complete event.
ScheduleTask(kNoDelayMs, [this, status, sco_handle, bd_addr,
link_parameters]() {
send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
ErrorCode(status), sco_handle, bd_addr,
link_parameters.extended ? bluetooth::hci::ScoLinkType::ESCO
: bluetooth::hci::ScoLinkType::SCO,
link_parameters.extended ? link_parameters.transmission_interval : 0,
link_parameters.extended ? link_parameters.retransmission_window : 0,
link_parameters.extended ? link_parameters.rx_packet_length : 0,
link_parameters.extended ? link_parameters.tx_packet_length : 0,
bluetooth::hci::ScoAirMode(link_parameters.air_mode)));
});
return ErrorCode::SUCCESS;
}
ErrorCode LinkLayerController::RejectSynchronousConnection(Address bd_addr,
uint16_t reason) {
LOG_INFO("Rejecting eSCO connection request from %s",
bd_addr.ToString().c_str());
if (reason == (uint8_t)ErrorCode::SUCCESS) {
reason = (uint8_t)ErrorCode::REMOTE_USER_TERMINATED_CONNECTION;
}
if (!connections_.HasPendingScoConnection(bd_addr)) {
return ErrorCode::COMMAND_DISALLOWED;
}
connections_.CancelPendingScoConnection(bd_addr);
// Send eSCO connection response to peer.
SendLinkLayerPacket(model::packets::ScoConnectionResponseBuilder::Create(
GetAddress(), bd_addr, reason, 0, 0, 0, 0, 0, 0));
// Schedule HCI Synchronous Connection Complete event.
ScheduleTask(kNoDelayMs, [this, reason, bd_addr]() {
send_event_(bluetooth::hci::SynchronousConnectionCompleteBuilder::Create(
ErrorCode(reason), 0, bd_addr, bluetooth::hci::ScoLinkType::ESCO, 0, 0,
0, 0, bluetooth::hci::ScoAirMode::TRANSPARENT));
});
return ErrorCode::SUCCESS;
}
void LinkLayerController::CheckExpiringConnection(uint16_t handle) {
if (!connections_.HasHandle(handle)) {
return;
}
if (connections_.HasLinkExpired(handle)) {
Disconnect(handle, ErrorCode::CONNECTION_TIMEOUT);
return;
}
if (connections_.IsLinkNearExpiring(handle)) {
AddressWithType my_address = connections_.GetOwnAddress(handle);
AddressWithType destination = connections_.GetAddress(handle);
SendLinkLayerPacket(model::packets::PingRequestBuilder::Create(
my_address.GetAddress(), destination.GetAddress()));
ScheduleTask(std::chrono::duration_cast<milliseconds>(
connections_.TimeUntilLinkExpired(handle)),
[this, handle] { CheckExpiringConnection(handle); });
return;
}
ScheduleTask(std::chrono::duration_cast<milliseconds>(
connections_.TimeUntilLinkNearExpiring(handle)),
[this, handle] { CheckExpiringConnection(handle); });
}
void LinkLayerController::IncomingPingRequest(
model::packets::LinkLayerPacketView packet) {
auto view = model::packets::PingRequestView::Create(packet);
ASSERT(view.IsValid());
SendLinkLayerPacket(model::packets::PingResponseBuilder::Create(
packet.GetDestinationAddress(), packet.GetSourceAddress()));
}
AsyncTaskId LinkLayerController::StartScoStream(Address address) {
auto sco_builder = bluetooth::hci::ScoBuilder::Create(
connections_.GetScoHandle(address), PacketStatusFlag::CORRECTLY_RECEIVED,
{0, 0, 0, 0, 0});
auto bytes = std::make_shared<std::vector<uint8_t>>();
bluetooth::packet::BitInserter bit_inserter(*bytes);
sco_builder->Serialize(bit_inserter);
auto raw_view =
bluetooth::hci::PacketView<bluetooth::hci::kLittleEndian>(bytes);
auto sco_view = bluetooth::hci::ScoView::Create(raw_view);
ASSERT(sco_view.IsValid());
return SchedulePeriodicTask(0ms, 20ms, [this, address, sco_view]() {
LOG_INFO("SCO sending...");
SendScoToRemote(sco_view);
});
}
} // namespace rootcanal