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 #pragma once
16 #include <memory>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h"
19 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
21 #include "pw_bluetooth_sapphire/internal/host/hci-spec/le_connection_parameters.h"
22 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h"
24 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h"
25 #include "pw_bluetooth_sapphire/internal/host/transport/command_channel.h"
26 #include "pw_bluetooth_sapphire/internal/host/transport/control_packets.h"
27 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
28 
29 namespace bt::hci {
30 
31 class LocalAddressDelegate;
32 
33 // A LowEnergyConnector abstracts over the HCI commands and events involved in
34 // initiating a direct link-layer connection with a peer device. This class also
35 // provides a way for a delegate to be notified when a connection is initiated
36 // by a remote.
37 //
38 // This class vends Connection objects for LE link layer connections.
39 //
40 // Instances of this class are expected to each exist as a singleton on a
41 // per-transport basis as multiple instances cannot accurately reflect the state
42 // of the controller while allowing simultaneous operations.
43 class LowEnergyConnector : public LocalAddressClient {
44  public:
45   // The IncomingConnectionDelegate defines the interface that
46   // LowEnergyConnector will use to callback on an incoming connection.
47   //
48   //  - |handle|: Data Connection Handle used for ACL and SCO logical link
49   //  connections.
50   //
51   //  - |role|: The role that this device is operating in for this connection.
52   //
53   //  - |peer_address|: The address of the remote peer.
54   //
55   //  - |conn_params|: Connection related parameters.
56   using IncomingConnectionDelegate =
57       fit::function<void(hci_spec::ConnectionHandle handle,
58                          pw::bluetooth::emboss::ConnectionRole role,
59                          const DeviceAddress& peer_address,
60                          const hci_spec::LEConnectionParameters& conn_params)>;
61 
62   // The constructor expects the following arguments:
63   //   - |hci|: The HCI transport this should operate on.
64   //
65   //   - |local_addr_delegate|: The delegate used to obtain the current public
66   //     or random device address to use in locally initiated requests.
67   //
68   //   - |dispatcher|: The dispatcher that will be used to run all
69   //     asynchronous operations. This must be bound to the thread on which the
70   //     LowEnergyConnector is created.
71   //
72   //   - |delegate|: The delegate that will be notified when a new logical link
73   //     is established due to an incoming request (remote initiated).
74   //
75   //   - |use_extended_operations|: If true, send LE Extended Create Connection
76   //     to the Controller instead of LE Create Connection.
77   LowEnergyConnector(Transport::WeakPtr hci,
78                      LocalAddressDelegate* local_addr_delegate,
79                      pw::async::Dispatcher& dispatcher,
80                      IncomingConnectionDelegate delegate,
81                      bool use_extended_operations = false);
82 
83   // Deleting an instance cancels any pending connection request.
84   ~LowEnergyConnector() override;
85 
86   // Creates a LE link layer connection to the remote device identified by
87   // |peer_address| with initial connection parameters |initial_parameters|.
88   // Returns false, if a create connection request is currently pending.
89   //
90   // If |use_accept_list| is true, then the controller filter accept list is
91   // used to determine which advertiser to connect to. Otherwise, the controller
92   // will connect to |peer_address|.
93   //
94   // |status_callback| is called asynchronously to notify the status of the
95   // operation. A valid |link| will be provided on success.
96   //
97   // |timeout_ms| specifies a time period after which the request will time out.
98   // When a request to create connection times out, |status_callback| will be
99   // called with a null |link| and a |status| with error Host::Error::kTimedOut.
100   using StatusCallback = fit::function<void(
101       Result<> status, std::unique_ptr<LowEnergyConnection> link)>;
102   bool CreateConnection(
103       bool use_accept_list,
104       const DeviceAddress& peer_address,
105       uint16_t scan_interval,
106       uint16_t scan_window,
107       const hci_spec::LEPreferredConnectionParameters& initial_parameters,
108       StatusCallback status_callback,
109       pw::chrono::SystemClock::duration timeout);
110 
111   // Cancels the currently pending connection attempt.
Cancel()112   void Cancel() { CancelInternal(false); }
113 
114   // Returns true if a connection request is currently pending.
request_pending()115   bool request_pending() const { return pending_request_.has_value(); }
116 
117   // Returns the peer address of a connection request if a connection request is
118   // currently pending.
119   std::optional<DeviceAddress> pending_peer_address() const;
120 
121   // Returns true if a connection timeout has been posted. Returns false if it
122   // was not posted or was canceled. This is intended for unit tests.
timeout_posted()123   bool timeout_posted() const { return request_timeout_task_.is_pending(); }
124 
125   // Disable central privacy and always use the local identity address as the
126   // local address when initiating connections, by-passing
127   // LocalAddressDelegate's current privacy setting. This policy allows better
128   // interoperability with peripherals that cannot resolve the local identity
129   // when a RPA is used during pairing.
130   //
131   // By default the address provided by the LocalAddressDelegate is used.
132   //
133   // TODO(fxbug.dev/42141593): Remove this temporary fix once we determine the
134   // root cause for authentication failures.
UseLocalIdentityAddress()135   void UseLocalIdentityAddress() { use_local_identity_address_ = true; }
136 
137   // LocalAddressClient override:
AllowsRandomAddressChange()138   bool AllowsRandomAddressChange() const override {
139     return !pending_request_ || !pending_request_->initiating;
140   }
141 
142  private:
143   struct PendingRequest {
144     PendingRequest() = default;
145     PendingRequest(const DeviceAddress& peer_address,
146                    StatusCallback status_callback);
147 
148     bool initiating = false;  // True if the HCI command has been sent.
149     bool canceled = false;
150     bool timed_out = false;
151     DeviceAddress local_address;
152     DeviceAddress peer_address;
153     StatusCallback status_callback;
154   };
155 
156   static CommandPacket BuildExtendedCreateConnectionPacket(
157       const DeviceAddress& local_address,
158       const DeviceAddress& peer_address,
159       const hci_spec::LEPreferredConnectionParameters& initial_params,
160       bool use_accept_list,
161       uint16_t scan_interval,
162       uint16_t scan_window);
163 
164   static CommandPacket BuildCreateConnectionPacket(
165       const DeviceAddress& local_address,
166       const DeviceAddress& peer_address,
167       const hci_spec::LEPreferredConnectionParameters& initial_params,
168       bool use_accept_list,
169       uint16_t scan_interval,
170       uint16_t scan_window);
171 
172   // Event handler for either the HCI LE Enhanced Connection Complete event or
173   // HCI LE Connection Complete Event
174   template <typename T>
175   void OnConnectionCompleteEvent(const EventPacket& event);
176 
hci()177   Transport::WeakPtr hci() const { return hci_; }
178 
179   // Called by CreateConnection() after the local device address has been
180   // obtained.
181   void CreateConnectionInternal(
182       const DeviceAddress& local_address,
183       bool use_accept_list,
184       const DeviceAddress& peer_address,
185       uint16_t scan_interval,
186       uint16_t scan_window,
187       const hci_spec::LEPreferredConnectionParameters& initial_params,
188       StatusCallback status_callback,
189       pw::chrono::SystemClock::duration timeout);
190 
191   // Called by Cancel() and by OnCreateConnectionTimeout().
192   void CancelInternal(bool timed_out = false);
193 
194   // Called when a LE Create Connection request has completed.
195   void OnCreateConnectionComplete(Result<> result,
196                                   std::unique_ptr<LowEnergyConnection> link);
197 
198   // Called when a LE Create Connection request has timed out.
199   void OnCreateConnectionTimeout();
200 
201   pw::async::Dispatcher& pw_dispatcher_;
202 
203   // The HCI transport.
204   Transport::WeakPtr hci_;
205 
206   // Used to obtain the local device address type to use during initiation.
207   LocalAddressDelegate* local_addr_delegate_;  // weak
208 
209   // The delegate that gets notified when a new link layer connection gets
210   // created.
211   IncomingConnectionDelegate delegate_;
212 
213   // The currently pending request.
214   std::optional<PendingRequest> pending_request_;
215 
216   // Task that runs when a request to create connection times out. We do not
217   // rely on CommandChannel's timer since that request completes when we receive
218   // the HCI Command Status event.
219   SmartTask request_timeout_task_{pw_dispatcher_};
220 
221   // Use the local public address if true.
222   // TODO(fxbug.dev/42141593): Remove this temporary fix once we determine the
223   // root cause for authentication failures.
224   bool use_local_identity_address_ = false;
225 
226   // send LE Extended Create Connection to the Controller instead of the legacy
227   // LE Create Connection
228   bool use_extended_operations_ = false;
229 
230   // Our event handle ID for the LE Enhanced Connection Complete event.
231   std::unordered_set<CommandChannel::EventHandlerId> event_handler_ids_;
232 
233   // Keep this as the last member to make sure that all weak pointers are
234   // invalidated before other members get destroyed.
235   WeakSelf<LowEnergyConnector> weak_self_;
236 
237   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnector);
238 };
239 
240 }  // namespace bt::hci
241