1 // Copyright 2024 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 #pragma once 16 17 #include <fuchsia/bluetooth/le/cpp/fidl.h> 18 19 #include <memory> 20 #include <unordered_map> 21 22 #include "lib/fidl/cpp/binding.h" 23 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/gatt_client_server.h" 24 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/low_energy_connection_server.h" 25 #include "pw_bluetooth_sapphire/fuchsia/host/fidl/server_base.h" 26 #include "pw_bluetooth_sapphire/internal/host/common/macros.h" 27 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h" 28 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_discovery_manager.h" 29 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h" 30 31 namespace bthost { 32 33 // Implements the low_energy::Central FIDL interface. 34 class LowEnergyCentralServer 35 : public AdapterServerBase<fuchsia::bluetooth::le::Central> { 36 public: 37 // The maximum number of peers that will be queued for a 38 // ScanResultWatcher.Watch call. This hard limit prevents unbounded memory 39 // usage for unresponsive clients. The value is mostly arbitrary, as queued 40 // `PeerId`s are small and peak memory usage, occurring when creating a vector 41 // of FIDL `le.Peer`s, is limited by the size of the FIDL channel. 42 constexpr static const size_t kMaxPendingScanResultWatcherPeers = 100; 43 44 LowEnergyCentralServer( 45 bt::gap::Adapter::WeakPtr adapter, 46 ::fidl::InterfaceRequest<fuchsia::bluetooth::le::Central> request, 47 bt::gatt::GATT::WeakPtr gatt); 48 ~LowEnergyCentralServer() override; 49 50 // Returns the connection pointer in the connections_deprecated_ map, if it 51 // exists. The pointer will be nullptr if a request is pending. Should only be 52 // used for testing. 53 std::optional<bt::gap::LowEnergyConnectionHandle*> FindConnectionForTesting( 54 bt::PeerId identifier); 55 56 private: 57 class ScanResultWatcherServer 58 : public ServerBase<fuchsia::bluetooth::le::ScanResultWatcher> { 59 public: 60 using WatchCallbackOnce = 61 fit::callback<void(std::vector<fuchsia::bluetooth::le::Peer>)>; 62 63 // `error_cb` will be called when the client closes the protocol. 64 ScanResultWatcherServer( 65 bt::gap::Adapter::WeakPtr adapter, 66 fidl::InterfaceRequest<fuchsia::bluetooth::le::ScanResultWatcher> 67 watcher, 68 fit::callback<void()> error_cb); 69 ~ScanResultWatcherServer() override = default; 70 71 // Closes the protocol and sends `epitaph` as the epitaph. Idempotent. 72 void Close(zx_status_t epitaph); 73 74 // Queue `peers` to be sent in response to `Watch()`. 75 void AddPeers(std::unordered_set<bt::PeerId> peers); 76 77 // fuchsia::bluetooth::le::ScanResultWatcher overrides: 78 void Watch(WatchCallback callback) override; 79 80 private: 81 // If the client has a pending `Watch()`, send the maximum number of peers 82 // that will fit in the channel. 83 void MaybeSendPeers(); 84 85 bt::gap::Adapter::WeakPtr adapter_; 86 std::unordered_set<bt::PeerId> updated_peers_; 87 WatchCallbackOnce watch_callback_ = nullptr; 88 fit::callback<void()> error_callback_; 89 }; 90 91 // ScanInstance represents a call to `Scan` that has not stopped yet. 92 class ScanInstance { 93 public: 94 using ScanCompleteCallback = fit::callback<void()>; 95 96 ScanInstance( 97 bt::gap::Adapter::WeakPtr adapter, 98 LowEnergyCentralServer* central_server, 99 std::vector<fuchsia::bluetooth::le::Filter> filters, 100 fidl::InterfaceRequest<fuchsia::bluetooth::le::ScanResultWatcher> 101 watcher, 102 ScanCallback cb); 103 ~ScanInstance(); 104 // Closes the ScanResultWatcher protocol with the epitaph `status` and sends 105 // an empty response to `Scan`. Idempotent. 106 void Close(zx_status_t status); 107 108 // Queue peers to be sent to the client via `ScanResultWatcher.Watch`. 109 // `peers` will be filtered by the client's `ScanOptions` filters before 110 // being sent. 111 void FilterAndAddPeers(std::unordered_set<bt::PeerId> peers); 112 113 private: 114 std::unique_ptr<bt::gap::LowEnergyDiscoverySession> scan_session_; 115 ScanResultWatcherServer result_watcher_; 116 // Callback used to send an empty response to the client's `Scan()` call. 117 ScanCompleteCallback scan_complete_callback_; 118 bt::gap::PeerCache::CallbackId peer_updated_callback_id_; 119 // The filters specified in `ScanOptions`. 120 std::vector<bt::gap::DiscoveryFilter> filters_; 121 LowEnergyCentralServer* central_server_; 122 bt::gap::Adapter::WeakPtr adapter_; 123 WeakSelf<ScanInstance> weak_self_; 124 }; 125 126 // fuchsia::bluetooth::le::Central overrides: 127 void Scan(fuchsia::bluetooth::le::ScanOptions options, 128 fidl::InterfaceRequest<fuchsia::bluetooth::le::ScanResultWatcher> 129 result_watcher, 130 ScanCallback callback) override; 131 void Connect(fuchsia::bluetooth::PeerId id, 132 fuchsia::bluetooth::le::ConnectionOptions options, 133 fidl::InterfaceRequest<::fuchsia::bluetooth::le::Connection> 134 request) override; 135 136 void GetPeripherals(::fidl::VectorPtr<::std::string> service_uuids, 137 GetPeripheralsCallback callback) override; 138 void GetPeripheral(::std::string identifier, 139 GetPeripheralCallback callback) override; 140 void StartScan(fuchsia::bluetooth::le::ScanFilterPtr filter, 141 StartScanCallback callback) override; 142 void StopScan() override; 143 void ConnectPeripheral( 144 ::std::string identifier, 145 fuchsia::bluetooth::le::ConnectionOptions connection_options, 146 ::fidl::InterfaceRequest<fuchsia::bluetooth::gatt::Client> client_request, 147 ConnectPeripheralCallback callback) override; 148 void DisconnectPeripheral(::std::string identifier, 149 DisconnectPeripheralCallback callback) override; 150 151 // fuchsia::bluetooth::le::ChannelListenerRegistry overrides: 152 void ListenL2cap( 153 fuchsia::bluetooth::le::ChannelListenerRegistryListenL2capRequest request, 154 ListenL2capCallback callback) override; 155 156 // Called by |scan_session_| when a device is discovered. 157 void OnScanResult(const bt::gap::Peer& peer); 158 159 // Notifies the delegate that the scan state for this Central has changed. 160 void NotifyScanStateChanged(bool scanning); 161 162 // Notifies the delegate that the device with the given identifier has been 163 // disconnected. 164 void NotifyPeripheralDisconnected(bt::PeerId peer_id); 165 ClearScan()166 void ClearScan() { scan_instance_.reset(); } 167 168 // GATT is used to construct GattClientServers upon connection. 169 bt::gatt::GATT::WeakPtr gatt_; 170 171 // Stores active GATT client FIDL servers. Only 1 client server per peer may 172 // exist. 173 std::unordered_map<bt::PeerId, std::unique_ptr<GattClientServer>> 174 gatt_client_servers_; 175 176 // The currently active LE discovery session. This is initialized when a 177 // client requests to perform a scan. 178 bool requesting_scan_deprecated_; 179 std::unique_ptr<bt::gap::LowEnergyDiscoverySession> scan_session_deprecated_; 180 181 std::unique_ptr<ScanInstance> scan_instance_; 182 183 // This client's connection references. A client can hold a connection to 184 // multiple peers. Each key is a peer identifier. Each value is 185 // a. nullptr, if a connect request to this device is currently pending. 186 // b. a valid reference if this Central is holding a connection reference to 187 // this device. 188 std::unordered_map<bt::PeerId, std::unique_ptr<LowEnergyConnectionServer>> 189 connections_; 190 std::unordered_map<bt::PeerId, 191 std::unique_ptr<bt::gap::LowEnergyConnectionHandle>> 192 connections_deprecated_; 193 194 // Keep this as the last member to make sure that all weak pointers are 195 // invalidated before other members get destroyed. 196 WeakSelf<LowEnergyCentralServer> weak_self_; 197 198 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyCentralServer); 199 }; 200 201 } // namespace bthost 202