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