[bt][gap] Wire up L2CAP security callbacks to GAP
- data::Domain now provides trampolines for L2CAP security functions.
- gap::LowEnergyConnectionManager no longer initiates pairing on every
connection. Instead, a SMP pairing request is initiated only after a
security request from L2CAP. The link is always encrypted upon
reconnection to an already paired device.
- gap::BrEdrConnectionManager currently logs a message and replies
negatively to security requests from L2CAP.
- The GAP layer currently has no unit tests for security procedures.
This is blocked on NET-1179.
NET-1151 #done
Test: 1. Boot fuchsia with the HOG driver disabled (driver.bthog.disable)
2. Connect to a LE HID device using bt-le-central. No pairing
should be initiated.
3. Enable notifications from a "Boot Input Report" characteristic
and follow these two scenarios:
- Do this without starting bt-pairing-tool which will cause the
pairing request to fail. The request to enable notifications
should fail with an "Insufficient Authentication" ATT error.
- Do this after starting bt-pairing-tool. The tool should
display a pairing request. After following the pairing
procedures the request to enable notifications should succeed.
4. Connecting to a HID device with the bthog driver enabled should
automatically initiate pairing (as bthog enables notifications).
Change-Id: Id75da08c1c818ddae6fb76e7f3a1af9fd21f14ca
diff --git a/drivers/bluetooth/lib/data/domain.cc b/drivers/bluetooth/lib/data/domain.cc
index 03bf059..0f2387b 100644
--- a/drivers/bluetooth/lib/data/domain.cc
+++ b/drivers/bluetooth/lib/data/domain.cc
@@ -65,17 +65,13 @@
void AddACLConnection(hci::ConnectionHandle handle,
hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher) override {
PostMessage([this, handle, role, lec = std::move(link_error_callback),
- dispatcher]() mutable {
+ suc = std::move(security_callback), dispatcher]() mutable {
if (l2cap_) {
- // TODO(NET-1151): Pass security upgrade callback from an argument.
- l2cap_->RegisterACL(
- handle, role, std::move(lec),
- [](auto, auto, auto) {
- bt_log(TRACE, "data-domain", "not implemented: L2CAP security");
- },
- dispatcher);
+ l2cap_->RegisterACL(handle, role, std::move(lec), std::move(suc),
+ dispatcher);
}
});
}
@@ -83,27 +79,24 @@
void AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_callback,
- l2cap::LEFixedChannelsCallback channel_callback,
l2cap::LEConnectionParameterUpdateCallback conn_param_callback,
+ l2cap::LEFixedChannelsCallback channel_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher) override {
- PostMessage([this, handle, role, cp_cb = std::move(conn_param_callback),
- link_err_cb = std::move(link_error_callback),
- chan_cb = std::move(channel_callback), dispatcher]() mutable {
+ PostMessage([this, handle, role, cpc = std::move(conn_param_callback),
+ lec = std::move(link_error_callback),
+ suc = std::move(security_callback),
+ cc = std::move(channel_callback), dispatcher]() mutable {
if (l2cap_) {
- // TODO(NET-1151): Pass security upgrade callback from an argument.
- l2cap_->RegisterLE(
- handle, role, std::move(cp_cb), std::move(link_err_cb),
- [](auto, auto, auto) {
- bt_log(TRACE, "data-domain", "not implemented: L2CAP security");
- },
- dispatcher);
+ l2cap_->RegisterLE(handle, role, std::move(cpc), std::move(lec),
+ std::move(suc), dispatcher);
auto att = l2cap_->OpenFixedChannel(handle, l2cap::kATTChannelId);
auto smp = l2cap_->OpenFixedChannel(handle, l2cap::kLESMPChannelId);
ZX_DEBUG_ASSERT(att);
ZX_DEBUG_ASSERT(smp);
async::PostTask(dispatcher, [att = std::move(att), smp = std::move(smp),
- cb = std::move(chan_cb)]() mutable {
+ cb = std::move(cc)]() mutable {
cb(std::move(att), std::move(smp));
});
}
@@ -118,6 +111,15 @@
});
}
+ void AssignLinkSecurityProperties(hci::ConnectionHandle handle,
+ sm::SecurityProperties security) override {
+ PostMessage([this, handle, security] {
+ if (l2cap_) {
+ l2cap_->AssignLinkSecurityProperties(handle, security);
+ }
+ });
+ }
+
void OpenL2capChannel(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelCallback cb,
async_dispatcher_t* dispatcher) override {
diff --git a/drivers/bluetooth/lib/data/domain.h b/drivers/bluetooth/lib/data/domain.h
index 1df4d8f..5ebcad8 100644
--- a/drivers/bluetooth/lib/data/domain.h
+++ b/drivers/bluetooth/lib/data/domain.h
@@ -52,11 +52,16 @@
// |link_error_callback| will be used to notify when a channel signals a link
// error. It will be posted onto |dispatcher|.
//
+ // |security_callback| will be used to request an upgrade to the link security
+ // level. This can be triggered by dynamic L2CAP channel creation or by a
+ // service-level client via Channel::UpgradeSecurity().
+ //
// Has no effect if this Domain is uninitialized or shut down.
- virtual void AddACLConnection(hci::ConnectionHandle handle,
- hci::Connection::Role role,
- l2cap::LinkErrorCallback link_error_callback,
- async_dispatcher_t* dispatcher) = 0;
+ virtual void AddACLConnection(
+ hci::ConnectionHandle handle, hci::Connection::Role role,
+ l2cap::LinkErrorCallback link_error_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
+ async_dispatcher_t* dispatcher) = 0;
// Registers an LE connection with the L2CAP layer. L2CAP channels can be
// opened on the logical link represented by |handle| after a call to this
@@ -68,6 +73,10 @@
// |link_error_callback| will be used to notify when a channel signals a link
// error.
//
+ // |security_callback| will be used to request an upgrade to the link security
+ // level. This can be triggered by dynamic L2CAP channel creation or by a
+ // service-level client via Channel::UpgradeSecurity().
+ //
// Upon successful registration of the link, |channel_callback| will be called
// with the ATT and SMP fixed channels.
//
@@ -75,8 +84,9 @@
virtual void AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_callback,
- l2cap::LEFixedChannelsCallback channel_callback,
l2cap::LEConnectionParameterUpdateCallback conn_param_callback,
+ l2cap::LEFixedChannelsCallback channel_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher) = 0;
// Removes a previously registered connection. All corresponding Channels will
@@ -90,6 +100,11 @@
// Has no effect if this Domain is uninitialized or shut down.
virtual void RemoveConnection(hci::ConnectionHandle handle) = 0;
+ // Assigns the security level of a logical link. Has no effect if |handle| has
+ // not been previously registered or the link is closed.
+ virtual void AssignLinkSecurityProperties(
+ hci::ConnectionHandle handle, sm::SecurityProperties security) = 0;
+
// Open an outbound dynamic channel against a peer's Protocol/Service
// Multiplexing (PSM) code |psm| on a link identified by |handle|.
//
diff --git a/drivers/bluetooth/lib/data/domain_unittest.cc b/drivers/bluetooth/lib/data/domain_unittest.cc
index 153e7d0..6c9f3b1 100644
--- a/drivers/bluetooth/lib/data/domain_unittest.cc
+++ b/drivers/bluetooth/lib/data/domain_unittest.cc
@@ -198,7 +198,8 @@
// Register a fake link.
domain()->AddACLConnection(
- kLinkHandle, hci::Connection::Role::kMaster, [] {}, dispatcher());
+ kLinkHandle, hci::Connection::Role::kMaster, [] {},
+ [](auto, auto, auto) {}, dispatcher());
zx::socket sock;
ASSERT_FALSE(sock);
@@ -244,7 +245,8 @@
// Register a fake link.
domain()->AddACLConnection(
- kLinkHandle, hci::Connection::Role::kMaster, [] {}, dispatcher());
+ kLinkHandle, hci::Connection::Role::kMaster, [] {},
+ [](auto, auto, auto) {}, dispatcher());
zx::socket sock;
ASSERT_FALSE(sock);
diff --git a/drivers/bluetooth/lib/data/fake_domain.cc b/drivers/bluetooth/lib/data/fake_domain.cc
index 4b3d197..94e4879 100644
--- a/drivers/bluetooth/lib/data/fake_domain.cc
+++ b/drivers/bluetooth/lib/data/fake_domain.cc
@@ -87,6 +87,7 @@
void FakeDomain::AddACLConnection(hci::ConnectionHandle handle,
hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_cb,
+ l2cap::SecurityUpgradeCallback security_cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
@@ -98,8 +99,9 @@
void FakeDomain::AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_cb,
- l2cap::LEFixedChannelsCallback channel_cb,
l2cap::LEConnectionParameterUpdateCallback conn_param_cb,
+ l2cap::LEFixedChannelsCallback channel_cb,
+ l2cap::SecurityUpgradeCallback security_cb,
async_dispatcher_t* dispatcher) {
if (!initialized_)
return;
@@ -122,6 +124,11 @@
links_.erase(handle);
}
+void FakeDomain::AssignLinkSecurityProperties(hci::ConnectionHandle handle,
+ sm::SecurityProperties security) {
+ // TODO(armansito): implement
+}
+
void FakeDomain::OpenL2capChannel(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelCallback cb,
async_dispatcher_t* dispatcher) {
diff --git a/drivers/bluetooth/lib/data/fake_domain.h b/drivers/bluetooth/lib/data/fake_domain.h
index 7e0db30..7a770f0 100644
--- a/drivers/bluetooth/lib/data/fake_domain.h
+++ b/drivers/bluetooth/lib/data/fake_domain.h
@@ -54,14 +54,18 @@
void AddACLConnection(hci::ConnectionHandle handle,
hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher) override;
void AddLEConnection(
hci::ConnectionHandle handle, hci::Connection::Role role,
l2cap::LinkErrorCallback link_error_callback,
- l2cap::LEFixedChannelsCallback channel_callback,
l2cap::LEConnectionParameterUpdateCallback conn_param_callback,
+ l2cap::LEFixedChannelsCallback channel_callback,
+ l2cap::SecurityUpgradeCallback security_callback,
async_dispatcher_t* dispatcher) override;
void RemoveConnection(hci::ConnectionHandle handle) override;
+ void AssignLinkSecurityProperties(hci::ConnectionHandle handle,
+ sm::SecurityProperties security) override;
void OpenL2capChannel(hci::ConnectionHandle handle, l2cap::PSM psm,
l2cap::ChannelCallback cb,
async_dispatcher_t* dispatcher) override;
diff --git a/drivers/bluetooth/lib/gap/bredr_connection_manager.cc b/drivers/bluetooth/lib/gap/bredr_connection_manager.cc
index 61c5ebd..1ad3019 100644
--- a/drivers/bluetooth/lib/gap/bredr_connection_manager.cc
+++ b/drivers/bluetooth/lib/gap/bredr_connection_manager.cc
@@ -16,6 +16,8 @@
namespace btlib {
namespace gap {
+using common::HostError;
+
namespace {
void SetPageScanEnabled(bool enabled, fxl::RefPtr<hci::Transport> hci,
@@ -327,6 +329,14 @@
return;
}
+ // TODO(armansito): Implement this callback.
+ auto security_callback = [](hci::ConnectionHandle handle,
+ sm::SecurityLevel level, auto cb) {
+ bt_log(INFO, "gap-bredr",
+ "Ignoring security upgrade request; not implemented");
+ cb(sm::Status(HostError::kNotSupported));
+ };
+
// Register with L2CAP to handle services on the ACL signaling channel.
self->data_domain_->AddACLConnection(
conn_ptr->handle(), conn_ptr->role(),
@@ -343,7 +353,7 @@
// TODO(NET-1442): Test link error behavior using FakeDevice.
conn_ptr->Close();
},
- self->dispatcher_);
+ std::move(security_callback), self->dispatcher_);
self->connections_.emplace(conn_ptr->handle(), std::move(conn_ptr));
device->MutBrEdr().SetConnectionState(
diff --git a/drivers/bluetooth/lib/gap/low_energy_connection_manager.cc b/drivers/bluetooth/lib/gap/low_energy_connection_manager.cc
index e2ec298..1dab1ce 100644
--- a/drivers/bluetooth/lib/gap/low_energy_connection_manager.cc
+++ b/drivers/bluetooth/lib/gap/low_energy_connection_manager.cc
@@ -37,19 +37,30 @@
LowEnergyConnection(const std::string& id,
std::unique_ptr<hci::Connection> link,
async_dispatcher_t* dispatcher,
- fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr)
+ fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr,
+ fbl::RefPtr<data::Domain> data_domain,
+ fbl::RefPtr<gatt::GATT> gatt)
: id_(id),
link_(std::move(link)),
dispatcher_(dispatcher),
conn_mgr_(conn_mgr),
+ data_domain_(data_domain),
+ gatt_(gatt),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(!id_.empty());
ZX_DEBUG_ASSERT(link_);
ZX_DEBUG_ASSERT(dispatcher_);
ZX_DEBUG_ASSERT(conn_mgr_);
+ ZX_DEBUG_ASSERT(data_domain_);
+ ZX_DEBUG_ASSERT(gatt_);
}
~LowEnergyConnection() override {
+ // Unregister this link from the GATT profile and the L2CAP plane. This
+ // invalidates all L2CAP channels that are associated with this link.
+ gatt_->RemoveConnection(id());
+ data_domain_->RemoveConnection(link_->handle());
+
// Tell the controller to disconnect the link if it is marked as open.
link_->Close();
@@ -83,63 +94,23 @@
// Registers this connection with L2CAP and initializes the fixed channel
// protocols.
- void InitializeFixedChannels(fbl::RefPtr<data::Domain> data_domain,
- fbl::RefPtr<gatt::GATT> gatt,
- l2cap::LEConnectionParameterUpdateCallback cp_cb,
+ void InitializeFixedChannels(l2cap::LEConnectionParameterUpdateCallback cp_cb,
l2cap::LinkErrorCallback link_error_cb) {
auto self = weak_ptr_factory_.GetWeakPtr();
- auto channels_cb = [self, gatt](fbl::RefPtr<l2cap::Channel> att,
- fbl::RefPtr<l2cap::Channel> smp) {
- if (!self || !att || !smp) {
- bt_log(TRACE, "gap-le",
- "link was closed before opening fixed channels");
- return;
- }
-
- // Obtain existing pairing data, if any.
- std::optional<sm::LTK> ltk;
- auto* dev = self->conn_mgr_->device_cache()->FindDeviceById(self->id_);
- ZX_DEBUG_ASSERT_MSG(dev, "connected device must be present in cache!");
-
- if (dev->le() && dev->le()->bond_data() && dev->le()->bond_data()->ltk) {
- ltk = *dev->le()->bond_data()->ltk;
- }
-
- // Obtain the local I/O capabilities from the delegate. Default to
- // NoInputNoOutput if no delegate is available.
- auto io_cap = sm::IOCapability::kNoInputNoOutput;
- if (self->conn_mgr_->pairing_delegate()) {
- io_cap = self->conn_mgr_->pairing_delegate()->io_capability();
- }
- auto pairing = std::make_unique<sm::PairingState>(
- self->link_->WeakPtr(), std::move(smp), io_cap, self);
-
- // TODO(NET-1151): We register the connection with GATT but don't perform
- // service discovery until after pairing has completed. Fix this so that
- // services are discovered immediately and pairing happens in response to
- // a service request unless the peer is already paired.
- gatt->AddConnection(self->id(), std::move(att));
-
- if (ltk) {
- bt_log(INFO, "gap-le", "encrypting link with existing LTK");
- pairing->SetCurrentSecurity(*ltk);
- gatt->DiscoverServices(self->id_);
- } else {
- bt_log(INFO, "gap-le", "pairing with device");
- pairing->UpgradeSecurity(
- sm::SecurityLevel::kEncrypted,
- [gatt, id = self->id()](sm::Status status, const auto& props) {
- bt_log(INFO, "gap-le", "pairing status: %s, properties: %s",
- status.ToString().c_str(), props.ToString().c_str());
- gatt->DiscoverServices(id);
- });
- }
- self->pairing_ = std::move(pairing);
- };
-
- data_domain->AddLEConnection(
+ data_domain_->AddLEConnection(
link_->handle(), link_->role(), std::move(link_error_cb),
- std::move(channels_cb), std::move(cp_cb), dispatcher_);
+ std::move(cp_cb),
+ [self](auto att, auto smp) {
+ if (self) {
+ self->OnL2capFixedChannelsOpened(std::move(att), std::move(smp));
+ }
+ },
+ [self](auto handle, auto level, auto cb) {
+ if (self) {
+ self->OnSecurityUpgradeRequest(handle, level, std::move(cb));
+ }
+ },
+ dispatcher_);
}
// Cancels any on-going pairing procedures and sets up SMP to use the provided
@@ -153,6 +124,68 @@
hci::Connection* link() const { return link_.get(); }
private:
+ // Called by the L2CAP layer once the link has been registered and the fixed
+ // channels have been opened.
+ void OnL2capFixedChannelsOpened(fbl::RefPtr<l2cap::Channel> att,
+ fbl::RefPtr<l2cap::Channel> smp) {
+ if (!att || !smp) {
+ bt_log(TRACE, "gap-le", "link was closed before opening fixed channels");
+ return;
+ }
+
+ bt_log(TRACE, "gap-le", "ATT and SMP fixed channels open");
+
+ // Obtain existing pairing data, if any.
+ std::optional<sm::LTK> ltk;
+ auto* dev = conn_mgr_->device_cache()->FindDeviceById(id());
+ ZX_DEBUG_ASSERT_MSG(dev, "connected device must be present in cache!");
+
+ if (dev->le() && dev->le()->bond_data()) {
+ // |ltk| will remain as std::nullopt if bonding data contains no LTK.
+ ltk = dev->le()->bond_data()->ltk;
+ }
+
+ // Obtain the local I/O capabilities from the delegate. Default to
+ // NoInputNoOutput if no delegate is available.
+ auto io_cap = sm::IOCapability::kNoInputNoOutput;
+ if (conn_mgr_->pairing_delegate()) {
+ io_cap = conn_mgr_->pairing_delegate()->io_capability();
+ }
+
+ pairing_ = std::make_unique<sm::PairingState>(
+ link_->WeakPtr(), std::move(smp), io_cap,
+ weak_ptr_factory_.GetWeakPtr());
+
+ // Encrypt the link with the current LTK if it exists.
+ if (ltk) {
+ bt_log(INFO, "gap-le", "encrypting link with existing LTK");
+ pairing_->SetCurrentSecurity(*ltk);
+ }
+
+ // Initialize the GATT layer.
+ gatt_->AddConnection(id(), std::move(att));
+ gatt_->DiscoverServices(id());
+ }
+
+ // Handles a security upgrade request received from the L2CAP layer.
+ void OnSecurityUpgradeRequest(hci::ConnectionHandle handle,
+ sm::SecurityLevel level,
+ sm::StatusCallback callback) {
+ ZX_DEBUG_ASSERT(link_->handle() == handle);
+ ZX_DEBUG_ASSERT(pairing_);
+
+ bt_log(TRACE, "gap-le", "received security upgrade request");
+
+ pairing_->UpgradeSecurity(
+ level, [handle, dd = data_domain_, cb = std::move(callback)](
+ sm::Status status, const auto& sp) {
+ bt_log(INFO, "gap-le", "pairing status: %s, properties: %s",
+ status.ToString().c_str(), sp.ToString().c_str());
+ dd->AssignLinkSecurityProperties(handle, sp);
+ cb(status);
+ });
+ }
+
// sm::PairingState::Delegate override:
void OnNewPairingData(const sm::PairingData& pairing_data) override {
// Consider the pairing temporary if no link key was received. This
@@ -176,6 +209,11 @@
: "",
pairing_data.csrk ? "csrk " : "", id().c_str());
+ // Update the data plane with the correct link security level.
+ ZX_DEBUG_ASSERT(pairing_);
+ data_domain_->AssignLinkSecurityProperties(link_->handle(),
+ pairing_->security());
+
if (!conn_mgr_->device_cache()->StoreLowEnergyBond(id_, pairing_data)) {
bt_log(ERROR, "gap-le", "failed to cache bonding data (id: %s)",
id().c_str());
@@ -185,6 +223,7 @@
// sm::PairingState::Delegate override:
void OnPairingComplete(sm::Status status) override {
bt_log(TRACE, "gap-le", "pairing complete: %s", status.ToString().c_str());
+
auto delegate = conn_mgr_->pairing_delegate();
if (delegate) {
delegate->CompletePairing(id_, status);
@@ -197,6 +236,13 @@
// stored link key is not valid.
bt_log(ERROR, "gap-le", "link layer authentication failed: %s",
status.ToString().c_str());
+
+ // Report the link to be non-secure.
+ // TODO(armansito): sm::PairingState::security() should accurately reflect
+ // the link security properties. It can currently contain a stale value
+ // following authentication failures.
+ data_domain_->AssignLinkSecurityProperties(link_->handle(),
+ sm::SecurityProperties());
}
// sm::PairingState::Delegate override:
@@ -263,6 +309,14 @@
async_dispatcher_t* dispatcher_;
fxl::WeakPtr<LowEnergyConnectionManager> conn_mgr_;
+ // Reference to the data plane is used to update the L2CAP layer to
+ // reflect the correct link security level.
+ fbl::RefPtr<data::Domain> data_domain_;
+
+ // Reference to the GATT profile layer is used to initiate service discovery
+ // and register the link.
+ fbl::RefPtr<gatt::GATT> gatt_;
+
// SMP pairing manager.
std::unique_ptr<sm::PairingState> pairing_;
@@ -646,9 +700,9 @@
// Initialize connection.
auto conn = std::make_unique<internal::LowEnergyConnection>(
- device_id, std::move(link), dispatcher_, weak_ptr_factory_.GetWeakPtr());
- conn->InitializeFixedChannels(data_domain_, gatt_,
- std::move(conn_param_update_cb),
+ device_id, std::move(link), dispatcher_, weak_ptr_factory_.GetWeakPtr(),
+ data_domain_, gatt_);
+ conn->InitializeFixedChannels(std::move(conn_param_update_cb),
std::move(link_error_cb));
auto first_ref = conn->AddRef();
@@ -687,13 +741,6 @@
peer->MutLe().SetConnectionState(
RemoteDevice::ConnectionState::kNotConnected);
- // Clean up GATT profile.
- gatt_->RemoveConnection(conn->id());
-
- // Remove the connection from L2CAP. This will invalidate all channels that
- // are associated with this link.
- data_domain_->RemoveConnection(conn->handle());
-
if (!close_link) {
// Mark the connection as already closed so that hci::Connection::Close()
// doesn't send HCI_Disconnect to the controller.
diff --git a/drivers/bluetooth/lib/sdp/server_unittest.cc b/drivers/bluetooth/lib/sdp/server_unittest.cc
index 1b7f545..fe99160 100644
--- a/drivers/bluetooth/lib/sdp/server_unittest.cc
+++ b/drivers/bluetooth/lib/sdp/server_unittest.cc
@@ -44,9 +44,9 @@
[this](auto fake_chan) { channel_ = std::move(fake_chan); });
l2cap_->Initialize();
l2cap_->AddACLConnection(kTestHandle1, hci::Connection::Role::kSlave,
- nullptr, nullptr);
+ nullptr, nullptr, nullptr);
l2cap_->AddACLConnection(kTestHandle2, hci::Connection::Role::kSlave,
- nullptr, nullptr);
+ nullptr, nullptr, nullptr);
server_ = std::make_unique<Server>(l2cap_);
}
@@ -834,7 +834,8 @@
ASSERT_EQ(DataElement::Type::kSequence, group_attr_it->second.type());
ASSERT_EQ(DataElement::Type::kUuid, group_attr_it->second.At(0)->type());
EXPECT_NE(attributes.end(), group_attr_it);
- EXPECT_EQ(kPublicBrowseRootUuid, *group_attr_it->second.At(0)->Get<common::UUID>());
+ EXPECT_EQ(kPublicBrowseRootUuid,
+ *group_attr_it->second.At(0)->Get<common::UUID>());
}
#undef SDP_ERROR_RSP