• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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