blob: ba114361c9ace940afcf7b8a84a391013cda4b9a [file] [log] [blame]
/*
* 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.
*/
#define LOG_TAG "link_layer_controller"
#include "link_layer_controller.h"
#include <base/logging.h>
#include "hci.h"
#include "osi/include/log.h"
#include "packets/hci/acl_packet_builder.h"
#include "packets/hci/command_packet_view.h"
#include "packets/hci/event_packet_builder.h"
#include "packets/hci/sco_packet_builder.h"
#include "packets/link_layer/command_builder.h"
#include "packets/link_layer/command_view.h"
#include "packets/link_layer/disconnect_view.h"
#include "packets/link_layer/encrypt_connection_view.h"
#include "packets/link_layer/inquiry_response_view.h"
#include "packets/link_layer/inquiry_view.h"
#include "packets/link_layer/io_capability_view.h"
#include "packets/link_layer/le_advertisement_view.h"
#include "packets/link_layer/page_response_view.h"
#include "packets/link_layer/page_view.h"
#include "packets/link_layer/response_view.h"
using std::vector;
using namespace std::chrono;
using namespace test_vendor_lib::packets;
namespace test_vendor_lib {
// TODO: Model Rssi?
static uint8_t GetRssi() {
static uint8_t rssi = 0;
rssi += 5;
if (rssi > 128) {
rssi = rssi % 7;
}
return -(rssi);
}
void LinkLayerController::SendLELinkLayerPacket(std::shared_ptr<LinkLayerPacketBuilder> packet) {
if (schedule_task_) {
schedule_task_(milliseconds(50), [this, packet]() { send_to_remote_(packet, Phy::Type::LOW_ENERGY); });
} else {
send_to_remote_(packet, Phy::Type::LOW_ENERGY);
}
}
void LinkLayerController::SendLinkLayerPacket(std::shared_ptr<LinkLayerPacketBuilder> packet) {
if (schedule_task_) {
schedule_task_(milliseconds(50), [this, packet]() { send_to_remote_(packet, Phy::Type::BR_EDR); });
} else {
send_to_remote_(packet, Phy::Type::BR_EDR);
}
}
hci::Status LinkLayerController::SendCommandToRemoteByAddress(hci::OpCode opcode, PacketView<true> args,
const Address& remote, bool use_public_address) {
std::shared_ptr<LinkLayerPacketBuilder> command;
Address local_address;
if (use_public_address) {
local_address = properties_.GetAddress();
} else {
local_address = properties_.GetLeAddress();
}
command = LinkLayerPacketBuilder::WrapCommand(CommandBuilder::Create(static_cast<uint16_t>(opcode), args),
local_address, remote);
SendLinkLayerPacket(std::move(command));
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::SendCommandToRemoteByHandle(hci::OpCode opcode, PacketView<true> args,
uint16_t handle) {
// TODO: Handle LE connections
bool use_public_address = true;
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
return SendCommandToRemoteByAddress(opcode, args, classic_connections_.GetAddress(handle), use_public_address);
}
hci::Status LinkLayerController::SendAclToRemote(AclPacketView acl_packet) {
// TODO: Handle LE connections
uint16_t handle = acl_packet.GetHandle();
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
std::unique_ptr<ViewForwarderBuilder> acl_builder = ViewForwarderBuilder::Create(acl_packet);
std::shared_ptr<LinkLayerPacketBuilder> acl = LinkLayerPacketBuilder::WrapAcl(
std::move(acl_builder), properties_.GetAddress(), classic_connections_.GetAddress(handle));
LOG_INFO(LOG_TAG, "%s(%s): handle 0x%x size %d", __func__, properties_.GetAddress().ToString().c_str(), handle,
static_cast<int>(acl_packet.size()));
schedule_task_(milliseconds(5), [this, handle]() {
send_event_(EventPacketBuilder::CreateNumberOfCompletedPacketsEvent(handle, 1)->ToVector());
});
SendLinkLayerPacket(acl);
return hci::Status::SUCCESS;
}
void LinkLayerController::IncomingPacket(LinkLayerPacketView incoming) {
// TODO: Resolvable private addresses?
if (incoming.GetDestinationAddress() != properties_.GetAddress() &&
incoming.GetDestinationAddress() != properties_.GetLeAddress() &&
incoming.GetDestinationAddress() != Address::kEmpty) {
// Drop packets not addressed to me
return;
}
switch (incoming.GetType()) {
case Link::PacketType::ACL:
IncomingAclPacket(incoming);
break;
case Link::PacketType::COMMAND:
IncomingCommandPacket(incoming);
break;
case Link::PacketType::DISCONNECT:
IncomingDisconnectPacket(incoming);
break;
case Link::PacketType::ENCRYPT_CONNECTION:
IncomingEncryptConnection(incoming);
break;
case Link::PacketType::ENCRYPT_CONNECTION_RESPONSE:
IncomingEncryptConnectionResponse(incoming);
break;
case Link::PacketType::INQUIRY:
if (inquiry_scans_enabled_) {
IncomingInquiryPacket(incoming);
}
break;
case Link::PacketType::INQUIRY_RESPONSE:
IncomingInquiryResponsePacket(incoming);
break;
case Link::PacketType::IO_CAPABILITY_REQUEST:
IncomingIoCapabilityRequestPacket(incoming);
break;
case Link::PacketType::IO_CAPABILITY_RESPONSE:
IncomingIoCapabilityResponsePacket(incoming);
break;
case Link::PacketType::IO_CAPABILITY_NEGATIVE_RESPONSE:
IncomingIoCapabilityNegativeResponsePacket(incoming);
break;
case Link::PacketType::LE_ADVERTISEMENT:
if (le_scan_enable_ || le_connect_) {
IncomingLeAdvertisementPacket(incoming);
}
break;
case Link::PacketType::LE_SCAN:
// TODO: Check Advertising flags and see if we are scannable.
IncomingLeScanPacket(incoming);
break;
case Link::PacketType::LE_SCAN_RESPONSE:
if (le_scan_enable_ && le_scan_type_ == 1) {
IncomingLeScanResponsePacket(incoming);
}
break;
case Link::PacketType::PAGE:
if (page_scans_enabled_) {
IncomingPagePacket(incoming);
}
break;
case Link::PacketType::PAGE_RESPONSE:
IncomingPageResponsePacket(incoming);
break;
case Link::PacketType::RESPONSE:
IncomingResponsePacket(incoming);
break;
default:
LOG_WARN(LOG_TAG, "Dropping unhandled packet of type %d", static_cast<int32_t>(incoming.GetType()));
}
}
void LinkLayerController::IncomingAclPacket(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "Acl Packet %s -> %s", incoming.GetSourceAddress().ToString().c_str(),
incoming.GetDestinationAddress().ToString().c_str());
AclPacketView acl_view = AclPacketView::Create(incoming.GetPayload());
LOG_INFO(LOG_TAG, "%s: remote handle 0x%x size %d", __func__, acl_view.GetHandle(),
static_cast<int>(acl_view.size()));
uint16_t local_handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
LOG_INFO(LOG_TAG, "%s: local handle 0x%x", __func__, local_handle);
acl::PacketBoundaryFlagsType boundary_flags = acl_view.GetPacketBoundaryFlags();
acl::BroadcastFlagsType broadcast_flags = acl_view.GetBroadcastFlags();
std::unique_ptr<ViewForwarderBuilder> builder = ViewForwarderBuilder::Create(acl_view.GetPayload());
std::unique_ptr<AclPacketBuilder> local_acl =
AclPacketBuilder::Create(local_handle, boundary_flags, broadcast_flags, std::move(builder));
send_acl_(local_acl->ToVector());
}
void LinkLayerController::IncomingCommandPacket(LinkLayerPacketView incoming) {
// TODO: Check the destination address to see if this packet is for me.
CommandView command = CommandView::GetCommand(incoming);
hci::OpCode opcode = static_cast<hci::OpCode>(command.GetOpcode());
auto args = command.GetData();
std::vector<uint64_t> response_data;
switch (opcode) {
case (hci::OpCode::REMOTE_NAME_REQUEST): {
std::vector<uint8_t> name = properties_.GetName();
LOG_INFO(LOG_TAG, "Remote Name (Local Name) %d", static_cast<int>(name.size()));
response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
response_data.push_back(name.size());
uint64_t word = 0;
for (size_t i = 0; i < name.size(); i++) {
if (i > 0 && (i % 8 == 0)) {
response_data.push_back(word);
word = 0;
}
word |= static_cast<uint64_t>(name[i]) << (8 * (i % 8));
}
response_data.push_back(word);
} break;
case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES):
LOG_INFO(LOG_TAG, "(%s) Remote Supported Features Requested by: %s %x",
incoming.GetDestinationAddress().ToString().c_str(), incoming.GetSourceAddress().ToString().c_str(),
static_cast<int>(properties_.GetSupportedFeatures()));
response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
response_data.push_back(properties_.GetSupportedFeatures());
break;
case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
uint8_t page_number = (args + 2).extract<uint8_t>(); // skip the handle
LOG_INFO(LOG_TAG, "(%s) Remote Extended Features %d Requested by: %s",
incoming.GetDestinationAddress().ToString().c_str(), page_number,
incoming.GetSourceAddress().ToString().c_str());
uint8_t max_page_number = properties_.GetExtendedFeaturesMaximumPageNumber();
if (page_number > max_page_number) {
response_data.push_back(static_cast<uint8_t>(hci::Status::INVALID_HCI_COMMAND_PARAMETERS));
response_data.push_back(page_number);
response_data.push_back(max_page_number);
response_data.push_back(0);
} else {
response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
response_data.push_back(page_number);
response_data.push_back(max_page_number);
response_data.push_back(properties_.GetExtendedFeatures(page_number));
}
} break;
case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION):
response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
response_data.push_back(properties_.GetLmpPalVersion());
response_data.push_back(properties_.GetManufacturerName());
response_data.push_back(properties_.GetLmpPalSubversion());
break;
case (hci::OpCode::READ_CLOCK_OFFSET):
response_data.push_back(static_cast<uint8_t>(hci::Status::SUCCESS));
response_data.push_back(properties_.GetClockOffset());
break;
default:
LOG_INFO(LOG_TAG, "Dropping unhandled command 0x%04x", static_cast<uint16_t>(opcode));
return;
}
SendLinkLayerPacket(
LinkLayerPacketBuilder::WrapResponse(ResponseBuilder::Create(static_cast<uint16_t>(opcode), response_data),
properties_.GetAddress(), incoming.GetSourceAddress()));
}
void LinkLayerController::IncomingDisconnectPacket(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "Disconnect Packet");
DisconnectView disconnect = DisconnectView::GetDisconnect(incoming);
Address peer = incoming.GetSourceAddress();
uint16_t handle = classic_connections_.GetHandle(peer);
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, peer.ToString().c_str());
return;
}
CHECK(classic_connections_.Disconnect(handle)) << "GetHandle() returned invalid handle " << handle;
uint8_t reason = disconnect.GetReason();
schedule_task_(milliseconds(20), [this, handle, reason]() { DisconnectCleanup(handle, reason); });
}
void LinkLayerController::IncomingEncryptConnection(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "%s", __func__);
// TODO: Check keys
Address peer = incoming.GetSourceAddress();
uint16_t handle = classic_connections_.GetHandle(peer);
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, peer.ToString().c_str());
return;
}
send_event_(EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, 1)->ToVector());
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapEncryptConnectionResponse(
EncryptConnectionBuilder::Create(security_manager_.GetKey(peer)), properties_.GetAddress(), peer));
}
void LinkLayerController::IncomingEncryptConnectionResponse(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "%s", __func__);
// TODO: Check keys
uint16_t handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s: Unknown connection @%s", __func__, incoming.GetSourceAddress().ToString().c_str());
return;
}
send_event_(EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, 1)->ToVector());
}
void LinkLayerController::IncomingInquiryPacket(LinkLayerPacketView incoming) {
InquiryView inquiry = InquiryView::GetInquiry(incoming);
std::unique_ptr<InquiryResponseBuilder> inquiry_response;
switch (inquiry.GetType()) {
case (Inquiry::InquiryType::STANDARD):
inquiry_response = InquiryResponseBuilder::CreateStandard(
properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(), properties_.GetClockOffset());
break;
case (Inquiry::InquiryType::RSSI):
inquiry_response =
InquiryResponseBuilder::CreateRssi(properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(),
properties_.GetClockOffset(), GetRssi());
break;
case (Inquiry::InquiryType::EXTENDED):
inquiry_response = InquiryResponseBuilder::CreateExtended(
properties_.GetPageScanRepetitionMode(), properties_.GetClassOfDevice(), properties_.GetClockOffset(),
GetRssi(), properties_.GetExtendedInquiryData());
break;
default:
LOG_WARN(LOG_TAG, "Unhandled Incoming Inquiry of type %d", static_cast<int>(inquiry.GetType()));
return;
}
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapInquiryResponse(std::move(inquiry_response), properties_.GetAddress(),
incoming.GetSourceAddress()));
// TODO: Send an Inquriy Response Notification Event 7.7.74
}
void LinkLayerController::IncomingInquiryResponsePacket(LinkLayerPacketView incoming) {
InquiryResponseView inquiry_response = InquiryResponseView::GetInquiryResponse(incoming);
std::vector<uint8_t> eir;
switch (inquiry_response.GetType()) {
case (Inquiry::InquiryType::STANDARD): {
LOG_WARN(LOG_TAG, "Incoming Standard Inquiry Response");
// TODO: Support multiple inquiries in the same packet.
std::unique_ptr<EventPacketBuilder> inquiry_result = EventPacketBuilder::CreateInquiryResultEvent();
bool result_added =
inquiry_result->AddInquiryResult(incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset());
CHECK(result_added);
send_event_(inquiry_result->ToVector());
} break;
case (Inquiry::InquiryType::RSSI):
LOG_WARN(LOG_TAG, "Incoming RSSI Inquiry Response");
send_event_(EventPacketBuilder::CreateExtendedInquiryResultEvent(
incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset(), GetRssi(), eir)
->ToVector());
break;
case (Inquiry::InquiryType::EXTENDED): {
LOG_WARN(LOG_TAG, "Incoming Extended Inquiry Response");
auto eir_itr = inquiry_response.GetExtendedData();
size_t eir_bytes = eir_itr.NumBytesRemaining();
LOG_WARN(LOG_TAG, "Payload size = %d", static_cast<int>(eir_bytes));
for (size_t i = 0; i < eir_bytes; i++) {
eir.push_back(eir_itr.extract<uint8_t>());
}
send_event_(EventPacketBuilder::CreateExtendedInquiryResultEvent(
incoming.GetSourceAddress(), inquiry_response.GetPageScanRepetitionMode(),
inquiry_response.GetClassOfDevice(), inquiry_response.GetClockOffset(), GetRssi(), eir)
->ToVector());
} break;
default:
LOG_WARN(LOG_TAG, "Unhandled Incoming Inquiry Response of type %d", static_cast<int>(inquiry_response.GetType()));
}
}
void LinkLayerController::IncomingIoCapabilityRequestPacket(LinkLayerPacketView incoming) {
LOG_DEBUG(LOG_TAG, "%s", __func__);
if (!simple_pairing_mode_enabled_) {
LOG_WARN(LOG_TAG, "%s: Only simple pairing mode is implemented", __func__);
return;
}
auto request = IoCapabilityView::GetIoCapability(incoming);
Address peer = incoming.GetSourceAddress();
uint8_t io_capability = request.GetIoCapability();
uint8_t oob_data_present = request.GetOobDataPresent();
uint8_t authentication_requirements = request.GetAuthenticationRequirements();
uint16_t handle = classic_connections_.GetHandle(peer);
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s: Device not connected %s", __func__, peer.ToString().c_str());
return;
}
security_manager_.AuthenticationRequest(peer, handle);
security_manager_.SetPeerIoCapability(peer, io_capability, oob_data_present, authentication_requirements);
send_event_(EventPacketBuilder::CreateIoCapabilityResponseEvent(peer, io_capability, oob_data_present,
authentication_requirements)
->ToVector());
StartSimplePairing(peer);
}
void LinkLayerController::IncomingIoCapabilityResponsePacket(LinkLayerPacketView incoming) {
LOG_DEBUG(LOG_TAG, "%s", __func__);
auto response = IoCapabilityView::GetIoCapability(incoming);
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);
send_event_(EventPacketBuilder::CreateIoCapabilityResponseEvent(peer, io_capability, oob_data_present,
authentication_requirements)
->ToVector());
PairingType pairing_type = security_manager_.GetSimplePairingType();
if (pairing_type != PairingType::INVALID) {
schedule_task_(milliseconds(5), [this, peer, pairing_type]() { AuthenticateRemoteStage1(peer, pairing_type); });
} else {
LOG_INFO(LOG_TAG, "%s: Security Manager returned INVALID", __func__);
}
}
void LinkLayerController::IncomingIoCapabilityNegativeResponsePacket(LinkLayerPacketView incoming) {
LOG_DEBUG(LOG_TAG, "%s", __func__);
Address peer = incoming.GetSourceAddress();
CHECK(security_manager_.GetAuthenticationAddress() == peer);
security_manager_.InvalidateIoCapabilities();
}
void LinkLayerController::IncomingLeAdvertisementPacket(LinkLayerPacketView incoming) {
// TODO: Handle multiple advertisements per packet.
LeAdvertisementView advertisement = LeAdvertisementView::GetLeAdvertisementView(incoming);
LeAdvertisement::AdvertisementType adv_type = advertisement.GetAdvertisementType();
LeAdvertisement::AddressType addr_type = advertisement.GetAddressType();
if (le_scan_enable_) {
vector<uint8_t> ad;
auto itr = advertisement.GetData();
size_t ad_size = itr.NumBytesRemaining();
for (size_t i = 0; i < ad_size; i++) {
ad.push_back(itr.extract<uint8_t>());
}
std::unique_ptr<EventPacketBuilder> le_adverts = EventPacketBuilder::CreateLeAdvertisingReportEvent();
if (!le_adverts->AddLeAdvertisingReport(adv_type, addr_type, incoming.GetSourceAddress(), ad, GetRssi())) {
LOG_INFO(LOG_TAG, "Couldn't add the advertising report.");
} else {
send_event_(le_adverts->ToVector());
}
}
#if 0
// Connect
if (le_connect_ && (adv_type == BTM_BLE_CONNECT_EVT ||
adv_type == BTM_BLE_CONNECT_DIR_EVT)) {
LOG_INFO(LOG_TAG, "Connecting to device %d", static_cast<int>(dev));
if (le_peer_address_ == addr && le_peer_address_type_ == addr_type &&
remote_devices_[dev]->LeConnect()) {
uint16_t handle = LeGetHandle();
send_event_(EventPacketBuilder::CreateLeConnectionCompleteEvent(
hci::Status::SUCCESS, handle, HCI_ROLE_MASTER, addr_type, addr, 1,
2, 3)->ToVector());
// TODO: LeGetConnInterval(), LeGetConnLatency(),
// LeGetSupervisionTimeout()));
le_connect_ = false;
std::shared_ptr<Connection> new_connection =
std::make_shared<Connection>(remote_devices_[dev], handle);
/*
connections_.push_back(new_connection);
remote_devices_[dev]->SetConnection(new_connection);
*/
}
if (LeWhiteListContainsDevice(addr, addr_type) &&
remote_devices_[dev]->LeConnect()) {
LOG_INFO(LOG_TAG, "White List Connecting to device %d",
static_cast<int>(dev));
uint16_t handle = LeGetHandle();
send_event_(EventPacketBuilder::CreateLeConnectionCompleteEvent(
hci::Status::SUCCESS, handle, HCI_ROLE_MASTER, addr_type, addr, 1,
2, 3)->ToVector());
// TODO: LeGetConnInterval(), LeGetConnLatency(),
// LeGetSupervisionTimeout()));
le_connect_ = false;
std::shared_ptr<Connection> new_connection =
std::make_shared<Connection>(remote_devices_[dev], handle);
/*
connections_.push_back(new_connection);
remote_devices_[dev]->SetConnection(new_connection);
*/
}
}
#endif
// Active scanning
if (le_scan_enable_ && le_scan_type_ == 1) {
std::shared_ptr<LinkLayerPacketBuilder> to_send =
LinkLayerPacketBuilder::WrapLeScan(properties_.GetLeAddress(), incoming.GetSourceAddress());
SendLELinkLayerPacket(to_send);
}
}
void LinkLayerController::IncomingLeScanPacket(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "LE Scan Packet");
std::unique_ptr<LeAdvertisementBuilder> response = LeAdvertisementBuilder::Create(
static_cast<LeAdvertisement::AddressType>(properties_.GetLeAddressType()),
static_cast<LeAdvertisement::AdvertisementType>(properties_.GetLeAdvertisementType()),
properties_.GetLeScanResponse());
std::shared_ptr<LinkLayerPacketBuilder> to_send = LinkLayerPacketBuilder::WrapLeScanResponse(
std::move(response), properties_.GetLeAddress(), incoming.GetSourceAddress());
SendLELinkLayerPacket(to_send);
}
void LinkLayerController::IncomingLeScanResponsePacket(LinkLayerPacketView incoming) {
LeAdvertisementView scan_response = LeAdvertisementView::GetLeAdvertisementView(incoming);
vector<uint8_t> ad;
auto itr = scan_response.GetData();
size_t scan_size = itr.NumBytesRemaining();
for (size_t i = 0; i < scan_size; i++) {
ad.push_back(itr.extract<uint8_t>());
}
std::unique_ptr<EventPacketBuilder> le_adverts = EventPacketBuilder::CreateLeAdvertisingReportEvent();
if (!le_adverts->AddLeAdvertisingReport(scan_response.GetAdvertisementType(), scan_response.GetAddressType(),
incoming.GetSourceAddress(), ad, GetRssi())) {
LOG_INFO(LOG_TAG, "Couldn't add the scan response.");
} else {
LOG_INFO(LOG_TAG, "Sending scan response");
send_event_(le_adverts->ToVector());
}
}
void LinkLayerController::IncomingPagePacket(LinkLayerPacketView incoming) {
PageView page = PageView::GetPage(incoming);
LOG_INFO(LOG_TAG, "%s from %s", __func__, incoming.GetSourceAddress().ToString().c_str());
if (!classic_connections_.CreatePendingConnection(incoming.GetSourceAddress())) {
// Send a response to indicate that we're busy, or drop the packet?
LOG_WARN(LOG_TAG, "%s: Failed to create a pending connection", __func__);
}
send_event_(EventPacketBuilder::CreateConnectionRequestEvent(incoming.GetSourceAddress(), page.GetClassOfDevice(),
hci::LinkType::ACL)
->ToVector());
}
void LinkLayerController::IncomingPageResponsePacket(LinkLayerPacketView incoming) {
LOG_INFO(LOG_TAG, "%s: %s", __func__, incoming.GetSourceAddress().ToString().c_str());
uint16_t handle = classic_connections_.CreateConnection(incoming.GetSourceAddress());
if (handle == acl::kReservedHandle) {
LOG_WARN(LOG_TAG, "%s: No free handles", __func__);
return;
}
LOG_INFO(LOG_TAG, "%s: Sending CreateConnectionComplete", __func__);
send_event_(EventPacketBuilder::CreateConnectionCompleteEvent(hci::Status::SUCCESS, handle,
incoming.GetSourceAddress(), hci::LinkType::ACL, false)
->ToVector());
}
void LinkLayerController::IncomingResponsePacket(LinkLayerPacketView incoming) {
ResponseView response = ResponseView::GetResponse(incoming);
// TODO: Check to see if I'm expecting this response.
hci::OpCode opcode = static_cast<hci::OpCode>(response.GetOpcode());
auto args = response.GetResponseData();
hci::Status status = static_cast<hci::Status>(args.extract<uint64_t>());
uint16_t handle = classic_connections_.GetHandle(incoming.GetSourceAddress());
switch (opcode) {
case (hci::OpCode::REMOTE_NAME_REQUEST): {
std::string remote_name = "";
size_t length = args.extract<uint64_t>();
uint64_t word = 0;
for (size_t b = 0; b < length; b++) {
size_t byte = b % 8;
if (byte == 0) {
word = args.extract<uint64_t>();
}
remote_name += static_cast<uint8_t>(word >> (byte * 8));
}
send_event_(
EventPacketBuilder::CreateRemoteNameRequestCompleteEvent(status, incoming.GetSourceAddress(), remote_name)
->ToVector());
} break;
case (hci::OpCode::READ_REMOTE_SUPPORTED_FEATURES): {
send_event_(
EventPacketBuilder::CreateRemoteSupportedFeaturesEvent(status, handle, args.extract<uint64_t>())->ToVector());
} break;
case (hci::OpCode::READ_REMOTE_EXTENDED_FEATURES): {
if (status == hci::Status::SUCCESS) {
send_event_(EventPacketBuilder::CreateReadRemoteExtendedFeaturesEvent(
status, handle, args.extract<uint64_t>(), args.extract<uint64_t>(), args.extract<uint64_t>())
->ToVector());
} else {
send_event_(EventPacketBuilder::CreateReadRemoteExtendedFeaturesEvent(status, handle, 0, 0, 0)->ToVector());
}
} break;
case (hci::OpCode::READ_REMOTE_VERSION_INFORMATION): {
send_event_(EventPacketBuilder::CreateReadRemoteVersionInformationEvent(
status, handle, args.extract<uint64_t>(), args.extract<uint64_t>(), args.extract<uint64_t>())
->ToVector());
LOG_INFO(LOG_TAG, "Read remote version handle 0x%04x", handle);
} break;
case (hci::OpCode::READ_CLOCK_OFFSET): {
send_event_(EventPacketBuilder::CreateReadClockOffsetEvent(status, handle, args.extract<uint64_t>())->ToVector());
} break;
default:
LOG_INFO(LOG_TAG, "Unhandled response to command 0x%04x", static_cast<uint16_t>(opcode));
}
}
void LinkLayerController::TimerTick() {
if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) Inquiry();
if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) PageScan();
Connections();
}
void LinkLayerController::Connections() {
// TODO: Keep connections alive?
}
void LinkLayerController::RegisterEventChannel(
const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
send_event_ = callback;
}
void LinkLayerController::RegisterAclChannel(
const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
send_acl_ = callback;
}
void LinkLayerController::RegisterScoChannel(
const std::function<void(std::shared_ptr<std::vector<uint8_t>>)>& callback) {
send_sco_ = callback;
}
void LinkLayerController::RegisterRemoteChannel(
const std::function<void(std::shared_ptr<LinkLayerPacketBuilder>, Phy::Type)>& callback) {
send_to_remote_ = callback;
}
void LinkLayerController::RegisterTaskScheduler(
std::function<AsyncTaskId(milliseconds, const TaskCallback&)> event_scheduler) {
schedule_task_ = event_scheduler;
}
void LinkLayerController::RegisterPeriodicTaskScheduler(
std::function<AsyncTaskId(milliseconds, milliseconds, const TaskCallback&)> periodic_event_scheduler) {
schedule_periodic_task_ = periodic_event_scheduler;
}
void LinkLayerController::RegisterTaskCancel(std::function<void(AsyncTaskId)> task_cancel) {
cancel_task_ = task_cancel;
}
void LinkLayerController::AddControllerEvent(milliseconds delay, const TaskCallback& task) {
controller_events_.push_back(schedule_task_(delay, task));
}
void LinkLayerController::WriteSimplePairingMode(bool enabled) {
CHECK(enabled) << "The spec says don't disable this!";
simple_pairing_mode_enabled_ = enabled;
}
void LinkLayerController::StartSimplePairing(const Address& address) {
// IO Capability Exchange (See the Diagram in the Spec)
send_event_(EventPacketBuilder::CreateIoCapabilityRequestEvent(address)->ToVector());
// Get a Key, then authenticate
// PublicKeyExchange(address);
// AuthenticateRemoteStage1(address);
// AuthenticateRemoteStage2(address);
}
void LinkLayerController::AuthenticateRemoteStage1(const Address& peer, PairingType pairing_type) {
CHECK(security_manager_.GetAuthenticationAddress() == peer);
// TODO: Public key exchange first?
switch (pairing_type) {
case PairingType::AUTO_CONFIRMATION:
send_event_(EventPacketBuilder::CreateUserConfirmationRequestEvent(peer, 123456)->ToVector());
break;
case PairingType::CONFIRM_Y_N:
CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
break;
case PairingType::DISPLAY_PIN:
CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
break;
case PairingType::DISPLAY_AND_CONFIRM:
CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
break;
case PairingType::INPUT_PIN:
CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
break;
case PairingType::INVALID:
CHECK(false) << __func__ << "Unimplemented PairingType" << static_cast<int>(pairing_type);
break;
default:
CHECK(false) << __func__ << ": Invalid PairingType " << static_cast<int>(pairing_type);
}
}
void LinkLayerController::AuthenticateRemoteStage2(const Address& peer) {
uint16_t handle = security_manager_.GetAuthenticationHandle();
CHECK(security_manager_.GetAuthenticationAddress() == peer);
// Check key in security_manager_ ?
send_event_(EventPacketBuilder::CreateAuthenticationCompleteEvent(hci::Status::SUCCESS, handle)->ToVector());
}
hci::Status LinkLayerController::LinkKeyRequestReply(const Address& peer, PacketView<true> key) {
std::vector<uint8_t> key_vec(key.begin(), key.end());
security_manager_.WriteKey(peer, key_vec);
security_manager_.AuthenticationRequestFinished();
schedule_task_(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::LinkKeyRequestNegativeReply(const Address& address) {
security_manager_.DeleteKey(address);
// Simple pairing to get a key
uint16_t handle = classic_connections_.GetHandle(address);
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s: Device not connected %s", __func__, address.ToString().c_str());
return hci::Status::UNKNOWN_CONNECTION;
}
security_manager_.AuthenticationRequest(address, handle);
schedule_task_(milliseconds(5), [this, address]() { StartSimplePairing(address); });
return hci::Status::SUCCESS;
}
hci::Status 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) {
schedule_task_(milliseconds(5), [this, peer, pairing_type]() { AuthenticateRemoteStage1(peer, pairing_type); });
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityResponse(
IoCapabilityBuilder::Create(io_capability, oob_data_present_flag, authentication_requirements),
properties_.GetAddress(), peer));
} else {
LOG_INFO(LOG_TAG, "%s: Requesting remote capability", __func__);
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityRequest(
IoCapabilityBuilder::Create(io_capability, oob_data_present_flag, authentication_requirements),
properties_.GetAddress(), peer));
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::IoCapabilityRequestNegativeReply(const Address& peer, hci::Status reason) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
security_manager_.InvalidateIoCapabilities();
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapIoCapabilityNegativeResponse(
IoCapabilityNegativeResponseBuilder::Create(static_cast<uint8_t>(reason)), properties_.GetAddress(), peer));
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::UserConfirmationRequestReply(const Address& peer) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
// TODO: Key could be calculated here.
std::vector<uint8_t> key_vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
security_manager_.WriteKey(peer, key_vec);
security_manager_.AuthenticationRequestFinished();
schedule_task_(milliseconds(5), [this, peer]() { AuthenticateRemoteStage2(peer); });
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::UserConfirmationRequestNegativeReply(const Address& peer) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::UserPasskeyRequestReply(const Address& peer, uint32_t numeric_value) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
LOG_INFO(LOG_TAG, "TODO:Do something with the passkey %06d", numeric_value);
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::UserPasskeyRequestNegativeReply(const Address& peer) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::RemoteOobDataRequestReply(const Address& peer, const std::vector<uint8_t>& c,
const std::vector<uint8_t>& r) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
LOG_INFO(LOG_TAG, "TODO:Do something with the OOB data c=%d r=%d", c[0], r[0]);
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::RemoteOobDataRequestNegativeReply(const Address& peer) {
if (security_manager_.GetAuthenticationAddress() != peer) {
return hci::Status::AUTHENTICATION_FAILURE;
}
return hci::Status::SUCCESS;
}
void LinkLayerController::HandleAuthenticationRequest(const Address& address, uint16_t handle) {
if (simple_pairing_mode_enabled_ == true) {
security_manager_.AuthenticationRequest(address, handle);
send_event_(EventPacketBuilder::CreateLinkKeyRequestEvent(address)->ToVector());
} else { // Should never happen for our phones
// Check for a key, try to authenticate, ask for a PIN.
send_event_(
EventPacketBuilder::CreateAuthenticationCompleteEvent(hci::Status::AUTHENTICATION_FAILURE, handle)->ToVector());
}
}
hci::Status LinkLayerController::AuthenticationRequested(uint16_t handle) {
if (!classic_connections_.HasHandle(handle)) {
LOG_INFO(LOG_TAG, "Authentication Requested for unknown handle %04x", handle);
return hci::Status::UNKNOWN_CONNECTION;
}
Address remote = classic_connections_.GetAddress(handle);
schedule_task_(milliseconds(5), [this, remote, handle]() { HandleAuthenticationRequest(remote, handle); });
return hci::Status::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 (classic_connections_.IsEncrypted(handle) && encryption_enable) {
send_event_(
EventPacketBuilder::CreateEncryptionChange(hci::Status::SUCCESS, handle, encryption_enable)->ToVector());
return;
}
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapEncryptConnection(
EncryptConnectionBuilder::Create(security_manager_.GetKey(peer)), properties_.GetAddress(), peer));
}
hci::Status LinkLayerController::SetConnectionEncryption(uint16_t handle, uint8_t encryption_enable) {
if (!classic_connections_.HasHandle(handle)) {
LOG_INFO(LOG_TAG, "Authentication Requested for unknown handle %04x", handle);
return hci::Status::UNKNOWN_CONNECTION;
}
if (classic_connections_.IsEncrypted(handle) && !encryption_enable) {
return hci::Status::ENCRYPTION_MODE_NOT_ACCEPTABLE;
}
Address remote = classic_connections_.GetAddress(handle);
schedule_task_(milliseconds(5), [this, remote, handle, encryption_enable]() {
HandleSetConnectionEncryption(remote, handle, encryption_enable);
});
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::AcceptConnectionRequest(const Address& addr, bool try_role_switch) {
if (!classic_connections_.HasPendingConnection(addr)) {
LOG_INFO(LOG_TAG, "%s: No pending connection", __func__);
return hci::Status::UNKNOWN_CONNECTION;
}
LOG_INFO(LOG_TAG, "%s: Accept in 200ms", __func__);
schedule_task_(milliseconds(200), [this, addr, try_role_switch]() {
LOG_INFO(LOG_TAG, "%s: Accepted", __func__);
MakeSlaveConnection(addr, try_role_switch);
});
return hci::Status::SUCCESS;
}
void LinkLayerController::MakeSlaveConnection(const Address& addr, bool try_role_switch) {
std::shared_ptr<LinkLayerPacketBuilder> to_send = LinkLayerPacketBuilder::WrapPageResponse(
PageResponseBuilder::Create(try_role_switch), properties_.GetAddress(), addr);
LOG_INFO(LOG_TAG, "%s sending page response to %s", __func__, addr.ToString().c_str());
SendLinkLayerPacket(to_send);
uint16_t handle = classic_connections_.CreateConnection(addr);
if (handle == acl::kReservedHandle) {
LOG_INFO(LOG_TAG, "%s CreateConnection failed", __func__);
return;
}
LOG_INFO(LOG_TAG, "%s CreateConnection returned handle 0x%x", __func__, handle);
send_event_(
EventPacketBuilder::CreateConnectionCompleteEvent(hci::Status::SUCCESS, handle, addr, hci::LinkType::ACL, false)
->ToVector());
}
hci::Status LinkLayerController::RejectConnectionRequest(const Address& addr, uint8_t reason) {
if (!classic_connections_.HasPendingConnection(addr)) {
LOG_INFO(LOG_TAG, "%s: No pending connection", __func__);
return hci::Status::UNKNOWN_CONNECTION;
}
LOG_INFO(LOG_TAG, "%s: Reject in 200ms", __func__);
schedule_task_(milliseconds(200), [this, addr, reason]() {
LOG_INFO(LOG_TAG, "%s: Reject", __func__);
RejectSlaveConnection(addr, reason);
});
return hci::Status::SUCCESS;
}
void LinkLayerController::RejectSlaveConnection(const Address& addr, uint8_t reason) {
CHECK(reason > 0x0f || reason < 0x0d);
send_event_(EventPacketBuilder::CreateConnectionCompleteEvent(static_cast<hci::Status>(reason), 0xeff, addr,
hci::LinkType::ACL, false)
->ToVector());
}
hci::Status LinkLayerController::CreateConnection(const Address& addr, uint16_t, uint8_t, uint16_t,
uint8_t allow_role_switch) {
if (!classic_connections_.CreatePendingConnection(addr)) {
return hci::Status::CONTROLLER_BUSY;
}
std::unique_ptr<PageBuilder> page = PageBuilder::Create(properties_.GetClassOfDevice(), allow_role_switch);
SendLinkLayerPacket(LinkLayerPacketBuilder::WrapPage(std::move(page), properties_.GetAddress(), addr));
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::CreateConnectionCancel(const Address& addr) {
if (!classic_connections_.CancelPendingConnection(addr)) {
return hci::Status::UNKNOWN_CONNECTION;
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::Disconnect(uint16_t handle, uint8_t reason) {
// TODO: Handle LE
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
const Address& remote = classic_connections_.GetAddress(handle);
std::shared_ptr<LinkLayerPacketBuilder> to_send =
LinkLayerPacketBuilder::WrapDisconnect(DisconnectBuilder::Create(reason), properties_.GetAddress(), remote);
SendLinkLayerPacket(to_send);
CHECK(classic_connections_.Disconnect(handle)) << "Disconnecting " << handle;
schedule_task_(milliseconds(20), [this, handle]() {
DisconnectCleanup(handle, static_cast<uint8_t>(hci::Status::CONNECTION_TERMINATED_BY_LOCAL_HOST));
});
return hci::Status::SUCCESS;
}
void LinkLayerController::DisconnectCleanup(uint16_t handle, uint8_t reason) {
// TODO: Clean up other connection state.
send_event_(EventPacketBuilder::CreateDisconnectionCompleteEvent(hci::Status::SUCCESS, handle, reason)->ToVector());
}
hci::Status LinkLayerController::ChangeConnectionPacketType(uint16_t handle, uint16_t types) {
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
std::unique_ptr<EventPacketBuilder> packet =
EventPacketBuilder::CreateConnectionPacketTypeChangedEvent(hci::Status::SUCCESS, handle, types);
std::shared_ptr<std::vector<uint8_t>> raw_packet = packet->ToVector();
if (schedule_task_) {
schedule_task_(milliseconds(20), [this, raw_packet]() { send_event_(raw_packet); });
} else {
send_event_(raw_packet);
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::WriteLinkPolicySettings(uint16_t handle, uint16_t) {
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
return hci::Status::SUCCESS;
}
hci::Status LinkLayerController::WriteLinkSupervisionTimeout(uint16_t handle, uint16_t) {
if (!classic_connections_.HasHandle(handle)) {
return hci::Status::UNKNOWN_CONNECTION;
}
return hci::Status::SUCCESS;
}
void LinkLayerController::LeWhiteListClear() {
le_white_list_.clear();
}
void LinkLayerController::LeWhiteListAddDevice(Address addr, uint8_t addr_type) {
std::tuple<Address, uint8_t> new_tuple = std::make_tuple(addr, addr_type);
for (auto dev : le_white_list_) {
if (dev == new_tuple) {
return;
}
}
le_white_list_.emplace_back(new_tuple);
}
void LinkLayerController::LeWhiteListRemoveDevice(Address addr, uint8_t addr_type) {
// TODO: Add checks to see if advertising, scanning, or a connection request
// with the white list is ongoing.
std::tuple<Address, uint8_t> erase_tuple = std::make_tuple(addr, addr_type);
for (size_t i = 0; i < le_white_list_.size(); i++) {
if (le_white_list_[i] == erase_tuple) {
le_white_list_.erase(le_white_list_.begin() + i);
}
}
}
bool LinkLayerController::LeWhiteListContainsDevice(Address addr, uint8_t addr_type) {
std::tuple<Address, uint8_t> sought_tuple = std::make_tuple(addr, addr_type);
for (size_t i = 0; i < le_white_list_.size(); i++) {
if (le_white_list_[i] == sought_tuple) {
return true;
}
}
return false;
}
bool LinkLayerController::LeWhiteListFull() {
return le_white_list_.size() >= properties_.GetLeWhiteListSize();
}
void LinkLayerController::Reset() {
inquiry_state_ = Inquiry::InquiryState::STANDBY;
last_inquiry_ = steady_clock::now();
le_scan_enable_ = 0;
le_connect_ = 0;
}
void LinkLayerController::PageScan() {}
void LinkLayerController::StartInquiry(milliseconds timeout) {
schedule_task_(milliseconds(timeout), [this]() { LinkLayerController::InquiryTimeout(); });
inquiry_state_ = Inquiry::InquiryState::INQUIRY;
LOG_INFO(LOG_TAG, "InquiryState = %d ", static_cast<int>(inquiry_state_));
}
void LinkLayerController::InquiryCancel() {
CHECK(inquiry_state_ == Inquiry::InquiryState::INQUIRY);
inquiry_state_ = Inquiry::InquiryState::STANDBY;
}
void LinkLayerController::InquiryTimeout() {
if (inquiry_state_ == Inquiry::InquiryState::INQUIRY) {
inquiry_state_ = Inquiry::InquiryState::STANDBY;
send_event_(EventPacketBuilder::CreateInquiryCompleteEvent(hci::Status::SUCCESS)->ToVector());
}
}
void LinkLayerController::SetInquiryMode(uint8_t mode) {
inquiry_mode_ = static_cast<Inquiry::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;
}
LOG_INFO(LOG_TAG, "Inquiry ");
std::unique_ptr<InquiryBuilder> inquiry = InquiryBuilder::Create(inquiry_mode_);
std::shared_ptr<LinkLayerPacketBuilder> to_send =
LinkLayerPacketBuilder::WrapInquiry(std::move(inquiry), properties_.GetAddress());
SendLinkLayerPacket(to_send);
last_inquiry_ = now;
}
void LinkLayerController::SetInquiryScanEnable(bool enable) {
inquiry_scans_enabled_ = enable;
}
void LinkLayerController::SetPageScanEnable(bool enable) {
page_scans_enabled_ = enable;
}
/* TODO: Connection handling
// TODO: Handle in the link manager.
uint16_t handle = LeGetHandle();
std::shared_ptr<Connection> new_connection =
std::make_shared<Connection>(peer, handle);
connections_.push_back(new_connection);
peer->SetConnection(new_connection);
send_event_(EventPacketBuilder::CreateLeEnhancedConnectionCompleteEvent(
hci::Status::SUCCESS, handle, 0x00, // role
le_peer_address_type_, le_peer_address_, Address::kEmpty,
Address::kEmpty, 0x0024, 0x0000, 0x01f4)->ToVector());
*/
} // namespace test_vendor_lib