• 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_manager.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <pw_assert/check.h>
19 #include <pw_preprocessor/compiler.h>
20 
21 #include <optional>
22 #include <vector>
23 
24 #include "pw_bluetooth_sapphire/internal/host/gap/gap.h"
25 #include "pw_bluetooth_sapphire/internal/host/gap/generic_access_client.h"
26 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection.h"
27 #include "pw_bluetooth_sapphire/internal/host/gap/pairing_delegate.h"
28 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
29 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h"
30 #include "pw_bluetooth_sapphire/internal/host/gatt/local_service_manager.h"
31 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
32 #include "pw_bluetooth_sapphire/internal/host/hci-spec/defaults.h"
33 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
34 #include "pw_bluetooth_sapphire/internal/host/hci-spec/util.h"
35 #include "pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h"
36 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h"
37 #include "pw_bluetooth_sapphire/internal/host/sm/error.h"
38 #include "pw_bluetooth_sapphire/internal/host/sm/security_manager.h"
39 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
40 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
41 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
42 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
43 
44 using bt::sm::BondableMode;
45 
46 namespace bt::gap {
47 
48 namespace {
49 
50 // If an auto-connect attempt fails with any of the following error codes, we
51 // will stop auto- connecting to the peer until the next successful connection.
52 // We have only observed this issue with the 0x3e
53 // "kConnectionFailedToBeEstablished" error in the field, but have included
54 // these other errors based on their descriptions in v5.2 Vol. 1 Part F
55 // Section 2.
ShouldStopAlwaysAutoConnecting(pw::bluetooth::emboss::StatusCode err)56 bool ShouldStopAlwaysAutoConnecting(pw::bluetooth::emboss::StatusCode err) {
57   PW_MODIFY_DIAGNOSTICS_PUSH();
58   PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
59   switch (err) {
60     case pw::bluetooth::emboss::StatusCode::CONNECTION_TIMEOUT:
61     case pw::bluetooth::emboss::StatusCode::CONNECTION_REJECTED_SECURITY:
62     case pw::bluetooth::emboss::StatusCode::CONNECTION_ACCEPT_TIMEOUT_EXCEEDED:
63     case pw::bluetooth::emboss::StatusCode::CONNECTION_TERMINATED_BY_LOCAL_HOST:
64     case pw::bluetooth::emboss::StatusCode::CONNECTION_FAILED_TO_BE_ESTABLISHED:
65       return true;
66     default:
67       return false;
68   }
69   PW_MODIFY_DIAGNOSTICS_POP();
70 }
71 
72 // During the initial connection to a peripheral we use the initial high
73 // duty-cycle parameters to ensure that initiating procedures (bonding,
74 // encryption setup, service discovery) are completed quickly. Once these
75 // procedures are complete, we will change the connection interval to the
76 // peripheral's preferred connection parameters (see v5.0, Vol 3, Part C,
77 // Section 9.3.12).
78 static const hci_spec::LEPreferredConnectionParameters
79     kInitialConnectionParameters(kLEInitialConnIntervalMin,
80                                  kLEInitialConnIntervalMax,
81                                  /*max_latency=*/0,
82                                  hci_spec::defaults::kLESupervisionTimeout);
83 
84 const char* kInspectRequestsNodeName = "pending_requests";
85 const char* kInspectRequestNodeNamePrefix = "pending_request_";
86 const char* kInspectConnectionsNodeName = "connections";
87 const char* kInspectConnectionNodePrefix = "connection_";
88 const char* kInspectOutboundConnectorNodeName = "outbound_connector";
89 const char* kInspectConnectionFailuresPropertyName =
90     "recent_connection_failures";
91 
92 const char* kInspectOutgoingSuccessCountNodeName =
93     "outgoing_connection_success_count";
94 const char* kInspectOutgoingFailureCountNodeName =
95     "outgoing_connection_failure_count";
96 const char* kInspectIncomingSuccessCountNodeName =
97     "incoming_connection_success_count";
98 const char* kInspectIncomingFailureCountNodeName =
99     "incoming_connection_failure_count";
100 
101 const char* kInspectDisconnectExplicitDisconnectNodeName =
102     "disconnect_explicit_disconnect_count";
103 const char* kInspectDisconnectLinkErrorNodeName = "disconnect_link_error_count";
104 const char* kInspectDisconnectZeroRefNodeName = "disconnect_zero_ref_count";
105 const char* kInspectDisconnectRemoteDisconnectionNodeName =
106     "disconnect_remote_disconnection_count";
107 
108 }  // namespace
109 
LowEnergyConnectionManager(hci::Transport::WeakPtr hci,hci::LocalAddressDelegate * addr_delegate,hci::LowEnergyConnector * connector,PeerCache * peer_cache,l2cap::ChannelManager * l2cap,gatt::GATT::WeakPtr gatt,LowEnergyDiscoveryManager::WeakPtr discovery_manager,sm::SecurityManagerFactory sm_creator,const AdapterState & adapter_state,pw::async::Dispatcher & dispatcher)110 LowEnergyConnectionManager::LowEnergyConnectionManager(
111     hci::Transport::WeakPtr hci,
112     hci::LocalAddressDelegate* addr_delegate,
113     hci::LowEnergyConnector* connector,
114     PeerCache* peer_cache,
115     l2cap::ChannelManager* l2cap,
116     gatt::GATT::WeakPtr gatt,
117     LowEnergyDiscoveryManager::WeakPtr discovery_manager,
118     sm::SecurityManagerFactory sm_creator,
119     const AdapterState& adapter_state,
120     pw::async::Dispatcher& dispatcher)
121     : dispatcher_(dispatcher),
122       hci_(std::move(hci)),
123       security_mode_(LESecurityMode::Mode1),
124       sm_factory_func_(std::move(sm_creator)),
125       request_timeout_(kLECreateConnectionTimeout),
126       peer_cache_(peer_cache),
127       l2cap_(l2cap),
128       gatt_(gatt),
129       adapter_state_(adapter_state),
130       discovery_manager_(discovery_manager),
131       hci_connector_(connector),
132       local_address_delegate_(addr_delegate),
133       weak_self_(this) {
134   PW_DCHECK(peer_cache_);
135   PW_DCHECK(l2cap_);
136   PW_DCHECK(gatt_.is_alive());
137   PW_DCHECK(hci_.is_alive());
138   PW_DCHECK(hci_connector_);
139   PW_DCHECK(local_address_delegate_);
140 }
141 
~LowEnergyConnectionManager()142 LowEnergyConnectionManager::~LowEnergyConnectionManager() {
143   bt_log(INFO, "gap-le", "LowEnergyConnectionManager shutting down");
144 
145   weak_self_.InvalidatePtrs();
146 
147   // Clear |pending_requests_| and notify failure.
148   for (auto& iter : pending_requests_) {
149     iter.second.NotifyCallbacks(fit::error(HostError::kFailed));
150   }
151   pending_requests_.clear();
152 
153   current_request_.reset();
154 
155   remote_connectors_.clear();
156 
157   // Clean up all connections.
158   for (auto& iter : connections_) {
159     CleanUpConnection(std::move(iter.second));
160   }
161 
162   connections_.clear();
163 }
164 
Connect(PeerId peer_id,ConnectionResultCallback callback,LowEnergyConnectionOptions connection_options)165 void LowEnergyConnectionManager::Connect(
166     PeerId peer_id,
167     ConnectionResultCallback callback,
168     LowEnergyConnectionOptions connection_options) {
169   Peer* peer = peer_cache_->FindById(peer_id);
170   if (!peer) {
171     bt_log(WARN, "gap-le", "peer not found (id: %s)", bt_str(peer_id));
172     callback(fit::error(HostError::kNotFound));
173     return;
174   }
175 
176   if (peer->technology() == TechnologyType::kClassic) {
177     bt_log(ERROR,
178            "gap-le",
179            "peer does not support LE: %s",
180            peer->ToString().c_str());
181     callback(fit::error(HostError::kNotFound));
182     return;
183   }
184 
185   if (!peer->connectable()) {
186     bt_log(
187         ERROR, "gap-le", "peer not connectable: %s", peer->ToString().c_str());
188     callback(fit::error(HostError::kNotFound));
189     return;
190   }
191 
192   // If we are already waiting to connect to |peer_id| then we store
193   // |callback| to be processed after the connection attempt completes (in
194   // either success of failure).
195   auto pending_iter = pending_requests_.find(peer_id);
196   if (pending_iter != pending_requests_.end()) {
197     if (!current_request_) {
198       bt_log(WARN,
199              "gap-le",
200              "Connect called for peer with pending request while no "
201              "current_request_ exists (peer: "
202              "%s)",
203              bt_str(peer_id));
204     }
205     // TODO(fxbug.dev/42144310): Merge connection_options with the options of
206     // the pending request.
207     pending_iter->second.AddCallback(std::move(callback));
208     // TODO(fxbug.dev/42148775): Try to create this connection.
209     return;
210   }
211 
212   // Add callback to connecting request if |peer_id| matches.
213   if (current_request_ && current_request_->request.peer_id() == peer_id) {
214     // TODO(fxbug.dev/42144310): Merge connection_options with the options of
215     // the current request.
216     current_request_->request.AddCallback(std::move(callback));
217     return;
218   }
219 
220   auto conn_iter = connections_.find(peer_id);
221   if (conn_iter != connections_.end()) {
222     // TODO(fxbug.dev/42144310): Handle connection_options that conflict with
223     // the existing connection.
224     callback(fit::ok(conn_iter->second->AddRef()));
225     return;
226   }
227 
228   internal::LowEnergyConnectionRequest request(
229       peer_id,
230       std::move(callback),
231       connection_options,
232       peer->MutLe().RegisterInitializingConnection());
233   request.AttachInspect(
234       inspect_pending_requests_node_,
235       inspect_pending_requests_node_.UniqueName(kInspectRequestNodeNamePrefix));
236   pending_requests_.emplace(peer_id, std::move(request));
237 
238   TryCreateNextConnection();
239 }
240 
Disconnect(PeerId peer_id,LowEnergyDisconnectReason reason)241 bool LowEnergyConnectionManager::Disconnect(PeerId peer_id,
242                                             LowEnergyDisconnectReason reason) {
243   auto remote_connector_iter = remote_connectors_.find(peer_id);
244   if (remote_connector_iter != remote_connectors_.end()) {
245     // Result callback will clean up connector.
246     remote_connector_iter->second.connector->Cancel();
247   }
248 
249   auto request_iter = pending_requests_.find(peer_id);
250   if (request_iter != pending_requests_.end()) {
251     PW_CHECK(current_request_->request.peer_id() != peer_id);
252     request_iter->second.NotifyCallbacks(fit::error(HostError::kCanceled));
253     pending_requests_.erase(request_iter);
254   }
255 
256   if (current_request_ && current_request_->request.peer_id() == peer_id) {
257     // Connector will call result callback to clean up connection.
258     current_request_->connector->Cancel();
259   }
260 
261   // Ignore Disconnect for peer that is not pending or connected:
262   auto iter = connections_.find(peer_id);
263   if (iter == connections_.end()) {
264     bt_log(INFO,
265            "gap-le",
266            "Disconnect called for unconnected peer (peer: %s)",
267            bt_str(peer_id));
268     return true;
269   }
270 
271   // Handle peer that is already connected:
272 
273   // Remove the connection state from the internal map right away.
274   auto conn = std::move(iter->second);
275   connections_.erase(iter);
276 
277   // Since this was an intentional disconnect, update the auto-connection
278   // behavior appropriately.
279   peer_cache_->SetAutoConnectBehaviorForIntentionalDisconnect(peer_id);
280 
281   bt_log(INFO,
282          "gap-le",
283          "disconnecting (peer: %s, link: %s)",
284          bt_str(conn->peer_id()),
285          bt_str(*conn->link()));
286 
287   if (reason == LowEnergyDisconnectReason::kApiRequest) {
288     inspect_properties_.disconnect_explicit_disconnect_count_.Add(1);
289   } else {
290     inspect_properties_.disconnect_link_error_count_.Add(1);
291   }
292 
293   CleanUpConnection(std::move(conn));
294   return true;
295 }
296 
Pair(PeerId peer_id,sm::SecurityLevel pairing_level,sm::BondableMode bondable_mode,sm::ResultFunction<> cb)297 void LowEnergyConnectionManager::Pair(PeerId peer_id,
298                                       sm::SecurityLevel pairing_level,
299                                       sm::BondableMode bondable_mode,
300                                       sm::ResultFunction<> cb) {
301   auto iter = connections_.find(peer_id);
302   if (iter == connections_.end()) {
303     bt_log(WARN,
304            "gap-le",
305            "cannot pair: peer not connected (peer: %s)",
306            bt_str(peer_id));
307     cb(bt::ToResult(bt::HostError::kNotFound));
308     return;
309   }
310   bt_log(INFO,
311          "gap-le",
312          "pairing with security level: %d (peer: %s)",
313          static_cast<int>(pairing_level),
314          bt_str(peer_id));
315   iter->second->UpgradeSecurity(pairing_level, bondable_mode, std::move(cb));
316 }
317 
SetSecurityMode(LESecurityMode mode)318 void LowEnergyConnectionManager::SetSecurityMode(LESecurityMode mode) {
319   security_mode_ = mode;
320   if (mode == LESecurityMode::SecureConnectionsOnly) {
321     // `Disconnect`ing the peer must not be done while iterating through
322     // `connections_` as it removes the connection from `connections_`, hence
323     // the helper vector.
324     std::vector<PeerId> insufficiently_secure_peers;
325     for (auto& [peer_id, connection] : connections_) {
326       if (connection->security().level() !=
327               sm::SecurityLevel::kSecureAuthenticated &&
328           connection->security().level() != sm::SecurityLevel::kNoSecurity) {
329         insufficiently_secure_peers.push_back(peer_id);
330       }
331     }
332     for (PeerId id : insufficiently_secure_peers) {
333       Disconnect(id);
334     }
335   }
336   for (auto& iter : connections_) {
337     iter.second->set_security_mode(mode);
338   }
339 }
340 
AttachInspect(inspect::Node & parent,std::string name)341 void LowEnergyConnectionManager::AttachInspect(inspect::Node& parent,
342                                                std::string name) {
343   inspect_node_ = parent.CreateChild(name);
344   inspect_properties_.recent_connection_failures.AttachInspect(
345       inspect_node_, kInspectConnectionFailuresPropertyName);
346   inspect_pending_requests_node_ =
347       inspect_node_.CreateChild(kInspectRequestsNodeName);
348   inspect_connections_node_ =
349       inspect_node_.CreateChild(kInspectConnectionsNodeName);
350   for (auto& request : pending_requests_) {
351     request.second.AttachInspect(inspect_pending_requests_node_,
352                                  inspect_pending_requests_node_.UniqueName(
353                                      kInspectRequestNodeNamePrefix));
354   }
355   for (auto& conn : connections_) {
356     conn.second->AttachInspect(
357         inspect_connections_node_,
358         inspect_connections_node_.UniqueName(kInspectConnectionNodePrefix));
359   }
360   if (current_request_) {
361     current_request_->connector->AttachInspect(
362         inspect_node_, kInspectOutboundConnectorNodeName);
363   }
364 
365   inspect_properties_.outgoing_connection_success_count_.AttachInspect(
366       inspect_node_, kInspectOutgoingSuccessCountNodeName);
367   inspect_properties_.outgoing_connection_failure_count_.AttachInspect(
368       inspect_node_, kInspectOutgoingFailureCountNodeName);
369   inspect_properties_.incoming_connection_success_count_.AttachInspect(
370       inspect_node_, kInspectIncomingSuccessCountNodeName);
371   inspect_properties_.incoming_connection_failure_count_.AttachInspect(
372       inspect_node_, kInspectIncomingFailureCountNodeName);
373 
374   inspect_properties_.disconnect_explicit_disconnect_count_.AttachInspect(
375       inspect_node_, kInspectDisconnectExplicitDisconnectNodeName);
376   inspect_properties_.disconnect_link_error_count_.AttachInspect(
377       inspect_node_, kInspectDisconnectLinkErrorNodeName);
378   inspect_properties_.disconnect_zero_ref_count_.AttachInspect(
379       inspect_node_, kInspectDisconnectZeroRefNodeName);
380   inspect_properties_.disconnect_remote_disconnection_count_.AttachInspect(
381       inspect_node_, kInspectDisconnectRemoteDisconnectionNodeName);
382 }
383 
RegisterRemoteInitiatedLink(std::unique_ptr<hci::LowEnergyConnection> link,sm::BondableMode bondable_mode,ConnectionResultCallback callback)384 void LowEnergyConnectionManager::RegisterRemoteInitiatedLink(
385     std::unique_ptr<hci::LowEnergyConnection> link,
386     sm::BondableMode bondable_mode,
387     ConnectionResultCallback callback) {
388   PW_CHECK(link);
389 
390   Peer* peer = UpdatePeerWithLink(*link);
391   auto peer_id = peer->identifier();
392 
393   bt_log(INFO,
394          "gap-le",
395          "new remote-initiated link (peer: %s, local addr: %s, link: %s)",
396          bt_str(peer_id),
397          bt_str(link->local_address()),
398          bt_str(*link));
399 
400   // TODO(fxbug.dev/42143994): Use own address when storing the connection.
401   // Currently this will refuse the connection and disconnect the link if |peer|
402   // is already connected to us by a different local address.
403   if (connections_.find(peer_id) != connections_.end()) {
404     bt_log(INFO,
405            "gap-le",
406            "multiple links from peer; remote-initiated connection refused "
407            "(peer: %s)",
408            bt_str(peer_id));
409     callback(fit::error(HostError::kFailed));
410     return;
411   }
412 
413   if (remote_connectors_.find(peer_id) != remote_connectors_.end()) {
414     bt_log(INFO,
415            "gap-le",
416            "remote connector for peer already exists; connection refused "
417            "(peer: %s)",
418            bt_str(peer_id));
419     callback(fit::error(HostError::kFailed));
420     return;
421   }
422 
423   LowEnergyConnectionOptions connection_options{.bondable_mode = bondable_mode};
424   internal::LowEnergyConnectionRequest request(
425       peer_id,
426       std::move(callback),
427       connection_options,
428       peer->MutLe().RegisterInitializingConnection());
429 
430   std::unique_ptr<internal::LowEnergyConnector> connector =
431       std::make_unique<internal::LowEnergyConnector>(peer_id,
432                                                      connection_options,
433                                                      hci_,
434                                                      peer_cache_,
435                                                      weak_self_.GetWeakPtr(),
436                                                      l2cap_,
437                                                      gatt_,
438                                                      adapter_state_,
439                                                      dispatcher_,
440                                                      local_address_delegate_);
441   auto [conn_iter, _] = remote_connectors_.emplace(
442       peer_id, RequestAndConnector{std::move(request), std::move(connector)});
443   // Wait until the connector is in the map to start in case the result callback
444   // is called synchronously.
445   auto result_cb =
446       std::bind(&LowEnergyConnectionManager::OnRemoteInitiatedConnectResult,
447                 this,
448                 peer_id,
449                 std::placeholders::_1);
450   conn_iter->second.connector->StartInbound(std::move(link),
451                                             std::move(result_cb));
452 }
453 
SetPairingDelegate(const PairingDelegate::WeakPtr & delegate)454 void LowEnergyConnectionManager::SetPairingDelegate(
455     const PairingDelegate::WeakPtr& delegate) {
456   // TODO(armansito): Add a test case for this once fxbug.dev/42169848 is done.
457   pairing_delegate_ = delegate;
458 
459   // Tell existing connections to abort ongoing pairing procedures. The new
460   // delegate will receive calls to PairingDelegate::CompletePairing, unless it
461   // is null.
462   for (auto& iter : connections_) {
463     iter.second->ResetSecurityManager(delegate.is_alive()
464                                           ? delegate->io_capability()
465                                           : sm::IOCapability::kNoInputNoOutput);
466   }
467 }
468 
OpenL2capChannel(PeerId peer_id,l2cap::Psm psm,l2cap::ChannelParameters params,sm::SecurityLevel security_level,l2cap::ChannelCallback cb)469 void LowEnergyConnectionManager::OpenL2capChannel(
470     PeerId peer_id,
471     l2cap::Psm psm,
472     l2cap::ChannelParameters params,
473     sm::SecurityLevel security_level,
474     l2cap::ChannelCallback cb) {
475   auto connection_iterator = connections_.find(peer_id);
476   if (connection_iterator == connections_.end()) {
477     bt_log(INFO,
478            "gap-le",
479            "can't open l2cap channel: connection not found (peer: %s)",
480            bt_str(peer_id));
481     cb(l2cap::Channel::WeakPtr());
482     return;
483   }
484 
485   auto& connection = connection_iterator->second;
486   PW_DCHECK(connection);
487 
488   auto pairing_cb = [connection_weak = connection->GetWeakPtr(),
489                      open_l2cap_cb = std::move(cb),
490                      peer_id,
491                      psm,
492                      params](sm::Result<> result) mutable {
493     if (!connection_weak.is_alive()) {
494       bt_log(INFO,
495              "gap-le",
496              "can't open l2cap channel: connection destroyed before pairing "
497              "completed (peer: %s)",
498              bt_str(peer_id));
499       open_l2cap_cb(l2cap::Channel::WeakPtr());
500       return;
501     }
502 
503     if (result.is_error()) {
504       bt_log(
505           WARN,
506           "gap-le",
507           "can't open l2cap channel: pairing failed with error: %s (peer: %s)",
508           bt_str(result.error_value()),
509           bt_str(peer_id));
510       open_l2cap_cb(l2cap::Channel::WeakPtr());
511       return;
512     }
513 
514     connection_weak->OpenL2capChannel(psm, params, std::move(open_l2cap_cb));
515   };
516 
517   connection->UpgradeSecurity(
518       security_level, connection->bondable_mode(), std::move(pairing_cb));
519 }
520 
SetDisconnectCallbackForTesting(DisconnectCallback callback)521 void LowEnergyConnectionManager::SetDisconnectCallbackForTesting(
522     DisconnectCallback callback) {
523   test_disconn_cb_ = std::move(callback);
524 }
525 
ReleaseReference(LowEnergyConnectionHandle * handle)526 void LowEnergyConnectionManager::ReleaseReference(
527     LowEnergyConnectionHandle* handle) {
528   PW_CHECK(handle);
529 
530   auto iter = connections_.find(handle->peer_identifier());
531   PW_CHECK(iter != connections_.end());
532 
533   iter->second->DropRef(handle);
534   if (iter->second->ref_count() != 0u)
535     return;
536 
537   // Move the connection object before erasing the entry.
538   auto conn = std::move(iter->second);
539   connections_.erase(iter);
540 
541   bt_log(INFO,
542          "gap-le",
543          "all refs dropped on connection (link: %s, peer: %s)",
544          bt_str(*conn->link()),
545          bt_str(conn->peer_id()));
546   inspect_properties_.disconnect_zero_ref_count_.Add(1);
547   CleanUpConnection(std::move(conn));
548 }
549 
TryCreateNextConnection()550 void LowEnergyConnectionManager::TryCreateNextConnection() {
551   if (current_request_.has_value()) {
552     bt_log(DEBUG, "gap-le", "%s: request already in progress", __FUNCTION__);
553     return;
554   }
555 
556   if (pending_requests_.empty()) {
557     bt_log(TRACE, "gap-le", "%s: no pending requests remaining", __FUNCTION__);
558     return;
559   }
560 
561   for (auto& iter : pending_requests_) {
562     auto peer_id = iter.first;
563     Peer* peer = peer_cache_->FindById(peer_id);
564     if (peer) {
565       auto request_pair = pending_requests_.extract(peer_id);
566       internal::LowEnergyConnectionRequest request =
567           std::move(request_pair.mapped());
568 
569       std::unique_ptr<internal::LowEnergyConnector> connector =
570           std::make_unique<internal::LowEnergyConnector>(
571               peer_id,
572               request.connection_options(),
573               hci_,
574               peer_cache_,
575               weak_self_.GetWeakPtr(),
576               l2cap_,
577               gatt_,
578               adapter_state_,
579               dispatcher_,
580               local_address_delegate_);
581       connector->AttachInspect(inspect_node_,
582                                kInspectOutboundConnectorNodeName);
583 
584       current_request_ =
585           RequestAndConnector{std::move(request), std::move(connector)};
586       // Wait until the connector is in current_request_ to start in case the
587       // result callback is called synchronously.
588       current_request_->connector->StartOutbound(
589           request_timeout_,
590           hci_connector_,
591           discovery_manager_,
592           fit::bind_member<
593               &LowEnergyConnectionManager::OnLocalInitiatedConnectResult>(
594               this));
595       return;
596     }
597 
598     bt_log(WARN,
599            "gap-le",
600            "deferring connection attempt (peer: %s)",
601            bt_str(peer_id));
602 
603     // TODO(fxbug.dev/42172291): For now the requests for this peer won't
604     // complete until the next peer discovery. This will no longer be an issue
605     // when we use background scanning.
606   }
607 }
608 
OnLocalInitiatedConnectResult(hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result)609 void LowEnergyConnectionManager::OnLocalInitiatedConnectResult(
610     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result) {
611   PW_CHECK(current_request_.has_value());
612 
613   internal::LowEnergyConnectionRequest request =
614       std::move(current_request_->request);
615   current_request_.reset();
616 
617   if (result.is_error()) {
618     inspect_properties_.outgoing_connection_failure_count_.Add(1);
619     bt_log(INFO,
620            "gap-le",
621            "failed to connect to peer (peer: %s, status: %s)",
622            bt_str(request.peer_id()),
623            bt_str(result));
624   } else {
625     inspect_properties_.outgoing_connection_success_count_.Add(1);
626     bt_log(INFO,
627            "gap-le",
628            "connection request successful (peer: %s)",
629            bt_str(request.peer_id()));
630   }
631 
632   ProcessConnectResult(std::move(result), std::move(request));
633   TryCreateNextConnection();
634 }
635 
OnRemoteInitiatedConnectResult(PeerId peer_id,hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result)636 void LowEnergyConnectionManager::OnRemoteInitiatedConnectResult(
637     PeerId peer_id,
638     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result) {
639   auto remote_connector_node = remote_connectors_.extract(peer_id);
640   PW_CHECK(!remote_connector_node.empty());
641 
642   internal::LowEnergyConnectionRequest request =
643       std::move(remote_connector_node.mapped().request);
644 
645   if (result.is_error()) {
646     inspect_properties_.incoming_connection_failure_count_.Add(1);
647     bt_log(INFO,
648            "gap-le",
649            "failed to complete remote initated connection with peer (peer: %s, "
650            "status: %s)",
651            bt_str(peer_id),
652            bt_str(result));
653   } else {
654     inspect_properties_.incoming_connection_success_count_.Add(1);
655     bt_log(INFO,
656            "gap-le",
657            "remote initiated connection successful (peer: %s)",
658            bt_str(peer_id));
659   }
660 
661   ProcessConnectResult(std::move(result), std::move(request));
662 }
663 
ProcessConnectResult(hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result,internal::LowEnergyConnectionRequest request)664 void LowEnergyConnectionManager::ProcessConnectResult(
665     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result,
666     internal::LowEnergyConnectionRequest request) {
667   PeerId peer_id = request.peer_id();
668   if (result.is_error()) {
669     const hci::Error err = result.error_value();
670     Peer* const peer = peer_cache_->FindById(peer_id);
671     // Peer may have been forgotten (causing this error).
672     // A separate connection may have been established in the other direction
673     // while this connection was connecting, in which case the peer state should
674     // not be updated.
675     if (peer && connections_.find(peer->identifier()) == connections_.end()) {
676       if (request.connection_options().auto_connect &&
677           err.is_protocol_error() &&
678           ShouldStopAlwaysAutoConnecting(err.protocol_error())) {
679         // We may see a peer's connectable advertisements, but fail to establish
680         // a connection to the peer (e.g. due to asymmetrical radio TX power).
681         // Unsetting the AutoConnect flag here prevents a loop of "see peer
682         // device, attempt auto-connect, fail to establish connection".
683         peer->MutLe().set_auto_connect_behavior(
684             Peer::AutoConnectBehavior::kSkipUntilNextConnection);
685       }
686     }
687 
688     const HostError host_error =
689         err.is_host_error() ? err.host_error() : HostError::kFailed;
690     request.NotifyCallbacks(fit::error(host_error));
691 
692     inspect_properties_.recent_connection_failures.Add(1);
693 
694     return;
695   }
696 
697   InitializeConnection(std::move(result).value(), std::move(request));
698 }
699 
InitializeConnection(std::unique_ptr<internal::LowEnergyConnection> connection,internal::LowEnergyConnectionRequest request)700 bool LowEnergyConnectionManager::InitializeConnection(
701     std::unique_ptr<internal::LowEnergyConnection> connection,
702     internal::LowEnergyConnectionRequest request) {
703   PW_CHECK(connection);
704 
705   auto peer_id = connection->peer_id();
706 
707   // TODO(fxbug.dev/42143994): For now reject having more than one link with the
708   // same peer. This should change once this has more context on the local
709   // destination for remote initiated connections.
710   if (connections_.find(peer_id) != connections_.end()) {
711     bt_log(INFO,
712            "gap-le",
713            "cannot initialize multiple links to same peer; connection refused "
714            "(peer: %s)",
715            bt_str(peer_id));
716     // Notify request that duplicate connection could not be initialized.
717     request.NotifyCallbacks(fit::error(HostError::kFailed));
718     // Do not update peer state, as there is another active LE connection in
719     // connections_ for this peer.
720     return false;
721   }
722 
723   Peer* peer = peer_cache_->FindById(peer_id);
724   PW_CHECK(peer);
725 
726   connection->AttachInspect(
727       inspect_connections_node_,
728       inspect_connections_node_.UniqueName(kInspectConnectionNodePrefix));
729   connection->set_peer_disconnect_callback(
730       std::bind(&LowEnergyConnectionManager::OnPeerDisconnect,
731                 this,
732                 connection->link(),
733                 std::placeholders::_1));
734   connection->set_error_callback([this, peer_id]() {
735     Disconnect(peer_id, LowEnergyDisconnectReason::kError);
736   });
737 
738   auto [conn_iter, inserted] =
739       connections_.try_emplace(peer_id, std::move(connection));
740   PW_CHECK(inserted);
741 
742   conn_iter->second->set_peer_conn_token(peer->MutLe().RegisterConnection());
743 
744   // Create first ref to ensure that connection is cleaned up on early returns
745   // or if first request callback does not retain a ref.
746   auto first_ref = conn_iter->second->AddRef();
747 
748   UpdatePeerWithLink(*conn_iter->second->link());
749 
750   bt_log(TRACE,
751          "gap-le",
752          "notifying connection request callbacks (peer: %s)",
753          bt_str(peer_id));
754 
755   request.NotifyCallbacks(fit::ok(std::bind(
756       &internal::LowEnergyConnection::AddRef, conn_iter->second.get())));
757 
758   return true;
759 }
760 
CleanUpConnection(std::unique_ptr<internal::LowEnergyConnection> conn)761 void LowEnergyConnectionManager::CleanUpConnection(
762     std::unique_ptr<internal::LowEnergyConnection> conn) {
763   PW_CHECK(conn);
764 
765   // Mark the peer peer as no longer connected.
766   Peer* peer = peer_cache_->FindById(conn->peer_id());
767   PW_CHECK(peer,
768            "A connection was active for an unknown peer! (id: %s)",
769            bt_str(conn->peer_id()));
770   conn.reset();
771 }
772 
UpdatePeerWithLink(const hci::LowEnergyConnection & link)773 Peer* LowEnergyConnectionManager::UpdatePeerWithLink(
774     const hci::LowEnergyConnection& link) {
775   Peer* peer = peer_cache_->FindByAddress(link.peer_address());
776   if (!peer) {
777     peer = peer_cache_->NewPeer(link.peer_address(), /*connectable=*/true);
778   }
779   peer->MutLe().SetConnectionParameters(link.low_energy_parameters());
780   peer_cache_->SetAutoConnectBehaviorForSuccessfulConnection(
781       peer->identifier());
782 
783   return peer;
784 }
785 
OnPeerDisconnect(const hci::Connection * connection,pw::bluetooth::emboss::StatusCode)786 void LowEnergyConnectionManager::OnPeerDisconnect(
787     const hci::Connection* connection, pw::bluetooth::emboss::StatusCode) {
788   auto handle = connection->handle();
789   if (test_disconn_cb_) {
790     test_disconn_cb_(handle);
791   }
792 
793   // See if we can find a connection with a matching handle by walking the
794   // connections list.
795   auto iter = FindConnection(handle);
796   if (iter == connections_.end()) {
797     bt_log(WARN,
798            "gap-le",
799            "disconnect from unknown connection handle: %#.4x",
800            handle);
801     return;
802   }
803 
804   // Found the connection. Remove the entry from |connections_| before notifying
805   // the "closed" handlers.
806   auto conn = std::move(iter->second);
807   connections_.erase(iter);
808 
809   bt_log(INFO,
810          "gap-le",
811          "peer disconnected (peer: %s, handle: %#.4x)",
812          bt_str(conn->peer_id()),
813          handle);
814 
815   inspect_properties_.disconnect_remote_disconnection_count_.Add(1);
816 
817   CleanUpConnection(std::move(conn));
818 }
819 
820 LowEnergyConnectionManager::ConnectionMap::iterator
FindConnection(hci_spec::ConnectionHandle handle)821 LowEnergyConnectionManager::FindConnection(hci_spec::ConnectionHandle handle) {
822   auto iter = connections_.begin();
823   for (; iter != connections_.end(); ++iter) {
824     const auto& conn = *iter->second;
825     if (conn.handle() == handle)
826       break;
827   }
828   return iter;
829 }
830 
831 }  // namespace bt::gap
832