• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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