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