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