xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gap/low_energy_connection_manager.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_manager.h"
16 
17 #include <cpp-string/string_printf.h>
18 #include <pw_preprocessor/compiler.h>
19 
20 #include <optional>
21 #include <vector>
22 
23 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
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   auto [conn_iter, _] = remote_connectors_.emplace(
441       peer_id, RequestAndConnector{std::move(request), std::move(connector)});
442   // Wait until the connector is in the map to start in case the result callback
443   // is called synchronously.
444   auto result_cb =
445       std::bind(&LowEnergyConnectionManager::OnRemoteInitiatedConnectResult,
446                 this,
447                 peer_id,
448                 std::placeholders::_1);
449   conn_iter->second.connector->StartInbound(std::move(link),
450                                             std::move(result_cb));
451 }
452 
SetPairingDelegate(const PairingDelegate::WeakPtr & delegate)453 void LowEnergyConnectionManager::SetPairingDelegate(
454     const PairingDelegate::WeakPtr& delegate) {
455   // TODO(armansito): Add a test case for this once fxbug.dev/42169848 is done.
456   pairing_delegate_ = delegate;
457 
458   // Tell existing connections to abort ongoing pairing procedures. The new
459   // delegate will receive calls to PairingDelegate::CompletePairing, unless it
460   // is null.
461   for (auto& iter : connections_) {
462     iter.second->ResetSecurityManager(delegate.is_alive()
463                                           ? delegate->io_capability()
464                                           : sm::IOCapability::kNoInputNoOutput);
465   }
466 }
467 
OpenL2capChannel(PeerId peer_id,l2cap::Psm psm,l2cap::ChannelParameters params,sm::SecurityLevel security_level,l2cap::ChannelCallback cb)468 void LowEnergyConnectionManager::OpenL2capChannel(
469     PeerId peer_id,
470     l2cap::Psm psm,
471     l2cap::ChannelParameters params,
472     sm::SecurityLevel security_level,
473     l2cap::ChannelCallback cb) {
474   auto connection_iterator = connections_.find(peer_id);
475   if (connection_iterator == connections_.end()) {
476     bt_log(INFO,
477            "gap-le",
478            "can't open l2cap channel: connection not found (peer: %s)",
479            bt_str(peer_id));
480     cb(l2cap::Channel::WeakPtr());
481     return;
482   }
483 
484   auto& connection = connection_iterator->second;
485   PW_DCHECK(connection);
486 
487   auto pairing_cb = [connection_weak = connection->GetWeakPtr(),
488                      open_l2cap_cb = std::move(cb),
489                      peer_id,
490                      psm,
491                      params](sm::Result<> result) mutable {
492     if (!connection_weak.is_alive()) {
493       bt_log(INFO,
494              "gap-le",
495              "can't open l2cap channel: connection destroyed before pairing "
496              "completed (peer: %s)",
497              bt_str(peer_id));
498       open_l2cap_cb(l2cap::Channel::WeakPtr());
499       return;
500     }
501 
502     if (result.is_error()) {
503       bt_log(
504           WARN,
505           "gap-le",
506           "can't open l2cap channel: pairing failed with error: %s (peer: %s)",
507           bt_str(result.error_value()),
508           bt_str(peer_id));
509       open_l2cap_cb(l2cap::Channel::WeakPtr());
510       return;
511     }
512 
513     connection_weak->OpenL2capChannel(psm, params, std::move(open_l2cap_cb));
514   };
515 
516   connection->UpgradeSecurity(
517       security_level, connection->bondable_mode(), std::move(pairing_cb));
518 }
519 
SetDisconnectCallbackForTesting(DisconnectCallback callback)520 void LowEnergyConnectionManager::SetDisconnectCallbackForTesting(
521     DisconnectCallback callback) {
522   test_disconn_cb_ = std::move(callback);
523 }
524 
ReleaseReference(LowEnergyConnectionHandle * handle)525 void LowEnergyConnectionManager::ReleaseReference(
526     LowEnergyConnectionHandle* handle) {
527   PW_CHECK(handle);
528 
529   auto iter = connections_.find(handle->peer_identifier());
530   PW_CHECK(iter != connections_.end());
531 
532   iter->second->DropRef(handle);
533   if (iter->second->ref_count() != 0u)
534     return;
535 
536   // Move the connection object before erasing the entry.
537   auto conn = std::move(iter->second);
538   connections_.erase(iter);
539 
540   bt_log(INFO,
541          "gap-le",
542          "all refs dropped on connection (link: %s, peer: %s)",
543          bt_str(*conn->link()),
544          bt_str(conn->peer_id()));
545   inspect_properties_.disconnect_zero_ref_count_.Add(1);
546   CleanUpConnection(std::move(conn));
547 }
548 
TryCreateNextConnection()549 void LowEnergyConnectionManager::TryCreateNextConnection() {
550   if (current_request_.has_value()) {
551     bt_log(DEBUG, "gap-le", "%s: request already in progress", __FUNCTION__);
552     return;
553   }
554 
555   if (pending_requests_.empty()) {
556     bt_log(TRACE, "gap-le", "%s: no pending requests remaining", __FUNCTION__);
557     return;
558   }
559 
560   for (auto& iter : pending_requests_) {
561     auto peer_id = iter.first;
562     Peer* peer = peer_cache_->FindById(peer_id);
563     if (peer) {
564       auto request_pair = pending_requests_.extract(peer_id);
565       internal::LowEnergyConnectionRequest request =
566           std::move(request_pair.mapped());
567 
568       std::unique_ptr<internal::LowEnergyConnector> connector =
569           std::make_unique<internal::LowEnergyConnector>(
570               peer_id,
571               request.connection_options(),
572               hci_,
573               peer_cache_,
574               weak_self_.GetWeakPtr(),
575               l2cap_,
576               gatt_,
577               adapter_state_,
578               dispatcher_);
579       connector->AttachInspect(inspect_node_,
580                                kInspectOutboundConnectorNodeName);
581 
582       current_request_ =
583           RequestAndConnector{std::move(request), std::move(connector)};
584       // Wait until the connector is in current_request_ to start in case the
585       // result callback is called synchronously.
586       current_request_->connector->StartOutbound(
587           request_timeout_,
588           hci_connector_,
589           discovery_manager_,
590           fit::bind_member<
591               &LowEnergyConnectionManager::OnLocalInitiatedConnectResult>(
592               this));
593       return;
594     }
595 
596     bt_log(WARN,
597            "gap-le",
598            "deferring connection attempt (peer: %s)",
599            bt_str(peer_id));
600 
601     // TODO(fxbug.dev/42172291): For now the requests for this peer won't
602     // complete until the next peer discovery. This will no longer be an issue
603     // when we use background scanning.
604   }
605 }
606 
OnLocalInitiatedConnectResult(hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result)607 void LowEnergyConnectionManager::OnLocalInitiatedConnectResult(
608     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result) {
609   PW_CHECK(current_request_.has_value());
610 
611   internal::LowEnergyConnectionRequest request =
612       std::move(current_request_->request);
613   current_request_.reset();
614 
615   if (result.is_error()) {
616     inspect_properties_.outgoing_connection_failure_count_.Add(1);
617     bt_log(INFO,
618            "gap-le",
619            "failed to connect to peer (peer: %s, status: %s)",
620            bt_str(request.peer_id()),
621            bt_str(result));
622   } else {
623     inspect_properties_.outgoing_connection_success_count_.Add(1);
624     bt_log(INFO,
625            "gap-le",
626            "connection request successful (peer: %s)",
627            bt_str(request.peer_id()));
628   }
629 
630   ProcessConnectResult(std::move(result), std::move(request));
631   TryCreateNextConnection();
632 }
633 
OnRemoteInitiatedConnectResult(PeerId peer_id,hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result)634 void LowEnergyConnectionManager::OnRemoteInitiatedConnectResult(
635     PeerId peer_id,
636     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result) {
637   auto remote_connector_node = remote_connectors_.extract(peer_id);
638   PW_CHECK(!remote_connector_node.empty());
639 
640   internal::LowEnergyConnectionRequest request =
641       std::move(remote_connector_node.mapped().request);
642 
643   if (result.is_error()) {
644     inspect_properties_.incoming_connection_failure_count_.Add(1);
645     bt_log(INFO,
646            "gap-le",
647            "failed to complete remote initated connection with peer (peer: %s, "
648            "status: %s)",
649            bt_str(peer_id),
650            bt_str(result));
651   } else {
652     inspect_properties_.incoming_connection_success_count_.Add(1);
653     bt_log(INFO,
654            "gap-le",
655            "remote initiated connection successful (peer: %s)",
656            bt_str(peer_id));
657   }
658 
659   ProcessConnectResult(std::move(result), std::move(request));
660 }
661 
ProcessConnectResult(hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result,internal::LowEnergyConnectionRequest request)662 void LowEnergyConnectionManager::ProcessConnectResult(
663     hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result,
664     internal::LowEnergyConnectionRequest request) {
665   PeerId peer_id = request.peer_id();
666   if (result.is_error()) {
667     const hci::Error err = result.error_value();
668     Peer* const peer = peer_cache_->FindById(peer_id);
669     // Peer may have been forgotten (causing this error).
670     // A separate connection may have been established in the other direction
671     // while this connection was connecting, in which case the peer state should
672     // not be updated.
673     if (peer && connections_.find(peer->identifier()) == connections_.end()) {
674       if (request.connection_options().auto_connect &&
675           err.is_protocol_error() &&
676           ShouldStopAlwaysAutoConnecting(err.protocol_error())) {
677         // We may see a peer's connectable advertisements, but fail to establish
678         // a connection to the peer (e.g. due to asymmetrical radio TX power).
679         // Unsetting the AutoConnect flag here prevents a loop of "see peer
680         // device, attempt auto-connect, fail to establish connection".
681         peer->MutLe().set_auto_connect_behavior(
682             Peer::AutoConnectBehavior::kSkipUntilNextConnection);
683       }
684     }
685 
686     const HostError host_error =
687         err.is_host_error() ? err.host_error() : HostError::kFailed;
688     request.NotifyCallbacks(fit::error(host_error));
689 
690     inspect_properties_.recent_connection_failures.Add(1);
691 
692     return;
693   }
694 
695   InitializeConnection(std::move(result).value(), std::move(request));
696 }
697 
InitializeConnection(std::unique_ptr<internal::LowEnergyConnection> connection,internal::LowEnergyConnectionRequest request)698 bool LowEnergyConnectionManager::InitializeConnection(
699     std::unique_ptr<internal::LowEnergyConnection> connection,
700     internal::LowEnergyConnectionRequest request) {
701   PW_CHECK(connection);
702 
703   auto peer_id = connection->peer_id();
704 
705   // TODO(fxbug.dev/42143994): For now reject having more than one link with the
706   // same peer. This should change once this has more context on the local
707   // destination for remote initiated connections.
708   if (connections_.find(peer_id) != connections_.end()) {
709     bt_log(INFO,
710            "gap-le",
711            "cannot initialize multiple links to same peer; connection refused "
712            "(peer: %s)",
713            bt_str(peer_id));
714     // Notify request that duplicate connection could not be initialized.
715     request.NotifyCallbacks(fit::error(HostError::kFailed));
716     // Do not update peer state, as there is another active LE connection in
717     // connections_ for this peer.
718     return false;
719   }
720 
721   Peer* peer = peer_cache_->FindById(peer_id);
722   PW_CHECK(peer);
723 
724   connection->AttachInspect(
725       inspect_connections_node_,
726       inspect_connections_node_.UniqueName(kInspectConnectionNodePrefix));
727   connection->set_peer_disconnect_callback(
728       std::bind(&LowEnergyConnectionManager::OnPeerDisconnect,
729                 this,
730                 connection->link(),
731                 std::placeholders::_1));
732   connection->set_error_callback([this, peer_id]() {
733     Disconnect(peer_id, LowEnergyDisconnectReason::kError);
734   });
735 
736   auto [conn_iter, inserted] =
737       connections_.try_emplace(peer_id, std::move(connection));
738   PW_CHECK(inserted);
739 
740   conn_iter->second->set_peer_conn_token(peer->MutLe().RegisterConnection());
741 
742   // Create first ref to ensure that connection is cleaned up on early returns
743   // or if first request callback does not retain a ref.
744   auto first_ref = conn_iter->second->AddRef();
745 
746   UpdatePeerWithLink(*conn_iter->second->link());
747 
748   bt_log(TRACE,
749          "gap-le",
750          "notifying connection request callbacks (peer: %s)",
751          bt_str(peer_id));
752 
753   request.NotifyCallbacks(fit::ok(std::bind(
754       &internal::LowEnergyConnection::AddRef, conn_iter->second.get())));
755 
756   return true;
757 }
758 
CleanUpConnection(std::unique_ptr<internal::LowEnergyConnection> conn)759 void LowEnergyConnectionManager::CleanUpConnection(
760     std::unique_ptr<internal::LowEnergyConnection> conn) {
761   PW_CHECK(conn);
762 
763   // Mark the peer peer as no longer connected.
764   Peer* peer = peer_cache_->FindById(conn->peer_id());
765   PW_CHECK(peer,
766            "A connection was active for an unknown peer! (id: %s)",
767            bt_str(conn->peer_id()));
768   conn.reset();
769 }
770 
UpdatePeerWithLink(const hci::LowEnergyConnection & link)771 Peer* LowEnergyConnectionManager::UpdatePeerWithLink(
772     const hci::LowEnergyConnection& link) {
773   Peer* peer = peer_cache_->FindByAddress(link.peer_address());
774   if (!peer) {
775     peer = peer_cache_->NewPeer(link.peer_address(), /*connectable=*/true);
776   }
777   peer->MutLe().SetConnectionParameters(link.low_energy_parameters());
778   peer_cache_->SetAutoConnectBehaviorForSuccessfulConnection(
779       peer->identifier());
780 
781   return peer;
782 }
783 
OnPeerDisconnect(const hci::Connection * connection,pw::bluetooth::emboss::StatusCode)784 void LowEnergyConnectionManager::OnPeerDisconnect(
785     const hci::Connection* connection, pw::bluetooth::emboss::StatusCode) {
786   auto handle = connection->handle();
787   if (test_disconn_cb_) {
788     test_disconn_cb_(handle);
789   }
790 
791   // See if we can find a connection with a matching handle by walking the
792   // connections list.
793   auto iter = FindConnection(handle);
794   if (iter == connections_.end()) {
795     bt_log(WARN,
796            "gap-le",
797            "disconnect from unknown connection handle: %#.4x",
798            handle);
799     return;
800   }
801 
802   // Found the connection. Remove the entry from |connections_| before notifying
803   // the "closed" handlers.
804   auto conn = std::move(iter->second);
805   connections_.erase(iter);
806 
807   bt_log(INFO,
808          "gap-le",
809          "peer disconnected (peer: %s, handle: %#.4x)",
810          bt_str(conn->peer_id()),
811          handle);
812 
813   inspect_properties_.disconnect_remote_disconnection_count_.Add(1);
814 
815   CleanUpConnection(std::move(conn));
816 }
817 
818 LowEnergyConnectionManager::ConnectionMap::iterator
FindConnection(hci_spec::ConnectionHandle handle)819 LowEnergyConnectionManager::FindConnection(hci_spec::ConnectionHandle handle) {
820   auto iter = connections_.begin();
821   for (; iter != connections_.end(); ++iter) {
822     const auto& conn = *iter->second;
823     if (conn.handle() == handle)
824       break;
825   }
826   return iter;
827 }
828 
829 }  // namespace bt::gap
830