| // 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. |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/adapter.h" |
| |
| #include <endian.h> |
| |
| #include <pw_async/dispatcher.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/metrics.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/random.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/bredr_connection_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/bredr_discovery_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/event_masks.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_address_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_advertising_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_discovery_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/peer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/util.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/vendor_protocol.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/android_extended_low_energy_advertiser.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/extended_low_energy_advertiser.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/extended_low_energy_scanner.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/legacy_low_energy_advertiser.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/legacy_low_energy_scanner.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/low_energy_connector.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/sequential_command_runner.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/l2cap/channel_manager.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/emboss_control_packets.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/transport.h" |
| |
| #include <pw_bluetooth/hci_commands.emb.h> |
| |
| namespace bt::gap { |
| |
| namespace hci_android = hci_spec::vendor::android; |
| namespace android_hci = pw::bluetooth::vendor::android_hci; |
| |
| static constexpr const char* kInspectLowEnergyDiscoveryManagerNodeName = |
| "low_energy_discovery_manager"; |
| static constexpr const char* kInspectLowEnergyConnectionManagerNodeName = |
| "low_energy_connection_manager"; |
| static constexpr const char* kInspectBrEdrConnectionManagerNodeName = |
| "bredr_connection_manager"; |
| static constexpr const char* kInspectBrEdrDiscoveryManagerNodeName = |
| "bredr_discovery_manager"; |
| |
| // All asynchronous callbacks are posted on the Loop on which this Adapter |
| // instance is created. |
| class AdapterImpl final : public Adapter { |
| public: |
| explicit AdapterImpl(pw::async::Dispatcher& pw_dispatcher, |
| hci::Transport::WeakPtr hci, |
| gatt::GATT::WeakPtr gatt, |
| std::unique_ptr<l2cap::ChannelManager> l2cap); |
| ~AdapterImpl() override; |
| |
| AdapterId identifier() const override { return identifier_; } |
| |
| bool Initialize(InitializeCallback callback, |
| fit::closure transport_error_cb) override; |
| |
| void ShutDown() override; |
| |
| bool IsInitializing() const override { |
| return init_state_ == State::kInitializing; |
| } |
| |
| bool IsInitialized() const override { |
| return init_state_ == State::kInitialized; |
| } |
| |
| const AdapterState& state() const override { return state_; } |
| |
| class LowEnergyImpl final : public LowEnergy { |
| public: |
| explicit LowEnergyImpl(AdapterImpl* adapter) : adapter_(adapter) {} |
| |
| void Connect(PeerId peer_id, |
| ConnectionResultCallback callback, |
| LowEnergyConnectionOptions connection_options) override { |
| adapter_->le_connection_manager_->Connect( |
| peer_id, std::move(callback), connection_options); |
| adapter_->metrics_.le.outgoing_connection_requests.Add(); |
| } |
| |
| bool Disconnect(PeerId peer_id) override { |
| return adapter_->le_connection_manager_->Disconnect(peer_id); |
| } |
| |
| void Pair(PeerId peer_id, |
| sm::SecurityLevel pairing_level, |
| sm::BondableMode bondable_mode, |
| sm::ResultFunction<> cb) override { |
| adapter_->le_connection_manager_->Pair( |
| peer_id, pairing_level, bondable_mode, std::move(cb)); |
| adapter_->metrics_.le.pair_requests.Add(); |
| } |
| |
| void SetLESecurityMode(LESecurityMode mode) override { |
| adapter_->le_connection_manager_->SetSecurityMode(mode); |
| } |
| |
| LESecurityMode security_mode() const override { |
| return adapter_->le_connection_manager_->security_mode(); |
| } |
| |
| void StartAdvertising( |
| AdvertisingData data, |
| AdvertisingData scan_rsp, |
| AdvertisingInterval interval, |
| bool extended_pdu, |
| bool anonymous, |
| bool include_tx_power_level, |
| std::optional<ConnectableAdvertisingParameters> connectable, |
| AdvertisingStatusCallback status_callback) override { |
| LowEnergyAdvertisingManager::ConnectionCallback advertisement_connect_cb = |
| nullptr; |
| if (connectable) { |
| BT_ASSERT(connectable->connection_cb); |
| |
| // All advertisement connections are first registered with |
| // LowEnergyConnectionManager before being reported to higher layers. |
| advertisement_connect_cb = |
| [this, connectable = std::move(connectable)]( |
| AdvertisementId advertisement_id, |
| std::unique_ptr<hci::LowEnergyConnection> link) mutable { |
| auto register_link_cb = |
| [advertisement_id, |
| connection_callback = std::move(connectable->connection_cb)]( |
| ConnectionResult result) { |
| connection_callback(advertisement_id, std::move(result)); |
| }; |
| |
| adapter_->le_connection_manager_->RegisterRemoteInitiatedLink( |
| std::move(link), |
| connectable->bondable_mode, |
| std::move(register_link_cb)); |
| }; |
| } |
| |
| adapter_->le_advertising_manager_->StartAdvertising( |
| std::move(data), |
| std::move(scan_rsp), |
| std::move(advertisement_connect_cb), |
| interval, |
| extended_pdu, |
| anonymous, |
| include_tx_power_level, |
| std::move(status_callback)); |
| adapter_->metrics_.le.start_advertising_events.Add(); |
| } |
| |
| void StopAdvertising(AdvertisementId advertisement_id) override { |
| adapter_->le_advertising_manager_->StopAdvertising(advertisement_id); |
| adapter_->metrics_.le.stop_advertising_events.Add(); |
| } |
| |
| void StartDiscovery(bool active, SessionCallback callback) override { |
| adapter_->le_discovery_manager_->StartDiscovery(active, |
| std::move(callback)); |
| adapter_->metrics_.le.start_discovery_events.Add(); |
| } |
| |
| void EnablePrivacy(bool enabled) override { |
| adapter_->le_address_manager_->EnablePrivacy(enabled); |
| } |
| |
| bool PrivacyEnabled() const override { |
| return adapter_->le_address_manager_->PrivacyEnabled(); |
| } |
| |
| const DeviceAddress& CurrentAddress() const override { |
| return adapter_->le_address_manager_->current_address(); |
| } |
| |
| void register_address_changed_callback(fit::closure callback) override { |
| auto cb = [cb = std::move(callback)](auto) { cb(); }; |
| adapter_->le_address_manager_->register_address_changed_callback( |
| std::move(cb)); |
| } |
| |
| void set_irk(const std::optional<UInt128>& irk) override { |
| adapter_->le_address_manager_->set_irk(irk); |
| } |
| |
| std::optional<UInt128> irk() const override { |
| return adapter_->le_address_manager_->irk(); |
| } |
| |
| void set_request_timeout_for_testing( |
| pw::chrono::SystemClock::duration value) override { |
| adapter_->le_connection_manager_->set_request_timeout_for_testing(value); |
| } |
| |
| void set_scan_period_for_testing( |
| pw::chrono::SystemClock::duration period) override { |
| adapter_->le_discovery_manager_->set_scan_period(period); |
| } |
| |
| private: |
| AdapterImpl* adapter_; |
| }; |
| |
| LowEnergy* le() const override { return low_energy_.get(); } |
| |
| class BrEdrImpl final : public BrEdr { |
| public: |
| explicit BrEdrImpl(AdapterImpl* adapter) : adapter_(adapter) {} |
| |
| bool Connect(PeerId peer_id, ConnectResultCallback callback) override { |
| return adapter_->bredr_connection_manager_->Connect(peer_id, |
| std::move(callback)); |
| adapter_->metrics_.bredr.outgoing_connection_requests.Add(); |
| } |
| |
| bool Disconnect(PeerId peer_id, DisconnectReason reason) override { |
| return adapter_->bredr_connection_manager_->Disconnect(peer_id, reason); |
| } |
| |
| void OpenL2capChannel(PeerId peer_id, |
| l2cap::Psm psm, |
| BrEdrSecurityRequirements security_requirements, |
| l2cap::ChannelParameters params, |
| l2cap::ChannelCallback cb) override { |
| adapter_->metrics_.bredr.open_l2cap_channel_requests.Add(); |
| adapter_->bredr_connection_manager_->OpenL2capChannel( |
| peer_id, psm, security_requirements, params, std::move(cb)); |
| } |
| |
| PeerId GetPeerId(hci_spec::ConnectionHandle handle) const override { |
| return adapter_->bredr_connection_manager_->GetPeerId(handle); |
| } |
| |
| SearchId AddServiceSearch(const UUID& uuid, |
| std::unordered_set<sdp::AttributeId> attributes, |
| SearchCallback callback) override { |
| return adapter_->bredr_connection_manager_->AddServiceSearch( |
| uuid, std::move(attributes), std::move(callback)); |
| } |
| |
| bool RemoveServiceSearch(SearchId id) override { |
| return adapter_->bredr_connection_manager_->RemoveServiceSearch(id); |
| } |
| |
| void Pair(PeerId peer_id, |
| BrEdrSecurityRequirements security, |
| hci::ResultFunction<> callback) override { |
| adapter_->bredr_connection_manager_->Pair( |
| peer_id, security, std::move(callback)); |
| adapter_->metrics_.bredr.pair_requests.Add(); |
| } |
| |
| void SetBrEdrSecurityMode(BrEdrSecurityMode mode) override { |
| adapter_->bredr_connection_manager_->SetSecurityMode(mode); |
| } |
| |
| BrEdrSecurityMode security_mode() const override { |
| return adapter_->bredr_connection_manager_->security_mode(); |
| } |
| |
| void SetConnectable(bool connectable, |
| hci::ResultFunction<> status_cb) override { |
| adapter_->bredr_connection_manager_->SetConnectable(connectable, |
| std::move(status_cb)); |
| if (connectable) { |
| adapter_->metrics_.bredr.set_connectable_true_events.Add(); |
| } else { |
| adapter_->metrics_.bredr.set_connectable_false_events.Add(); |
| } |
| } |
| |
| void RequestDiscovery(DiscoveryCallback callback) override { |
| adapter_->bredr_discovery_manager_->RequestDiscovery(std::move(callback)); |
| adapter_->metrics_.bredr.request_discovery_events.Add(); |
| } |
| |
| void RequestDiscoverable(DiscoverableCallback callback) override { |
| adapter_->bredr_discovery_manager_->RequestDiscoverable( |
| std::move(callback)); |
| adapter_->metrics_.bredr.request_discoverable_events.Add(); |
| } |
| |
| RegistrationHandle RegisterService( |
| std::vector<sdp::ServiceRecord> records, |
| l2cap::ChannelParameters chan_params, |
| ServiceConnectCallback conn_cb) override { |
| return adapter_->sdp_server_->RegisterService( |
| std::move(records), chan_params, std::move(conn_cb)); |
| } |
| |
| bool UnregisterService(RegistrationHandle handle) override { |
| return adapter_->sdp_server_->UnregisterService(handle); |
| } |
| |
| std::optional<ScoRequestHandle> OpenScoConnection( |
| PeerId peer_id, |
| const bt::StaticPacket< |
| pw::bluetooth::emboss::SynchronousConnectionParametersWriter>& |
| parameters, |
| sco::ScoConnectionManager::OpenConnectionCallback callback) override { |
| return adapter_->bredr_connection_manager_->OpenScoConnection( |
| peer_id, parameters, std::move(callback)); |
| } |
| std::optional<ScoRequestHandle> AcceptScoConnection( |
| PeerId peer_id, |
| const std::vector<bt::StaticPacket< |
| pw::bluetooth::emboss::SynchronousConnectionParametersWriter>> |
| parameters, |
| sco::ScoConnectionManager::AcceptConnectionCallback callback) override { |
| return adapter_->bredr_connection_manager_->AcceptScoConnection( |
| peer_id, std::move(parameters), std::move(callback)); |
| } |
| |
| private: |
| AdapterImpl* adapter_; |
| }; |
| |
| BrEdr* bredr() const override { return bredr_.get(); } |
| |
| PeerCache* peer_cache() override { return &peer_cache_; } |
| |
| bool AddBondedPeer(BondingData bonding_data) override; |
| |
| void SetPairingDelegate(PairingDelegate::WeakPtr delegate) override; |
| |
| bool IsDiscoverable() const override; |
| |
| bool IsDiscovering() const override; |
| |
| void SetLocalName(std::string name, hci::ResultFunction<> callback) override; |
| |
| std::string local_name() const override { |
| return bredr_discovery_manager_->local_name(); |
| } |
| |
| void SetDeviceClass(DeviceClass dev_class, |
| hci::ResultFunction<> callback) override; |
| |
| void set_auto_connect_callback(AutoConnectCallback callback) override { |
| auto_conn_cb_ = std::move(callback); |
| } |
| |
| void AttachInspect(inspect::Node& parent, std::string name) override; |
| |
| WeakSelf<Adapter>::WeakPtr AsWeakPtr() override { |
| return weak_self_adapter_.GetWeakPtr(); |
| } |
| |
| private: |
| // Called by Initialize() after Transport is initialized. |
| void InitializeStep1(); |
| |
| // Second step of the initialization sequence. Called by InitializeStep1() |
| // when the first batch of HCI commands have been sent. |
| void InitializeStep2(); |
| |
| // Third step of the initialization sequence. Called by InitializeStep2() when |
| // the second batch of HCI commands have been sent. |
| void InitializeStep3(); |
| |
| // Fourth step of the initialization sequence. Called by InitializeStep3() |
| // when the third batch of HCI commands have been sent. |
| void InitializeStep4(); |
| |
| // Returns true if initialization was completed, or false if initialization is |
| // not in progress. |
| bool CompleteInitialization(bool success); |
| |
| // Reads LMP feature mask's bits from |page| |
| void InitQueueReadLMPFeatureMaskPage(uint8_t page); |
| |
| // Assigns properties to |adapter_node_| using values discovered during other |
| // initialization steps. |
| void UpdateInspectProperties(); |
| |
| // Called by ShutDown() and during Initialize() in case of failure. This |
| // synchronously cleans up the transports and resets initialization state. |
| void CleanUp(); |
| |
| // Called by Transport after it experiences a fatal error. |
| void OnTransportError(); |
| |
| // Called when a directed connectable advertisement is received from a bonded |
| // LE device. This amounts to a connection request from a bonded peripheral |
| // which is handled by routing the request to |le_connection_manager_| to |
| // initiate a Direct Connection Establishment procedure (Vol 3, Part C, |
| // 9.3.8). |
| void OnLeAutoConnectRequest(Peer* peer); |
| |
| // Called by |le_address_manager_| to query whether it is currently allowed to |
| // reconfigure the LE random address. |
| bool IsLeRandomAddressChangeAllowed(); |
| |
| // Called when we receive an LE Get Vendor Capabilities Command Complete from |
| // the Controller |
| void ParseLEGetVendorCapabilitiesCommandComplete( |
| const hci::EmbossEventPacket& event); |
| |
| std::unique_ptr<hci::LowEnergyAdvertiser> CreateAdvertiser(bool extended) { |
| if (extended) { |
| return std::make_unique<hci::ExtendedLowEnergyAdvertiser>(hci_); |
| } |
| |
| constexpr pw::bluetooth::Controller::FeaturesBits feature = |
| pw::bluetooth::Controller::FeaturesBits::kAndroidVendorExtensions; |
| if (!state().IsControllerFeatureSupported(feature)) { |
| return std::make_unique<hci::LegacyLowEnergyAdvertiser>(hci_); |
| } |
| |
| if (!state().android_vendor_capabilities) { |
| bt_log( |
| WARN, |
| "gap", |
| "controller supports android vendor extensions, but failed to parse " |
| "LEGetVendorCapabilitiesCommandComplete, using legacy advertiser"); |
| return std::make_unique<hci::LegacyLowEnergyAdvertiser>(hci_); |
| } |
| |
| uint8_t max_advt = |
| state().android_vendor_capabilities->max_simultaneous_advertisements(); |
| bt_log(INFO, |
| "gap", |
| "controller supports android vendor extensions, max simultaneous " |
| "advertisements: %d", |
| max_advt); |
| return std::make_unique<hci::AndroidExtendedLowEnergyAdvertiser>(hci_, |
| max_advt); |
| } |
| |
| std::unique_ptr<hci::LowEnergyConnector> CreateConnector(bool extended) { |
| return std::make_unique<hci::LowEnergyConnector>( |
| hci_, |
| le_address_manager_.get(), |
| dispatcher_, |
| fit::bind_member<&hci::LowEnergyAdvertiser::OnIncomingConnection>( |
| hci_le_advertiser_.get()), |
| extended); |
| } |
| |
| std::unique_ptr<hci::LowEnergyScanner> CreateScanner(bool extended) { |
| if (extended) { |
| return std::make_unique<hci::ExtendedLowEnergyScanner>( |
| le_address_manager_.get(), hci_, dispatcher_); |
| } |
| |
| return std::make_unique<hci::LegacyLowEnergyScanner>( |
| le_address_manager_.get(), hci_, dispatcher_); |
| } |
| |
| // Must be initialized first so that child nodes can be passed to other |
| // constructors. |
| inspect::Node adapter_node_; |
| struct InspectProperties { |
| inspect::StringProperty adapter_id; |
| inspect::StringProperty hci_version; |
| inspect::UintProperty bredr_max_num_packets; |
| inspect::UintProperty bredr_max_data_length; |
| inspect::UintProperty le_max_num_packets; |
| inspect::UintProperty le_max_data_length; |
| inspect::UintProperty sco_max_num_packets; |
| inspect::UintProperty sco_max_data_length; |
| inspect::StringProperty lmp_features; |
| inspect::StringProperty le_features; |
| }; |
| InspectProperties inspect_properties_; |
| |
| // Metrics properties |
| inspect::Node metrics_node_; |
| inspect::Node metrics_bredr_node_; |
| inspect::Node metrics_le_node_; |
| struct AdapterMetrics { |
| struct LeMetrics { |
| UintMetricCounter outgoing_connection_requests; |
| UintMetricCounter pair_requests; |
| UintMetricCounter start_advertising_events; |
| UintMetricCounter stop_advertising_events; |
| UintMetricCounter start_discovery_events; |
| } le; |
| struct BrEdrMetrics { |
| UintMetricCounter outgoing_connection_requests; |
| UintMetricCounter pair_requests; |
| UintMetricCounter set_connectable_true_events; |
| UintMetricCounter set_connectable_false_events; |
| UintMetricCounter request_discovery_events; |
| UintMetricCounter request_discoverable_events; |
| UintMetricCounter open_l2cap_channel_requests; |
| } bredr; |
| }; |
| AdapterMetrics metrics_; |
| |
| // Uniquely identifies this adapter on the current system. |
| AdapterId identifier_; |
| |
| hci::Transport::WeakPtr hci_; |
| |
| // Callback invoked to notify clients when the underlying transport is closed. |
| fit::closure transport_error_cb_; |
| |
| // Parameters relevant to the initialization sequence. |
| // TODO(armansito): The Initialize()/ShutDown() pattern has become common |
| // enough in this project that it might be worth considering moving the |
| // init-state-keeping into an abstract base. |
| enum State { |
| kNotInitialized = 0, |
| kInitializing, |
| kInitialized, |
| }; |
| std::atomic<State> init_state_; |
| std::unique_ptr<hci::SequentialCommandRunner> init_seq_runner_; |
| |
| // The callback passed to Initialize(). Null after initialization completes. |
| InitializeCallback init_cb_; |
| |
| // Contains the global adapter state. |
| AdapterState state_; |
| |
| // The maximum LMP feature page that we will read. |
| std::optional<size_t> max_lmp_feature_page_index_; |
| |
| // Provides access to discovered, connected, and/or bonded remote Bluetooth |
| // devices. |
| PeerCache peer_cache_; |
| |
| // L2CAP layer used by GAP. This must be destroyed after the following members |
| // because they raw pointers to this member. |
| std::unique_ptr<l2cap::ChannelManager> l2cap_; |
| |
| // The GATT profile. We use this reference to add and remove data bearers and |
| // for service discovery. |
| gatt::GATT::WeakPtr gatt_; |
| |
| // Objects that abstract the controller for connection and advertising |
| // procedures. |
| std::unique_ptr<hci::LowEnergyAdvertiser> hci_le_advertiser_; |
| std::unique_ptr<hci::LowEnergyConnector> hci_le_connector_; |
| std::unique_ptr<hci::LowEnergyScanner> hci_le_scanner_; |
| |
| // Objects that perform LE procedures. |
| std::unique_ptr<LowEnergyAddressManager> le_address_manager_; |
| std::unique_ptr<LowEnergyDiscoveryManager> le_discovery_manager_; |
| std::unique_ptr<LowEnergyConnectionManager> le_connection_manager_; |
| std::unique_ptr<LowEnergyAdvertisingManager> le_advertising_manager_; |
| std::unique_ptr<LowEnergyImpl> low_energy_; |
| |
| // Objects that perform BR/EDR procedures. |
| std::unique_ptr<BrEdrConnectionManager> bredr_connection_manager_; |
| std::unique_ptr<BrEdrDiscoveryManager> bredr_discovery_manager_; |
| std::unique_ptr<sdp::Server> sdp_server_; |
| std::unique_ptr<BrEdrImpl> bredr_; |
| |
| // Callback to propagate ownership of an auto-connected LE link. |
| AutoConnectCallback auto_conn_cb_; |
| |
| pw::async::Dispatcher& dispatcher_; |
| |
| // This must remain the last member to make sure that all weak pointers are |
| // invalidating before other members are destroyed. |
| WeakSelf<AdapterImpl> weak_self_; |
| WeakSelf<Adapter> weak_self_adapter_; |
| |
| BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdapterImpl); |
| }; |
| |
| AdapterImpl::AdapterImpl(pw::async::Dispatcher& pw_dispatcher, |
| hci::Transport::WeakPtr hci, |
| gatt::GATT::WeakPtr gatt, |
| std::unique_ptr<l2cap::ChannelManager> l2cap) |
| : identifier_(Random<AdapterId>()), |
| hci_(std::move(hci)), |
| init_state_(State::kNotInitialized), |
| peer_cache_(pw_dispatcher), |
| l2cap_(std::move(l2cap)), |
| gatt_(std::move(gatt)), |
| dispatcher_(pw_dispatcher), |
| weak_self_(this), |
| weak_self_adapter_(this) { |
| BT_DEBUG_ASSERT(hci_.is_alive()); |
| BT_DEBUG_ASSERT(gatt_.is_alive()); |
| |
| auto self = weak_self_.GetWeakPtr(); |
| hci_->SetTransportErrorCallback([self] { |
| if (self.is_alive()) { |
| self->OnTransportError(); |
| } |
| }); |
| |
| gatt_->SetPersistServiceChangedCCCCallback( |
| [this](PeerId peer_id, gatt::ServiceChangedCCCPersistedData gatt_data) { |
| Peer* peer = peer_cache_.FindById(peer_id); |
| if (!peer) { |
| bt_log(WARN, |
| "gap", |
| "Unable to find peer %s when storing persisted GATT data.", |
| bt_str(peer_id)); |
| } else if (!peer->le()) { |
| bt_log(WARN, |
| "gap", |
| "Tried to store persisted GATT data for non-LE peer %s.", |
| bt_str(peer_id)); |
| } else { |
| peer->MutLe().set_service_changed_gatt_data(gatt_data); |
| } |
| }); |
| |
| gatt_->SetRetrieveServiceChangedCCCCallback([this](PeerId peer_id) { |
| Peer* peer = peer_cache_.FindById(peer_id); |
| if (!peer) { |
| bt_log(WARN, |
| "gap", |
| "Unable to find peer %s when retrieving persisted GATT data.", |
| peer_id.ToString().c_str()); |
| return std::optional<gatt::ServiceChangedCCCPersistedData>(); |
| } |
| |
| if (!peer->le()) { |
| bt_log(WARN, |
| "gap", |
| "Tried to retrieve persisted GATT data for non-LE peer %s.", |
| peer_id.ToString().c_str()); |
| return std::optional<gatt::ServiceChangedCCCPersistedData>(); |
| } |
| |
| return std::optional(peer->le()->get_service_changed_gatt_data()); |
| }); |
| } |
| |
| AdapterImpl::~AdapterImpl() { |
| if (IsInitialized()) { |
| ShutDown(); |
| } |
| } |
| |
| bool AdapterImpl::Initialize(InitializeCallback callback, |
| fit::closure transport_error_cb) { |
| BT_DEBUG_ASSERT(callback); |
| BT_DEBUG_ASSERT(transport_error_cb); |
| |
| if (IsInitialized()) { |
| bt_log(WARN, "gap", "Adapter already initialized"); |
| return false; |
| } |
| |
| BT_DEBUG_ASSERT(!IsInitializing()); |
| BT_DEBUG_ASSERT(!init_seq_runner_); |
| |
| init_state_ = State::kInitializing; |
| init_cb_ = std::move(callback); |
| transport_error_cb_ = std::move(transport_error_cb); |
| |
| hci_->Initialize([this](bool success) { |
| if (!success) { |
| bt_log(ERROR, "gap", "Failed to initialize Transport"); |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| init_seq_runner_ = std::make_unique<hci::SequentialCommandRunner>( |
| hci_->command_channel()->AsWeakPtr()); |
| |
| InitializeStep1(); |
| }); |
| |
| return true; |
| } |
| |
| void AdapterImpl::ShutDown() { |
| bt_log(DEBUG, "gap", "adapter shutting down"); |
| |
| if (IsInitializing()) { |
| BT_DEBUG_ASSERT(!init_seq_runner_->IsReady()); |
| init_seq_runner_->Cancel(); |
| } |
| |
| CleanUp(); |
| } |
| |
| bool AdapterImpl::AddBondedPeer(BondingData bonding_data) { |
| return peer_cache()->AddBondedPeer(bonding_data); |
| } |
| |
| void AdapterImpl::SetPairingDelegate(PairingDelegate::WeakPtr delegate) { |
| le_connection_manager_->SetPairingDelegate(delegate); |
| bredr_connection_manager_->SetPairingDelegate(delegate); |
| } |
| |
| bool AdapterImpl::IsDiscoverable() const { |
| if (bredr_discovery_manager_ && bredr_discovery_manager_->discoverable()) { |
| return true; |
| } |
| |
| // If LE Privacy is enabled, then we are not discoverable. |
| // TODO(https://fxbug.dev/42060496): Make this dependent on whether the LE |
| // Public advertisement is active or not. |
| if (le_address_manager_ && le_address_manager_->PrivacyEnabled()) { |
| return false; |
| } |
| |
| return (le_advertising_manager_ && le_advertising_manager_->advertising()); |
| } |
| |
| bool AdapterImpl::IsDiscovering() const { |
| return (le_discovery_manager_ && le_discovery_manager_->discovering()) || |
| (bredr_discovery_manager_ && bredr_discovery_manager_->discovering()); |
| } |
| |
| void AdapterImpl::SetLocalName(std::string name, |
| hci::ResultFunction<> callback) { |
| // TODO(https://fxbug.dev/42116852): set the public LE advertisement name from |
| // |name| If BrEdr is not supported, skip the name update. |
| if (!bredr_discovery_manager_) { |
| callback(ToResult(bt::HostError::kNotSupported)); |
| return; |
| } |
| |
| // Make a copy of |name| to move separately into the lambda. |
| std::string name_copy(name); |
| bredr_discovery_manager_->UpdateLocalName( |
| std::move(name), |
| [this, cb = std::move(callback), local_name = std::move(name_copy)]( |
| auto status) { |
| if (!bt_is_error(status, WARN, "gap", "set local name failed")) { |
| state_.local_name = local_name; |
| } |
| cb(status); |
| }); |
| } |
| |
| void AdapterImpl::SetDeviceClass(DeviceClass dev_class, |
| hci::ResultFunction<> callback) { |
| auto write_dev_class = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::WriteClassOfDeviceCommandWriter>( |
| hci_spec::kWriteClassOfDevice); |
| write_dev_class.view_t().class_of_device().BackingStorage().WriteUInt( |
| dev_class.to_int()); |
| hci_->command_channel()->SendCommand( |
| std::move(write_dev_class), |
| [cb = std::move(callback)](auto, const hci::EventPacket& event) { |
| hci_is_error(event, WARN, "gap", "set device class failed"); |
| cb(event.ToResult()); |
| }); |
| } |
| |
| void AdapterImpl::AttachInspect(inspect::Node& parent, std::string name) { |
| adapter_node_ = parent.CreateChild(name); |
| UpdateInspectProperties(); |
| |
| peer_cache_.AttachInspect(adapter_node_); |
| |
| metrics_node_ = adapter_node_.CreateChild(kMetricsInspectNodeName); |
| |
| metrics_le_node_ = metrics_node_.CreateChild("le"); |
| metrics_.le.outgoing_connection_requests.AttachInspect( |
| metrics_le_node_, "outgoing_connection_requests"); |
| metrics_.le.pair_requests.AttachInspect(metrics_le_node_, "pair_requests"); |
| metrics_.le.start_advertising_events.AttachInspect( |
| metrics_le_node_, "start_advertising_events"); |
| metrics_.le.stop_advertising_events.AttachInspect(metrics_le_node_, |
| "stop_advertising_events"); |
| metrics_.le.start_discovery_events.AttachInspect(metrics_le_node_, |
| "start_discovery_events"); |
| |
| metrics_bredr_node_ = metrics_node_.CreateChild("bredr"); |
| metrics_.bredr.outgoing_connection_requests.AttachInspect( |
| metrics_bredr_node_, "outgoing_connection_requests"); |
| metrics_.bredr.pair_requests.AttachInspect(metrics_bredr_node_, |
| "pair_requests"); |
| metrics_.bredr.set_connectable_true_events.AttachInspect( |
| metrics_bredr_node_, "set_connectable_true_events"); |
| metrics_.bredr.set_connectable_false_events.AttachInspect( |
| metrics_bredr_node_, "set_connectable_false_events"); |
| metrics_.bredr.request_discovery_events.AttachInspect( |
| metrics_bredr_node_, "request_discovery_events"); |
| metrics_.bredr.request_discoverable_events.AttachInspect( |
| metrics_bredr_node_, "request_discoverable_events"); |
| metrics_.bredr.open_l2cap_channel_requests.AttachInspect( |
| metrics_bredr_node_, "open_l2cap_channel_requests"); |
| } |
| |
| void AdapterImpl::ParseLEGetVendorCapabilitiesCommandComplete( |
| const hci::EmbossEventPacket& event) { |
| // NOTE: There can be various versions of this command complete event |
| // sent by the Controller. As fields are added, the version_supported |
| // field is incremented to signify which fields are available. In a previous |
| // undertaking (pwrev.dev/203950, fxrev.dev/1029396), we attempted to use |
| // Emboss' conditional fields feature to define fields based on the version |
| // they are included in. However, in practice, we've found vendors sometimes |
| // send the wrong number of bytes required for the version they claim to send. |
| // To tolerate these types of errors, we simply define all the fields in |
| // Emboss. If we receive a response smaller than what we expect, we use what |
| // the vendor sends, and fill the rest with zero to disable the feature. If we |
| // receive a response larger than what we expect, we read up to what we |
| // support and drop the rest of the data. |
| StaticPacket<android_hci::LEGetVendorCapabilitiesCommandCompleteEventView> |
| packet; |
| packet.SetToZeros(); |
| size_t copy_size = std::min(packet.data().size(), event.size()); |
| packet.mutable_data().Write(event.data().data(), copy_size); |
| |
| auto params = packet.view(); |
| state_.android_vendor_capabilities = AndroidVendorCapabilities::New(params); |
| |
| size_t expected_size = 0; |
| uint8_t major = params.version_supported().major_number().Read(); |
| uint8_t minor = params.version_supported().minor_number().Read(); |
| |
| if (major == 0 && minor == 0) { |
| // The version_supported field was only introduced into the command in |
| // Version 0.95. Controllers that use the base version, Version 0.55, |
| // don't have the version_supported field. |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_0_55_size(); |
| } else if (major == 0 && minor == 95) { |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_0_95_size(); |
| } else if (major == 0 && minor == 96) { |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_0_96_size(); |
| } else if (major == 0 && minor == 98) { |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_0_98_size(); |
| } else if (major == 1 && minor == 03) { |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_1_03_size(); |
| } else if (major == 1 && minor == 04) { |
| expected_size = android_hci::LEGetVendorCapabilitiesCommandCompleteEvent:: |
| version_1_04_size(); |
| } |
| |
| if (event.size() != expected_size) { |
| bt_log(WARN, |
| "gap", |
| "LE Get Vendor Capabilities Command Complete, received %zu bytes, " |
| "expected %zu bytes, version: %d.%d", |
| event.size(), |
| expected_size, |
| major, |
| minor); |
| } |
| } |
| |
| void AdapterImpl::InitializeStep1() { |
| state_.controller_features = hci_->GetFeatures(); |
| |
| // Start by resetting the controller to a clean state and then send |
| // informational parameter commands that are not specific to LE or BR/EDR. The |
| // commands sent here are mandatory for all LE controllers. |
| // |
| // NOTE: It's safe to pass capture |this| directly in the callbacks as |
| // |init_seq_runner_| will internally invalidate the callbacks if it ever gets |
| // deleted. |
| |
| // HCI_Reset |
| auto reset_command = |
| hci::EmbossCommandPacket::New<pw::bluetooth::emboss::ResetCommandWriter>( |
| hci_spec::kReset); |
| init_seq_runner_->QueueCommand(std::move(reset_command)); |
| |
| // HCI_Read_Local_Version_Information |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadLocalVersionInformationCommandView>( |
| hci_spec::kReadLocalVersionInfo), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error( |
| cmd_complete, WARN, "gap", "read local version info failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete |
| .return_params<hci_spec::ReadLocalVersionInfoReturnParams>(); |
| state_.hci_version = params->hci_version; |
| }); |
| |
| // HCI_Read_Local_Supported_Commands |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadLocalSupportedCommandsCommandView>( |
| hci_spec::kReadLocalSupportedCommands), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "read local supported commands failed")) { |
| return; |
| } |
| auto params = cmd_complete.return_params< |
| hci_spec::ReadLocalSupportedCommandsReturnParams>(); |
| std::memcpy(state_.supported_commands, |
| params->supported_commands, |
| sizeof(params->supported_commands)); |
| }); |
| |
| // HCI_Read_Local_Supported_Features |
| InitQueueReadLMPFeatureMaskPage(0); |
| |
| // HCI_Read_BD_ADDR |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadBdAddrCommandView>(hci_spec::kReadBDADDR), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, WARN, "gap", "read BR_ADDR failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete.return_params<hci_spec::ReadBDADDRReturnParams>(); |
| state_.controller_address = params->bd_addr; |
| }); |
| |
| if (state().IsControllerFeatureSupported( |
| pw::bluetooth::Controller::FeaturesBits::kAndroidVendorExtensions)) { |
| bt_log(INFO, |
| "gap", |
| "controller supports android hci extensions, querying exact feature " |
| "set"); |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| android_hci::LEGetVendorCapabilitiesCommandView>( |
| hci_android::kLEGetVendorCapabilities), |
| [this](const hci::EmbossEventPacket& event) { |
| if (hci_is_error( |
| event, |
| WARN, |
| "gap", |
| "Failed to query android hci extension capabilities")) { |
| return; |
| } |
| |
| ParseLEGetVendorCapabilitiesCommandComplete(event); |
| }); |
| } |
| |
| init_seq_runner_->RunCommands([this](hci::Result<> status) mutable { |
| if (bt_is_error(status, |
| ERROR, |
| "gap", |
| "Failed to obtain initial controller information: %s", |
| bt_str(status))) { |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| |
| InitializeStep2(); |
| }); |
| } |
| |
| void AdapterImpl::InitializeStep2() { |
| BT_DEBUG_ASSERT(IsInitializing()); |
| |
| // Low Energy MUST be supported. We don't support BR/EDR-only controllers. |
| if (!state_.IsLowEnergySupported()) { |
| bt_log(ERROR, "gap", "Bluetooth LE not supported by controller"); |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| |
| // Check the HCI version. We officially only support 4.2+ only but for now we |
| // just log a warning message if the version is legacy. |
| if (state_.hci_version < |
| pw::bluetooth::emboss::CoreSpecificationVersion::V4_2) { |
| bt_log(WARN, |
| "gap", |
| "controller is using legacy HCI version %s", |
| hci_spec::HCIVersionToString(state_.hci_version).c_str()); |
| } |
| |
| BT_DEBUG_ASSERT(init_seq_runner_->IsReady()); |
| |
| // If the controller supports the Read Buffer Size command then send it. |
| // Otherwise we'll default to 0 when initializing the ACLDataChannel. |
| if (state_.IsCommandSupported(/*octet=*/14, |
| hci_spec::SupportedCommand::kReadBufferSize)) { |
| // HCI_Read_Buffer_Size |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadBufferSizeCommandView>( |
| hci_spec::kReadBufferSize), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error( |
| cmd_complete, WARN, "gap", "read buffer size failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete |
| .return_params<hci_spec::ReadBufferSizeReturnParams>(); |
| uint16_t acl_mtu = le16toh(params->hc_acl_data_packet_length); |
| uint16_t acl_max_count = |
| le16toh(params->hc_total_num_acl_data_packets); |
| if (acl_mtu && acl_max_count) { |
| state_.bredr_data_buffer_info = |
| hci::DataBufferInfo(acl_mtu, acl_max_count); |
| } |
| uint16_t sco_mtu = le16toh(params->hc_synchronous_data_packet_length); |
| uint16_t sco_max_count = |
| le16toh(params->hc_total_num_synchronous_data_packets); |
| if (sco_mtu && sco_max_count) { |
| state_.sco_buffer_info = |
| hci::DataBufferInfo(sco_mtu, sco_max_count); |
| } |
| }); |
| } |
| |
| // HCI_LE_Read_Local_Supported_Features |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::LEReadLocalSupportedFeaturesCommandView>( |
| hci_spec::kLEReadLocalSupportedFeatures), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "LE read local supported features failed")) { |
| return; |
| } |
| auto params = cmd_complete.return_params< |
| hci_spec::LEReadLocalSupportedFeaturesReturnParams>(); |
| state_.low_energy_state.supported_features_ = |
| le64toh(params->le_features); |
| }); |
| |
| // HCI_LE_Read_Supported_States |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::LEReadSupportedStatesCommandView>( |
| hci_spec::kLEReadSupportedStates), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "LE read local supported states failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete |
| .return_params<hci_spec::LEReadSupportedStatesReturnParams>(); |
| state_.low_energy_state.supported_states_ = le64toh(params->le_states); |
| }); |
| |
| if (state_.IsCommandSupported( |
| /*octet=*/41, hci_spec::SupportedCommand::kLEReadBufferSizeV2)) { |
| // HCI_LE_Read_Buffer_Size [v2] |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::LEReadBufferSizeCommandV2View>( |
| hci_spec::kLEReadBufferSizeV2), |
| [this](const hci::EmbossEventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "LE read buffer size [v2] failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete |
| .view<pw::bluetooth::emboss:: |
| LEReadBufferSizeV2CommandCompleteEventView>(); |
| uint16_t acl_mtu = params.le_acl_data_packet_length().Read(); |
| uint8_t acl_max_count = params.total_num_le_acl_data_packets().Read(); |
| if (acl_mtu && acl_max_count) { |
| state_.low_energy_state.acl_data_buffer_info_ = |
| hci::DataBufferInfo(acl_mtu, acl_max_count); |
| } |
| uint16_t iso_mtu = params.iso_data_packet_length().Read(); |
| uint8_t iso_max_count = params.total_num_iso_data_packets().Read(); |
| if (iso_mtu && iso_max_count) { |
| state_.low_energy_state.iso_data_buffer_info_ = |
| hci::DataBufferInfo(iso_mtu, iso_max_count); |
| } |
| }); |
| } else { |
| // HCI_LE_Read_Buffer_Size [v1] |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::LEReadBufferSizeCommandV1View>( |
| hci_spec::kLEReadBufferSizeV1), |
| [this](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error( |
| cmd_complete, WARN, "gap", "LE read buffer size failed")) { |
| return; |
| } |
| auto params = |
| cmd_complete |
| .return_params<hci_spec::LEReadBufferSizeV1ReturnParams>(); |
| uint16_t mtu = le16toh(params->hc_le_acl_data_packet_length); |
| uint8_t max_count = params->hc_total_num_le_acl_data_packets; |
| if (mtu && max_count) { |
| state_.low_energy_state.acl_data_buffer_info_ = |
| hci::DataBufferInfo(mtu, max_count); |
| } |
| }); |
| } |
| |
| if (state_.features.HasBit( |
| /*page=*/0u, |
| hci_spec::LMPFeature::kSecureSimplePairingControllerSupport)) { |
| // HCI_Write_Simple_Pairing_Mode |
| auto write_spm = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::WriteSimplePairingModeCommandWriter>( |
| hci_spec::kWriteSimplePairingMode); |
| auto write_ssp_params = write_spm.view_t(); |
| write_ssp_params.simple_pairing_mode().Write( |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| init_seq_runner_->QueueCommand( |
| std::move(write_spm), [](const hci::EventPacket& event) { |
| // Warn if the command failed |
| hci_is_error(event, WARN, "gap", "write simple pairing mode failed"); |
| }); |
| } |
| |
| // If there are extended features then try to read the first page of the |
| // extended features. |
| if (state_.features.HasBit(/*page=*/0u, |
| hci_spec::LMPFeature::kExtendedFeatures)) { |
| // HCI_Write_LE_Host_Support |
| if (!state_.IsCommandSupported( |
| /*octet=*/24, hci_spec::SupportedCommand::kWriteLEHostSupport)) { |
| bt_log(INFO, "gap", "LE Host is not supported"); |
| } else { |
| bt_log(INFO, "gap", "LE Host is supported. Enabling LE Host mode"); |
| auto cmd_packet = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::WriteLEHostSupportCommandWriter>( |
| hci_spec::kWriteLEHostSupport); |
| auto params = cmd_packet.view_t(); |
| params.le_supported_host().Write( |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| init_seq_runner_->QueueCommand( |
| std::move(cmd_packet), [](const hci::EventPacket& event) { |
| hci_is_error(event, WARN, "gap", "Write LE Host support failed"); |
| }); |
| } |
| |
| // HCI_Write_Secure_Connections_Host_Support |
| if (!state_.IsCommandSupported( |
| /*octet=*/32, |
| hci_spec::SupportedCommand::kWriteSecureConnectionsHostSupport)) { |
| bt_log(INFO, "gap", "Secure Connections (Host Support) is not supported"); |
| } else { |
| bt_log(INFO, |
| "gap", |
| "Secure Connections (Host Support) is supported. " |
| "Enabling Secure Connections (Host Support) mode"); |
| auto cmd_packet = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss:: |
| WriteSecureConnectionsHostSupportCommandWriter>( |
| hci_spec::kWriteSecureConnectionsHostSupport); |
| auto params = cmd_packet.view_t(); |
| params.secure_connections_host_support().Write( |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| init_seq_runner_->QueueCommand( |
| std::move(cmd_packet), [](const hci::EventPacket& event) { |
| hci_is_error(event, |
| WARN, |
| "gap", |
| "Write Secure Connections (Host Support) failed"); |
| }); |
| } |
| |
| // Read updated page 1 after host support bits enabled. |
| InitQueueReadLMPFeatureMaskPage(1); |
| } |
| |
| init_seq_runner_->RunCommands([this](hci::Result<> status) mutable { |
| if (bt_is_error( |
| status, |
| ERROR, |
| "gap", |
| "failed to obtain initial controller information (step 2)")) { |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| InitializeStep3(); |
| }); |
| } |
| |
| void AdapterImpl::InitializeStep3() { |
| BT_ASSERT(IsInitializing()); |
| BT_ASSERT(init_seq_runner_->IsReady()); |
| BT_ASSERT(!init_seq_runner_->HasQueuedCommands()); |
| |
| if (!state_.bredr_data_buffer_info.IsAvailable() && |
| !state_.low_energy_state.acl_data_buffer_info().IsAvailable()) { |
| bt_log(ERROR, "gap", "Both BR/EDR and LE buffers are unavailable"); |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| |
| // Now that we have all the ACL data buffer information it's time to |
| // initialize the ACLDataChannel. |
| if (!hci_->InitializeACLDataChannel( |
| state_.bredr_data_buffer_info, |
| state_.low_energy_state.acl_data_buffer_info())) { |
| bt_log(ERROR, "gap", "Failed to initialize ACLDataChannel (step 3)"); |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| |
| // The controller may not support SCO flow control (as implied by not |
| // supporting HCI_Write_Synchronous_Flow_Control_Enable), in which case we |
| // don't support HCI SCO on this controller yet. |
| // TODO(https://fxbug.dev/42171056): Support controllers that don't support |
| // SCO flow control. |
| bool sco_flow_control_supported = state_.IsCommandSupported( |
| /*octet=*/10, |
| hci_spec::SupportedCommand::kWriteSynchronousFlowControlEnable); |
| if (state_.sco_buffer_info.IsAvailable() && sco_flow_control_supported) { |
| // Enable SCO flow control. |
| auto sync_flow_control = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::WriteSynchronousFlowControlEnableCommandWriter>( |
| hci_spec::kWriteSynchronousFlowControlEnable); |
| auto flow_control_params = sync_flow_control.view_t(); |
| flow_control_params.synchronous_flow_control_enable().Write( |
| pw::bluetooth::emboss::GenericEnableParam::ENABLE); |
| init_seq_runner_->QueueCommand( |
| std::move(sync_flow_control), [this](const hci::EventPacket& event) { |
| if (hci_is_error(event, |
| ERROR, |
| "gap", |
| "Write synchronous flow control enable failed, " |
| "proceeding without HCI " |
| "SCO support")) { |
| return; |
| } |
| |
| if (!hci_->InitializeScoDataChannel(state_.sco_buffer_info)) { |
| bt_log(WARN, |
| "gap", |
| "Failed to initialize ScoDataChannel, proceeding without " |
| "HCI SCO support"); |
| return; |
| } |
| bt_log(DEBUG, "gap", "ScoDataChannel initialized successfully"); |
| }); |
| } else { |
| bt_log(INFO, |
| "gap", |
| "HCI SCO not supported (SCO buffer available: %d, SCO flow control " |
| "supported: %d)", |
| state_.sco_buffer_info.IsAvailable(), |
| sco_flow_control_supported); |
| } |
| |
| hci_->AttachInspect(adapter_node_); |
| |
| // Create ChannelManager, if we haven't been provided one for testing. Doing |
| // so here lets us guarantee that AclDataChannel's lifetime is a superset of |
| // ChannelManager's lifetime. |
| if (!l2cap_) { |
| // Initialize ChannelManager to make it available for the next |
| // initialization step. The AclDataChannel must be initialized before |
| // creating ChannelManager. |
| l2cap_ = l2cap::ChannelManager::Create(hci_->acl_data_channel(), |
| hci_->command_channel(), |
| /*random_channel_ids=*/true, |
| dispatcher_); |
| l2cap_->AttachInspect(adapter_node_, |
| l2cap::ChannelManager::kInspectNodeName); |
| } |
| |
| // HCI_Set_Event_Mask |
| { |
| uint64_t event_mask = BuildEventMask(); |
| auto set_event = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::SetEventMaskCommandWriter>( |
| hci_spec::kSetEventMask); |
| auto set_event_params = set_event.view_t(); |
| set_event_params.event_mask().Write(event_mask); |
| init_seq_runner_->QueueCommand( |
| std::move(set_event), [](const hci::EventPacket& event) { |
| hci_is_error(event, WARN, "gap", "set event mask failed"); |
| }); |
| } |
| |
| // HCI_LE_Set_Event_Mask |
| { |
| uint64_t event_mask = BuildLEEventMask(); |
| auto cmd_packet = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::LESetEventMaskCommandWriter>( |
| hci_spec::kLESetEventMask); |
| cmd_packet.view_t().le_event_mask().BackingStorage().WriteUInt(event_mask); |
| init_seq_runner_->QueueCommand( |
| std::move(cmd_packet), [](const hci::EventPacket& event) { |
| hci_is_error(event, WARN, "gap", "LE set event mask failed"); |
| }); |
| } |
| |
| // If page 2 of the extended features bitfield is available, read it |
| if (max_lmp_feature_page_index_.has_value() && |
| max_lmp_feature_page_index_.value() > 1) { |
| InitQueueReadLMPFeatureMaskPage(2); |
| } |
| |
| init_seq_runner_->RunCommands([this](hci::Result<> status) mutable { |
| if (bt_is_error( |
| status, |
| ERROR, |
| "gap", |
| "failed to obtain initial controller information (step 3)")) { |
| CompleteInitialization(/*success=*/false); |
| return; |
| } |
| InitializeStep4(); |
| }); |
| } |
| |
| void AdapterImpl::InitializeStep4() { |
| // Initialize the scan manager and low energy adapters based on current |
| // feature support |
| BT_DEBUG_ASSERT(IsInitializing()); |
| |
| // We use the public controller address as the local LE identity address. |
| DeviceAddress adapter_identity(DeviceAddress::Type::kLEPublic, |
| state_.controller_address); |
| |
| // Initialize the LE local address manager. |
| le_address_manager_ = std::make_unique<LowEnergyAddressManager>( |
| adapter_identity, |
| fit::bind_member<&AdapterImpl::IsLeRandomAddressChangeAllowed>(this), |
| hci_->command_channel()->AsWeakPtr(), |
| dispatcher_); |
| |
| // Initialize the HCI adapters. Note: feature support for extended |
| // scanning, connections, etc are all grouped under the extended advertising |
| // feature flag. |
| bool extended = state().low_energy_state.IsFeatureSupported( |
| hci_spec::LESupportedFeature::kLEExtendedAdvertising); |
| bt_log(INFO, |
| "gap", |
| "controller support for extended operations: %s", |
| extended ? "yes" : "no"); |
| hci_le_advertiser_ = CreateAdvertiser(extended); |
| hci_le_connector_ = CreateConnector(extended); |
| hci_le_scanner_ = CreateScanner(extended); |
| |
| // Initialize the LE manager objects |
| le_discovery_manager_ = std::make_unique<LowEnergyDiscoveryManager>( |
| hci_le_scanner_.get(), &peer_cache_, dispatcher_); |
| le_discovery_manager_->AttachInspect( |
| adapter_node_, kInspectLowEnergyDiscoveryManagerNodeName); |
| le_discovery_manager_->set_peer_connectable_callback( |
| fit::bind_member<&AdapterImpl::OnLeAutoConnectRequest>(this)); |
| |
| le_connection_manager_ = std::make_unique<LowEnergyConnectionManager>( |
| hci_->command_channel()->AsWeakPtr(), |
| le_address_manager_.get(), |
| hci_le_connector_.get(), |
| &peer_cache_, |
| l2cap_.get(), |
| gatt_, |
| le_discovery_manager_->GetWeakPtr(), |
| sm::SecurityManager::Create, |
| state(), |
| dispatcher_); |
| le_connection_manager_->AttachInspect( |
| adapter_node_, kInspectLowEnergyConnectionManagerNodeName); |
| |
| le_advertising_manager_ = std::make_unique<LowEnergyAdvertisingManager>( |
| hci_le_advertiser_.get(), le_address_manager_.get()); |
| low_energy_ = std::make_unique<LowEnergyImpl>(this); |
| |
| // Initialize the BR/EDR manager objects if the controller supports BR/EDR. |
| if (state_.IsBREDRSupported()) { |
| DeviceAddress local_bredr_address(DeviceAddress::Type::kBREDR, |
| state_.controller_address); |
| |
| bredr_connection_manager_ = std::make_unique<BrEdrConnectionManager>( |
| hci_, |
| &peer_cache_, |
| local_bredr_address, |
| l2cap_.get(), |
| state_.features.HasBit(/*page=*/0, |
| hci_spec::LMPFeature::kInterlacedPageScan), |
| state_.IsLocalSecureConnectionsSupported(), |
| dispatcher_); |
| bredr_connection_manager_->AttachInspect( |
| adapter_node_, kInspectBrEdrConnectionManagerNodeName); |
| |
| pw::bluetooth::emboss::InquiryMode mode = |
| pw::bluetooth::emboss::InquiryMode::STANDARD; |
| if (state_.features.HasBit( |
| /*page=*/0, hci_spec::LMPFeature::kExtendedInquiryResponse)) { |
| mode = pw::bluetooth::emboss::InquiryMode::EXTENDED; |
| } else if (state_.features.HasBit( |
| /*page=*/0, hci_spec::LMPFeature::kRSSIwithInquiryResults)) { |
| mode = pw::bluetooth::emboss::InquiryMode::RSSI; |
| } |
| |
| bredr_discovery_manager_ = std::make_unique<BrEdrDiscoveryManager>( |
| dispatcher_, hci_->command_channel()->AsWeakPtr(), mode, &peer_cache_); |
| bredr_discovery_manager_->AttachInspect( |
| adapter_node_, kInspectBrEdrDiscoveryManagerNodeName); |
| |
| sdp_server_ = std::make_unique<sdp::Server>(l2cap_.get()); |
| sdp_server_->AttachInspect(adapter_node_); |
| |
| bredr_ = std::make_unique<BrEdrImpl>(this); |
| } |
| |
| // Override the current privacy setting and always use the local stable |
| // identity address (i.e. not a RPA) when initiating connections. This |
| // improves interoperability with certain Bluetooth peripherals that fail to |
| // authenticate following a RPA rotation. |
| // |
| // The implication here is that the public address is revealed in LL |
| // connection request PDUs. LE central privacy is still preserved during an |
| // active scan, i.e. in LL scan request PDUs. |
| // |
| // TODO(https://fxbug.dev/42141593): Remove this temporary fix once we |
| // determine the root cause for authentication failures. |
| hci_le_connector_->UseLocalIdentityAddress(); |
| |
| // Update properties before callback called so properties can be verified in |
| // unit tests. |
| UpdateInspectProperties(); |
| |
| // Assign a default name and device class before notifying completion. |
| auto self = weak_self_.GetWeakPtr(); |
| SetLocalName(kDefaultLocalName, [self](auto status) mutable { |
| // Set the default device class - a computer with audio. |
| // TODO(https://fxbug.dev/42074312): set this from a platform configuration |
| // file |
| DeviceClass dev_class(DeviceClass::MajorClass::kComputer); |
| dev_class.SetServiceClasses({DeviceClass::ServiceClass::kAudio}); |
| self->SetDeviceClass(dev_class, [self](const auto&) { |
| self->CompleteInitialization(/*success=*/true); |
| }); |
| }); |
| } |
| |
| bool AdapterImpl::CompleteInitialization(bool success) { |
| if (!init_cb_) { |
| return false; |
| } |
| |
| if (success) { |
| init_state_ = State::kInitialized; |
| } else { |
| CleanUp(); |
| } |
| |
| init_cb_(success); |
| return true; |
| } |
| |
| void AdapterImpl::InitQueueReadLMPFeatureMaskPage(uint8_t page) { |
| BT_DEBUG_ASSERT(init_seq_runner_); |
| BT_DEBUG_ASSERT(init_seq_runner_->IsReady()); |
| |
| if (max_lmp_feature_page_index_.has_value() && |
| page > max_lmp_feature_page_index_.value()) { |
| bt_log(WARN, |
| "gap", |
| "Maximum value of LMP features mask page is %lu. Received page %hu", |
| max_lmp_feature_page_index_.value(), |
| page); |
| return; |
| } |
| |
| if (page == 0) { |
| init_seq_runner_->QueueCommand( |
| hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadLocalSupportedFeaturesCommandView>( |
| hci_spec::kReadLocalSupportedFeatures), |
| [this, page](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "read local supported features failed")) { |
| return; |
| } |
| auto params = cmd_complete.return_params< |
| hci_spec::ReadLocalSupportedFeaturesReturnParams>(); |
| state_.features.SetPage(page, le64toh(params->lmp_features)); |
| }); |
| return; |
| } |
| |
| if (!state_.features.HasBit(/*page=*/0u, |
| hci_spec::LMPFeature::kExtendedFeatures)) { |
| bt_log(WARN, "gap", "LMP features mask does not have extended features"); |
| max_lmp_feature_page_index_ = 0; |
| return; |
| } |
| |
| if (!max_lmp_feature_page_index_.has_value() || |
| page <= max_lmp_feature_page_index_.value()) { |
| // HCI_Read_Local_Extended_Features |
| auto cmd_packet = hci::EmbossCommandPacket::New< |
| pw::bluetooth::emboss::ReadLocalExtendedFeaturesCommandWriter>( |
| hci_spec::kReadLocalExtendedFeatures); |
| cmd_packet.view_t().page_number().Write(page); // Try to read |page| |
| |
| init_seq_runner_->QueueCommand( |
| std::move(cmd_packet), |
| [this, page](const hci::EventPacket& cmd_complete) { |
| if (hci_is_error(cmd_complete, |
| WARN, |
| "gap", |
| "read local extended features failed")) { |
| return; |
| } |
| auto params = cmd_complete.return_params< |
| hci_spec::ReadLocalExtendedFeaturesReturnParams>(); |
| state_.features.SetPage(page, le64toh(params->extended_lmp_features)); |
| max_lmp_feature_page_index_ = params->maximum_page_number; |
| }); |
| } |
| } |
| |
| void AdapterImpl::UpdateInspectProperties() { |
| inspect_properties_.adapter_id = |
| adapter_node_.CreateString("adapter_id", identifier_.ToString()); |
| inspect_properties_.hci_version = adapter_node_.CreateString( |
| "hci_version", hci_spec::HCIVersionToString(state_.hci_version)); |
| |
| inspect_properties_.bredr_max_num_packets = adapter_node_.CreateUint( |
| "bredr_max_num_packets", state_.bredr_data_buffer_info.max_num_packets()); |
| inspect_properties_.bredr_max_data_length = adapter_node_.CreateUint( |
| "bredr_max_data_length", state_.bredr_data_buffer_info.max_data_length()); |
| |
| inspect_properties_.le_max_num_packets = adapter_node_.CreateUint( |
| "le_max_num_packets", |
| state_.low_energy_state.acl_data_buffer_info().max_num_packets()); |
| inspect_properties_.le_max_data_length = adapter_node_.CreateUint( |
| "le_max_data_length", |
| state_.low_energy_state.acl_data_buffer_info().max_data_length()); |
| |
| inspect_properties_.sco_max_num_packets = adapter_node_.CreateUint( |
| "sco_max_num_packets", state_.sco_buffer_info.max_num_packets()); |
| inspect_properties_.sco_max_data_length = adapter_node_.CreateUint( |
| "sco_max_data_length", state_.sco_buffer_info.max_data_length()); |
| |
| inspect_properties_.lmp_features = |
| adapter_node_.CreateString("lmp_features", state_.features.ToString()); |
| |
| auto le_features = bt_lib_cpp_string::StringPrintf( |
| "0x%016lx", state_.low_energy_state.supported_features()); |
| inspect_properties_.le_features = |
| adapter_node_.CreateString("le_features", le_features); |
| } |
| |
| void AdapterImpl::CleanUp() { |
| if (init_state_ == State::kNotInitialized) { |
| bt_log(DEBUG, "gap", "clean up: not initialized"); |
| return; |
| } |
| |
| init_state_ = State::kNotInitialized; |
| state_ = AdapterState(); |
| transport_error_cb_ = nullptr; |
| |
| // Destroy objects in reverse order of construction. |
| low_energy_ = nullptr; |
| bredr_ = nullptr; |
| sdp_server_ = nullptr; |
| bredr_discovery_manager_ = nullptr; |
| le_advertising_manager_ = nullptr; |
| le_connection_manager_ = nullptr; |
| le_discovery_manager_ = nullptr; |
| |
| hci_le_connector_ = nullptr; |
| hci_le_advertiser_ = nullptr; |
| hci_le_scanner_ = nullptr; |
| |
| le_address_manager_ = nullptr; |
| |
| l2cap_ = nullptr; |
| |
| hci_.reset(); |
| } |
| |
| void AdapterImpl::OnTransportError() { |
| bt_log(INFO, "gap", "HCI transport error"); |
| if (CompleteInitialization(/*success=*/false)) { |
| return; |
| } |
| if (transport_error_cb_) { |
| transport_error_cb_(); |
| } |
| } |
| |
| void AdapterImpl::OnLeAutoConnectRequest(Peer* peer) { |
| BT_DEBUG_ASSERT(le_connection_manager_); |
| BT_DEBUG_ASSERT(peer); |
| BT_DEBUG_ASSERT(peer->le()); |
| |
| PeerId peer_id = peer->identifier(); |
| |
| if (!peer->le()->should_auto_connect()) { |
| bt_log(DEBUG, |
| "gap", |
| "ignoring auto-connection (peer->should_auto_connect() is false) " |
| "(peer: %s)", |
| bt_str(peer_id)); |
| return; |
| } |
| |
| LowEnergyConnectionOptions options{.auto_connect = true}; |
| |
| auto self = weak_self_.GetWeakPtr(); |
| le_connection_manager_->Connect( |
| peer_id, |
| [self, peer_id](auto result) { |
| if (!self.is_alive()) { |
| bt_log(DEBUG, "gap", "ignoring auto-connection (adapter destroyed)"); |
| return; |
| } |
| |
| if (result.is_error()) { |
| bt_log(INFO, |
| "gap", |
| "failed to auto-connect (peer: %s, error: %s)", |
| bt_str(peer_id), |
| HostErrorToString(result.error_value()).c_str()); |
| return; |
| } |
| |
| auto conn = std::move(result).value(); |
| BT_ASSERT(conn); |
| bt_log(INFO, "gap", "peer auto-connected (peer: %s)", bt_str(peer_id)); |
| if (self->auto_conn_cb_) { |
| self->auto_conn_cb_(std::move(conn)); |
| } |
| }, |
| options); |
| } |
| |
| bool AdapterImpl::IsLeRandomAddressChangeAllowed() { |
| return hci_le_advertiser_->AllowsRandomAddressChange() && |
| hci_le_scanner_->AllowsRandomAddressChange() && |
| hci_le_connector_->AllowsRandomAddressChange(); |
| } |
| |
| std::unique_ptr<Adapter> Adapter::Create( |
| pw::async::Dispatcher& pw_dispatcher, |
| hci::Transport::WeakPtr hci, |
| gatt::GATT::WeakPtr gatt, |
| std::unique_ptr<l2cap::ChannelManager> l2cap) { |
| return std::make_unique<AdapterImpl>( |
| pw_dispatcher, hci, gatt, std::move(l2cap)); |
| } |
| |
| } // namespace bt::gap |