xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/hci/low_energy_connector.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/hci/low_energy_connector.h"
16 
17 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
18 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
19 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
20 #include "pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
22 
23 namespace bt::hci {
24 using hci_spec::ConnectionHandle;
25 using hci_spec::LEConnectionParameters;
26 using pw::bluetooth::emboss::ConnectionRole;
27 using pw::bluetooth::emboss::GenericEnableParam;
28 using pw::bluetooth::emboss::LEAddressType;
29 using pw::bluetooth::emboss::LEConnectionCompleteSubeventView;
30 using pw::bluetooth::emboss::LECreateConnectionCancelCommandView;
31 using pw::bluetooth::emboss::LECreateConnectionCommandWriter;
32 using pw::bluetooth::emboss::LEEnhancedConnectionCompleteSubeventV1View;
33 using pw::bluetooth::emboss::LEExtendedCreateConnectionCommandV1Writer;
34 using pw::bluetooth::emboss::LEMetaEventView;
35 using pw::bluetooth::emboss::LEOwnAddressType;
36 using pw::bluetooth::emboss::LEPeerAddressType;
37 using pw::bluetooth::emboss::StatusCode;
38 
PendingRequest(const DeviceAddress & init_peer_address,StatusCallback init_status_callback)39 LowEnergyConnector::PendingRequest::PendingRequest(
40     const DeviceAddress& init_peer_address, StatusCallback init_status_callback)
41     : peer_address(init_peer_address),
42       status_callback(std::move(init_status_callback)) {}
43 
LowEnergyConnector(Transport::WeakPtr hci,LocalAddressDelegate * local_addr_delegate,pw::async::Dispatcher & dispatcher,IncomingConnectionDelegate delegate,bool use_extended_operations)44 LowEnergyConnector::LowEnergyConnector(
45     Transport::WeakPtr hci,
46     LocalAddressDelegate* local_addr_delegate,
47     pw::async::Dispatcher& dispatcher,
48     IncomingConnectionDelegate delegate,
49     bool use_extended_operations)
50     : pw_dispatcher_(dispatcher),
51       hci_(std::move(hci)),
52       local_addr_delegate_(local_addr_delegate),
53       delegate_(std::move(delegate)),
54       use_extended_operations_(use_extended_operations),
55       weak_self_(this) {
56   request_timeout_task_.set_function(
57       [this](pw::async::Context& /*ctx*/, pw::Status status) {
58         if (status.ok()) {
59           OnCreateConnectionTimeout();
60         }
61       });
62 
63   CommandChannel::EventHandlerId id =
64       hci_->command_channel()->AddLEMetaEventHandler(
65           hci_spec::kLEConnectionCompleteSubeventCode,
66           [this](const EventPacket& event) {
67             OnConnectionCompleteEvent<LEConnectionCompleteSubeventView>(event);
68             return CommandChannel::EventCallbackResult::kContinue;
69           });
70   event_handler_ids_.insert(id);
71 
72   id = hci_->command_channel()->AddLEMetaEventHandler(
73       hci_spec::kLEEnhancedConnectionCompleteSubeventCode,
74       [this](const EventPacket& event) {
75         OnConnectionCompleteEvent<LEEnhancedConnectionCompleteSubeventV1View>(
76             event);
77         return CommandChannel::EventCallbackResult::kContinue;
78       });
79   event_handler_ids_.insert(id);
80 }
81 
~LowEnergyConnector()82 LowEnergyConnector::~LowEnergyConnector() {
83   if (request_pending()) {
84     Cancel();
85   }
86 
87   if (hci_.is_alive() && hci_->command_channel()) {
88     for (CommandChannel::EventHandlerId id : event_handler_ids_) {
89       hci_->command_channel()->RemoveEventHandler(id);
90     }
91   }
92 }
93 
CreateConnection(bool use_accept_list,const DeviceAddress & peer_address,uint16_t scan_interval,uint16_t scan_window,const hci_spec::LEPreferredConnectionParameters & initial_parameters,StatusCallback status_callback,pw::chrono::SystemClock::duration timeout)94 bool LowEnergyConnector::CreateConnection(
95     bool use_accept_list,
96     const DeviceAddress& peer_address,
97     uint16_t scan_interval,
98     uint16_t scan_window,
99     const hci_spec::LEPreferredConnectionParameters& initial_parameters,
100     StatusCallback status_callback,
101     pw::chrono::SystemClock::duration timeout) {
102   PW_DCHECK(status_callback);
103   PW_DCHECK(timeout.count() > 0);
104 
105   if (request_pending()) {
106     return false;
107   }
108 
109   PW_DCHECK(!request_timeout_task_.is_pending());
110   pending_request_ = PendingRequest(peer_address, std::move(status_callback));
111 
112   if (use_local_identity_address_) {
113     // Use the identity address if privacy override was enabled.
114     DeviceAddress address = local_addr_delegate_->identity_address();
115     CreateConnectionInternal(address,
116                              use_accept_list,
117                              peer_address,
118                              scan_interval,
119                              scan_window,
120                              initial_parameters,
121                              std::move(status_callback),
122                              timeout);
123     return true;
124   }
125 
126   local_addr_delegate_->EnsureLocalAddress(
127       /*address_type=*/std::nullopt,
128       [this,
129        use_accept_list,
130        peer_address,
131        scan_interval,
132        scan_window,
133        initial_parameters,
134        timeout,
135        callback = std::move(status_callback)](
136           fit::result<HostError, const DeviceAddress> result) mutable {
137         if (result.is_error()) {
138           callback(fit::error(result.error_value()), nullptr);
139           return;
140         }
141         CreateConnectionInternal(result.value(),
142                                  use_accept_list,
143                                  peer_address,
144                                  scan_interval,
145                                  scan_window,
146                                  initial_parameters,
147                                  std::move(callback),
148                                  timeout);
149       });
150 
151   return true;
152 }
153 
pending_peer_address() const154 std::optional<DeviceAddress> LowEnergyConnector::pending_peer_address() const {
155   if (pending_request_) {
156     return pending_request_->peer_address;
157   }
158 
159   return std::nullopt;
160 }
161 
CreateConnectionInternal(const DeviceAddress & local_address,bool use_accept_list,const DeviceAddress & peer_address,uint16_t scan_interval,uint16_t scan_window,const hci_spec::LEPreferredConnectionParameters & initial_params,StatusCallback,pw::chrono::SystemClock::duration timeout)162 void LowEnergyConnector::CreateConnectionInternal(
163     const DeviceAddress& local_address,
164     bool use_accept_list,
165     const DeviceAddress& peer_address,
166     uint16_t scan_interval,
167     uint16_t scan_window,
168     const hci_spec::LEPreferredConnectionParameters& initial_params,
169     StatusCallback,
170     pw::chrono::SystemClock::duration timeout) {
171   if (!hci_.is_alive()) {
172     return;
173   }
174 
175   // Check if the connection request was canceled via Cancel().
176   if (!pending_request_ || pending_request_->canceled) {
177     bt_log(DEBUG,
178            "hci-le",
179            "connection request was canceled while obtaining local address");
180     pending_request_.reset();
181     return;
182   }
183 
184   PW_DCHECK(!pending_request_->initiating);
185 
186   pending_request_->initiating = true;
187   pending_request_->local_address = local_address;
188 
189   // HCI Command Status Event will be sent as our completion callback.
190   auto self = weak_self_.GetWeakPtr();
191   auto complete_cb = [self, timeout](auto, const EventPacket& event) {
192     PW_DCHECK(event.event_code() == hci_spec::kCommandStatusEventCode);
193 
194     if (!self.is_alive()) {
195       return;
196     }
197 
198     Result<> result = event.ToResult();
199     if (result.is_error()) {
200       self->OnCreateConnectionComplete(result, nullptr);
201       return;
202     }
203 
204     // The request was started but has not completed; initiate the command
205     // timeout period. NOTE: The request will complete when the controller
206     // asynchronously notifies us of with a LE Connection Complete event.
207     self->request_timeout_task_.Cancel();
208     self->request_timeout_task_.PostAfter(timeout);
209   };
210 
211   std::optional<CommandPacket> request;
212   if (use_extended_operations_) {
213     request.emplace(BuildExtendedCreateConnectionPacket(local_address,
214                                                         peer_address,
215                                                         initial_params,
216                                                         use_accept_list,
217                                                         scan_interval,
218                                                         scan_window));
219   } else {
220     request.emplace(BuildCreateConnectionPacket(local_address,
221                                                 peer_address,
222                                                 initial_params,
223                                                 use_accept_list,
224                                                 scan_interval,
225                                                 scan_window));
226   }
227 
228   hci_->command_channel()->SendCommand(std::move(request.value()),
229                                        complete_cb,
230                                        hci_spec::kCommandStatusEventCode);
231 }
232 
BuildExtendedCreateConnectionPacket(const DeviceAddress & local_address,const DeviceAddress & peer_address,const hci_spec::LEPreferredConnectionParameters & initial_params,bool use_accept_list,uint16_t scan_interval,uint16_t scan_window)233 CommandPacket LowEnergyConnector::BuildExtendedCreateConnectionPacket(
234     const DeviceAddress& local_address,
235     const DeviceAddress& peer_address,
236     const hci_spec::LEPreferredConnectionParameters& initial_params,
237     bool use_accept_list,
238     uint16_t scan_interval,
239     uint16_t scan_window) {
240   // The LE Extended Create Connection Command ends with a variable amount of
241   // data: per PHY connection settings. Depending on the PHYs we select to scan
242   // on when connecting, the variable amount of data at the end of the packet
243   // grows. Currently, we scan on all available PHYs. Thus, we use the maximum
244   // size of this packet.
245   size_t max_size = pw::bluetooth::emboss::LEExtendedCreateConnectionCommandV1::
246       MaxSizeInBytes();
247 
248   auto packet = CommandPacket::New<LEExtendedCreateConnectionCommandV1Writer>(
249       hci_spec::kLEExtendedCreateConnection, max_size);
250   auto params = packet.view_t();
251 
252   if (use_accept_list) {
253     params.initiator_filter_policy().Write(GenericEnableParam::ENABLE);
254   } else {
255     params.initiator_filter_policy().Write(GenericEnableParam::DISABLE);
256   }
257 
258   // TODO(b/328311582): Use the resolved address types for <5.0 LE
259   // Privacy.
260   if (peer_address.IsPublic()) {
261     params.peer_address_type().Write(LEPeerAddressType::PUBLIC);
262   } else {
263     params.peer_address_type().Write(LEPeerAddressType::RANDOM);
264   }
265 
266   if (local_address.IsPublic()) {
267     params.own_address_type().Write(LEOwnAddressType::PUBLIC);
268   } else {
269     params.own_address_type().Write(LEOwnAddressType::RANDOM);
270   }
271 
272   params.peer_address().CopyFrom(peer_address.value().view());
273 
274   // We scan on all available PHYs for a connection
275   params.initiating_phys().le_1m().Write(true);
276   params.initiating_phys().le_2m().Write(true);
277   params.initiating_phys().le_coded().Write(true);
278 
279   for (int i = 0; i < params.num_entries().Read(); i++) {
280     params.data()[i].scan_interval().Write(scan_interval);
281     params.data()[i].scan_window().Write(scan_window);
282     params.data()[i].connection_interval_min().Write(
283         initial_params.min_interval());
284     params.data()[i].connection_interval_max().Write(
285         initial_params.max_interval());
286     params.data()[i].max_latency().Write(initial_params.max_latency());
287     params.data()[i].supervision_timeout().Write(
288         initial_params.supervision_timeout());
289 
290     // These fields provide the expected minimum and maximum duration of
291     // connection events. We have no preference, so we set them to zero and let
292     // the Controller decide. Some Controllers require
293     // max_ce_length < max_connection_interval * 2.
294     params.data()[i].min_connection_event_length().Write(0x0000);
295     params.data()[i].max_connection_event_length().Write(0x0000);
296   }
297 
298   return packet;
299 }
300 
BuildCreateConnectionPacket(const DeviceAddress & local_address,const DeviceAddress & peer_address,const hci_spec::LEPreferredConnectionParameters & initial_params,bool use_accept_list,uint16_t scan_interval,uint16_t scan_window)301 CommandPacket LowEnergyConnector::BuildCreateConnectionPacket(
302     const DeviceAddress& local_address,
303     const DeviceAddress& peer_address,
304     const hci_spec::LEPreferredConnectionParameters& initial_params,
305     bool use_accept_list,
306     uint16_t scan_interval,
307     uint16_t scan_window) {
308   auto packet = CommandPacket::New<LECreateConnectionCommandWriter>(
309       hci_spec::kLECreateConnection);
310   auto params = packet.view_t();
311 
312   if (use_accept_list) {
313     params.initiator_filter_policy().Write(GenericEnableParam::ENABLE);
314   } else {
315     params.initiator_filter_policy().Write(GenericEnableParam::DISABLE);
316   }
317 
318   // TODO(b/328311582): Use the resolved address types for <5.0 LE
319   // Privacy.
320   if (peer_address.IsPublic()) {
321     params.peer_address_type().Write(LEAddressType::PUBLIC);
322   } else {
323     params.peer_address_type().Write(LEAddressType::RANDOM);
324   }
325 
326   if (local_address.IsPublic()) {
327     params.own_address_type().Write(LEOwnAddressType::PUBLIC);
328   } else {
329     params.own_address_type().Write(LEOwnAddressType::RANDOM);
330   }
331 
332   params.le_scan_interval().Write(scan_interval);
333   params.le_scan_window().Write(scan_window);
334   params.peer_address().CopyFrom(peer_address.value().view());
335   params.connection_interval_min().Write(initial_params.min_interval());
336   params.connection_interval_max().Write(initial_params.max_interval());
337   params.max_latency().Write(initial_params.max_latency());
338   params.supervision_timeout().Write(initial_params.supervision_timeout());
339 
340   // These fields provide the expected minimum and maximum duration of
341   // connection events. We have no preference, so we set them to zero and let
342   // the Controller decide. Some Controllers require
343   // max_ce_length < max_connection_interval * 2.
344   params.min_connection_event_length().Write(0x0000);
345   params.max_connection_event_length().Write(0x0000);
346 
347   return packet;
348 }
349 
CancelInternal(bool timed_out)350 void LowEnergyConnector::CancelInternal(bool timed_out) {
351   PW_DCHECK(request_pending());
352 
353   if (pending_request_->canceled) {
354     bt_log(WARN, "hci-le", "connection attempt already canceled!");
355     return;
356   }
357 
358   // At this point we do not know whether the pending connection request has
359   // completed or not (it may have completed in the controller but that does not
360   // mean that we have processed the corresponding LE Connection Complete
361   // event). Below we mark the request as canceled and tell the controller to
362   // cancel its pending connection attempt.
363   pending_request_->canceled = true;
364   pending_request_->timed_out = timed_out;
365 
366   request_timeout_task_.Cancel();
367 
368   // Tell the controller to cancel the connection initiation attempt if a
369   // request is outstanding. Otherwise there is no need to talk to the
370   // controller.
371   if (pending_request_->initiating && hci_.is_alive()) {
372     bt_log(
373         DEBUG, "hci-le", "telling controller to cancel LE connection attempt");
374     auto complete_cb = [](auto, const EventPacket& event) {
375       HCI_IS_ERROR(
376           event, WARN, "hci-le", "failed to cancel connection request");
377     };
378     auto cancel = CommandPacket::New<LECreateConnectionCancelCommandView>(
379         hci_spec::kLECreateConnectionCancel);
380     hci_->command_channel()->SendCommand(std::move(cancel), complete_cb);
381 
382     // A connection complete event will be generated by the controller after
383     // processing the cancel command.
384     return;
385   }
386 
387   bt_log(DEBUG, "hci-le", "connection initiation aborted");
388   OnCreateConnectionComplete(ToResult(HostError::kCanceled), nullptr);
389 }
390 
391 template <typename T>
OnConnectionCompleteEvent(const EventPacket & event)392 void LowEnergyConnector::OnConnectionCompleteEvent(const EventPacket& event) {
393   auto params = event.view<T>();
394 
395   DeviceAddress::Type address_type =
396       DeviceAddress::LeAddrToDeviceAddr(params.peer_address_type().Read());
397   DeviceAddressBytes address_bytes = DeviceAddressBytes(params.peer_address());
398   DeviceAddress peer_address = DeviceAddress(address_type, address_bytes);
399 
400   // First check if this event is related to the currently pending request.
401   const bool matches_pending_request =
402       pending_request_ &&
403       (pending_request_->peer_address.value() == peer_address.value());
404 
405   if (Result<> result = event.ToResult(); result.is_error()) {
406     if (!matches_pending_request) {
407       bt_log(WARN,
408              "hci-le",
409              "unexpected connection complete event with error received: %s",
410              bt_str(result));
411       return;
412     }
413 
414     // The "Unknown Connect Identifier" error code is returned if this event
415     // was sent due to a successful cancelation via the
416     // HCI_LE_Create_Connection_Cancel command (sent by Cancel()).
417     if (pending_request_->timed_out) {
418       result = ToResult(HostError::kTimedOut);
419     } else if (params.status().Read() == StatusCode::UNKNOWN_CONNECTION_ID) {
420       result = ToResult(HostError::kCanceled);
421     }
422 
423     OnCreateConnectionComplete(result, nullptr);
424     return;
425   }
426 
427   ConnectionRole role = params.role().Read();
428   ConnectionHandle handle = params.connection_handle().Read();
429   LEConnectionParameters connection_params =
430       LEConnectionParameters(params.connection_interval().Read(),
431                              params.peripheral_latency().Read(),
432                              params.supervision_timeout().Read());
433 
434   // If the connection did not match a pending request then we pass the
435   // information down to the incoming connection delegate.
436   if (!matches_pending_request) {
437     delegate_(handle, role, peer_address, connection_params);
438     return;
439   }
440 
441   // A new link layer connection was created. Create an object to track this
442   // connection. Destroying this object will disconnect the link.
443   auto connection =
444       std::make_unique<LowEnergyConnection>(handle,
445                                             pending_request_->local_address,
446                                             peer_address,
447                                             connection_params,
448                                             role,
449                                             hci_);
450 
451   Result<> result = fit::ok();
452   if (pending_request_->timed_out) {
453     result = ToResult(HostError::kTimedOut);
454   } else if (pending_request_->canceled) {
455     result = ToResult(HostError::kCanceled);
456   }
457 
458   // If we were requested to cancel the connection after the logical link
459   // is created we disconnect it.
460   if (result.is_error()) {
461     connection = nullptr;
462   }
463 
464   OnCreateConnectionComplete(result, std::move(connection));
465 }
466 
OnCreateConnectionComplete(Result<> result,std::unique_ptr<LowEnergyConnection> link)467 void LowEnergyConnector::OnCreateConnectionComplete(
468     Result<> result, std::unique_ptr<LowEnergyConnection> link) {
469   PW_DCHECK(pending_request_);
470   bt_log(DEBUG, "hci-le", "connection complete - status: %s", bt_str(result));
471 
472   request_timeout_task_.Cancel();
473 
474   auto status_cb = std::move(pending_request_->status_callback);
475   pending_request_.reset();
476 
477   status_cb(result, std::move(link));
478 }
479 
OnCreateConnectionTimeout()480 void LowEnergyConnector::OnCreateConnectionTimeout() {
481   PW_DCHECK(pending_request_);
482   bt_log(INFO, "hci-le", "create connection timed out: canceling request");
483 
484   // TODO(armansito): This should cancel the connection attempt only if the
485   // connection attempt isn't using the filter accept list.
486   CancelInternal(true);
487 }
488 }  // namespace bt::hci
489