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