1 // Copyright 2023 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 #include <memory> 17 18 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h" 19 #include "pw_bluetooth_sapphire/internal/host/hci/local_address_delegate.h" 20 #include "pw_bluetooth_sapphire/internal/host/hci/low_energy_connection.h" 21 #include "pw_bluetooth_sapphire/internal/host/hci/sequential_command_runner.h" 22 #include "pw_bluetooth_sapphire/internal/host/transport/error.h" 23 24 namespace bt { 25 class AdvertisingData; 26 27 namespace hci { 28 class Transport; 29 30 class AdvertisingIntervalRange final { 31 public: 32 // Constructs an advertising interval range, capping the values based on the 33 // allowed range (Vol 2, Part E, 7.8.5). AdvertisingIntervalRange(uint16_t min,uint16_t max)34 constexpr AdvertisingIntervalRange(uint16_t min, uint16_t max) 35 : min_(std::max(min, hci_spec::kLEAdvertisingIntervalMin)), 36 max_(std::min(max, hci_spec::kLEAdvertisingIntervalMax)) { 37 BT_ASSERT(min < max); 38 } 39 min()40 uint16_t min() const { return min_; } max()41 uint16_t max() const { return max_; } 42 43 private: 44 uint16_t min_, max_; 45 }; 46 47 class LowEnergyAdvertiser : public LocalAddressClient { 48 public: 49 explicit LowEnergyAdvertiser(hci::Transport::WeakPtr hci); 50 ~LowEnergyAdvertiser() override = default; 51 52 // Get the current limit in bytes of the advertisement data supported. 53 virtual size_t GetSizeLimit() const = 0; 54 55 // TODO(armansito): The |address| parameter of this function doesn't always 56 // correspond to the advertised device address as the local address for an 57 // advertisement cannot always be configured by the advertiser. This is the 58 // case especially in the following conditions: 59 // 60 // 1. The type of |address| is "LE Public". The advertised address always 61 // corresponds to the 62 // controller's BD_ADDR. This is the case in both legacy and extended 63 // advertising. 64 // 65 // 2. The type of |address| is "LE Random" and the advertiser implements 66 // legacy advertising. 67 // Since the controller local address is shared between scan, initiation, 68 // and advertising procedures, the advertiser cannot configure this 69 // address without interfering with the state of other ongoing 70 // procedures. 71 // 72 // We should either revisit this interface or update the documentation to 73 // reflect the fact that the |address| is sometimes a hint and may or may not 74 // end up being advertised. Currently the GAP layer decides which address to 75 // pass to this call but the layering should be revisited when we add support 76 // for extended advertising. 77 // 78 // ----- 79 // 80 // Attempt to start advertising |data| with |options.flags| and scan response 81 // |scan_rsp| using advertising address |address|. If |options.anonymous| is 82 // set, |address| is ignored. 83 // 84 // If |address| is currently advertised, the advertisement is updated. 85 // 86 // If |connect_callback| is provided, the advertisement will be connectable, 87 // and the provided |status_callback| will be called with a connection 88 // reference when this advertisement is connected to and the advertisement has 89 // been stopped. 90 // 91 // |options.interval| must be a value in "controller timeslices". See 92 // hci-spec/hci_constants.h for the valid range. 93 // 94 // Provides results in |status_callback|. If advertising is setup, the final 95 // interval of advertising is provided in |interval| and |status| is kSuccess. 96 // Otherwise, |status| indicates the type of error and |interval| has no 97 // meaning. 98 // 99 // |status_callback| may be called before this function returns, but will be 100 // called before any calls to |connect_callback|. 101 // 102 // The maximum advertising and scan response data sizes are determined by the 103 // Bluetooth controller (4.x supports up to 31 bytes while 5.x is extended up 104 // to 251). If |data| and |scan_rsp| exceed this internal limit, a 105 // HostError::kAdvertisingDataTooLong or HostError::kScanResponseTooLong error 106 // will be generated. 107 struct AdvertisingOptions { AdvertisingOptionsAdvertisingOptions108 AdvertisingOptions(AdvertisingIntervalRange interval, 109 bool anonymous, 110 AdvFlags flags, 111 bool include_tx_power_level) 112 : interval(interval), 113 anonymous(anonymous), 114 flags(flags), 115 include_tx_power_level(include_tx_power_level) {} 116 117 AdvertisingIntervalRange interval; 118 bool anonymous; // TODO(fxbug.dev/42157563): anonymous advertising is 119 // currently not supported 120 AdvFlags flags; 121 bool include_tx_power_level; 122 }; 123 using ConnectionCallback = 124 fit::function<void(std::unique_ptr<hci::LowEnergyConnection> link)>; 125 virtual void StartAdvertising(const DeviceAddress& address, 126 const AdvertisingData& data, 127 const AdvertisingData& scan_rsp, 128 AdvertisingOptions options, 129 ConnectionCallback connect_callback, 130 ResultFunction<> result_callback) = 0; 131 132 // Stops advertisement on all currently advertising addresses. Idempotent and 133 // asynchronous. 134 virtual void StopAdvertising(); 135 136 // Stops any advertisement currently active on |address|. Idempotent and 137 // asynchronous. 138 virtual void StopAdvertising(const DeviceAddress& address) = 0; 139 140 // Callback for an incoming LE connection. This function should be called in 141 // reaction to any connection that was not initiated locally. This object will 142 // determine if it was a result of an active advertisement and route the 143 // connection accordingly. 144 virtual void OnIncomingConnection( 145 hci_spec::ConnectionHandle handle, 146 pw::bluetooth::emboss::ConnectionRole role, 147 const DeviceAddress& peer_address, 148 const hci_spec::LEConnectionParameters& conn_params) = 0; 149 150 // Returns true if currently advertising at all IsAdvertising()151 bool IsAdvertising() const { return !connection_callbacks_.empty(); } 152 153 // Returns true if currently advertising for the given address IsAdvertising(const DeviceAddress & address)154 bool IsAdvertising(const DeviceAddress& address) const { 155 return connection_callbacks_.count(address) != 0; 156 } 157 158 // Returns the number of advertisements currently registered NumAdvertisements()159 size_t NumAdvertisements() const { return connection_callbacks_.size(); } 160 161 // Returns the maximum number of advertisements that can be supported 162 virtual size_t MaxAdvertisements() const = 0; 163 164 protected: 165 // Build the HCI command packet to enable advertising for the flavor of low 166 // energy advertising being implemented. 167 virtual EmbossCommandPacket BuildEnablePacket( 168 const DeviceAddress& address, 169 pw::bluetooth::emboss::GenericEnableParam enable) = 0; 170 171 // Build the HCI command packet to set the advertising parameters for the 172 // flavor of low energy advertising being implemented. 173 virtual CommandChannel::CommandPacketVariant BuildSetAdvertisingParams( 174 const DeviceAddress& address, 175 pw::bluetooth::emboss::LEAdvertisingType type, 176 pw::bluetooth::emboss::LEOwnAddressType own_address_type, 177 AdvertisingIntervalRange interval) = 0; 178 179 // Build the HCI command packet to set the advertising data for the flavor of 180 // low energy advertising being implemented. 181 virtual CommandChannel::CommandPacketVariant BuildSetAdvertisingData( 182 const DeviceAddress& address, 183 const AdvertisingData& data, 184 AdvFlags flags) = 0; 185 186 // Build the HCI command packet to delete the advertising parameters from the 187 // controller for the flavor of low energy advertising being implemented. This 188 // method is used when stopping an advertisement. 189 virtual CommandChannel::CommandPacketVariant BuildUnsetAdvertisingData( 190 const DeviceAddress& address) = 0; 191 192 // Build the HCI command packet to set the data sent in a scan response (if 193 // requested) for the flavor of low energy advertising being implemented. 194 virtual CommandChannel::CommandPacketVariant BuildSetScanResponse( 195 const DeviceAddress& address, const AdvertisingData& scan_rsp) = 0; 196 197 // Build the HCI command packet to delete the advertising parameters from the 198 // controller for the flavor of low energy advertising being implemented. 199 virtual CommandChannel::CommandPacketVariant BuildUnsetScanResponse( 200 const DeviceAddress& address) = 0; 201 202 // Build the HCI command packet to remove the advertising set entirely from 203 // the controller's memory for the flavor of low energy advertising being 204 // implemented. 205 virtual EmbossCommandPacket BuildRemoveAdvertisingSet( 206 const DeviceAddress& address) = 0; 207 208 // Called when the command packet created with BuildSetAdvertisingParams 209 // returns with a result OnSetAdvertisingParamsComplete(const EventPacket & event)210 virtual void OnSetAdvertisingParamsComplete(const EventPacket& event) {} 211 212 // Called when a sequence of HCI commands that form a single operation (e.g. 213 // start advertising, stop advertising) completes in its entirety. Subclasses 214 // can override this method to be notified when the HCI command runner is 215 // available once again. OnCurrentOperationComplete()216 virtual void OnCurrentOperationComplete() {} 217 218 // Check whether we can actually start advertising given the combination of 219 // input parameters (e.g. check that the requested advertising data and scan 220 // response will actually fit within the size limitations of the advertising 221 // PDUs) 222 fit::result<HostError> CanStartAdvertising( 223 const DeviceAddress& address, 224 const AdvertisingData& data, 225 const AdvertisingData& scan_rsp, 226 const AdvertisingOptions& options) const; 227 228 // Unconditionally start advertising (all checks must be performed in the 229 // methods that call this one). 230 void StartAdvertisingInternal(const DeviceAddress& address, 231 const AdvertisingData& data, 232 const AdvertisingData& scan_rsp, 233 AdvertisingIntervalRange interval, 234 AdvFlags flags, 235 ConnectionCallback connect_callback, 236 hci::ResultFunction<> callback); 237 238 // Unconditionally stop advertising (all checks muts be performed in the 239 // methods that call this one). 240 void StopAdvertisingInternal(const DeviceAddress& address); 241 242 // Handle shared housekeeping tasks when an incoming connection is completed 243 // (e.g. clean up internal state, call callbacks, etc) 244 void CompleteIncomingConnection( 245 hci_spec::ConnectionHandle handle, 246 pw::bluetooth::emboss::ConnectionRole role, 247 const DeviceAddress& local_address, 248 const DeviceAddress& peer_address, 249 const hci_spec::LEConnectionParameters& conn_params); 250 hci_cmd_runner()251 SequentialCommandRunner& hci_cmd_runner() const { return *hci_cmd_runner_; } hci()252 hci::Transport::WeakPtr hci() const { return hci_; } 253 254 const std::unordered_map<DeviceAddress, ConnectionCallback>& connection_callbacks()255 connection_callbacks() const { 256 return connection_callbacks_; 257 } 258 259 private: 260 struct StagedParameters { 261 AdvertisingData data; 262 AdvertisingData scan_rsp; 263 resetStagedParameters264 void reset() { 265 AdvertisingData blank; 266 blank.Copy(&data); 267 blank.Copy(&scan_rsp); 268 } 269 }; 270 271 // Continuation function for starting advertising, called automatically via 272 // callbacks in StartAdvertisingInternal. Developers should not call this 273 // function directly. 274 bool StartAdvertisingInternalStep2(const DeviceAddress& address, 275 AdvFlags flags, 276 ConnectionCallback connect_callback, 277 hci::ResultFunction<> status_callback); 278 279 // Enqueue onto the HCI command runner the HCI commands necessary to stop 280 // advertising and completely remove a given address from the controller's 281 // memory. If even one of the HCI commands cannot be generated for some 282 // reason, no HCI commands are enqueued. 283 bool EnqueueStopAdvertisingCommands(const DeviceAddress& address); 284 285 hci::Transport::WeakPtr hci_; 286 std::unique_ptr<SequentialCommandRunner> hci_cmd_runner_; 287 std::unordered_map<DeviceAddress, ConnectionCallback> connection_callbacks_; 288 StagedParameters staged_parameters_; 289 290 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyAdvertiser); 291 }; 292 293 } // namespace hci 294 } // namespace bt 295