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 #include <lib/async/cpp/wait.h> 19 #include <lib/fidl/cpp/binding.h> 20 21 #include <memory> 22 #include <unordered_map> 23 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/common/weak_self.h" 28 #include "pw_bluetooth_sapphire/internal/host/gap/adapter.h" 29 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_advertising_manager.h" 30 #include "pw_bluetooth_sapphire/internal/host/gap/low_energy_connection_manager.h" 31 32 namespace bthost { 33 34 // Implements the low_energy::Peripheral FIDL interface. 35 class LowEnergyPeripheralServer 36 : public AdapterServerBase<fuchsia::bluetooth::le::Peripheral> { 37 public: 38 LowEnergyPeripheralServer( 39 bt::gap::Adapter::WeakPtr adapter, 40 bt::gatt::GATT::WeakPtr gatt, 41 fidl::InterfaceRequest<fuchsia::bluetooth::le::Peripheral> request, 42 bool privileged = false); 43 ~LowEnergyPeripheralServer() override; 44 45 // fuchsia::bluetooth::le::Peripheral overrides: 46 void Advertise( 47 fuchsia::bluetooth::le::AdvertisingParameters parameters, 48 fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> 49 advertised_peripheral, 50 AdvertiseCallback callback) override; 51 void StartAdvertising( 52 fuchsia::bluetooth::le::AdvertisingParameters parameters, 53 ::fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle> token, 54 StartAdvertisingCallback callback) override; 55 56 // fuchsia::bluetooth::le::ChannelListenerRegistry overrides: 57 void ListenL2cap( 58 fuchsia::bluetooth::le::ChannelListenerRegistryListenL2capRequest request, 59 ListenL2capCallback callback) override; 60 61 // Returns the connection handle associated with the given |id|, or nullptr if 62 // the peer with |id| is no longer connected. Should only be used for testing. 63 const bt::gap::LowEnergyConnectionHandle* FindConnectionForTesting( 64 bt::PeerId id) const; 65 66 private: 67 using ConnectionRefPtr = std::unique_ptr<bt::gap::LowEnergyConnectionHandle>; 68 using AdvertisementInstanceId = uint64_t; 69 using ConnectionServerId = uint64_t; 70 71 // Manages state associated with a single invocation of the 72 // `Peripheral.Advertise` method. 73 class AdvertisementInstance final { 74 public: 75 using AdvertiseCompleteCallback = fit::callback<void( 76 fuchsia::bluetooth::le::Peripheral_Advertise_Result)>; 77 78 // |complete_cb| will be called to send a Peripheral.Advertise response to 79 // the client when an error occurs or this AdvertisementInstance is 80 // destroyed. This is done so that the FIDL client can determine when the 81 // server has terminated this AdvertisementInstance (this is useful for 82 // reconfiguring an advertisement). 83 AdvertisementInstance( 84 LowEnergyPeripheralServer* peripheral_server, 85 AdvertisementInstanceId id, 86 fuchsia::bluetooth::le::AdvertisingParameters parameters, 87 fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> 88 handle, 89 AdvertiseCompleteCallback complete_cb); 90 ~AdvertisementInstance(); 91 92 // This method is separate from the constructor because HCI-level 93 // advertising may be started many times over the life of this object. 94 void StartAdvertising(); 95 96 // Called when a central connects to us. When this is called, the 97 // advertisement in |advertisement_id| has been stopped. 98 void OnConnected(bt::gap::AdvertisementId advertisement_id, 99 bt::gap::Adapter::LowEnergy::ConnectionResult result); 100 101 private: 102 // After advertising successfully starts, the advertisement instance must be 103 // registered to tie advertising to the lifetime of this object. 104 void Register(bt::gap::AdvertisementInstance instance); 105 106 // End the advertisement with a result. Idempotent. 107 // This object should be destroyed immediately after calling this method. 108 void CloseWith( 109 fpromise::result<void, fuchsia::bluetooth::le::PeripheralError> result); 110 111 LowEnergyPeripheralServer* peripheral_server_; 112 AdvertisementInstanceId id_; 113 fuchsia::bluetooth::le::AdvertisingParameters parameters_; 114 115 // The advertising handle set by Register. When destroyed, advertising will 116 // be stopped. 117 std::optional<bt::gap::AdvertisementInstance> instance_; 118 119 // The AdvertisedPeripheral protocol representing this advertisement. 120 fidl::InterfacePtr<fuchsia::bluetooth::le::AdvertisedPeripheral> 121 advertised_peripheral_; 122 123 // Callback used to send a response to the Advertise request that started 124 // this advertisement. 125 AdvertiseCompleteCallback advertise_complete_cb_; 126 127 WeakSelf<AdvertisementInstance> weak_self_; 128 129 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdvertisementInstance); 130 }; 131 132 class AdvertisementInstanceDeprecated final { 133 public: 134 explicit AdvertisementInstanceDeprecated( 135 fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle> 136 handle); 137 ~AdvertisementInstanceDeprecated(); 138 139 // Begin watching for ZX_CHANNEL_PEER_CLOSED events on the AdvertisingHandle 140 // this was initialized with. The returned status will indicate an error if 141 // wait cannot be initiated (e.g. because the peer closed its end of the 142 // channel). 143 zx_status_t Register(bt::gap::AdvertisementInstance instance); 144 145 // Returns the ID assigned to this instance, or 146 // bt::gap::kInvalidAdvertisementId if one wasn't assigned. id()147 bt::gap::AdvertisementId id() const { 148 return instance_ ? instance_->id() : bt::gap::kInvalidAdvertisementId; 149 } 150 151 private: 152 std::optional<bt::gap::AdvertisementInstance> instance_; 153 fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle> handle_; 154 async::Wait handle_closed_wait_; 155 156 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(AdvertisementInstanceDeprecated); 157 }; 158 159 // Called when a central connects to us. When this is called, the 160 // advertisement in |advertisement_id| has been stopped. 161 void OnConnectedDeprecated( 162 bt::gap::AdvertisementId advertisement_id, 163 bt::gap::Adapter::LowEnergy::ConnectionResult result); 164 165 // Sets up a Connection server and returns the client end. 166 fidl::InterfaceHandle<fuchsia::bluetooth::le::Connection> 167 CreateConnectionServer( 168 std::unique_ptr<bt::gap::LowEnergyConnectionHandle> connection); 169 170 // Common advertising initiation code shared by Peripheral.{Advertise, 171 // StartAdvertising}. If advertising was initiated by `Advertise`, 172 // `advertisement_instance` must be set to the identifier of the 173 // `AdvertisementInstance` that connections to this advertisement should be 174 // routed to. Otherwise, connections will be sent in a 175 // `Peripheral.OnConnected` event. 176 void StartAdvertisingInternal( 177 fuchsia::bluetooth::le::AdvertisingParameters& parameters, 178 bt::gap::Adapter::LowEnergy::AdvertisingStatusCallback status_cb, 179 std::optional<AdvertisementInstanceId> advertisement_instance = 180 std::nullopt); 181 RemoveAdvertisingInstance(AdvertisementInstanceId id)182 void RemoveAdvertisingInstance(AdvertisementInstanceId id) { 183 advertisements_.erase(id); 184 } 185 186 // Represents the current advertising instance: 187 // - Contains no value if advertising was never requested. 188 // - Contains a value while advertising is being (re)enabled and during 189 // advertising. 190 // - May correspond to an invalidated advertising instance if advertising is 191 // stopped by closing 192 // the AdvertisingHandle. 193 std::optional<AdvertisementInstanceDeprecated> advertisement_deprecated_; 194 195 // Map of all active advertisement instances associated with a call to 196 // `Advertise`. bt::gap::AdvertisementId cannot be used as a map key because 197 // it is received asynchronously, and we need an advertisement ID to refer to 198 // before advertising starts. 199 // TODO: https://fxbug.dev/42157682 - Support AdvertisedPeripheral protocols 200 // that outlive this Peripheral protocol. This may require passing 201 // AdvertisementInstances to HostServer. 202 AdvertisementInstanceId next_advertisement_instance_id_ = 0u; 203 std::unordered_map<AdvertisementInstanceId, AdvertisementInstance> 204 advertisements_; 205 206 // Connections that were initiated to this peripheral. A single Peripheral 207 // instance can hold many connections across numerous advertisements that it 208 // initiates during its lifetime. 209 ConnectionServerId next_connection_server_id_ = 0u; 210 std::unordered_map<ConnectionServerId, 211 std::unique_ptr<LowEnergyConnectionServer>> 212 connections_; 213 214 bt::gatt::GATT::WeakPtr gatt_; 215 216 // True if PrivilegedPeripheral created this server. Defaults to false. 217 bool privileged_; 218 219 // Keep this as the last member to make sure that all weak pointers are 220 // invalidated before other members get destroyed. 221 WeakSelf<LowEnergyPeripheralServer> weak_self_; 222 223 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyPeripheralServer); 224 }; 225 226 // Implements the fuchsia::bluetooth::le::PrivilegedPeripheral FIDL interface. 227 class LowEnergyPrivilegedPeripheralServer 228 : public AdapterServerBase<fuchsia::bluetooth::le::PrivilegedPeripheral> { 229 public: 230 LowEnergyPrivilegedPeripheralServer( 231 const bt::gap::Adapter::WeakPtr& adapter, 232 bt::gatt::GATT::WeakPtr gatt, 233 fidl::InterfaceRequest<fuchsia::bluetooth::le::PrivilegedPeripheral> 234 request); 235 236 // fuchsia::bluetooth::le::Peripheral overrides: 237 void Advertise( 238 fuchsia::bluetooth::le::AdvertisingParameters parameters, 239 fidl::InterfaceHandle<fuchsia::bluetooth::le::AdvertisedPeripheral> 240 advertised_peripheral, 241 AdvertiseCallback callback) override; 242 void StartAdvertising( 243 fuchsia::bluetooth::le::AdvertisingParameters parameters, 244 ::fidl::InterfaceRequest<fuchsia::bluetooth::le::AdvertisingHandle> token, 245 StartAdvertisingCallback callback) override; 246 247 // fuchsia::bluetooth::le::ChannelListenerRegistry overrides: 248 void ListenL2cap( 249 fuchsia::bluetooth::le::ChannelListenerRegistryListenL2capRequest request, 250 ListenL2capCallback callback) override; 251 252 private: 253 std::unique_ptr<LowEnergyPeripheralServer> le_peripheral_server_; 254 255 WeakSelf<LowEnergyPrivilegedPeripheralServer> weak_self_; 256 257 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyPrivilegedPeripheralServer); 258 }; 259 260 } // namespace bthost 261