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