| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_PUBLIC_PW_BLUETOOTH_SAPPHIRE_INTERNAL_HOST_HCI_LOW_ENERGY_CONNECTOR_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_PUBLIC_PW_BLUETOOTH_SAPPHIRE_INTERNAL_HOST_HCI_LOW_ENERGY_CONNECTOR_H_ |
| |
| #include <memory> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/device_address.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/macros.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/constants.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/le_connection_parameters.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/command_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/control_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/transport.h" |
| |
| namespace bt::hci { |
| |
| class LocalAddressDelegate; |
| |
| // A LowEnergyConnector abstracts over the HCI commands and events involved in |
| // initiating a direct link-layer connection with a peer device. This class also |
| // provides a way for a delegate to be notified when a connection is initiated |
| // by a remote. |
| // |
| // This class vends Connection objects for LE link layer connections. |
| // |
| // Instances of this class are expected to each exist as a singleton on a |
| // per-transport basis as multiple instances cannot accurately reflect the state |
| // of the controller while allowing simultaneous operations. |
| class LowEnergyConnector : public LocalAddressClient { |
| public: |
| // The IncomingConnectionDelegate defines the interface that |
| // LowEnergyConnector will use to callback on an incoming connection. |
| // |
| // - |handle|: Data Connection Handle used for ACL and SCO logical link |
| // connections. |
| // |
| // - |role|: The role that this device is operating in for this connection. |
| // |
| // - |peer_address|: The address of the remote peer. |
| // |
| // - |conn_params|: Connection related parameters. |
| using IncomingConnectionDelegate = |
| fit::function<void(hci_spec::ConnectionHandle handle, |
| pw::bluetooth::emboss::ConnectionRole role, |
| const DeviceAddress& peer_address, |
| const hci_spec::LEConnectionParameters& conn_params)>; |
| |
| // The constructor expects the following arguments: |
| // - |hci|: The HCI transport this should operate on. |
| // |
| // - |local_addr_delegate|: The delegate used to obtain the current public |
| // or random device address to use in locally initiated requests. |
| // |
| // - |dispatcher|: The dispatcher that will be used to run all |
| // asynchronous operations. This must be bound to the thread on which the |
| // LowEnergyConnector is created. |
| // |
| // - |delegate|: The delegate that will be notified when a new logical link |
| // is established due to an incoming request (remote initiated). |
| // |
| // - |use_extended_operations|: If true, send LE Extended Create Connection |
| // to the Controller instead of LE Create Connection. |
| LowEnergyConnector(Transport::WeakPtr hci, |
| LocalAddressDelegate* local_addr_delegate, |
| pw::async::Dispatcher& dispatcher, |
| IncomingConnectionDelegate delegate, |
| bool use_extended_operations = false); |
| |
| // Deleting an instance cancels any pending connection request. |
| ~LowEnergyConnector() override; |
| |
| // Creates a LE link layer connection to the remote device identified by |
| // |peer_address| with initial connection parameters |initial_parameters|. |
| // Returns false, if a create connection request is currently pending. |
| // |
| // If |use_accept_list| is true, then the controller filter accept list is |
| // used to determine which advertiser to connect to. Otherwise, the controller |
| // will connect to |peer_address|. |
| // |
| // |status_callback| is called asynchronously to notify the status of the |
| // operation. A valid |link| will be provided on success. |
| // |
| // |timeout_ms| specifies a time period after which the request will time out. |
| // When a request to create connection times out, |status_callback| will be |
| // called with a null |link| and a |status| with error Host::Error::kTimedOut. |
| using StatusCallback = fit::function<void( |
| Result<> status, std::unique_ptr<LowEnergyConnection> link)>; |
| bool CreateConnection( |
| bool use_accept_list, |
| const DeviceAddress& peer_address, |
| uint16_t scan_interval, |
| uint16_t scan_window, |
| const hci_spec::LEPreferredConnectionParameters& initial_parameters, |
| StatusCallback status_callback, |
| pw::chrono::SystemClock::duration timeout); |
| |
| // Cancels the currently pending connection attempt. |
| void Cancel() { CancelInternal(false); } |
| |
| // Returns true if a connection request is currently pending. |
| bool request_pending() const { return pending_request_.has_value(); } |
| |
| // Returns the peer address of a connection request if a connection request is |
| // currently pending. |
| std::optional<DeviceAddress> pending_peer_address() const; |
| |
| // Returns true if a connection timeout has been posted. Returns false if it |
| // was not posted or was canceled. This is intended for unit tests. |
| bool timeout_posted() const { return request_timeout_task_.is_pending(); } |
| |
| // Disable central privacy and always use the local identity address as the |
| // local address when initiating connections, by-passing |
| // LocalAddressDelegate's current privacy setting. This policy allows better |
| // interoperability with peripherals that cannot resolve the local identity |
| // when a RPA is used during pairing. |
| // |
| // By default the address provided by the LocalAddressDelegate is used. |
| // |
| // TODO(https://fxbug.dev/42141593): Remove this temporary fix once we |
| // determine the root cause for authentication failures. |
| void UseLocalIdentityAddress() { use_local_identity_address_ = true; } |
| |
| // LocalAddressClient override: |
| bool AllowsRandomAddressChange() const override { |
| return !pending_request_ || !pending_request_->initiating; |
| } |
| |
| private: |
| struct PendingRequest { |
| PendingRequest() = default; |
| PendingRequest(const DeviceAddress& peer_address, |
| StatusCallback status_callback); |
| |
| bool initiating = false; // True if the HCI command has been sent. |
| bool canceled = false; |
| bool timed_out = false; |
| DeviceAddress local_address; |
| DeviceAddress peer_address; |
| StatusCallback status_callback; |
| }; |
| |
| static EmbossCommandPacket BuildExtendedCreateConnectionPacket( |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| const hci_spec::LEPreferredConnectionParameters& initial_params, |
| bool use_accept_list, |
| uint16_t scan_interval, |
| uint16_t scan_window); |
| |
| static EmbossCommandPacket BuildCreateConnectionPacket( |
| const DeviceAddress& local_address, |
| const DeviceAddress& peer_address, |
| const hci_spec::LEPreferredConnectionParameters& initial_params, |
| bool use_accept_list, |
| uint16_t scan_interval, |
| uint16_t scan_window); |
| |
| // Event handler for either the HCI LE Enhanced Connection Complete event or |
| // HCI LE Connection Complete Event |
| template <typename T> |
| void OnConnectionCompleteEvent(const EmbossEventPacket& event); |
| |
| Transport::WeakPtr hci() const { return hci_; } |
| |
| // Called by CreateConnection() after the local device address has been |
| // obtained. |
| void CreateConnectionInternal( |
| const DeviceAddress& local_address, |
| bool use_accept_list, |
| const DeviceAddress& peer_address, |
| uint16_t scan_interval, |
| uint16_t scan_window, |
| const hci_spec::LEPreferredConnectionParameters& initial_params, |
| StatusCallback status_callback, |
| pw::chrono::SystemClock::duration timeout); |
| |
| // Called by Cancel() and by OnCreateConnectionTimeout(). |
| void CancelInternal(bool timed_out = false); |
| |
| // Called when a LE Create Connection request has completed. |
| void OnCreateConnectionComplete(Result<> result, |
| std::unique_ptr<LowEnergyConnection> link); |
| |
| // Called when a LE Create Connection request has timed out. |
| void OnCreateConnectionTimeout(); |
| |
| pw::async::Dispatcher& pw_dispatcher_; |
| |
| // The HCI transport. |
| Transport::WeakPtr hci_; |
| |
| // Used to obtain the local device address type to use during initiation. |
| LocalAddressDelegate* local_addr_delegate_; // weak |
| |
| // The delegate that gets notified when a new link layer connection gets |
| // created. |
| IncomingConnectionDelegate delegate_; |
| |
| // The currently pending request. |
| std::optional<PendingRequest> pending_request_; |
| |
| // Task that runs when a request to create connection times out. We do not |
| // rely on CommandChannel's timer since that request completes when we receive |
| // the HCI Command Status event. |
| SmartTask request_timeout_task_{pw_dispatcher_}; |
| |
| // Use the local public address if true. |
| // TODO(https://fxbug.dev/42141593): Remove this temporary fix once we |
| // determine the root cause for authentication failures. |
| bool use_local_identity_address_ = false; |
| |
| // send LE Extended Create Connection to the Controller instead of the legacy |
| // LE Create Connection |
| bool use_extended_operations_ = false; |
| |
| // Our event handle ID for the LE Enhanced Connection Complete event. |
| std::unordered_set<CommandChannel::EventHandlerId> event_handler_ids_; |
| |
| // Keep this as the last member to make sure that all weak pointers are |
| // invalidated before other members get destroyed. |
| WeakSelf<LowEnergyConnector> weak_self_; |
| |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnector); |
| }; |
| |
| } // namespace bt::hci |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_PUBLIC_PW_BLUETOOTH_SAPPHIRE_INTERNAL_HOST_HCI_LOW_ENERGY_CONNECTOR_H_ |