| // 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_GAP_LOW_ENERGY_CONNECTION_MANAGER_H_ |
| #define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTION_MANAGER_H_ |
| |
| #include <lib/async/dispatcher.h> |
| #include <lib/fit/function.h> |
| |
| #include <list> |
| #include <memory> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include <fbl/macros.h> |
| |
| #include "lib/fitx/result.h" |
| #include "low_energy_connection_request.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/error.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/metrics.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/windowed_inspect_numeric_property.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/low_energy_connector.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/low_energy_discovery_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gatt/gatt.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_connector.h" |
| #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/error.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/types.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/command_channel.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/control_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/transport/error.h" |
| #include "src/lib/fxl/memory/weak_ptr.h" |
| |
| namespace bt { |
| |
| namespace hci { |
| class LocalAddressDelegate; |
| class Transport; |
| } // namespace hci |
| |
| namespace gap { |
| |
| namespace internal { |
| class LowEnergyConnection; |
| } // namespace internal |
| |
| // TODO(armansito): Document the usage pattern. |
| |
| class LowEnergyConnectionManager; |
| class PairingDelegate; |
| class Peer; |
| class PeerCache; |
| |
| enum class LowEnergyDisconnectReason : uint8_t { |
| // Explicit disconnect request |
| kApiRequest, |
| // An internal error was encountered |
| kError, |
| }; |
| |
| // LowEnergyConnectionManager is responsible for connecting and initializing new connections, |
| // interrogating connections, intiating pairing, and disconnecting connections. |
| class LowEnergyConnectionManager final { |
| public: |
| // Duration after which connection failures are removed from Inspect. |
| static constexpr zx::duration kInspectRecentConnectionFailuresExpiryDuration = zx::min(10); |
| |
| // |hci|: The HCI transport used to track link layer connection events from |
| // the controller. |
| // |addr_delegate|: Used to obtain local identity information during pairing |
| // procedures. |
| // |connector|: Adapter object for initiating link layer connections. This |
| // object abstracts the legacy and extended HCI command sets. |
| // |peer_cache|: The cache that stores peer peer data. The connection |
| // manager stores and retrieves pairing data and connection |
| // parameters to/from the cache. It also updates the |
| // connection and bonding state of a peer via the cache. |
| // |l2cap|: Used to interact with the L2CAP layer. |
| // |gatt|: Used to interact with the GATT profile layer. |
| LowEnergyConnectionManager(fxl::WeakPtr<hci::Transport> hci, |
| hci::LocalAddressDelegate* addr_delegate, |
| hci::LowEnergyConnector* connector, PeerCache* peer_cache, |
| l2cap::L2cap* l2cap, fxl::WeakPtr<gatt::GATT> gatt, |
| fxl::WeakPtr<LowEnergyDiscoveryManager> discovery_manager, |
| sm::SecurityManagerFactory sm_creator); |
| ~LowEnergyConnectionManager(); |
| |
| // Allows a caller to claim shared ownership over a connection to the requested remote LE peer |
| // identified by |peer_id|. |
| // * If |peer_id| is not recognized, |callback| is called with an error. |
| // |
| // * If the requested peer is already connected, |callback| is called with a |
| // LowEnergyConnectionHandle immediately. |
| // This is done for both local and remote initiated connections (i.e. the local adapter |
| // can either be in the LE central or peripheral roles). |
| // |
| // * If the requested peer is NOT connected, then this method initiates a |
| // connection to the requested peer using the internal::LowEnergyConnector. See that class's |
| // documentation for a more detailed overview of the Connection process. A |
| // LowEnergyConnectionHandle is asynchronously returned to the caller once the connection has |
| // been set up. |
| // |
| // The status of the procedure is reported in |callback| in the case of an |
| // error. |
| using ConnectionResult = fitx::result<HostError, std::unique_ptr<LowEnergyConnectionHandle>>; |
| using ConnectionResultCallback = fit::function<void(ConnectionResult)>; |
| void Connect(PeerId peer_id, ConnectionResultCallback callback, |
| LowEnergyConnectionOptions connection_options); |
| |
| hci::LocalAddressDelegate* local_address_delegate() const { return local_address_delegate_; } |
| |
| // Disconnects any existing or pending LE connection to |peer_id|, invalidating all |
| // active LowEnergyConnectionHandles. Returns false if the peer can not be |
| // disconnected. |
| bool Disconnect(PeerId peer_id, |
| LowEnergyDisconnectReason reason = LowEnergyDisconnectReason::kApiRequest); |
| |
| // Initializes a new connection over the given |link| and asynchronously returns a connection |
| // reference. |
| // |
| // |link| must be the result of a remote initiated connection. |
| // |
| // |callback| will be called with a connection status and connection reference. The connection |
| // reference will be nullptr if the connection was rejected (as indicated by a failure status). |
| // |
| // TODO(armansito): Add an |own_address| parameter for the locally advertised |
| // address that was connected to. |
| // |
| // A link with the given handle should not have been previously registered. |
| void RegisterRemoteInitiatedLink(std::unique_ptr<hci::LowEnergyConnection> link, |
| sm::BondableMode bondable_mode, |
| ConnectionResultCallback callback); |
| |
| // Returns the PairingDelegate currently assigned to this connection manager. |
| PairingDelegate* pairing_delegate() const { return pairing_delegate_.get(); } |
| |
| // Assigns a new PairingDelegate to handle LE authentication challenges. |
| // Replacing an existing pairing delegate cancels all ongoing pairing |
| // procedures. If a delegate is not set then all pairing requests will be |
| // rejected. |
| void SetPairingDelegate(fxl::WeakPtr<PairingDelegate> delegate); |
| |
| // TODO(armansito): Add a PeerCache::Observer interface and move these |
| // callbacks there. |
| |
| // Called when a link with the given handle gets disconnected. This event is |
| // guaranteed to be called before invalidating connection references. |
| // |callback| is run on the creation thread. |
| // |
| // NOTE: This is intended ONLY for unit tests. Clients should watch for |
| // disconnection events using LowEnergyConnectionHandle::set_closed_callback() |
| // instead. DO NOT use outside of tests. |
| using DisconnectCallback = fit::function<void(hci_spec::ConnectionHandle)>; |
| void SetDisconnectCallbackForTesting(DisconnectCallback callback); |
| |
| // Sets the timeout interval to be used on future connect requests. The |
| // default value is kLECreateConnectionTimeout. |
| void set_request_timeout_for_testing(zx::duration value) { request_timeout_ = value; } |
| |
| // Callback for hci::Connection, called when the peer disconnects. |
| // |reason| is used to control retry logic. |
| void OnPeerDisconnect(const hci::Connection* connection, hci_spec::StatusCode reason); |
| |
| // Initiates the pairing process. Expected to only be called during higher-level testing. |
| // |peer_id|: the peer to pair to - if the peer is not connected, |cb| is called with an error. |
| // |pairing_level|: determines the security level of the pairing. **Note**: If the security |
| // level of the link is already >= |pairing level|, no pairing takes place. |
| // |bondable_mode|: sets the bonding mode of this connection. A device in bondable mode forms a |
| // bond to the peer upon pairing, assuming the peer is also in bondable mode. |
| // A device in non-bondable mode will not allow pairing that forms a bond. |
| // |cb|: callback called upon completion of this function, whether pairing takes place or not. |
| void Pair(PeerId peer_id, sm::SecurityLevel pairing_level, sm::BondableMode bondable_mode, |
| sm::ResultFunction<> cb); |
| |
| // Sets the LE security mode of the local device (see v5.2 Vol. 3 Part C Section 10.2). If set to |
| // SecureConnectionsOnly, any currently encrypted links not meeting the requirements of Security |
| // Mode 1 Level 4 will be disconnected. |
| void SetSecurityMode(LESecurityMode mode); |
| |
| // Attach manager inspect node as a child node of |parent|. |
| void AttachInspect(inspect::Node& parent, std::string name); |
| |
| LESecurityMode security_mode() const { return security_mode_; } |
| sm::SecurityManagerFactory sm_factory_func() const { return sm_factory_func_; } |
| |
| private: |
| friend class internal::LowEnergyConnection; |
| |
| // Mapping from peer identifiers to open LE connections. |
| using ConnectionMap = std::unordered_map<PeerId, std::unique_ptr<internal::LowEnergyConnection>>; |
| |
| // Called by LowEnergyConnectionHandle::Release(). |
| void ReleaseReference(LowEnergyConnectionHandle* handle); |
| |
| // Initiates a new connection attempt for the next peer in the pending list, if any. |
| void TryCreateNextConnection(); |
| |
| // Called by internal::LowEnergyConnector to indicate the result of a local connect request. |
| void OnLocalInitiatedConnectResult( |
| hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result); |
| |
| // Called by internal::LowEnergyConnector to indicate the result of a remote connect request. |
| void OnRemoteInitiatedConnectResult( |
| PeerId peer_id, hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result); |
| |
| // Either report an error to clients or initialize the connection and report success to clients. |
| void ProcessConnectResult(hci::Result<std::unique_ptr<internal::LowEnergyConnection>> result, |
| internal::LowEnergyConnectionRequest request); |
| |
| // Finish setting up connection, adding to |connections_| map, and notifying clients. |
| bool InitializeConnection(std::unique_ptr<internal::LowEnergyConnection> connection, |
| internal::LowEnergyConnectionRequest request); |
| |
| // Cleans up a connection state. This results in a HCI_Disconnect command if the connection has |
| // not already been disconnected, and notifies any referenced LowEnergyConnectionHandles of the |
| // disconnection. Marks the corresponding PeerCache entry as disconnected and cleans up all data |
| // bearers. |
| // |
| // |conn_state| will have been removed from the underlying map at the time of |
| // a call. Its ownership is passed to the method for disposal. |
| // |
| // This is also responsible for unregistering the link from managed subsystems |
| // (e.g. L2CAP). |
| void CleanUpConnection(std::unique_ptr<internal::LowEnergyConnection> conn); |
| |
| // Updates |peer_cache_| with the given |link| and returns the corresponding |
| // Peer. |
| // |
| // Creates a new Peer if |link| matches a peer that did not |
| // previously exist in the cache. Otherwise this updates and returns an |
| // existing Peer. |
| // |
| // The returned peer is marked as non-temporary and its connection |
| // parameters are updated. |
| // |
| // Called by RegisterRemoteInitiatedLink() and RegisterLocalInitiatedLink(). |
| Peer* UpdatePeerWithLink(const hci::LowEnergyConnection& link); |
| |
| // Called when the peer disconnects with a "Connection Failed to be Established" error. |
| // Cleans up the existing connection and adds the connection request back to the queue for a |
| // retry. |
| void CleanUpAndRetryConnection(std::unique_ptr<internal::LowEnergyConnection> connection); |
| |
| // Returns an iterator into |connections_| if a connection is found that |
| // matches the given logical link |handle|. Otherwise, returns an iterator |
| // that is equal to |connections_.end()|. |
| // |
| // The general rules of validity around std::unordered_map::iterator apply to |
| // the returned value. |
| ConnectionMap::iterator FindConnection(hci_spec::ConnectionHandle handle); |
| |
| fxl::WeakPtr<hci::Transport> hci_; |
| |
| // The pairing delegate used for authentication challenges. If nullptr, all |
| // pairing requests will be rejected. |
| fxl::WeakPtr<PairingDelegate> pairing_delegate_; |
| |
| // The GAP LE security mode of the device (v5.2 Vol. 3 Part C 10.2). |
| LESecurityMode security_mode_; |
| |
| // The function used to create each channel's SecurityManager implementation. |
| sm::SecurityManagerFactory sm_factory_func_; |
| |
| // Time after which a connection attempt is considered to have timed out. This |
| // is configurable to allow unit tests to set a shorter value. |
| zx::duration request_timeout_; |
| |
| // Task called after a peer scan attempt is times out. |
| std::optional<async::TaskClosure> scan_timeout_task_; |
| |
| // The dispatcher for all asynchronous tasks. |
| async_dispatcher_t* dispatcher_; |
| |
| // The peer cache is used to look up and persist remote peer data that is |
| // relevant during connection establishment (such as the address, preferred |
| // connection parameters, etc). Expected to outlive this instance. |
| PeerCache* peer_cache_; // weak |
| |
| // The reference to L2CAP, used to interact with the L2CAP layer to |
| // manage LE logical links, fixed channels, and LE-specific L2CAP signaling |
| // events (e.g. connection parameter update). |
| l2cap::L2cap* l2cap_; |
| |
| // The GATT layer reference, used to add and remove ATT data bearers and |
| // service discovery. |
| fxl::WeakPtr<gatt::GATT> gatt_; |
| |
| // Local GATT service registry. |
| std::unique_ptr<gatt::LocalServiceManager> gatt_registry_; |
| |
| fxl::WeakPtr<LowEnergyDiscoveryManager> discovery_manager_; |
| |
| // Callbacks used by unit tests to observe connection state events. |
| DisconnectCallback test_disconn_cb_; |
| |
| // Outstanding connection requests based on remote peer ID. |
| std::unordered_map<PeerId, internal::LowEnergyConnectionRequest> pending_requests_; |
| |
| // Mapping from peer identifiers to currently open LE connections. |
| ConnectionMap connections_; |
| |
| struct RequestAndConnector { |
| internal::LowEnergyConnectionRequest request; |
| std::unique_ptr<internal::LowEnergyConnector> connector; |
| }; |
| // The in-progress locally initiated connection request, if any. |
| std::optional<RequestAndConnector> current_request_; |
| |
| // Active connectors for remote connection requests. |
| std::unordered_map<PeerId, RequestAndConnector> remote_connectors_; |
| |
| // For passing to internal::LowEnergyConnector. |hci_connector_| must |
| // out-live this connection manager. |
| hci::LowEnergyConnector* hci_connector_; // weak |
| |
| // Address manager is used to obtain local identity information during pairing |
| // procedures. Expected to outlive this instance. |
| hci::LocalAddressDelegate* local_address_delegate_; // weak |
| |
| // True if the connection manager is performing a scan for a peer before connecting. |
| bool scanning_ = false; |
| |
| struct InspectProperties { |
| // Count of connection failures in the past 10 minutes. |
| WindowedInspectIntProperty recent_connection_failures{ |
| kInspectRecentConnectionFailuresExpiryDuration}; |
| |
| UintMetricCounter outgoing_connection_success_count_; |
| UintMetricCounter outgoing_connection_failure_count_; |
| UintMetricCounter incoming_connection_success_count_; |
| UintMetricCounter incoming_connection_failure_count_; |
| |
| UintMetricCounter disconnect_explicit_disconnect_count_; |
| UintMetricCounter disconnect_link_error_count_; |
| UintMetricCounter disconnect_zero_ref_count_; |
| UintMetricCounter disconnect_remote_disconnection_count_; |
| }; |
| InspectProperties inspect_properties_; |
| inspect::Node inspect_node_; |
| // Container node for pending request nodes. |
| inspect::Node inspect_pending_requests_node_; |
| // container node for connection nodes. |
| inspect::Node inspect_connections_node_; |
| |
| // Keep this as the last member to make sure that all weak pointers are |
| // invalidated before other members get destroyed. |
| fxl::WeakPtrFactory<LowEnergyConnectionManager> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyConnectionManager); |
| }; |
| |
| } // namespace gap |
| } // namespace bt |
| |
| #endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_GAP_LOW_ENERGY_CONNECTION_MANAGER_H_ |