• 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 #include "pw_bluetooth_sapphire/internal/host/hci/android_extended_low_energy_advertiser.h"
16 
17 #include <pw_bluetooth/hci_android.emb.h>
18 #include <pw_bluetooth/hci_common.emb.h>
19 
20 #include "pw_bluetooth_sapphire/internal/host/hci-spec/vendor_protocol.h"
21 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
22 
23 namespace bt::hci {
24 
25 constexpr int8_t kTransmitPower =
26     -25;  // Android range -70 to +20, select the middle for now
27 
28 namespace hci_android = hci_spec::vendor::android;
29 
AndroidExtendedLowEnergyAdvertiser(hci::Transport::WeakPtr hci_ptr,uint8_t max_advertisements)30 AndroidExtendedLowEnergyAdvertiser::AndroidExtendedLowEnergyAdvertiser(
31     hci::Transport::WeakPtr hci_ptr, uint8_t max_advertisements)
32     : LowEnergyAdvertiser(std::move(hci_ptr)),
33       max_advertisements_(max_advertisements),
34       advertising_handle_map_(max_advertisements_),
35       weak_self_(this) {
36   auto self = weak_self_.GetWeakPtr();
37   state_changed_event_handler_id_ =
38       hci()->command_channel()->AddVendorEventHandler(
39           hci_android::kLEMultiAdvtStateChangeSubeventCode,
40           [self](const EmbossEventPacket& event_packet) {
41             if (self.is_alive()) {
42               return self->OnAdvertisingStateChangedSubevent(event_packet);
43             }
44 
45             return CommandChannel::EventCallbackResult::kRemove;
46           });
47 }
48 
~AndroidExtendedLowEnergyAdvertiser()49 AndroidExtendedLowEnergyAdvertiser::~AndroidExtendedLowEnergyAdvertiser() {
50   // This object is probably being destroyed because the stack is shutting down,
51   // in which case the HCI layer may have already been destroyed.
52   if (!hci().is_alive() || !hci()->command_channel()) {
53     return;
54   }
55   hci()->command_channel()->RemoveEventHandler(state_changed_event_handler_id_);
56   // TODO(fxbug.dev/42063496): This will only cancel one advertisement, after
57   // which the SequentialCommandRunner will have been destroyed and no further
58   // commands will be sent.
59   StopAdvertising();
60 }
61 
BuildEnablePacket(const DeviceAddress & address,pw::bluetooth::emboss::GenericEnableParam enable)62 EmbossCommandPacket AndroidExtendedLowEnergyAdvertiser::BuildEnablePacket(
63     const DeviceAddress& address,
64     pw::bluetooth::emboss::GenericEnableParam enable) {
65   std::optional<hci_spec::AdvertisingHandle> handle =
66       advertising_handle_map_.GetHandle(address);
67   BT_ASSERT(handle);
68 
69   auto packet = hci::EmbossCommandPacket::New<
70       pw::bluetooth::vendor::android_hci::LEMultiAdvtEnableCommandWriter>(
71       hci_android::kLEMultiAdvt);
72   auto packet_view = packet.view_t();
73   packet_view.vendor_command().sub_opcode().Write(
74       hci_android::kLEMultiAdvtEnableSubopcode);
75   packet_view.enable().Write(enable);
76   packet_view.advertising_handle().Write(handle.value());
77   return packet;
78 }
79 
80 CommandChannel::CommandPacketVariant
BuildSetAdvertisingParams(const DeviceAddress & address,pw::bluetooth::emboss::LEAdvertisingType type,pw::bluetooth::emboss::LEOwnAddressType own_address_type,AdvertisingIntervalRange interval)81 AndroidExtendedLowEnergyAdvertiser::BuildSetAdvertisingParams(
82     const DeviceAddress& address,
83     pw::bluetooth::emboss::LEAdvertisingType type,
84     pw::bluetooth::emboss::LEOwnAddressType own_address_type,
85     AdvertisingIntervalRange interval) {
86   std::unique_ptr<CommandPacket> packet = CommandPacket::New(
87       hci_android::kLEMultiAdvt,
88       sizeof(hci_android::LEMultiAdvtSetAdvtParamCommandParams));
89   packet->mutable_view()->mutable_payload_data().SetToZeros();
90   auto payload = packet->mutable_payload<
91       hci_android::LEMultiAdvtSetAdvtParamCommandParams>();
92 
93   std::optional<hci_spec::AdvertisingHandle> handle =
94       advertising_handle_map_.MapHandle(address);
95   if (!handle) {
96     bt_log(WARN,
97            "hci-le",
98            "could not (al)locate advertising handle for address: %s",
99            bt_str(address));
100     return std::unique_ptr<CommandPacket>();
101   }
102 
103   payload->opcode = hci_android::kLEMultiAdvtSetAdvtParamSubopcode;
104   payload->adv_interval_min = htole16(interval.min());
105   payload->adv_interval_max = htole16(interval.max());
106   payload->adv_type = type;
107   payload->own_address_type = own_address_type;
108   payload->adv_channel_map = hci_spec::kLEAdvertisingChannelAll;
109   payload->adv_filter_policy = hci_spec::LEAdvFilterPolicy::kAllowAll;
110   payload->adv_handle = handle.value();
111   payload->adv_tx_power = hci_spec::kLEExtendedAdvertisingTxPowerNoPreference;
112 
113   // We don't support directed advertising yet, so leave peer_address and
114   // peer_address_type as 0x00
115   // (|packet| parameters are initialized to zero above).
116 
117   return packet;
118 }
119 
120 CommandChannel::CommandPacketVariant
BuildSetAdvertisingData(const DeviceAddress & address,const AdvertisingData & data,AdvFlags flags)121 AndroidExtendedLowEnergyAdvertiser::BuildSetAdvertisingData(
122     const DeviceAddress& address, const AdvertisingData& data, AdvFlags flags) {
123   std::optional<hci_spec::AdvertisingHandle> handle =
124       advertising_handle_map_.GetHandle(address);
125   BT_ASSERT(handle);
126 
127   std::unique_ptr<CommandPacket> packet = CommandPacket::New(
128       hci_android::kLEMultiAdvt,
129       sizeof(hci_android::LEMultiAdvtSetAdvtDataCommandParams));
130   packet->mutable_view()->mutable_payload_data().SetToZeros();
131   auto payload =
132       packet
133           ->mutable_payload<hci_android::LEMultiAdvtSetAdvtDataCommandParams>();
134 
135   payload->opcode = hci_android::kLEMultiAdvtSetAdvtDataSubopcode;
136   payload->adv_data_length =
137       static_cast<uint8_t>(data.CalculateBlockSize(/*include_flags=*/true));
138   payload->adv_handle = handle.value();
139 
140   MutableBufferView data_view(payload->adv_data, payload->adv_data_length);
141   data.WriteBlock(&data_view, flags);
142 
143   return packet;
144 }
145 
146 CommandChannel::CommandPacketVariant
BuildUnsetAdvertisingData(const DeviceAddress & address)147 AndroidExtendedLowEnergyAdvertiser::BuildUnsetAdvertisingData(
148     const DeviceAddress& address) {
149   std::optional<hci_spec::AdvertisingHandle> handle =
150       advertising_handle_map_.GetHandle(address);
151   BT_ASSERT(handle);
152 
153   std::unique_ptr<CommandPacket> packet = CommandPacket::New(
154       hci_android::kLEMultiAdvt,
155       sizeof(hci_android::LEMultiAdvtSetAdvtDataCommandParams));
156   packet->mutable_view()->mutable_payload_data().SetToZeros();
157   auto payload =
158       packet
159           ->mutable_payload<hci_android::LEMultiAdvtSetAdvtDataCommandParams>();
160 
161   payload->opcode = hci_android::kLEMultiAdvtSetAdvtDataSubopcode;
162   payload->adv_data_length = 0;
163   payload->adv_handle = handle.value();
164 
165   return packet;
166 }
167 
168 CommandChannel::CommandPacketVariant
BuildSetScanResponse(const DeviceAddress & address,const AdvertisingData & scan_rsp)169 AndroidExtendedLowEnergyAdvertiser::BuildSetScanResponse(
170     const DeviceAddress& address, const AdvertisingData& scan_rsp) {
171   std::optional<hci_spec::AdvertisingHandle> handle =
172       advertising_handle_map_.GetHandle(address);
173   BT_ASSERT(handle);
174 
175   std::unique_ptr<CommandPacket> packet = CommandPacket::New(
176       hci_android::kLEMultiAdvt,
177       sizeof(hci_android::LEMultiAdvtSetScanRespCommandParams));
178   packet->mutable_view()->mutable_payload_data().SetToZeros();
179   auto payload =
180       packet
181           ->mutable_payload<hci_android::LEMultiAdvtSetScanRespCommandParams>();
182 
183   payload->opcode = hci_android::kLEMultiAdvtSetScanRespSubopcode;
184   payload->scan_rsp_data_length =
185       static_cast<uint8_t>(scan_rsp.CalculateBlockSize());
186   payload->adv_handle = handle.value();
187 
188   MutableBufferView scan_rsp_view(payload->scan_rsp_data,
189                                   payload->scan_rsp_data_length);
190   scan_rsp.WriteBlock(&scan_rsp_view, std::nullopt);
191   return packet;
192 }
193 
194 CommandChannel::CommandPacketVariant
BuildUnsetScanResponse(const DeviceAddress & address)195 AndroidExtendedLowEnergyAdvertiser::BuildUnsetScanResponse(
196     const DeviceAddress& address) {
197   std::optional<hci_spec::AdvertisingHandle> handle =
198       advertising_handle_map_.GetHandle(address);
199   BT_ASSERT(handle);
200 
201   std::unique_ptr<CommandPacket> packet = CommandPacket::New(
202       hci_android::kLEMultiAdvt,
203       sizeof(hci_android::LEMultiAdvtSetScanRespCommandParams));
204   packet->mutable_view()->mutable_payload_data().SetToZeros();
205   auto payload =
206       packet
207           ->mutable_payload<hci_android::LEMultiAdvtSetScanRespCommandParams>();
208 
209   payload->opcode = hci_android::kLEMultiAdvtSetScanRespSubopcode;
210   payload->scan_rsp_data_length = 0;
211   payload->adv_handle = handle.value();
212 
213   return packet;
214 }
215 
216 EmbossCommandPacket
BuildRemoveAdvertisingSet(const DeviceAddress & address)217 AndroidExtendedLowEnergyAdvertiser::BuildRemoveAdvertisingSet(
218     const DeviceAddress& address) {
219   std::optional<hci_spec::AdvertisingHandle> handle =
220       advertising_handle_map_.GetHandle(address);
221   BT_ASSERT(handle);
222 
223   auto packet = hci::EmbossCommandPacket::New<
224       pw::bluetooth::vendor::android_hci::LEMultiAdvtEnableCommandWriter>(
225       hci_android::kLEMultiAdvt);
226   auto packet_view = packet.view_t();
227   packet_view.vendor_command().sub_opcode().Write(
228       hci_android::kLEMultiAdvtEnableSubopcode);
229   packet_view.enable().Write(
230       pw::bluetooth::emboss::GenericEnableParam::DISABLE);
231   packet_view.advertising_handle().Write(handle.value());
232   return packet;
233 }
234 
StartAdvertising(const DeviceAddress & address,const AdvertisingData & data,const AdvertisingData & scan_rsp,AdvertisingOptions options,ConnectionCallback connect_callback,ResultFunction<> result_callback)235 void AndroidExtendedLowEnergyAdvertiser::StartAdvertising(
236     const DeviceAddress& address,
237     const AdvertisingData& data,
238     const AdvertisingData& scan_rsp,
239     AdvertisingOptions options,
240     ConnectionCallback connect_callback,
241     ResultFunction<> result_callback) {
242   AdvertisingData copied_data;
243   data.Copy(&copied_data);
244 
245   AdvertisingData copied_scan_rsp;
246   scan_rsp.Copy(&copied_scan_rsp);
247 
248   // if there is an operation currently in progress, enqueue this operation and
249   // we will get to it the next time we have a chance
250   if (!hci_cmd_runner().IsReady()) {
251     bt_log(INFO,
252            "hci-le",
253            "hci cmd runner not ready, queuing advertisement commands for now");
254 
255     op_queue_.push([this,
256                     address,
257                     data = std::move(copied_data),
258                     scan_rsp = std::move(copied_scan_rsp),
259                     options,
260                     conn_cb = std::move(connect_callback),
261                     result_cb = std::move(result_callback)]() mutable {
262       StartAdvertising(address,
263                        data,
264                        scan_rsp,
265                        options,
266                        std::move(conn_cb),
267                        std::move(result_cb));
268     });
269 
270     return;
271   }
272 
273   fit::result<HostError> result =
274       CanStartAdvertising(address, data, scan_rsp, options);
275   if (result.is_error()) {
276     result_callback(ToResult(result.error_value()));
277     return;
278   }
279 
280   if (IsAdvertising(address)) {
281     bt_log(DEBUG,
282            "hci-le",
283            "updating existing advertisement for %s",
284            bt_str(address));
285   }
286 
287   if (options.include_tx_power_level) {
288     copied_data.SetTxPower(kTransmitPower);
289     copied_scan_rsp.SetTxPower(kTransmitPower);
290   }
291 
292   StartAdvertisingInternal(address,
293                            copied_data,
294                            copied_scan_rsp,
295                            options.interval,
296                            options.flags,
297                            std::move(connect_callback),
298                            std::move(result_callback));
299 }
300 
StopAdvertising()301 void AndroidExtendedLowEnergyAdvertiser::StopAdvertising() {
302   LowEnergyAdvertiser::StopAdvertising();
303   advertising_handle_map_.Clear();
304 
305   // std::queue doesn't have a clear method so we have to resort to this
306   // tomfoolery :(
307   decltype(op_queue_) empty;
308   std::swap(op_queue_, empty);
309 }
310 
StopAdvertising(const DeviceAddress & address)311 void AndroidExtendedLowEnergyAdvertiser::StopAdvertising(
312     const DeviceAddress& address) {
313   // if there is an operation currently in progress, enqueue this operation and
314   // we will get to it the next time we have a chance
315   if (!hci_cmd_runner().IsReady()) {
316     bt_log(
317         INFO,
318         "hci-le",
319         "hci cmd runner not ready, queueing stop advertising command for now");
320     op_queue_.push([this, address]() { StopAdvertising(address); });
321     return;
322   }
323 
324   LowEnergyAdvertiser::StopAdvertisingInternal(address);
325   advertising_handle_map_.RemoveAddress(address);
326 }
327 
OnIncomingConnection(hci_spec::ConnectionHandle handle,pw::bluetooth::emboss::ConnectionRole role,const DeviceAddress & peer_address,const hci_spec::LEConnectionParameters & conn_params)328 void AndroidExtendedLowEnergyAdvertiser::OnIncomingConnection(
329     hci_spec::ConnectionHandle handle,
330     pw::bluetooth::emboss::ConnectionRole role,
331     const DeviceAddress& peer_address,
332     const hci_spec::LEConnectionParameters& conn_params) {
333   staged_connections_map_[handle] = {role, peer_address, conn_params};
334 }
335 
336 // The LE multi-advertising state change subevent contains the mapping between
337 // connection handle and advertising handle. After the LE multi-advertising
338 // state change subevent, we have all the information necessary to create a
339 // connection object within the Host layer.
340 CommandChannel::EventCallbackResult
OnAdvertisingStateChangedSubevent(const EmbossEventPacket & event)341 AndroidExtendedLowEnergyAdvertiser::OnAdvertisingStateChangedSubevent(
342     const EmbossEventPacket& event) {
343   BT_ASSERT(event.event_code() == hci_spec::kVendorDebugEventCode);
344   BT_ASSERT(event.view<pw::bluetooth::emboss::VendorDebugEventView>()
345                 .subevent_code()
346                 .Read() == hci_android::kLEMultiAdvtStateChangeSubeventCode);
347 
348   Result<> result = event.ToResult();
349   if (bt_is_error(result,
350                   ERROR,
351                   "hci-le",
352                   "advertising state change event, error received %s",
353                   bt_str(result))) {
354     return CommandChannel::EventCallbackResult::kContinue;
355   }
356 
357   auto view = event.view<
358       pw::bluetooth::vendor::android_hci::LEMultiAdvtStateChangeSubeventView>();
359 
360   hci_spec::ConnectionHandle connection_handle =
361       view.connection_handle().Read();
362   auto staged_parameters_node =
363       staged_connections_map_.extract(connection_handle);
364 
365   if (staged_parameters_node.empty()) {
366     bt_log(ERROR,
367            "hci-le",
368            "advertising state change event, staged params not available "
369            "(handle: %d)",
370            view.advertising_handle().Read());
371     return CommandChannel::EventCallbackResult::kContinue;
372   }
373 
374   hci_spec::AdvertisingHandle adv_handle = view.advertising_handle().Read();
375   std::optional<DeviceAddress> opt_local_address =
376       advertising_handle_map_.GetAddress(adv_handle);
377 
378   // We use the identity address as the local address if we aren't advertising
379   // or otherwise don't know about this advertising set. This is obviously
380   // wrong. However, the link will be disconnected in that case before it can
381   // propagate to higher layers.
382   static DeviceAddress identity_address =
383       DeviceAddress(DeviceAddress::Type::kLEPublic, {0});
384   DeviceAddress local_address = identity_address;
385   if (opt_local_address) {
386     local_address = opt_local_address.value();
387   }
388 
389   StagedConnectionParameters staged = staged_parameters_node.mapped();
390 
391   CompleteIncomingConnection(connection_handle,
392                              staged.role,
393                              local_address,
394                              staged.peer_address,
395                              staged.conn_params);
396   return CommandChannel::EventCallbackResult::kContinue;
397 }
398 
OnCurrentOperationComplete()399 void AndroidExtendedLowEnergyAdvertiser::OnCurrentOperationComplete() {
400   if (op_queue_.empty()) {
401     return;  // no more queued operations so nothing to do
402   }
403 
404   fit::closure closure = std::move(op_queue_.front());
405   op_queue_.pop();
406   closure();
407 }
408 
409 }  // namespace bt::hci
410