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