blob: 818991b8f1352579e1446961e1f32a30e1b41af2 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_peer.h"
#include <endian.h>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/l2cap_defs.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
namespace bt::testing {
using pw::bluetooth::emboss::LEExtendedAdvertisingReportDataWriter;
FakePeer::FakePeer(const DeviceAddress& address,
pw::async::Dispatcher& pw_dispatcher,
bool connectable,
bool scannable,
bool send_advertising_report)
: controller_(nullptr),
address_(address),
name_("FakePeer"),
connected_(false),
connectable_(connectable),
scannable_(scannable),
advertising_enabled_(true),
directed_(false),
address_resolved_(false),
send_advertising_report_(send_advertising_report),
connect_status_(pw::bluetooth::emboss::StatusCode::SUCCESS),
connect_response_(pw::bluetooth::emboss::StatusCode::SUCCESS),
force_pending_connect_(false),
supports_ll_conn_update_procedure_(true),
le_features_(hci_spec::LESupportedFeatures{0}),
l2cap_(fit::bind_member<&FakePeer::SendPacket>(this)),
gatt_server_(this),
sdp_server_(pw_dispatcher) {
signaling_server_.RegisterWithL2cap(&l2cap_);
gatt_server_.RegisterWithL2cap(&l2cap_);
sdp_server_.RegisterWithL2cap(&l2cap_);
}
void FakePeer::set_scan_response(const ByteBuffer& data) {
BT_DEBUG_ASSERT(scannable_);
scan_response_ = DynamicByteBuffer(data);
}
DynamicByteBuffer FakePeer::CreateInquiryResponseEvent(
pw::bluetooth::emboss::InquiryMode mode) const {
BT_DEBUG_ASSERT(address_.type() == DeviceAddress::Type::kBREDR);
if (mode == pw::bluetooth::emboss::InquiryMode::STANDARD) {
size_t packet_size =
pw::bluetooth::emboss::InquiryResultEvent::MinSizeInBytes() +
pw::bluetooth::emboss::InquiryResult::IntrinsicSizeInBytes();
auto packet = hci::EmbossEventPacket::New<
pw::bluetooth::emboss::InquiryResultEventWriter>(
hci_spec::kInquiryResultEventCode, packet_size);
auto view = packet.view_t();
view.num_responses().Write(1);
view.responses()[0].bd_addr().CopyFrom(address_.value().view());
view.responses()[0].page_scan_repetition_mode().Write(
pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
view.responses()[0].class_of_device().BackingStorage().WriteUInt(
class_of_device_.to_int());
return DynamicByteBuffer{packet.data()};
}
constexpr size_t packet_size =
pw::bluetooth::emboss::InquiryResultWithRssiEvent::MinSizeInBytes() +
pw::bluetooth::emboss::InquiryResultWithRssi::IntrinsicSizeInBytes();
auto packet = hci::EmbossEventPacket::New<
pw::bluetooth::emboss::InquiryResultWithRssiEventWriter>(
hci_spec::kInquiryResultEventCode, packet_size);
auto view = packet.view_t();
// TODO(jamuraa): simulate clock offset and RSSI
view.num_responses().Write(1);
auto response = view.responses()[0];
response.bd_addr().CopyFrom(address_.value().view());
response.page_scan_repetition_mode().Write(
pw::bluetooth::emboss::PageScanRepetitionMode::R0_);
response.class_of_device().BackingStorage().WriteUInt(
class_of_device_.to_int());
response.clock_offset().BackingStorage().WriteUInt(0);
response.rssi().Write(-30);
return DynamicByteBuffer{packet.data()};
}
void FakePeer::AddLink(hci_spec::ConnectionHandle handle) {
BT_DEBUG_ASSERT(!HasLink(handle));
logical_links_.insert(handle);
if (logical_links_.size() == 1u) {
set_connected(true);
}
}
void FakePeer::RemoveLink(hci_spec::ConnectionHandle handle) {
BT_DEBUG_ASSERT(HasLink(handle));
logical_links_.erase(handle);
if (logical_links_.empty())
set_connected(false);
}
bool FakePeer::HasLink(hci_spec::ConnectionHandle handle) const {
return logical_links_.count(handle) != 0u;
}
FakePeer::HandleSet FakePeer::Disconnect() {
set_connected(false);
return std::move(logical_links_);
}
void FakePeer::OnRxL2CAP(hci_spec::ConnectionHandle conn,
const ByteBuffer& pdu) {
if (pdu.size() < sizeof(l2cap::BasicHeader)) {
bt_log(WARN, "fake-hci", "malformed L2CAP packet!");
return;
}
l2cap_.HandlePdu(conn, pdu);
}
void FakePeer::SendPacket(hci_spec::ConnectionHandle conn,
l2cap::ChannelId cid,
const ByteBuffer& packet) const {
controller()->SendL2CAPBFrame(conn, cid, packet);
}
void FakePeer::WriteScanResponseReport(
pw::bluetooth::emboss::LEAdvertisingReportDataWriter report) const {
BT_DEBUG_ASSERT(scannable_);
report.data_length().Write(scan_response_.size());
report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
report.BackingStorage().data(),
report.SizeInBytes() + scan_response_.size());
report.event_type().Write(
pw::bluetooth::emboss::LEAdvertisingEventType::SCAN_RESPONSE);
report.address().CopyFrom(address_.value().view());
report.address_type().Write(
address_.type() == DeviceAddress::Type::kLERandom
? pw::bluetooth::emboss::LEAddressType::RANDOM
: pw::bluetooth::emboss::LEAddressType::PUBLIC);
std::memcpy(report.data().BackingStorage().data(),
scan_response_.data(),
scan_response_.size());
report.rssi().Write(rssi());
}
DynamicByteBuffer FakePeer::BuildLegacyAdvertisingReportEvent(
bool include_scan_rsp) const {
BT_DEBUG_ASSERT(advertising_data_.size() <=
hci_spec::kMaxLEAdvertisingDataLength);
std::vector<size_t> reports_sizes;
reports_sizes.push_back(
pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
advertising_data_.size());
size_t reports_size = reports_sizes[0];
if (include_scan_rsp) {
BT_DEBUG_ASSERT(scannable_);
reports_sizes.push_back(
pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
scan_response_.size());
reports_size += reports_sizes[1];
}
size_t packet_size =
reports_size +
pw::bluetooth::emboss::LEAdvertisingReportSubevent::MinSizeInBytes();
auto event = hci::EmbossEventPacket::New<
pw::bluetooth::emboss::LEAdvertisingReportSubeventWriter>(
hci_spec::kLEMetaEventCode, packet_size);
auto view = event.view_t();
view.le_meta_event().subevent_code().Write(
hci_spec::kLEAdvertisingReportSubeventCode);
view.num_reports().Write(reports_sizes.size());
// Initially construct an incomplete view to write the |data_length| field.
auto report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
view.reports().BackingStorage().data(), reports_sizes[0]);
report.data_length().Write(advertising_data_.size());
// Remake view with the proper size, taking into account |data_length|.
report = pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
view.reports().BackingStorage().data(), reports_sizes[0]);
if (directed_) {
report.event_type().Write(
pw::bluetooth::emboss::LEAdvertisingEventType::CONNECTABLE_DIRECTED);
} else if (connectable_) {
report.event_type().Write(pw::bluetooth::emboss::LEAdvertisingEventType::
CONNECTABLE_AND_SCANNABLE_UNDIRECTED);
} else if (scannable_) {
report.event_type().Write(
pw::bluetooth::emboss::LEAdvertisingEventType::SCANNABLE_UNDIRECTED);
} else {
report.event_type().Write(pw::bluetooth::emboss::LEAdvertisingEventType::
NON_CONNECTABLE_UNDIRECTED);
}
if (address_.type() == DeviceAddress::Type::kLERandom) {
report.address_type().Write(
address_resolved_
? pw::bluetooth::emboss::LEAddressType::RANDOM_IDENTITY
: pw::bluetooth::emboss::LEAddressType::RANDOM);
} else {
report.address_type().Write(
address_resolved_
? pw::bluetooth::emboss::LEAddressType::PUBLIC_IDENTITY
: pw::bluetooth::emboss::LEAddressType::PUBLIC);
}
report.address().CopyFrom(address_.value().view());
std::memcpy(report.data().BackingStorage().data(),
advertising_data_.data(),
advertising_data_.size());
report.rssi().Write(rssi());
if (include_scan_rsp) {
WriteScanResponseReport(
pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
view.reports().BackingStorage().data() + reports_sizes[0],
reports_sizes[1]));
}
return DynamicByteBuffer(event.data());
}
DynamicByteBuffer FakePeer::BuildLegacyScanResponseReportEvent() const {
BT_DEBUG_ASSERT(scannable_);
BT_DEBUG_ASSERT(scan_response_.size() <=
hci_spec::kMaxLEAdvertisingDataLength);
size_t report_size =
pw::bluetooth::emboss::LEAdvertisingReportData::MinSizeInBytes() +
scan_response_.size();
size_t packet_size =
report_size +
pw::bluetooth::emboss::LEAdvertisingReportSubevent::MinSizeInBytes();
auto event = hci::EmbossEventPacket::New<
pw::bluetooth::emboss::LEAdvertisingReportSubeventWriter>(
hci_spec::kLEMetaEventCode, packet_size);
auto view = event.view_t();
view.le_meta_event().subevent_code().Write(
hci_spec::kLEAdvertisingReportSubeventCode);
view.num_reports().Write(1);
WriteScanResponseReport(
pw::bluetooth::emboss::MakeLEAdvertisingReportDataView(
view.reports().BackingStorage().data(), report_size));
return DynamicByteBuffer(event.data());
}
void FakePeer::FillExtendedAdvertisingReport(
LEExtendedAdvertisingReportDataWriter report,
const ByteBuffer& data,
bool is_fragmented,
bool is_scan_response) const {
if (use_extended_advertising_pdus_) {
report.event_type().directed().Write(directed_);
report.event_type().connectable().Write(connectable_);
report.event_type().scannable().Write(scannable_);
report.event_type().scan_response().Write(is_scan_response);
if (is_fragmented) {
report.event_type().data_status().Write(
pw::bluetooth::emboss::LEAdvertisingDataStatus::INCOMPLETE);
} else {
report.event_type().data_status().Write(
pw::bluetooth::emboss::LEAdvertisingDataStatus::COMPLETE);
}
} else {
report.event_type().legacy().Write(true);
if (is_scan_response) {
report.event_type().scan_response().Write(true);
}
if (directed_) { // ADV_DIRECT_IND
report.event_type().directed().Write(true);
report.event_type().connectable().Write(true);
} else if (connectable_) { // ADV_IND
report.event_type().connectable().Write(true);
report.event_type().scannable().Write(true);
} else if (scannable_) { // ADV_SCAN_IND
report.event_type().scannable().Write(true);
}
// else ADV_NONCONN_IND
}
if (address_.type() == DeviceAddress::Type::kLERandom) {
if (address_resolved_) {
report.address_type().Write(
pw::bluetooth::emboss::LEExtendedAddressType::RANDOM_IDENTITY);
} else {
report.address_type().Write(
pw::bluetooth::emboss::LEExtendedAddressType::RANDOM);
}
} else {
if (address_resolved_) {
report.address_type().Write(
pw::bluetooth::emboss::LEExtendedAddressType::PUBLIC_IDENTITY);
} else {
report.address_type().Write(
pw::bluetooth::emboss::LEExtendedAddressType::PUBLIC);
}
}
report.address().bd_addr().CopyFrom(address_.value().view().bd_addr());
report.primary_phy().Write(
pw::bluetooth::emboss::LEPrimaryAdvertisingPHY::LE_1M);
report.secondary_phy().Write(
pw::bluetooth::emboss::LESecondaryAdvertisingPHY::NONE);
report.advertising_sid().Write(0);
report.tx_power().Write(tx_power());
report.rssi().Write(rssi());
report.periodic_advertising_interval().Write(0);
// skip direct_address_type and direct_address for now since we don't use it
report.data_length().Write(data.size());
std::memcpy(report.data().BackingStorage().begin(), data.data(), data.size());
}
DynamicByteBuffer FakePeer::BuildExtendedAdvertisingReports(
const ByteBuffer& data, bool is_scan_response) const {
namespace LEExtendedAdvertisingReportData =
pw::bluetooth::emboss::LEExtendedAdvertisingReportData;
using pw::bluetooth::emboss::LEExtendedAdvertisingReportDataWriter;
using pw::bluetooth::emboss::LEExtendedAdvertisingReportSubeventWriter;
size_t num_full_reports =
data.size() / LEExtendedAdvertisingReportData::data_length_max();
size_t full_report_size =
pw::bluetooth::emboss::LEExtendedAdvertisingReportData::MinSizeInBytes() +
LEExtendedAdvertisingReportData::data_length_max();
size_t last_report_size =
pw::bluetooth::emboss::LEExtendedAdvertisingReportData::MinSizeInBytes() +
(data.size() % LEExtendedAdvertisingReportData::data_length_max());
size_t reports_size = num_full_reports * full_report_size + last_report_size;
size_t packet_size =
pw::bluetooth::emboss::LEExtendedAdvertisingReportSubevent::
MinSizeInBytes() +
reports_size;
auto event =
hci::EmbossEventPacket::New<LEExtendedAdvertisingReportSubeventWriter>(
hci_spec::kLEMetaEventCode, packet_size);
auto packet =
event.view<LEExtendedAdvertisingReportSubeventWriter>(reports_size);
packet.le_meta_event().subevent_code().Write(
hci_spec::kLEExtendedAdvertisingReportSubeventCode);
uint8_t num_reports = num_full_reports + 1;
packet.num_reports().Write(num_reports);
for (size_t i = 0; i < num_full_reports; i++) {
bool is_fragmented = false;
if (num_reports > 1) {
is_fragmented = true;
}
LEExtendedAdvertisingReportDataWriter report(
packet.reports().BackingStorage().begin() + full_report_size * i,
full_report_size);
FillExtendedAdvertisingReport(
report, data, is_fragmented, is_scan_response);
}
LEExtendedAdvertisingReportDataWriter report(
packet.reports().BackingStorage().begin() +
full_report_size * num_full_reports,
last_report_size);
FillExtendedAdvertisingReport(
report, data, /*is_fragmented=*/false, is_scan_response);
return event.release();
}
DynamicByteBuffer FakePeer::BuildExtendedAdvertisingReportEvent() const {
BT_DEBUG_ASSERT(advertising_data_.size() <=
hci_spec::kMaxLEExtendedAdvertisingDataLength);
return BuildExtendedAdvertisingReports(advertising_data_,
/*is_scan_response=*/false);
}
DynamicByteBuffer FakePeer::BuildExtendedScanResponseEvent() const {
BT_DEBUG_ASSERT(scannable_);
BT_DEBUG_ASSERT(scan_response_.size() <=
hci_spec::kMaxLEExtendedAdvertisingDataLength);
return BuildExtendedAdvertisingReports(scan_response_,
/*is_scan_response=*/true);
}
} // namespace bt::testing