1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection.h"
16
17 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h"
18
19 namespace bt::gap::internal {
20
21 namespace {
22
23 constexpr const char* kInspectPeerIdPropertyName = "peer_id";
24 constexpr const char* kInspectPeerAddressPropertyName = "peer_address";
25 constexpr const char* kInspectRefsPropertyName = "ref_count";
26
27 // Connection parameters to use when the peer's preferred connection parameters
28 // are not known.
29 static const hci_spec::LEPreferredConnectionParameters
30 kDefaultPreferredConnectionParameters(
31 hci_spec::defaults::kLEConnectionIntervalMin,
32 hci_spec::defaults::kLEConnectionIntervalMax,
33 /*max_latency=*/0,
34 hci_spec::defaults::kLESupervisionTimeout);
35
36 } // namespace
37
Create(Peer::WeakPtr peer,std::unique_ptr<hci::LowEnergyConnection> link,LowEnergyConnectionOptions connection_options,PeerDisconnectCallback peer_disconnect_cb,ErrorCallback error_cb,WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,l2cap::ChannelManager * l2cap,gatt::GATT::WeakPtr gatt,hci::CommandChannel::WeakPtr cmd_channel,pw::async::Dispatcher & dispatcher)38 std::unique_ptr<LowEnergyConnection> LowEnergyConnection::Create(
39 Peer::WeakPtr peer,
40 std::unique_ptr<hci::LowEnergyConnection> link,
41 LowEnergyConnectionOptions connection_options,
42 PeerDisconnectCallback peer_disconnect_cb,
43 ErrorCallback error_cb,
44 WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,
45 l2cap::ChannelManager* l2cap,
46 gatt::GATT::WeakPtr gatt,
47 hci::CommandChannel::WeakPtr cmd_channel,
48 pw::async::Dispatcher& dispatcher) {
49 // Catch any errors/disconnects during connection initialization so that they
50 // are reported by returning a nullptr. This is less error-prone than calling
51 // the user's callbacks during initialization.
52 bool error = false;
53 auto peer_disconnect_cb_temp = [&error](auto) { error = true; };
54 auto error_cb_temp = [&error] { error = true; };
55 std::unique_ptr<LowEnergyConnection> connection(
56 new LowEnergyConnection(std::move(peer),
57 std::move(link),
58 connection_options,
59 std::move(peer_disconnect_cb_temp),
60 std::move(error_cb_temp),
61 std::move(conn_mgr),
62 l2cap,
63 std::move(gatt),
64 std::move(cmd_channel),
65 dispatcher));
66
67 // This looks strange, but it is possible for InitializeFixedChannels() to
68 // trigger an error and still return true, so |error| can change between the
69 // first and last check.
70 if (error || !connection->InitializeFixedChannels() || error) {
71 return nullptr;
72 }
73
74 // Now it is safe to set the user's callbacks, as no more errors/disconnects
75 // can be signaled before returning.
76 connection->set_peer_disconnect_callback(std::move(peer_disconnect_cb));
77 connection->set_error_callback(std::move(error_cb));
78 return connection;
79 }
80
LowEnergyConnection(Peer::WeakPtr peer,std::unique_ptr<hci::LowEnergyConnection> link,LowEnergyConnectionOptions connection_options,PeerDisconnectCallback peer_disconnect_cb,ErrorCallback error_cb,WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,l2cap::ChannelManager * l2cap,gatt::GATT::WeakPtr gatt,hci::CommandChannel::WeakPtr cmd_channel,pw::async::Dispatcher & dispatcher)81 LowEnergyConnection::LowEnergyConnection(
82 Peer::WeakPtr peer,
83 std::unique_ptr<hci::LowEnergyConnection> link,
84 LowEnergyConnectionOptions connection_options,
85 PeerDisconnectCallback peer_disconnect_cb,
86 ErrorCallback error_cb,
87 WeakSelf<LowEnergyConnectionManager>::WeakPtr conn_mgr,
88 l2cap::ChannelManager* l2cap,
89 gatt::GATT::WeakPtr gatt,
90 hci::CommandChannel::WeakPtr cmd_channel,
91 pw::async::Dispatcher& dispatcher)
92 : dispatcher_(dispatcher),
93 peer_(std::move(peer)),
94 link_(std::move(link)),
95 connection_options_(connection_options),
96 conn_mgr_(std::move(conn_mgr)),
97 l2cap_(l2cap),
98 gatt_(std::move(gatt)),
99 cmd_(std::move(cmd_channel)),
100 peer_disconnect_callback_(std::move(peer_disconnect_cb)),
101 error_callback_(std::move(error_cb)),
102 refs_(/*convert=*/[](const auto& refs) { return refs.size(); }),
103 weak_self_(this),
104 weak_delegate_(this) {
105 BT_ASSERT(peer_.is_alive());
106 BT_ASSERT(link_);
107 BT_ASSERT(conn_mgr_.is_alive());
108 BT_ASSERT(gatt_.is_alive());
109 BT_ASSERT(cmd_.is_alive());
110 BT_ASSERT(peer_disconnect_callback_);
111 BT_ASSERT(error_callback_);
112
113 link_->set_peer_disconnect_callback(
__anon618f30710502(const auto&, auto reason) 114 [this](const auto&, auto reason) { peer_disconnect_callback_(reason); });
115
116 RegisterEventHandlers();
117 StartConnectionPauseTimeout();
118 }
119
~LowEnergyConnection()120 LowEnergyConnection::~LowEnergyConnection() {
121 cmd_->RemoveEventHandler(conn_update_cmpl_handler_id_);
122
123 // Unregister this link from the GATT profile and the L2CAP plane. This
124 // invalidates all L2CAP channels that are associated with this link.
125 gatt_->RemoveConnection(peer_id());
126 l2cap_->RemoveConnection(link_->handle());
127
128 // Notify all active references that the link is gone. This will
129 // synchronously notify all refs.
130 CloseRefs();
131 }
132
133 std::unique_ptr<bt::gap::LowEnergyConnectionHandle>
AddRef()134 LowEnergyConnection::AddRef() {
135 auto self = GetWeakPtr();
136 auto release_cb = [self](LowEnergyConnectionHandle* handle) {
137 if (self.is_alive()) {
138 self->conn_mgr_->ReleaseReference(handle);
139 }
140 };
141 auto bondable_cb = [self] {
142 BT_ASSERT(self.is_alive());
143 return self->bondable_mode();
144 };
145 auto security_cb = [self] {
146 BT_ASSERT(self.is_alive());
147 return self->security();
148 };
149 std::unique_ptr<bt::gap::LowEnergyConnectionHandle> conn_ref(
150 new LowEnergyConnectionHandle(peer_id(),
151 handle(),
152 std::move(release_cb),
153 std::move(bondable_cb),
154 std::move(security_cb)));
155 BT_ASSERT(conn_ref);
156
157 refs_.Mutable()->insert(conn_ref.get());
158
159 bt_log(DEBUG,
160 "gap-le",
161 "added ref (peer: %s, handle %#.4x, count: %lu)",
162 bt_str(peer_id()),
163 handle(),
164 ref_count());
165
166 return conn_ref;
167 }
168
DropRef(LowEnergyConnectionHandle * ref)169 void LowEnergyConnection::DropRef(LowEnergyConnectionHandle* ref) {
170 BT_DEBUG_ASSERT(ref);
171
172 size_t res = refs_.Mutable()->erase(ref);
173 BT_ASSERT_MSG(res == 1u, "DropRef called with wrong connection reference");
174 bt_log(DEBUG,
175 "gap-le",
176 "dropped ref (peer: %s, handle: %#.4x, count: %lu)",
177 bt_str(peer_id()),
178 handle(),
179 ref_count());
180 }
181
182 // Registers this connection with L2CAP and initializes the fixed channel
183 // protocols.
InitializeFixedChannels()184 [[nodiscard]] bool LowEnergyConnection::InitializeFixedChannels() {
185 auto self = GetWeakPtr();
186 // Ensure error_callback_ is only called once if link_error_cb is called
187 // multiple times.
188 auto link_error_cb = [self]() {
189 if (self.is_alive() && self->error_callback_) {
190 self->error_callback_();
191 }
192 };
193 auto update_conn_params_cb = [self](auto params) {
194 if (self.is_alive()) {
195 self->OnNewLEConnectionParams(params);
196 }
197 };
198 auto security_upgrade_cb = [self](auto handle, auto level, auto cb) {
199 if (!self.is_alive()) {
200 return;
201 }
202
203 bt_log(INFO,
204 "gap-le",
205 "received security upgrade request on L2CAP channel (level: %s, "
206 "peer: %s, handle: %#.4x)",
207 sm::LevelToString(level),
208 bt_str(self->peer_id()),
209 handle);
210 BT_ASSERT(self->handle() == handle);
211 self->OnSecurityRequest(level, std::move(cb));
212 };
213 l2cap::ChannelManager::LEFixedChannels fixed_channels =
214 l2cap_->AddLEConnection(link_->handle(),
215 link_->role(),
216 std::move(link_error_cb),
217 update_conn_params_cb,
218 security_upgrade_cb);
219
220 return OnL2capFixedChannelsOpened(std::move(fixed_channels.att),
221 std::move(fixed_channels.smp),
222 connection_options_);
223 }
224
225 // Used to respond to protocol/service requests for increased security.
OnSecurityRequest(sm::SecurityLevel level,sm::ResultFunction<> cb)226 void LowEnergyConnection::OnSecurityRequest(sm::SecurityLevel level,
227 sm::ResultFunction<> cb) {
228 BT_ASSERT(sm_);
229 sm_->UpgradeSecurity(
230 level,
231 [cb = std::move(cb), peer_id = peer_id(), handle = handle()](
232 sm::Result<> status, const auto& sp) {
233 bt_log(INFO,
234 "gap-le",
235 "pairing status: %s, properties: %s (peer: %s, handle: %#.4x)",
236 bt_str(status),
237 bt_str(sp),
238 bt_str(peer_id),
239 handle);
240 cb(status);
241 });
242 }
243
244 // Handles a pairing request (i.e. security upgrade) received from "higher
245 // levels", likely initiated from GAP. This will only be used by pairing
246 // requests that are initiated in the context of testing. May only be called on
247 // an already-established connection.
UpgradeSecurity(sm::SecurityLevel level,sm::BondableMode bondable_mode,sm::ResultFunction<> cb)248 void LowEnergyConnection::UpgradeSecurity(sm::SecurityLevel level,
249 sm::BondableMode bondable_mode,
250 sm::ResultFunction<> cb) {
251 BT_ASSERT(sm_);
252 sm_->set_bondable_mode(bondable_mode);
253 OnSecurityRequest(level, std::move(cb));
254 }
255
256 // Cancels any on-going pairing procedures and sets up SMP to use the provided
257 // new I/O capabilities for future pairing procedures.
ResetSecurityManager(sm::IOCapability ioc)258 void LowEnergyConnection::ResetSecurityManager(sm::IOCapability ioc) {
259 sm_->Reset(ioc);
260 }
261
OnInterrogationComplete()262 void LowEnergyConnection::OnInterrogationComplete() {
263 BT_ASSERT(!interrogation_completed_);
264 interrogation_completed_ = true;
265 MaybeUpdateConnectionParameters();
266 }
267
AttachInspect(inspect::Node & parent,std::string name)268 void LowEnergyConnection::AttachInspect(inspect::Node& parent,
269 std::string name) {
270 inspect_node_ = parent.CreateChild(name);
271 inspect_properties_.peer_id = inspect_node_.CreateString(
272 kInspectPeerIdPropertyName, peer_id().ToString());
273 inspect_properties_.peer_address = inspect_node_.CreateString(
274 kInspectPeerAddressPropertyName,
275 link_.get() ? link_->peer_address().ToString() : "");
276 refs_.AttachInspect(inspect_node_, kInspectRefsPropertyName);
277 }
278
StartConnectionPauseTimeout()279 void LowEnergyConnection::StartConnectionPauseTimeout() {
280 if (link_->role() == pw::bluetooth::emboss::ConnectionRole::CENTRAL) {
281 StartConnectionPauseCentralTimeout();
282 } else {
283 StartConnectionPausePeripheralTimeout();
284 }
285 }
286
RegisterEventHandlers()287 void LowEnergyConnection::RegisterEventHandlers() {
288 auto self = GetWeakPtr();
289 conn_update_cmpl_handler_id_ = cmd_->AddLEMetaEventHandler(
290 hci_spec::kLEConnectionUpdateCompleteSubeventCode,
291 [self](const hci::EmbossEventPacket& event) {
292 if (self.is_alive()) {
293 self->OnLEConnectionUpdateComplete(event);
294 return hci::CommandChannel::EventCallbackResult::kContinue;
295 }
296 return hci::CommandChannel::EventCallbackResult::kRemove;
297 });
298 }
299
300 // Connection parameter updates by the peripheral are not allowed until the
301 // central has been idle for kLEConnectionPauseCentral and
302 // kLEConnectionPausePeripheral has passed since the connection was established
303 // (Core Spec v5.2, Vol 3, Part C, Sec 9.3.12).
304 // TODO(fxbug.dev/42159733): Wait to update connection parameters until all
305 // initialization procedures have completed.
StartConnectionPausePeripheralTimeout()306 void LowEnergyConnection::StartConnectionPausePeripheralTimeout() {
307 BT_ASSERT(!conn_pause_peripheral_timeout_.has_value());
308 conn_pause_peripheral_timeout_.emplace(
309 dispatcher_, [this](pw::async::Context /*ctx*/, pw::Status status) {
310 if (!status.ok()) {
311 return;
312 }
313 // Destroying this task will invalidate the capture list,
314 // so we need to save a self pointer.
315 auto self = this;
316 conn_pause_peripheral_timeout_.reset();
317 self->MaybeUpdateConnectionParameters();
318 });
319 conn_pause_peripheral_timeout_->PostAfter(kLEConnectionPausePeripheral);
320 }
321
322 // Connection parameter updates by the central are not allowed until the central
323 // is idle and the peripheral has been idle for kLEConnectionPauseCentral (Core
324 // Spec v5.2, Vol 3, Part C, Sec 9.3.12).
325 // TODO(fxbug.dev/42159733): Wait to update connection parameters until all
326 // initialization procedures have completed.
StartConnectionPauseCentralTimeout()327 void LowEnergyConnection::StartConnectionPauseCentralTimeout() {
328 BT_ASSERT(!conn_pause_central_timeout_.has_value());
329 conn_pause_central_timeout_.emplace(
330 dispatcher_, [this](pw::async::Context /*ctx*/, pw::Status status) {
331 if (!status.ok()) {
332 return;
333 }
334 // Destroying this task will invalidate the capture list, so
335 // we need to save a self pointer.
336 auto self = this;
337 conn_pause_central_timeout_.reset();
338 self->MaybeUpdateConnectionParameters();
339 });
340 conn_pause_central_timeout_->PostAfter(kLEConnectionPauseCentral);
341 }
342
OnL2capFixedChannelsOpened(l2cap::Channel::WeakPtr att,l2cap::Channel::WeakPtr smp,LowEnergyConnectionOptions connection_options)343 bool LowEnergyConnection::OnL2capFixedChannelsOpened(
344 l2cap::Channel::WeakPtr att,
345 l2cap::Channel::WeakPtr smp,
346 LowEnergyConnectionOptions connection_options) {
347 bt_log(DEBUG,
348 "gap-le",
349 "ATT and SMP fixed channels open (peer: %s)",
350 bt_str(peer_id()));
351
352 // Obtain existing pairing data, if any.
353 std::optional<sm::LTK> ltk;
354
355 if (peer_->le() && peer_->le()->bond_data()) {
356 // Legacy pairing allows both devices to generate and exchange LTKs. "The
357 // Central must have the security information (LTK, EDIV, and Rand)
358 // distributed by the Peripheral in LE legacy [...] to setup an encrypted
359 // session" (v5.3, Vol. 3 Part H 2.4.4.2). For Secure Connections peer_ltk
360 // and local_ltk will be equal, so this check is unnecessary but correct.
361 ltk = (link()->role() == pw::bluetooth::emboss::ConnectionRole::CENTRAL)
362 ? peer_->le()->bond_data()->peer_ltk
363 : peer_->le()->bond_data()->local_ltk;
364 }
365
366 // Obtain the local I/O capabilities from the delegate. Default to
367 // NoInputNoOutput if no delegate is available.
368 auto io_cap = sm::IOCapability::kNoInputNoOutput;
369 if (conn_mgr_->pairing_delegate().is_alive()) {
370 io_cap = conn_mgr_->pairing_delegate()->io_capability();
371 }
372 LESecurityMode security_mode = conn_mgr_->security_mode();
373 sm_ = conn_mgr_->sm_factory_func()(link_->GetWeakPtr(),
374 std::move(smp),
375 io_cap,
376 weak_delegate_.GetWeakPtr(),
377 connection_options.bondable_mode,
378 security_mode,
379 dispatcher_);
380
381 // Provide SMP with the correct LTK from a previous pairing with the peer, if
382 // it exists. This will start encryption if the local device is the link-layer
383 // central.
384 if (ltk) {
385 bt_log(INFO,
386 "gap-le",
387 "assigning existing LTK (peer: %s, handle: %#.4x)",
388 bt_str(peer_id()),
389 handle());
390 sm_->AssignLongTermKey(*ltk);
391 }
392
393 return InitializeGatt(std::move(att), connection_options.service_uuid);
394 }
395
OnNewLEConnectionParams(const hci_spec::LEPreferredConnectionParameters & params)396 void LowEnergyConnection::OnNewLEConnectionParams(
397 const hci_spec::LEPreferredConnectionParameters& params) {
398 bt_log(INFO,
399 "gap-le",
400 "LE connection parameters received (peer: %s, handle: %#.4x)",
401 bt_str(peer_id()),
402 link_->handle());
403
404 BT_ASSERT(peer_.is_alive());
405
406 peer_->MutLe().SetPreferredConnectionParameters(params);
407
408 UpdateConnectionParams(params);
409 }
410
RequestConnectionParameterUpdate(const hci_spec::LEPreferredConnectionParameters & params)411 void LowEnergyConnection::RequestConnectionParameterUpdate(
412 const hci_spec::LEPreferredConnectionParameters& params) {
413 BT_ASSERT_MSG(
414 link_->role() == pw::bluetooth::emboss::ConnectionRole::PERIPHERAL,
415 "tried to send connection parameter update request as central");
416
417 BT_ASSERT(peer_.is_alive());
418 // Ensure interrogation has completed.
419 BT_ASSERT(peer_->le()->features().has_value());
420
421 // TODO(fxbug.dev/42126713): check local controller support for LL Connection
422 // Parameters Request procedure (mask is currently in Adapter le state,
423 // consider propagating down)
424 bool ll_connection_parameters_req_supported =
425 peer_->le()->features()->le_features &
426 static_cast<uint64_t>(
427 hci_spec::LESupportedFeature::kConnectionParametersRequestProcedure);
428
429 bt_log(TRACE,
430 "gap-le",
431 "ll connection parameters req procedure supported: %s",
432 ll_connection_parameters_req_supported ? "true" : "false");
433
434 if (ll_connection_parameters_req_supported) {
435 auto self = weak_self_.GetWeakPtr();
436 auto status_cb = [self, params](hci::Result<> status) {
437 if (!self.is_alive()) {
438 return;
439 }
440
441 self->HandleRequestConnectionParameterUpdateCommandStatus(params, status);
442 };
443
444 UpdateConnectionParams(params, std::move(status_cb));
445 } else {
446 L2capRequestConnectionParameterUpdate(params);
447 }
448 }
449
HandleRequestConnectionParameterUpdateCommandStatus(hci_spec::LEPreferredConnectionParameters params,hci::Result<> status)450 void LowEnergyConnection::HandleRequestConnectionParameterUpdateCommandStatus(
451 hci_spec::LEPreferredConnectionParameters params, hci::Result<> status) {
452 // The next LE Connection Update complete event is for this command iff the
453 // command |status| is success.
454 if (status.is_error()) {
455 if (status ==
456 ToResult(
457 pw::bluetooth::emboss::StatusCode::UNSUPPORTED_REMOTE_FEATURE)) {
458 // Retry connection parameter update with l2cap if the peer doesn't
459 // support LL procedure.
460 bt_log(INFO,
461 "gap-le",
462 "peer does not support HCI LE Connection Update command, trying "
463 "l2cap request (peer: %s)",
464 bt_str(peer_id()));
465 L2capRequestConnectionParameterUpdate(params);
466 }
467 return;
468 }
469
470 // Note that this callback is for the Connection Update Complete event, not
471 // the Connection Update status event, which is handled by the above code (see
472 // v5.2, Vol. 4, Part E 7.7.15 / 7.7.65.3).
473 le_conn_update_complete_command_callback_ =
474 [this, params](pw::bluetooth::emboss::StatusCode status) {
475 // Retry connection parameter update with l2cap if the peer doesn't
476 // support LL procedure.
477 if (status ==
478 pw::bluetooth::emboss::StatusCode::UNSUPPORTED_REMOTE_FEATURE) {
479 bt_log(INFO,
480 "gap-le",
481 "peer does not support HCI LE Connection Update command, "
482 "trying l2cap request "
483 "(peer: %s)",
484 bt_str(peer_id()));
485 L2capRequestConnectionParameterUpdate(params);
486 }
487 };
488 }
489
L2capRequestConnectionParameterUpdate(const hci_spec::LEPreferredConnectionParameters & params)490 void LowEnergyConnection::L2capRequestConnectionParameterUpdate(
491 const hci_spec::LEPreferredConnectionParameters& params) {
492 BT_ASSERT_MSG(
493 link_->role() == pw::bluetooth::emboss::ConnectionRole::PERIPHERAL,
494 "tried to send l2cap connection parameter update request as central");
495
496 bt_log(DEBUG,
497 "gap-le",
498 "sending l2cap connection parameter update request (peer: %s)",
499 bt_str(peer_id()));
500
501 auto response_cb = [handle = handle(), peer_id = peer_id()](bool accepted) {
502 if (accepted) {
503 bt_log(DEBUG,
504 "gap-le",
505 "peer accepted l2cap connection parameter update request (peer: "
506 "%s, handle: %#.4x)",
507 bt_str(peer_id),
508 handle);
509 } else {
510 bt_log(INFO,
511 "gap-le",
512 "peer rejected l2cap connection parameter update request (peer: "
513 "%s, handle: %#.4x)",
514 bt_str(peer_id),
515 handle);
516 }
517 };
518
519 // TODO(fxbug.dev/42126716): don't send request until after
520 // kLEConnectionParameterTimeout of an l2cap conn parameter update response
521 // being received (Core Spec v5.2, Vol 3, Part C, Sec 9.3.9).
522 l2cap_->RequestConnectionParameterUpdate(
523 handle(), params, std::move(response_cb));
524 }
525
UpdateConnectionParams(const hci_spec::LEPreferredConnectionParameters & params,StatusCallback status_cb)526 void LowEnergyConnection::UpdateConnectionParams(
527 const hci_spec::LEPreferredConnectionParameters& params,
528 StatusCallback status_cb) {
529 bt_log(DEBUG,
530 "gap-le",
531 "updating connection parameters (peer: %s)",
532 bt_str(peer_id()));
533 auto command = hci::EmbossCommandPacket::New<
534 pw::bluetooth::emboss::LEConnectionUpdateCommandWriter>(
535 hci_spec::kLEConnectionUpdate);
536 auto view = command.view_t();
537 view.connection_handle().Write(handle());
538 // TODO(fxbug.dev/42074287): Handle invalid connection parameters before
539 // sending them to the controller.
540 view.connection_interval_min().UncheckedWrite(params.min_interval());
541 view.connection_interval_max().UncheckedWrite(params.max_interval());
542 view.max_latency().UncheckedWrite(params.max_latency());
543 view.supervision_timeout().UncheckedWrite(params.supervision_timeout());
544 view.min_connection_event_length().Write(0x0000);
545 view.max_connection_event_length().Write(0x0000);
546
547 auto status_cb_wrapper = [handle = handle(), cb = std::move(status_cb)](
548 auto id, const hci::EventPacket& event) mutable {
549 BT_ASSERT(event.event_code() == hci_spec::kCommandStatusEventCode);
550 hci_is_error(event,
551 TRACE,
552 "gap-le",
553 "controller rejected connection parameters (handle: %#.4x)",
554 handle);
555 if (cb) {
556 cb(event.ToResult());
557 }
558 };
559
560 cmd_->SendCommand(std::move(command),
561 std::move(status_cb_wrapper),
562 hci_spec::kCommandStatusEventCode);
563 }
564
OnLEConnectionUpdateComplete(const hci::EmbossEventPacket & event)565 void LowEnergyConnection::OnLEConnectionUpdateComplete(
566 const hci::EmbossEventPacket& event) {
567 BT_ASSERT(event.event_code() == hci_spec::kLEMetaEventCode);
568 auto view = event.view<pw::bluetooth::emboss::LEMetaEventView>();
569 BT_ASSERT(view.subevent_code().Read() ==
570 hci_spec::kLEConnectionUpdateCompleteSubeventCode);
571
572 auto payload = event.view<
573 pw::bluetooth::emboss::LEConnectionUpdateCompleteSubeventView>();
574 hci_spec::ConnectionHandle handle = payload.connection_handle().Read();
575
576 // Ignore events for other connections.
577 if (handle != link_->handle()) {
578 return;
579 }
580
581 // This event may be the result of the LE Connection Update command.
582 if (le_conn_update_complete_command_callback_) {
583 le_conn_update_complete_command_callback_(payload.status().Read());
584 }
585
586 if (payload.status().Read() != pw::bluetooth::emboss::StatusCode::SUCCESS) {
587 bt_log(WARN,
588 "gap-le",
589 "HCI LE Connection Update Complete event with error "
590 "(peer: %s, status: %#.2hhx, handle: %#.4x)",
591 bt_str(peer_id()),
592 static_cast<unsigned char>(payload.status().Read()),
593 handle);
594
595 return;
596 }
597
598 bt_log(
599 INFO, "gap-le", "conn. parameters updated (peer: %s)", bt_str(peer_id()));
600
601 hci_spec::LEConnectionParameters params(
602 payload.connection_interval().UncheckedRead(),
603 payload.peripheral_latency().UncheckedRead(),
604 payload.supervision_timeout().UncheckedRead());
605 link_->set_low_energy_parameters(params);
606
607 BT_ASSERT(peer_.is_alive());
608 peer_->MutLe().SetConnectionParameters(params);
609 }
610
MaybeUpdateConnectionParameters()611 void LowEnergyConnection::MaybeUpdateConnectionParameters() {
612 if (connection_parameters_update_requested_ || conn_pause_central_timeout_ ||
613 conn_pause_peripheral_timeout_ || !interrogation_completed_) {
614 return;
615 }
616
617 connection_parameters_update_requested_ = true;
618
619 if (link_->role() == pw::bluetooth::emboss::ConnectionRole::CENTRAL) {
620 // If the GAP service preferred connection parameters characteristic has not
621 // been read by now, just use the default parameters.
622 // TODO(fxbug.dev/42144795): Wait for preferred connection parameters to be
623 // read.
624 BT_ASSERT(peer_.is_alive());
625 auto conn_params = peer_->le()->preferred_connection_parameters().value_or(
626 kDefaultPreferredConnectionParameters);
627 UpdateConnectionParams(conn_params);
628 } else {
629 RequestConnectionParameterUpdate(kDefaultPreferredConnectionParameters);
630 }
631 }
632
InitializeGatt(l2cap::Channel::WeakPtr att_channel,std::optional<UUID> service_uuid)633 bool LowEnergyConnection::InitializeGatt(l2cap::Channel::WeakPtr att_channel,
634 std::optional<UUID> service_uuid) {
635 att_bearer_ = att::Bearer::Create(std::move(att_channel), dispatcher_);
636 if (!att_bearer_) {
637 // This can happen if the link closes before the Bearer activates the
638 // channel.
639 bt_log(WARN, "gatt", "failed to initialize ATT bearer");
640 return false;
641 }
642
643 // The att::Bearer object is owned by LowEnergyConnection, so it outlives the
644 // gatt::Server and Client objects. As such, they can safely take WeakPtrs to
645 // the Bearer.
646 auto server_factory =
647 [att_bearer = att_bearer_->GetWeakPtr()](
648 PeerId peer_id,
649 gatt::LocalServiceManager::WeakPtr local_services) mutable {
650 return gatt::Server::Create(
651 peer_id, std::move(local_services), std::move(att_bearer));
652 };
653 std::unique_ptr<gatt::Client> gatt_client =
654 gatt::Client::Create(att_bearer_->GetWeakPtr());
655 gatt_->AddConnection(
656 peer_id(), std::move(gatt_client), std::move(server_factory));
657
658 std::vector<UUID> service_uuids;
659 if (service_uuid) {
660 // TODO(fxbug.dev/42144310): De-duplicate services.
661 service_uuids = {*service_uuid, kGenericAccessService};
662 }
663 gatt_->InitializeClient(peer_id(), std::move(service_uuids));
664
665 auto self = weak_self_.GetWeakPtr();
666 gatt_->ListServices(
667 peer_id(), {kGenericAccessService}, [self](auto status, auto services) {
668 if (self.is_alive()) {
669 self->OnGattServicesResult(status, std::move(services));
670 }
671 });
672
673 return true;
674 }
675
OnGattServicesResult(att::Result<> status,gatt::ServiceList services)676 void LowEnergyConnection::OnGattServicesResult(att::Result<> status,
677 gatt::ServiceList services) {
678 if (bt_is_error(status,
679 INFO,
680 "gap-le",
681 "error discovering GAP service (peer: %s)",
682 bt_str(peer_id()))) {
683 return;
684 }
685
686 if (services.empty()) {
687 // The GAP service is mandatory for both central and peripheral, so this is
688 // unexpected.
689 bt_log(
690 INFO, "gap-le", "GAP service not found (peer: %s)", bt_str(peer_id()));
691 return;
692 }
693
694 gap_service_client_.emplace(peer_id(), services.front());
695 auto self = weak_self_.GetWeakPtr();
696
697 gap_service_client_->ReadDeviceName([self](att::Result<std::string> result) {
698 if (!self.is_alive() || result.is_error()) {
699 return;
700 }
701
702 self->peer_->RegisterName(result.value(),
703 Peer::NameSource::kGenericAccessService);
704 });
705
706 gap_service_client_->ReadAppearance([self](att::Result<uint16_t> result) {
707 if (!self.is_alive() || result.is_error()) {
708 return;
709 }
710
711 self->peer_->SetAppearance(result.value());
712 });
713
714 if (!peer_->le()->preferred_connection_parameters().has_value()) {
715 gap_service_client_->ReadPeripheralPreferredConnectionParameters(
716 [self](att::Result<hci_spec::LEPreferredConnectionParameters> result) {
717 if (!self.is_alive()) {
718 return;
719 }
720
721 if (result.is_error()) {
722 bt_log(INFO,
723 "gap-le",
724 "error reading peripheral preferred connection parameters "
725 "(status: %s, peer: %s)",
726 ::bt::internal::ToString(result).c_str(),
727 bt_str(self->peer_id()));
728 return;
729 }
730
731 auto params = result.value();
732 self->peer_->MutLe().SetPreferredConnectionParameters(params);
733 });
734 }
735 }
736
CloseRefs()737 void LowEnergyConnection::CloseRefs() {
738 for (auto* ref : *refs_.Mutable()) {
739 ref->MarkClosed();
740 }
741
742 refs_.Mutable()->clear();
743 }
744
OnNewPairingData(const sm::PairingData & pairing_data)745 void LowEnergyConnection::OnNewPairingData(
746 const sm::PairingData& pairing_data) {
747 const std::optional<sm::LTK> ltk =
748 pairing_data.peer_ltk ? pairing_data.peer_ltk : pairing_data.local_ltk;
749 // Consider the pairing temporary if no link key was received. This
750 // means we'll remain encrypted with the STK without creating a bond and
751 // reinitiate pairing when we reconnect in the future.
752 if (!ltk.has_value()) {
753 bt_log(INFO,
754 "gap-le",
755 "temporarily paired with peer (peer: %s)",
756 bt_str(peer_id()));
757 return;
758 }
759
760 bt_log(INFO,
761 "gap-le",
762 "new %s pairing data: [%s%s%s%s%s%s] (peer: %s)",
763 ltk->security().secure_connections() ? "secure connections" : "legacy",
764 pairing_data.peer_ltk ? "peer_ltk " : "",
765 pairing_data.local_ltk ? "local_ltk " : "",
766 pairing_data.irk ? "irk " : "",
767 pairing_data.cross_transport_key ? "ct_key " : "",
768 pairing_data.identity_address
769 ? bt_lib_cpp_string::StringPrintf(
770 "(identity: %s) ", bt_str(*pairing_data.identity_address))
771 .c_str()
772 : "",
773 pairing_data.csrk ? "csrk " : "",
774 bt_str(peer_id()));
775
776 if (!peer_->MutLe().StoreBond(pairing_data)) {
777 bt_log(ERROR,
778 "gap-le",
779 "failed to cache bonding data (id: %s)",
780 bt_str(peer_id()));
781 }
782 }
783
OnPairingComplete(sm::Result<> status)784 void LowEnergyConnection::OnPairingComplete(sm::Result<> status) {
785 bt_log(INFO,
786 "gap-le",
787 "pairing complete (status: %s, peer: %s)",
788 bt_str(status),
789 bt_str(peer_id()));
790
791 auto delegate = conn_mgr_->pairing_delegate();
792 if (delegate.is_alive()) {
793 delegate->CompletePairing(peer_id(), status);
794 }
795 }
796
OnAuthenticationFailure(hci::Result<> status)797 void LowEnergyConnection::OnAuthenticationFailure(hci::Result<> status) {
798 // TODO(armansito): Clear bonding data from the remote peer cache as any
799 // stored link key is not valid.
800 bt_log(WARN,
801 "gap-le",
802 "link layer authentication failed (status: %s, peer: %s)",
803 bt_str(status),
804 bt_str(peer_id()));
805 }
806
OnNewSecurityProperties(const sm::SecurityProperties & sec)807 void LowEnergyConnection::OnNewSecurityProperties(
808 const sm::SecurityProperties& sec) {
809 bt_log(INFO,
810 "gap-le",
811 "new link security properties (properties: %s, peer: %s)",
812 bt_str(sec),
813 bt_str(peer_id()));
814 // Update the data plane with the correct link security level.
815 l2cap_->AssignLinkSecurityProperties(link_->handle(), sec);
816 }
817
818 std::optional<sm::IdentityInfo>
OnIdentityInformationRequest()819 LowEnergyConnection::OnIdentityInformationRequest() {
820 if (!conn_mgr_->local_address_delegate()->irk()) {
821 bt_log(TRACE, "gap-le", "no local identity information to exchange");
822 return std::nullopt;
823 }
824
825 bt_log(DEBUG,
826 "gap-le",
827 "will distribute local identity information (peer: %s)",
828 bt_str(peer_id()));
829 sm::IdentityInfo id_info;
830 id_info.irk = *conn_mgr_->local_address_delegate()->irk();
831 id_info.address = conn_mgr_->local_address_delegate()->identity_address();
832
833 return id_info;
834 }
835
ConfirmPairing(ConfirmCallback confirm)836 void LowEnergyConnection::ConfirmPairing(ConfirmCallback confirm) {
837 bt_log(INFO,
838 "gap-le",
839 "pairing delegate request for pairing confirmation w/ no passkey "
840 "(peer: %s)",
841 bt_str(peer_id()));
842
843 auto delegate = conn_mgr_->pairing_delegate();
844 if (!delegate.is_alive()) {
845 bt_log(ERROR,
846 "gap-le",
847 "rejecting pairing without a PairingDelegate! (peer: %s)",
848 bt_str(peer_id()));
849 confirm(false);
850 } else {
851 delegate->ConfirmPairing(peer_id(), std::move(confirm));
852 }
853 }
854
DisplayPasskey(uint32_t passkey,sm::Delegate::DisplayMethod method,ConfirmCallback confirm)855 void LowEnergyConnection::DisplayPasskey(uint32_t passkey,
856 sm::Delegate::DisplayMethod method,
857 ConfirmCallback confirm) {
858 bt_log(INFO,
859 "gap-le",
860 "pairing delegate request (method: %s, peer: %s)",
861 sm::util::DisplayMethodToString(method).c_str(),
862 bt_str(peer_id()));
863
864 auto delegate = conn_mgr_->pairing_delegate();
865 if (!delegate.is_alive()) {
866 bt_log(ERROR, "gap-le", "rejecting pairing without a PairingDelegate!");
867 confirm(false);
868 } else {
869 delegate->DisplayPasskey(peer_id(), passkey, method, std::move(confirm));
870 }
871 }
872
RequestPasskey(PasskeyResponseCallback respond)873 void LowEnergyConnection::RequestPasskey(PasskeyResponseCallback respond) {
874 bt_log(INFO,
875 "gap-le",
876 "pairing delegate request for passkey entry (peer: %s)",
877 bt_str(peer_id()));
878
879 auto delegate = conn_mgr_->pairing_delegate();
880 if (!delegate.is_alive()) {
881 bt_log(ERROR,
882 "gap-le",
883 "rejecting pairing without a PairingDelegate! (peer: %s)",
884 bt_str(peer_id()));
885 respond(-1);
886 } else {
887 delegate->RequestPasskey(peer_id(), std::move(respond));
888 }
889 }
890
891 } // namespace bt::gap::internal
892