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