blob: 32fa04eaf85f5493b6eefb7733efa8715ce3b404 [file] [log] [blame]
// Copyright 2018 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.
#include <functional>
#include <optional>
#include "src/connectivity/bluetooth/core/bt-host/data/domain.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/bredr_interrogator.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/connection_request.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/pairing_state.h"
#include "src/connectivity/bluetooth/core/bt-host/gap/peer.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/bredr_connection_request.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/command_channel.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/control_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/hci.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/status.h"
#include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap.h"
#include "src/connectivity/bluetooth/core/bt-host/sdp/service_discoverer.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace bt {
namespace hci {
class SequentialCommandRunner;
class Transport;
} // namespace hci
namespace gap {
class PairingDelegate;
class PeerCache;
class BrEdrConnectionManager;
// Represents a connection that is currently open with the controller (i.e. after receiving a
// Connection Complete and before either user disconnection or Disconnection Complete).
class BrEdrConnection final {
using Request = ConnectionRequest<BrEdrConnection*>;
BrEdrConnection(BrEdrConnectionManager* connection_manager, PeerId peer_id,
std::unique_ptr<hci::Connection> link, std::optional<Request> request);
BrEdrConnection(BrEdrConnection&&) = default;
BrEdrConnection& operator=(BrEdrConnection&&) = default;
// Called after interrogation completes to mark this connection as available for upper layers,
// i.e. L2CAP on |domain|. Also signals any requesters with a successful status and this
// connection. If not called and this connection is deleted (e.g. by disconnection), requesters
// will be signaled with |HostError::kNotSupported| (to indicate interrogation error).
void Start(data::Domain& domain);
// If |Start| has been called, opens an L2CAP channel using the preferred parameters |params| on
// the Domain provided. Otherwise, calls |cb| with a |ZX_HANDLE_INVALID| socket.
void OpenL2capChannel(l2cap::PSM psm, l2cap::ChannelParameters params,
data::Domain::SocketCallback cb);
const hci::Connection& link() const { return *link_; }
hci::Connection& link() { return *link_; }
PeerId peer_id() const { return peer_id_; }
PairingState& pairing_state() { return pairing_state_; }
PeerId peer_id_;
std::unique_ptr<hci::Connection> link_;
PairingState pairing_state_;
std::optional<Request> request_;
std::optional<std::reference_wrapper<data::Domain>> domain_; // clear until Start is called
// Manages all activity related to connections in the BR/EDR section of the
// controller, including whether the peer can be connected to, incoming
// connections, and initiating connections.
// There are two flows for destroying connections: explicit local disconnections, and peer
// disconnections. When the connection is disconnected explicitly with |Disconnect()|, the
// connection is immediately cleaned up and removed from the internal |connections_| map and owned
// by itself until the HCI Disconnection Complete event is received by the underlying
// hci::Connection object. When the peer disconnects, the |OnPeerDisconnect()| callback is
// called by the underlying hci::Connection object and the connection is cleaned up and removed from
// the internal |connections_| map.
class BrEdrConnectionManager final {
BrEdrConnectionManager(fxl::WeakPtr<hci::Transport> hci, PeerCache* peer_cache,
DeviceAddress local_address, fbl::RefPtr<data::Domain> data_domain,
bool use_interlaced_scan);
// Set whether this host is connectable
void SetConnectable(bool connectable, hci::StatusCallback status_cb);
// Returns the PairingDelegate currently assigned to this connection manager.
PairingDelegate* pairing_delegate() const { return pairing_delegate_.get(); }
// Assigns a new PairingDelegate to handle BR/EDR 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);
// Retrieves the peer id that is connected to the connection |handle|.
// Returns kInvalidPeerId if no such peer exists.
PeerId GetPeerId(hci::ConnectionHandle handle) const;
// Opens a new L2CAP channel to service |psm| on |peer_id| using the preferred parameters
// |params|. Returns false if the peer is not already connected.
// |cb| will be called with a zx::socket corresponding to the channel created to the remote or
// ZX_INVALID_HANDLE if the channel creation resulted in an error.
// On successful channel creation, |chan_info| will contain the configured channel parameters.
using SocketCallback = fit::function<void(l2cap::ChannelSocket)>;
bool OpenL2capChannel(PeerId peer_id, l2cap::PSM psm, l2cap::ChannelParameters params,
SocketCallback cb);
// Add a service search to be performed on new connected remote peers.
// This search will happen on every peer connection.
// |callback| will be called with the |attributes| that exist in the service
// entry on the remote SDP server. This callback is called in the same
// dispatcher as BrEdrConnectionManager was created.
// If |attributes| is empty, all attributes on the server will be returned.
// Returns a SearchId which can be used to remove the search later.
// Identical searches will perform the same search for each search added.
// TODO(BT-785): Make identifcal searches just search once
using SearchCallback = sdp::ServiceDiscoverer::ResultCallback;
using SearchId = sdp::ServiceDiscoverer::SearchId;
SearchId AddServiceSearch(const UUID& uuid, std::unordered_set<sdp::AttributeId> attributes,
SearchCallback callback);
// Remove a search previously added with AddServiceSearch()
// Returns true if a search was removed.
// This function is idempotent.
bool RemoveServiceSearch(SearchId id);
using ConnectResultCallback = fit::function<void(hci::Status, BrEdrConnection*)>;
// Initiates an outgoing Create Connection Request to attempt to connect to
// the peer identified by |peer_id|. Returns false if the connection
// request was invalid, otherwise returns true and |callback| will be called
// with the result of the procedure, whether successful or not
// TODO(BT-820) - implement a timeout
[[nodiscard]] bool Connect(PeerId peer_id, ConnectResultCallback callback);
// Initiate pairing to the peer with |peer_id| using the bondable preference. All pairing config,
// such as authentication requirements, bonding preference, etc. will be determined by defaults.
// |callback| will be called with the result of the procedure, successful or not.
void Pair(PeerId peer_id, hci::StatusCallback callback);
// Called when the controller can not begin a new connection.
void OnConnectFailure(hci::Status status, PeerId peer_id);
// Called to cancel an outgoing connection request
void SendCreateConnectionCancelCommand(DeviceAddress addr);
// Disconnects any existing BR/EDR connection to |peer_id|. Returns true if
// the peer is disconnected, false if the peer can not be disconnected.
bool Disconnect(PeerId peer_id);
// Callback for hci::Connection. Called when the peer disconnects.
void OnPeerDisconnect(const hci::Connection* connection);
// Reads the controller page scan settings.
void ReadPageScanSettings();
// Writes page scan parameters to the controller.
// If |interlaced| is true, and the controller does not support interlaced
// page scan mode, standard mode is used.
void WritePageScanSettings(uint16_t interval, uint16_t window, bool interlaced,
hci::StatusCallback cb);
// Helper to register an event handler to run.
hci::CommandChannel::EventHandlerId AddEventHandler(const hci::EventCode& code,
hci::CommandChannel::EventCallback cb);
// Find the handle for a connection to |peer_id|. Returns nullopt if no BR/EDR
// |peer_id| is connected.
std::optional<std::pair<hci::ConnectionHandle, BrEdrConnection*>> FindConnectionById(
PeerId peer_id);
// Find the handle for a connection to |bd_addr|. Returns nullopt if no BR/EDR
// |bd_addr| is connected.
std::optional<std::pair<hci::ConnectionHandle, BrEdrConnection*>> FindConnectionByAddress(
const DeviceAddressBytes& bd_addr);
// Find a peer with |addr| or create one if not found.
Peer* FindOrInitPeer(DeviceAddress addr);
// Initialize ACL connection state from |connection_handle| obtained from the controller and begin
// interrogation.
void InitializeConnection(DeviceAddress addr, hci::ConnectionHandle connection_handle);
// Called once interrogation completes to make connection identified by |handle| available to
// upper layers and begin new connection procedures.
void CompleteConnectionSetup(Peer* peer, hci::ConnectionHandle handle);
// Callbacks for registered events
hci::CommandChannel::EventCallbackResult OnAuthenticationComplete(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnConnectionRequest(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnConnectionComplete(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnIoCapabilityRequest(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnIoCapabilityResponse(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnLinkKeyRequest(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnLinkKeyNotification(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnSimplePairingComplete(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnUserConfirmationRequest(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnUserPasskeyRequest(const hci::EventPacket& event);
hci::CommandChannel::EventCallbackResult OnUserPasskeyNotification(const hci::EventPacket& event);
// Called when we complete a pending request. Initiates a new connection
// attempt for the next peer in the pending list, if any.
void TryCreateNextConnection();
// Called when a request times out waiting for a connection complete packet,
// *after* the command status was received. This is responsible for canceling
// the request and initiating the next one in the queue
void OnRequestTimeout();
// Clean up |conn| after it has been deliberately disconnected or after its
// link closed. Unregisters the connection from the data domain and marks the
// peer's BR/EDR cache state as disconnected. Takes ownership of |conn| and
// destroys it.
void CleanUpConnection(hci::ConnectionHandle handle, BrEdrConnection conn);
// Helpers for sending commands on the command channel for this controller.
// All callbacks will run on |dispatcher_|.
void SendIoCapabilityRequestReply(DeviceAddressBytes bd_addr, hci::IOCapability io_capability,
uint8_t oob_data_present,
hci::AuthRequirements auth_requirements,
hci::StatusCallback cb = nullptr);
void SendIoCapabilityRequestNegativeReply(DeviceAddressBytes bd_addr, hci::StatusCode reason,
hci::StatusCallback cb = nullptr);
void SendUserConfirmationRequestReply(DeviceAddressBytes bd_addr,
hci::StatusCallback cb = nullptr);
void SendUserConfirmationRequestNegativeReply(DeviceAddressBytes bd_addr,
hci::StatusCallback cb = nullptr);
void SendUserPasskeyRequestReply(DeviceAddressBytes bd_addr, uint32_t numeric_value,
hci::StatusCallback cb = nullptr);
void SendUserPasskeyRequestNegativeReply(DeviceAddressBytes bd_addr,
hci::StatusCallback cb = nullptr);
// Send the HCI command encoded in |command_packet|. If |cb| is not nullptr, the event returned
// will be decoded for its status, which is passed to |cb|.
void SendCommandWithStatusCallback(std::unique_ptr<hci::CommandPacket> command_packet,
hci::StatusCallback cb);
// Acts as both a command to initiate pairing and a query to see if that pairing was actually
// initiated. If true, the connnection was not already pairing, and this function has started the
// pairing process. If false, the PairingState could not start pairing.
bool InitiatesPairing(PeerId peer_id, BrEdrConnection* connection, hci::ConnectionHandle handle,
PairingState::StatusCallback pairing_callback);
using ConnectionMap = std::unordered_map<hci::ConnectionHandle, BrEdrConnection>;
fxl::WeakPtr<hci::Transport> hci_;
std::unique_ptr<hci::SequentialCommandRunner> hci_cmd_runner_;
// The pairing delegate used for authentication challenges. If nullptr, all
// pairing requests will be rejected.
fxl::WeakPtr<PairingDelegate> pairing_delegate_;
// Peer cache is used to look up parameters for connecting to peers and
// update the state of connected peers as well as introduce unknown peers.
// This object must outlive this instance.
PeerCache* cache_;
const DeviceAddress local_address_;
fbl::RefPtr<data::Domain> data_domain_;
// Interregator for new connections to pass.
BrEdrInterrogator interrogator_;
// Discoverer for SDP services
sdp::ServiceDiscoverer discoverer_;
// Holds the connections that are active.
ConnectionMap connections_;
// Handler IDs for registered events
std::vector<hci::CommandChannel::EventHandlerId> event_handler_ids_;
// The current page scan parameters of the controller.
// Set to 0 when non-connectable.
uint16_t page_scan_interval_;
uint16_t page_scan_window_;
hci::PageScanType page_scan_type_;
bool use_interlaced_scan_;
// Outstanding connection requests based on remote peer ID.
std::unordered_map<PeerId, ConnectionRequest<BrEdrConnection*>> connection_requests_;
std::optional<hci::BrEdrConnectionRequest> pending_request_;
// Time after which a connection attempt is considered to have timed out.
zx::duration request_timeout_;
// The dispatcher that all commands are queued on.
async_dispatcher_t* dispatcher_;
// Keep this as the last member to make sure that all weak pointers are
// invalidated before other members get destroyed.
fxl::WeakPtrFactory<BrEdrConnectionManager> weak_ptr_factory_;
} // namespace gap
} // namespace bt