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