• 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_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