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 17 #include <lib/fit/function.h> 18 #include <pw_status/status.h> 19 20 #include <memory> 21 #include <string> 22 23 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h" 24 #include "pw_bluetooth_sapphire/internal/host/common/identifier.h" 25 #include "pw_bluetooth_sapphire/internal/host/common/inspect.h" 26 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 27 #include "pw_bluetooth_sapphire/internal/host/gap/adapter_state.h" 28 #include "pw_bluetooth_sapphire/internal/host/gap/bonding_data.h" 29 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_connection_manager.h" 30 #include "pw_bluetooth_sapphire/internal/host/gap/bredr_discovery_manager.h" 31 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_advertising_manager.h" 32 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h" 33 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_discovery_manager.h" 34 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h" 35 #include "pw_bluetooth_sapphire/internal/host/gap/types.h" 36 #include "pw_bluetooth_sapphire/internal/host/gatt/gatt.h" 37 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h" 38 #include "pw_bluetooth_sapphire/internal/host/sdp/server.h" 39 #include "pw_bluetooth_sapphire/internal/host/sdp/service_discoverer.h" 40 41 namespace bt { 42 43 namespace hci { 44 class LowEnergyAdvertiser; 45 class LowEnergyConnector; 46 class LowEnergyScanner; 47 class SequentialCommandRunner; 48 class Transport; 49 } // namespace hci 50 51 namespace gap { 52 53 class BrEdrConnectionManager; 54 class BrEdrDiscoveryManager; 55 class PairingDelegate; 56 class LowEnergyAddressManager; 57 58 // TODO(fxbug.dev/42082764): Consider removing this identifier from the bt-host 59 // layer. 60 class AdapterId : public Identifier<uint64_t> { 61 public: AdapterId(uint64_t value)62 constexpr explicit AdapterId(uint64_t value) : Identifier<uint64_t>(value) {} 63 AdapterId() = default; 64 }; 65 66 // Represents the host-subsystem state for a Bluetooth controller. 67 // 68 // This class is not guaranteed to be thread-safe and it is intended to be 69 // created, deleted, and accessed on the same event loop. No internal locking is 70 // provided. 71 // 72 // NOTE: We currently only support primary controllers. AMP controllers are not 73 // supported. 74 class Adapter { 75 public: 76 struct Config { 77 // When True, BR/EDR pairing may attempt to use legacy pairing if the peer 78 // does not support SSP. 79 bool legacy_pairing_enabled = false; 80 }; 81 82 static constexpr const char* kMetricsInspectNodeName = "metrics"; 83 84 // Optionally, a FakeL2cap may be passed for testing purposes as |l2cap|. If 85 // nullptr is passed, then the Adapter will create and initialize its own 86 // L2cap. 87 static std::unique_ptr<Adapter> Create( 88 pw::async::Dispatcher& pw_dispatcher, 89 hci::Transport::WeakPtr hci, 90 gatt::GATT::WeakPtr gatt, 91 Config config, 92 std::unique_ptr<l2cap::ChannelManager> l2cap = nullptr); 93 virtual ~Adapter() = default; 94 95 // Returns a uniquely identifier for this adapter on the current system. 96 virtual AdapterId identifier() const = 0; 97 98 // Initializes the host-subsystem state for the HCI device this was created 99 // for. This performs the initial HCI transport set up. Returns false if an 100 // immediate error occurs. Otherwise this returns true and asynchronously 101 // notifies the caller on the initialization status via |callback|. 102 // 103 // After successful initialization, |transport_closed_callback| will be 104 // invoked when the underlying HCI transport closed for any reason (e.g. the 105 // device disappeared or the transport channels were closed for an unknown 106 // reason). The implementation is responsible for cleaning up this adapter by 107 // calling ShutDown(). 108 using InitializeCallback = fit::callback<void(bool success)>; 109 virtual bool Initialize(InitializeCallback callback, 110 fit::closure transport_error_callback) = 0; 111 112 // Shuts down this Adapter. Invokes |callback| when shut down has completed. 113 // TODO(armansito): This needs to do several things to potentially preserve 114 // the state of various sub-protocols. For now we keep the interface pretty 115 // simple. 116 virtual void ShutDown() = 0; 117 118 // Returns true if the Initialize() sequence has started but not completed yet 119 // (i.e. the InitializeCallback that was passed to Initialize() has not yet 120 // been called). 121 virtual bool IsInitializing() const = 0; 122 123 // Returns true if this Adapter has been fully initialized. 124 virtual bool IsInitialized() const = 0; 125 126 // Returns the global adapter setting parameters. 127 virtual const AdapterState& state() const = 0; 128 129 // Interface to the LE features of the adapter. 130 class LowEnergy { 131 public: 132 virtual ~LowEnergy() = default; 133 134 // Allows a caller to claim shared ownership over a connection to the 135 // requested remote LE peer identified by |peer_id|. 136 // 137 // * If the requested peer is already connected, |callback| is called with 138 // a 139 // LowEnergyConnectionHandle. 140 // 141 // * If the requested peer is NOT connected, then this method initiates a 142 // connection to the requested peer. A LowEnergyConnectionHandle is 143 // asynchronously returned to the caller once the connection has been 144 // set up. 145 // 146 // The status of the procedure is reported in |callback| in the case of 147 // an error. 148 using ConnectionResult = gap::LowEnergyConnectionManager::ConnectionResult; 149 using ConnectionResultCallback = 150 gap::LowEnergyConnectionManager::ConnectionResultCallback; 151 virtual void Connect(PeerId peer_id, 152 ConnectionResultCallback callback, 153 LowEnergyConnectionOptions connection_options) = 0; 154 155 // Disconnects any existing LE connection to |peer_id|, invalidating all 156 // active LowEnergyConnectionHandles. Returns false if the peer can not be 157 // disconnected. 158 virtual bool Disconnect(PeerId peer_id) = 0; 159 160 // Opens a new L2CAP channel to service |psm| on |peer_id| using the 161 // preferred parameters |params|. 162 // 163 // |cb| will be called with the channel created to the peer, or nullptr if 164 // the channel creation resulted in an error. 165 virtual void OpenL2capChannel(PeerId peer_id, 166 l2cap::Psm psm, 167 l2cap::ChannelParameters params, 168 sm::SecurityLevel security_level, 169 l2cap::ChannelCallback cb) = 0; 170 171 // Initiates the pairing process. Expected to only be called during 172 // higher-level testing. 173 // |peer_id|: the peer to pair to - if the peer is not connected, |cb| is 174 // called with an error. |pairing_level|: determines the security level of 175 // the pairing. **Note**: If the 176 // security level of the link is already >= |pairing 177 // level|, no pairing takes place. 178 // |bondable_mode|: sets the bonding mode of this connection. A device in 179 // bondable mode forms 180 // a bond to the peer upon pairing, assuming the peer is 181 // also in bondable mode. 182 // |cb|: callback called upon completion of this function, whether pairing 183 // takes place or not. 184 virtual void Pair(PeerId peer_id, 185 sm::SecurityLevel pairing_level, 186 sm::BondableMode bondable_mode, 187 sm::ResultFunction<> cb) = 0; 188 189 // Sets the LE security mode of the local device (see v5.2 Vol. 3 Part C 190 // Section 10.2). If set to SecureConnectionsOnly, any currently encrypted 191 // links not meeting the requirements of Security Mode 1 Level 4 will be 192 // disconnected. 193 virtual void SetLESecurityMode(LESecurityMode mode) = 0; 194 195 // Returns the current LE security mode. 196 virtual LESecurityMode security_mode() const = 0; 197 198 // Asynchronously attempts to start advertising a set of |data| with 199 // additional scan response data |scan_rsp|. 200 // 201 // If |connectable| is provided, the advertisement will be connectable. 202 // The |connectable.connection_cb| will be called with the returned 203 // advertisement ID and a connection result when a peer attempts to connect 204 // to the advertisement, at which point the advertisement will have been 205 // stopped. |connectable.bondable_mode| indicates the bondable mode to 206 // initialize connections with. 207 // 208 // Returns false if the parameters represent an invalid advertisement: 209 // * if |anonymous| is true but |callback| is set 210 // 211 // |address_type| is used to determine whether to perform LE advertising 212 // using a public or random address, depending on whether privacy has been 213 // enabled or not and the value of |address_type| if given. 214 // 215 // |status_callback| may be called synchronously within this function. 216 // |status_callback| provides one of: 217 // - an |advertisement_id|, which can be used to stop advertising 218 // or disambiguate calls to |callback|, and a success |status|. 219 // - kInvalidAdvertisementId and an error indication in |status|: 220 // * HostError::kInvalidParameters if the advertising parameters 221 // are invalid (e.g. |data| is too large). 222 // * HostError::kNotSupported if another set cannot be advertised 223 // or if the requested parameters are not supported by the hardware. 224 // * HostError::kProtocolError with a HCI error reported from 225 // the controller, otherwise. 226 using ConnectionCallback = 227 fit::function<void(AdvertisementId, ConnectionResult)>; 228 using AdvertisingStatusCallback = 229 LowEnergyAdvertisingManager::AdvertisingStatusCallback; 230 struct ConnectableAdvertisingParameters { 231 ConnectionCallback connection_cb; 232 sm::BondableMode bondable_mode; 233 }; 234 virtual void StartAdvertising( 235 AdvertisingData data, 236 AdvertisingData scan_rsp, 237 AdvertisingInterval interval, 238 bool extended_pdu, 239 bool anonymous, 240 bool include_tx_power_level, 241 std::optional<ConnectableAdvertisingParameters> connectable, 242 std::optional<DeviceAddress::Type> address_type, 243 AdvertisingStatusCallback status_callback) = 0; 244 245 // Starts a new discovery session and reports the result via |callback|. If 246 // a session has been successfully started the caller will receive a new 247 // LowEnergyDiscoverySession instance via |callback| which it uniquely owns. 248 // |active| indicates whether active or passive discovery should occur. 249 // On failure a nullptr will be returned via |callback|. 250 using SessionCallback = LowEnergyDiscoveryManager::SessionCallback; 251 virtual void StartDiscovery(bool active, 252 std::vector<hci::DiscoveryFilter> filters, 253 SessionCallback callback) = 0; 254 255 // Enable or disable the privacy feature. When enabled, the controller 256 // will be configured to use a new random address if it is currently 257 // allowed to do so. 258 virtual void EnablePrivacy(bool enabled) = 0; 259 // Returns true if the privacy feature is currently enabled. 260 virtual bool PrivacyEnabled() const = 0; 261 // Returns the current LE address. 262 virtual const DeviceAddress CurrentAddress() const = 0; 263 // Register a callback to be notified any time the LE address changes. 264 virtual void register_address_changed_callback(fit::closure callback) = 0; 265 266 // Assigns the IRK to generate a RPA for the next address refresh when 267 // privacy is enabled. 268 virtual void set_irk(const std::optional<UInt128>& irk) = 0; 269 270 // Returns the currently assigned Identity Resolving Key, if any. 271 virtual std::optional<UInt128> irk() const = 0; 272 273 // Sets the timeout interval to be used on future connect requests. The 274 // default value is kLECreateConnectionTimeout. 275 virtual void set_request_timeout_for_testing( 276 pw::chrono::SystemClock::duration value) = 0; 277 278 // Sets a new scan period to any future and ongoing discovery procedures. 279 virtual void set_scan_period_for_testing( 280 pw::chrono::SystemClock::duration period) = 0; 281 }; 282 283 virtual LowEnergy* le() const = 0; 284 285 // Interface to the classic features of the adapter. 286 class BrEdr { 287 public: 288 virtual ~BrEdr() = default; 289 290 // Initiates an outgoing Create Connection Request to attempt to connect to 291 // the peer identified by |peer_id|. Returns false if the connection 292 // request was invalid, otherwise returns true and |callback| will be called 293 // with the result of the procedure, whether successful or not 294 using ConnectResultCallback = BrEdrConnectionManager::ConnectResultCallback; 295 [[nodiscard]] virtual bool Connect(PeerId peer_id, 296 ConnectResultCallback callback) = 0; 297 298 // Disconnects any existing BR/EDR connection to |peer_id|. Returns true if 299 // the peer is disconnected, false if the peer can not be disconnected. 300 virtual bool Disconnect(PeerId peer_id, DisconnectReason reason) = 0; 301 302 // Opens a new L2CAP channel to service |psm| on |peer_id| using the 303 // preferred parameters |params|. If the current connection doesn't meet 304 // |security_requirements|, attempt to upgrade the link key and report an 305 // error via |cb| if the upgrade fails. 306 // 307 // |cb| will be called with the channel created to the peer, or nullptr if 308 // the channel creation resulted in an error. 309 virtual void OpenL2capChannel( 310 PeerId peer_id, 311 l2cap::Psm psm, 312 BrEdrSecurityRequirements security_requirements, 313 l2cap::ChannelParameters params, 314 l2cap::ChannelCallback cb) = 0; 315 316 // Retrieves the peer id that is connected to the connection |handle|. 317 // Returns kInvalidPeerId if no such peer exists. 318 virtual PeerId GetPeerId(hci_spec::ConnectionHandle handle) const = 0; 319 320 // Add a service search to be performed on new connected remote peers. 321 // This search will happen on every peer connection. 322 // |callback| will be called with the |attributes| that exist in the service 323 // entry on the peer's SDP server. If |attributes| is empty, all attributes 324 // on the server will be returned. Returns a SearchId which can be used to 325 // remove the search later. Identical searches will perform the same search 326 // for each search added. Results of added service searches will be added to 327 // each Peer's BrEdrData. 328 using SearchCallback = sdp::ServiceDiscoverer::ResultCallback; 329 using SearchId = sdp::ServiceDiscoverer::SearchId; 330 virtual SearchId AddServiceSearch( 331 const UUID& uuid, 332 std::unordered_set<sdp::AttributeId> attributes, 333 SearchCallback callback) = 0; 334 335 // Remove a search previously added with AddServiceSearch() 336 // Returns true if a search was removed. 337 virtual bool RemoveServiceSearch(SearchId id) = 0; 338 339 // Initiate pairing to the peer with |peer_id| using the bondable 340 // preference. Pairing will only be initiated if the current link key does 341 // not meet the |security| requirements. |callback| will be called with the 342 // result of the procedure, successful or not. 343 virtual void Pair(PeerId peer_id, 344 BrEdrSecurityRequirements security, 345 hci::ResultFunction<> callback) = 0; 346 347 // Sets the BR/EDR security mode of the local device (Core Spec v5.4, Vol 3, 348 // Part C, 5.2.2). If set to SecureConnectionsOnly, any currently encrypted 349 // links not meeting the requirements of Security Mode 4 Level 4 will be 350 // disconnected. 351 virtual void SetBrEdrSecurityMode(BrEdrSecurityMode mode) = 0; 352 353 // Returns the current BR/EDR security mode. 354 virtual BrEdrSecurityMode security_mode() const = 0; 355 356 // Set whether this host is connectable. 357 virtual void SetConnectable(bool connectable, 358 hci::ResultFunction<> status_cb) = 0; 359 360 // Starts discovery and reports the status via |callback|. If discovery has 361 // been successfully started, the callback will receive a session object 362 // that it owns. If no sessions are owned, peer discovery is stopped. 363 using DiscoveryCallback = BrEdrDiscoveryManager::DiscoveryCallback; 364 virtual void RequestDiscovery(DiscoveryCallback callback) = 0; 365 366 // Requests this device be discoverable. We are discoverable as long as 367 // anyone holds a discoverable session. 368 using DiscoverableCallback = BrEdrDiscoveryManager::DiscoverableCallback; 369 virtual void RequestDiscoverable(DiscoverableCallback callback) = 0; 370 371 // Given incomplete ServiceRecords, register services that will be made 372 // available over SDP. Takes ownership of |records|. Channels created for 373 // this service will be configured using the preferred parameters in 374 // |chan_params|. 375 // 376 // A non-zero RegistrationHandle will be returned if the service was 377 // successfully registered. 378 // 379 // If any record in |records| fails registration checks, none of the 380 // services will be registered. 381 // 382 // |conn_cb| will be called for any connections made to any of the services 383 // in |records| with a connected channel and the descriptor list for the 384 // endpoint which was connected. 385 using ServiceConnectCallback = sdp::Server::ConnectCallback; 386 using RegistrationHandle = sdp::Server::RegistrationHandle; 387 virtual RegistrationHandle RegisterService( 388 std::vector<sdp::ServiceRecord> records, 389 l2cap::ChannelParameters chan_params, 390 ServiceConnectCallback conn_cb) = 0; 391 392 // Unregister services previously registered with RegisterService. 393 // Idempotent. Returns |true| if any records were removed. 394 virtual bool UnregisterService(RegistrationHandle handle) = 0; 395 396 // Return the set of registered services that were previously registered 397 // with RegisterService, identified by |handle|. 398 virtual std::vector<sdp::ServiceRecord> GetRegisteredServices( 399 RegistrationHandle handle) const = 0; 400 401 // Initiate an outbound connection. A request will be queued if a 402 // connection is already in progress. On error, |callback| will be called 403 // with an error result. The error will be |kCanceled| if a connection was 404 // never attempted, or |kFailed| if establishing a connection failed. 405 // Returns a handle that will cancel the request when dropped (if connection 406 // establishment has not started). If a BR/EDR connection with the peer does 407 // not exist, returns nullopt. 408 using ScoRequestHandle = BrEdrConnection::ScoRequestHandle; 409 virtual std::optional<ScoRequestHandle> OpenScoConnection( 410 PeerId peer_id, 411 const bt::StaticPacket< 412 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>& 413 parameters, 414 sco::ScoConnectionManager::OpenConnectionCallback callback) = 0; 415 416 // Accept inbound connection requests using the parameters given in order. 417 // The parameters will be tried in order until either a connection is 418 // successful, all parameters have been rejected, or the procedure is 419 // canceled. On success, |callback| will be called with the connection 420 // object and the index of the parameters used to establish the connection. 421 // On error, |callback| will be called with an error result. If another 422 // Open/Accept request is made before a connection request is received, this 423 // request will be canceled (with error |kCanceled|). Returns a handle that 424 // will cancel the request when destroyed (if connection establishment has 425 // not started). If a BR/EDR connection with the peer does not exist, 426 // returns nullopt. 427 virtual std::optional<ScoRequestHandle> AcceptScoConnection( 428 PeerId peer_id, 429 std::vector<bt::StaticPacket< 430 pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> 431 parameters, 432 sco::ScoConnectionManager::AcceptConnectionCallback callback) = 0; 433 }; 434 435 // Returns nullptr if the controller does not support classic. 436 virtual BrEdr* bredr() const = 0; 437 438 // Returns this Adapter's peer cache. 439 virtual PeerCache* peer_cache() = 0; 440 441 // Add a previously bonded device to the peer cache and set it up for 442 // auto-connect procedures. 443 virtual bool AddBondedPeer(BondingData bonding_data) = 0; 444 445 // Assigns a pairing delegate to this adapter. This PairingDelegate and its 446 // I/O capabilities will be used for all future pairing procedures. Setting a 447 // new PairingDelegate cancels all ongoing pairing procedures. 448 virtual void SetPairingDelegate(PairingDelegate::WeakPtr delegate) = 0; 449 450 // Returns true if this adapter is currently in discoverable mode on the LE or 451 // BR/EDR transports. 452 virtual bool IsDiscoverable() const = 0; 453 454 // Returns true if any discovery process (LE or BR/EDR) is running on this 455 // adapter. 456 virtual bool IsDiscovering() const = 0; 457 458 // Sets the Local Name of this adapter, for both BR/EDR discoverability and 459 // public LE services. 460 virtual void SetLocalName(std::string name, 461 hci::ResultFunction<> callback) = 0; 462 463 virtual std::string local_name() const = 0; 464 465 // Sets the Device Class of this adapter. 466 virtual void SetDeviceClass(DeviceClass dev_class, 467 hci::ResultFunction<> callback) = 0; 468 469 // If the operation is successful, specifies the minimum and maximum local 470 // delay (in microseconds) supported by the controller for the codec 471 // specified. 472 // Returns PW_STATUS_UNIMPLEMENTED if the operation is not supported on this 473 // controller. Returns PW_STATUS_UNKNOWN if the operation fails to complete 474 // successfully. 475 using GetSupportedDelayRangeCallback = fit::function<void( 476 pw::Status status, uint32_t min_delay_us, uint32_t max_delay_us)>; 477 virtual void GetSupportedDelayRange( 478 const bt::StaticPacket<pw::bluetooth::emboss::CodecIdWriter>& codec_id, 479 pw::bluetooth::emboss::LogicalTransportType logical_transport_type, 480 pw::bluetooth::emboss::DataPathDirection direction, 481 const std::optional<std::vector<uint8_t>>& codec_configuration, 482 GetSupportedDelayRangeCallback cb) = 0; 483 484 // Assign a callback to be notified when a connection is automatically 485 // established to a bonded LE peer in the directed connectable mode (Vol 3, 486 // Part C, 9.3.3). 487 using AutoConnectCallback = 488 fit::function<void(std::unique_ptr<bt::gap::LowEnergyConnectionHandle>)>; 489 virtual void set_auto_connect_callback(AutoConnectCallback callback) = 0; 490 491 // Attach Adapter's inspect node as a child node under |parent| with the given 492 // |name|. 493 virtual void AttachInspect(inspect::Node& parent, std::string name) = 0; 494 495 // Returns a weak pointer to this adapter. 496 using WeakPtr = WeakSelf<Adapter>::WeakPtr; 497 virtual Adapter::WeakPtr AsWeakPtr() = 0; 498 }; 499 500 } // namespace gap 501 } // namespace bt 502