• 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/gap/bredr_discovery_manager.h"
16 
17 #include <lib/fit/defer.h>
18 #include <lib/stdcompat/functional.h>
19 #include <pw_bluetooth/hci_commands.emb.h>
20 #include <pw_bluetooth/hci_events.emb.h>
21 
22 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
25 #include "pw_bluetooth_sapphire/internal/host/common/supplement_data.h"
26 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h"
27 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
28 #include "pw_bluetooth_sapphire/internal/host/hci-spec/protocol.h"
29 #include "pw_bluetooth_sapphire/internal/host/transport/emboss_control_packets.h"
30 #include "pw_bluetooth_sapphire/internal/host/transport/transport.h"
31 
32 namespace bt::gap {
33 
34 namespace {
35 
36 // Make an existing peer connectable, or add a connectable peer if one does not
37 // already exist.
AddOrUpdateConnectablePeer(PeerCache * cache,const DeviceAddress & addr)38 Peer* AddOrUpdateConnectablePeer(PeerCache* cache, const DeviceAddress& addr) {
39   Peer* peer = cache->FindByAddress(addr);
40   if (!peer) {
41     peer = cache->NewPeer(addr, /*connectable=*/true);
42   } else {
43     peer->set_connectable(true);
44   }
45   BT_ASSERT(peer);
46   return peer;
47 }
48 
ProcessInquiryResultEvent(PeerCache * cache,const pw::bluetooth::emboss::InquiryResultWithRssiEventView & event)49 std::unordered_set<Peer*> ProcessInquiryResultEvent(
50     PeerCache* cache,
51     const pw::bluetooth::emboss::InquiryResultWithRssiEventView& event) {
52   bt_log(TRACE, "gap-bredr", "inquiry result received");
53   std::unordered_set<Peer*> updated;
54   auto responses = event.responses();
55   for (auto response : responses) {
56     DeviceAddress addr(DeviceAddress::Type::kBREDR,
57                        DeviceAddressBytes(response.bd_addr()));
58     Peer* peer = AddOrUpdateConnectablePeer(cache, addr);
59     peer->MutBrEdr().SetInquiryData(response);
60     updated.insert(peer);
61   }
62   return updated;
63 }
64 
65 }  // namespace
66 
BrEdrDiscoverySession(BrEdrDiscoveryManager::WeakPtr manager)67 BrEdrDiscoverySession::BrEdrDiscoverySession(
68     BrEdrDiscoveryManager::WeakPtr manager)
69     : manager_(std::move(manager)) {}
70 
~BrEdrDiscoverySession()71 BrEdrDiscoverySession::~BrEdrDiscoverySession() {
72   manager_->RemoveDiscoverySession(this);
73 }
74 
NotifyDiscoveryResult(const Peer & peer) const75 void BrEdrDiscoverySession::NotifyDiscoveryResult(const Peer& peer) const {
76   if (peer_found_callback_) {
77     peer_found_callback_(peer);
78   }
79 }
80 
NotifyError() const81 void BrEdrDiscoverySession::NotifyError() const {
82   if (error_callback_) {
83     error_callback_();
84   }
85 }
86 
BrEdrDiscoverableSession(BrEdrDiscoveryManager::WeakPtr manager)87 BrEdrDiscoverableSession::BrEdrDiscoverableSession(
88     BrEdrDiscoveryManager::WeakPtr manager)
89     : manager_(std::move(manager)) {}
90 
~BrEdrDiscoverableSession()91 BrEdrDiscoverableSession::~BrEdrDiscoverableSession() {
92   manager_->RemoveDiscoverableSession(this);
93 }
94 
BrEdrDiscoveryManager(pw::async::Dispatcher & pw_dispatcher,hci::CommandChannel::WeakPtr cmd,pw::bluetooth::emboss::InquiryMode mode,PeerCache * peer_cache)95 BrEdrDiscoveryManager::BrEdrDiscoveryManager(
96     pw::async::Dispatcher& pw_dispatcher,
97     hci::CommandChannel::WeakPtr cmd,
98     pw::bluetooth::emboss::InquiryMode mode,
99     PeerCache* peer_cache)
100     : cmd_(std::move(cmd)),
101       dispatcher_(pw_dispatcher),
102       cache_(peer_cache),
103       result_handler_id_(0u),
104       desired_inquiry_mode_(mode),
105       current_inquiry_mode_(pw::bluetooth::emboss::InquiryMode::STANDARD),
106       weak_self_(this) {
107   BT_DEBUG_ASSERT(cache_);
108   BT_DEBUG_ASSERT(cmd_.is_alive());
109 
110   result_handler_id_ = cmd_->AddEventHandler(
111       hci_spec::kInquiryResultEventCode,
112       fit::bind_member<&BrEdrDiscoveryManager::InquiryResult>(this));
113   BT_DEBUG_ASSERT(result_handler_id_);
114   rssi_handler_id_ = cmd_->AddEventHandler(
115       hci_spec::kInquiryResultWithRSSIEventCode,
116       cpp20::bind_front(&BrEdrDiscoveryManager::InquiryResultWithRssi, this));
117   BT_DEBUG_ASSERT(rssi_handler_id_);
118   eir_handler_id_ = cmd_->AddEventHandler(
119       hci_spec::kExtendedInquiryResultEventCode,
120       cpp20::bind_front(&BrEdrDiscoveryManager::ExtendedInquiryResult, this));
121   BT_DEBUG_ASSERT(eir_handler_id_);
122 
123   // Set the Inquiry Scan Settings
124   WriteInquiryScanSettings(
125       kInquiryScanInterval, kInquiryScanWindow, /*interlaced=*/true);
126 }
127 
~BrEdrDiscoveryManager()128 BrEdrDiscoveryManager::~BrEdrDiscoveryManager() {
129   cmd_->RemoveEventHandler(eir_handler_id_);
130   cmd_->RemoveEventHandler(rssi_handler_id_);
131   cmd_->RemoveEventHandler(result_handler_id_);
132   InvalidateDiscoverySessions();
133 }
134 
RequestDiscovery(DiscoveryCallback callback)135 void BrEdrDiscoveryManager::RequestDiscovery(DiscoveryCallback callback) {
136   BT_DEBUG_ASSERT(callback);
137 
138   bt_log(INFO, "gap-bredr", "RequestDiscovery");
139 
140   // If we're already waiting on a callback, then scanning is already starting.
141   // Queue this to create a session when the scanning starts.
142   if (!pending_discovery_.empty()) {
143     bt_log(DEBUG, "gap-bredr", "discovery starting, add to pending");
144     pending_discovery_.push(std::move(callback));
145     return;
146   }
147 
148   // If we're already scanning, just add a session.
149   if (!discovering_.empty() || !zombie_discovering_.empty()) {
150     bt_log(DEBUG, "gap-bredr", "add to active sessions");
151     auto session = AddDiscoverySession();
152     callback(fit::ok(), std::move(session));
153     return;
154   }
155 
156   pending_discovery_.push(std::move(callback));
157   MaybeStartInquiry();
158 }
159 
160 // Starts the inquiry procedure if any sessions exist or are waiting to start.
MaybeStartInquiry()161 void BrEdrDiscoveryManager::MaybeStartInquiry() {
162   if (pending_discovery_.empty() && discovering_.empty()) {
163     bt_log(DEBUG, "gap-bredr", "no sessions, not starting inquiry");
164     return;
165   }
166 
167   bt_log(TRACE, "gap-bredr", "starting inquiry");
168 
169   auto self = weak_self_.GetWeakPtr();
170   if (desired_inquiry_mode_ != current_inquiry_mode_) {
171     auto packet = hci::EmbossCommandPacket::New<
172         pw::bluetooth::emboss::WriteInquiryModeCommandWriter>(
173         hci_spec::kWriteInquiryMode);
174     packet.view_t().inquiry_mode().Write(desired_inquiry_mode_);
175     cmd_->SendCommand(
176         std::move(packet),
177         [self, mode = desired_inquiry_mode_](auto /*unused*/,
178                                              const auto& event) {
179           if (!self.is_alive()) {
180             return;
181           }
182 
183           if (!hci_is_error(
184                   event, ERROR, "gap-bredr", "write inquiry mode failed")) {
185             self->current_inquiry_mode_ = mode;
186           }
187         });
188   }
189 
190   auto inquiry = hci::EmbossCommandPacket::New<
191       pw::bluetooth::emboss::InquiryCommandWriter>(hci_spec::kInquiry);
192   auto view = inquiry.view_t();
193   view.lap().Write(pw::bluetooth::emboss::InquiryAccessCode::GIAC);
194   view.inquiry_length().Write(kInquiryLengthDefault);
195   view.num_responses().Write(0);
196 
197   cmd_->SendExclusiveCommand(
198       std::move(inquiry),
199       [self](auto, const hci::EventPacket& event) {
200         if (!self.is_alive()) {
201           return;
202         }
203         auto status = event.ToResult();
204         if (bt_is_error(status, WARN, "gap-bredr", "inquiry error")) {
205           // Failure of some kind, signal error to the sessions.
206           self->InvalidateDiscoverySessions();
207 
208           // Fallthrough for callback to pending sessions.
209         }
210 
211         // Resolve the request if the controller sent back a Command Complete or
212         // Status event.
213         // TODO(fxbug.dev/42062242): Make it impossible for Command Complete to
214         // happen here and remove handling for it.
215         if (event.event_code() == hci_spec::kCommandStatusEventCode ||
216             event.event_code() == hci_spec::kCommandCompleteEventCode) {
217           // Inquiry started, make sessions for our waiting callbacks.
218           while (!self->pending_discovery_.empty()) {
219             auto callback = std::move(self->pending_discovery_.front());
220             self->pending_discovery_.pop();
221             callback(status,
222                      (status.is_ok() ? self->AddDiscoverySession() : nullptr));
223           }
224           return;
225         }
226 
227         BT_DEBUG_ASSERT(event.event_code() ==
228                         hci_spec::kInquiryCompleteEventCode);
229         self->zombie_discovering_.clear();
230 
231         if (bt_is_error(status, TRACE, "gap", "inquiry complete error")) {
232           return;
233         }
234 
235         // We've stopped scanning because we timed out.
236         bt_log(TRACE, "gap-bredr", "inquiry complete, restart");
237         self->MaybeStartInquiry();
238       },
239       hci_spec::kInquiryCompleteEventCode,
240       {hci_spec::kRemoteNameRequest});
241 }
242 
243 // Stops the inquiry procedure.
StopInquiry()244 void BrEdrDiscoveryManager::StopInquiry() {
245   BT_DEBUG_ASSERT(result_handler_id_);
246   bt_log(TRACE, "gap-bredr", "cancelling inquiry");
247 
248   const hci::EmbossCommandPacket inq_cancel = hci::EmbossCommandPacket::New<
249       pw::bluetooth::emboss::InquiryCancelCommandView>(
250       hci_spec::kInquiryCancel);
251   cmd_->SendCommand(std::move(inq_cancel), [](int64_t, const auto& event) {
252     // Warn if the command failed.
253     hci_is_error(event, WARN, "gap-bredr", "inquiry cancel failed");
254   });
255 }
256 
InquiryResult(const hci::EmbossEventPacket & event)257 hci::CommandChannel::EventCallbackResult BrEdrDiscoveryManager::InquiryResult(
258     const hci::EmbossEventPacket& event) {
259   BT_DEBUG_ASSERT(event.event_code() == hci_spec::kInquiryResultEventCode);
260   std::unordered_set<Peer*> peers;
261 
262   auto view = event.view<pw::bluetooth::emboss::InquiryResultEventView>();
263   for (int i = 0; i < view.num_responses().Read(); i++) {
264     const auto response = view.responses()[i];
265     DeviceAddress addr(DeviceAddress::Type::kBREDR,
266                        DeviceAddressBytes{response.bd_addr()});
267     Peer* peer = AddOrUpdateConnectablePeer(cache_, addr);
268     peer->MutBrEdr().SetInquiryData(response);
269     peers.insert(peer);
270   }
271 
272   NotifyPeersUpdated(peers);
273 
274   return hci::CommandChannel::EventCallbackResult::kContinue;
275 }
276 
277 hci::CommandChannel::EventCallbackResult
InquiryResultWithRssi(const hci::EmbossEventPacket & event)278 BrEdrDiscoveryManager::InquiryResultWithRssi(
279     const hci::EmbossEventPacket& event) {
280   std::unordered_set<Peer*> peers = ProcessInquiryResultEvent(
281       cache_,
282       event.view<pw::bluetooth::emboss::InquiryResultWithRssiEventView>());
283   NotifyPeersUpdated(peers);
284   return hci::CommandChannel::EventCallbackResult::kContinue;
285 }
286 
287 hci::CommandChannel::EventCallbackResult
ExtendedInquiryResult(const hci::EmbossEventPacket & event)288 BrEdrDiscoveryManager::ExtendedInquiryResult(
289     const hci::EmbossEventPacket& event) {
290   bt_log(TRACE, "gap-bredr", "ExtendedInquiryResult received");
291   const auto result =
292       event.view<pw::bluetooth::emboss::ExtendedInquiryResultEventView>();
293 
294   DeviceAddress addr(DeviceAddress::Type::kBREDR,
295                      DeviceAddressBytes(result.bd_addr()));
296   Peer* peer = AddOrUpdateConnectablePeer(cache_, addr);
297   peer->MutBrEdr().SetInquiryData(result);
298 
299   NotifyPeersUpdated({peer});
300   return hci::CommandChannel::EventCallbackResult::kContinue;
301 }
302 
UpdateEIRResponseData(std::string name,hci::ResultFunction<> callback)303 void BrEdrDiscoveryManager::UpdateEIRResponseData(
304     std::string name, hci::ResultFunction<> callback) {
305   DataType name_type = DataType::kCompleteLocalName;
306   size_t name_size = name.size();
307   if (name.size() >= (hci_spec::kExtendedInquiryResponseMaxNameBytes)) {
308     name_type = DataType::kShortenedLocalName;
309     name_size = hci_spec::kExtendedInquiryResponseMaxNameBytes;
310   }
311   auto self = weak_self_.GetWeakPtr();
312 
313   auto write_eir = hci::EmbossCommandPacket::New<
314       pw::bluetooth::emboss::WriteExtendedInquiryResponseCommandWriter>(
315       hci_spec::kWriteExtendedInquiryResponse);
316   auto write_eir_params = write_eir.view_t();
317   write_eir_params.fec_required().Write(0x00);
318 
319   // Create MutableBufferView of BackingStorage
320   unsigned char* eir_data =
321       write_eir_params.extended_inquiry_response().BackingStorage().data();
322   MutableBufferView eir_response_buf =
323       MutableBufferView(eir_data, hci_spec::kExtendedInquiryResponseBytes);
324   eir_response_buf.Fill(0);
325   eir_response_buf[0] = name_size + 1;
326   eir_response_buf[1] = static_cast<uint8_t>(name_type);
327   eir_response_buf.mutable_view(2).Write(
328       reinterpret_cast<const uint8_t*>(name.data()), name_size);
329 
330   self->cmd_->SendCommand(
331       std::move(write_eir),
332       [self, name = std::move(name), cb = std::move(callback)](
333           auto, const hci::EventPacket& event) mutable {
334         if (!hci_is_error(event, WARN, "gap", "write EIR failed")) {
335           self->local_name_ = std::move(name);
336         }
337         cb(event.ToResult());
338       });
339 }
340 
UpdateLocalName(std::string name,hci::ResultFunction<> callback)341 void BrEdrDiscoveryManager::UpdateLocalName(std::string name,
342                                             hci::ResultFunction<> callback) {
343   auto self = weak_self_.GetWeakPtr();
344 
345   auto write_name = hci::EmbossCommandPacket::New<
346       pw::bluetooth::emboss::WriteLocalNameCommandWriter>(
347       hci_spec::kWriteLocalName);
348   auto write_name_view = write_name.view_t();
349   auto local_name = write_name_view.local_name().BackingStorage();
350   size_t name_size = std::min(name.size(), hci_spec::kMaxNameLength);
351 
352   // Use ContiguousBuffer instead of constructing LocalName view in case of
353   // invalid view being created when name is not large enough for the view
354   auto name_buf = emboss::support::ReadOnlyContiguousBuffer(&name);
355   local_name.CopyFrom(name_buf, name_size);
356 
357   cmd_->SendCommand(
358       std::move(write_name),
359       [self, name = std::move(name), cb = std::move(callback)](
360           auto, const hci::EventPacket& event) mutable {
361         if (hci_is_error(event, WARN, "gap", "set local name failed")) {
362           cb(event.ToResult());
363           return;
364         }
365         // If the WriteLocalName command was successful, update the extended
366         // inquiry data.
367         self->UpdateEIRResponseData(std::move(name), std::move(cb));
368       });
369 }
370 
AttachInspect(inspect::Node & parent,std::string name)371 void BrEdrDiscoveryManager::AttachInspect(inspect::Node& parent,
372                                           std::string name) {
373   auto node = parent.CreateChild(name);
374   inspect_properties_.Initialize(std::move(node));
375   UpdateInspectProperties();
376 }
377 
Initialize(inspect::Node new_node)378 void BrEdrDiscoveryManager::InspectProperties::Initialize(
379     inspect::Node new_node) {
380   discoverable_sessions = new_node.CreateUint("discoverable_sessions", 0);
381   pending_discoverable_sessions =
382       new_node.CreateUint("pending_discoverable", 0);
383   discoverable_sessions_count =
384       new_node.CreateUint("discoverable_sessions_count", 0);
385   last_discoverable_length_sec =
386       new_node.CreateUint("last_discoverable_length_sec", 0);
387 
388   discovery_sessions = new_node.CreateUint("discovery_sessions", 0);
389   last_inquiry_length_sec = new_node.CreateUint("last_inquiry_length_sec", 0);
390   inquiry_sessions_count = new_node.CreateUint("inquiry_sessions_count", 0);
391 
392   discoverable_started_time.reset();
393   inquiry_started_time.reset();
394 
395   node = std::move(new_node);
396 }
397 
Update(size_t discoverable_count,size_t pending_discoverable_count,size_t discovery_count,pw::chrono::SystemClock::time_point now)398 void BrEdrDiscoveryManager::InspectProperties::Update(
399     size_t discoverable_count,
400     size_t pending_discoverable_count,
401     size_t discovery_count,
402     pw::chrono::SystemClock::time_point now) {
403   if (!node) {
404     return;
405   }
406 
407   if (!discoverable_started_time.has_value() && discoverable_count != 0) {
408     discoverable_started_time.emplace(now);
409   } else if (discoverable_started_time.has_value() && discoverable_count == 0) {
410     discoverable_sessions_count.Add(1);
411     pw::chrono::SystemClock::duration length =
412         now - discoverable_started_time.value();
413     last_discoverable_length_sec.Set(
414         std::chrono::duration_cast<std::chrono::seconds>(length).count());
415     discoverable_started_time.reset();
416   }
417 
418   if (!inquiry_started_time.has_value() && discovery_count != 0) {
419     inquiry_started_time.emplace(now);
420   } else if (inquiry_started_time.has_value() && discovery_count == 0) {
421     inquiry_sessions_count.Add(1);
422     pw::chrono::SystemClock::duration length =
423         now - inquiry_started_time.value();
424     last_inquiry_length_sec.Set(
425         std::chrono::duration_cast<std::chrono::seconds>(length).count());
426     inquiry_started_time.reset();
427   }
428 
429   discoverable_sessions.Set(discoverable_count);
430   pending_discoverable_sessions.Set(pending_discoverable_count);
431   discovery_sessions.Set(discovery_count);
432 }
433 
UpdateInspectProperties()434 void BrEdrDiscoveryManager::UpdateInspectProperties() {
435   inspect_properties_.Update(discoverable_.size(),
436                              pending_discoverable_.size(),
437                              discovering_.size(),
438                              dispatcher_.now());
439 }
440 
NotifyPeersUpdated(const std::unordered_set<Peer * > & peers)441 void BrEdrDiscoveryManager::NotifyPeersUpdated(
442     const std::unordered_set<Peer*>& peers) {
443   for (Peer* peer : peers) {
444     if (!peer->name()) {
445       RequestPeerName(peer->identifier());
446     }
447     for (const auto& session : discovering_) {
448       session->NotifyDiscoveryResult(*peer);
449     }
450   }
451 }
452 
RequestPeerName(PeerId id)453 void BrEdrDiscoveryManager::RequestPeerName(PeerId id) {
454   if (requesting_names_.count(id)) {
455     bt_log(TRACE, "gap-bredr", "already requesting name for %s", bt_str(id));
456     return;
457   }
458   Peer* peer = cache_->FindById(id);
459   if (!peer) {
460     bt_log(
461         WARN, "gap-bredr", "cannot request name, unknown peer: %s", bt_str(id));
462     return;
463   }
464   auto packet = hci::EmbossCommandPacket::New<
465       pw::bluetooth::emboss::RemoteNameRequestCommandWriter>(
466       hci_spec::kRemoteNameRequest);
467   auto params = packet.view_t();
468   BT_DEBUG_ASSERT(peer->bredr());
469   BT_DEBUG_ASSERT(peer->bredr()->page_scan_repetition_mode());
470   params.bd_addr().CopyFrom(peer->address().value().view());
471   params.page_scan_repetition_mode().Write(
472       *(peer->bredr()->page_scan_repetition_mode()));
473   if (peer->bredr()->clock_offset()) {
474     params.clock_offset().valid().Write(true);
475     uint16_t offset = peer->bredr()->clock_offset().value();
476     params.clock_offset().clock_offset().Write(offset);
477   }
478 
479   auto cb = [id, self = weak_self_.GetWeakPtr()](
480                 auto, const hci::EmbossEventPacket& event) {
481     if (!self.is_alive()) {
482       return;
483     }
484     if (hci_is_error(event, TRACE, "gap-bredr", "remote name request failed")) {
485       self->requesting_names_.erase(id);
486       return;
487     }
488 
489     if (event.event_code() == hci_spec::kCommandStatusEventCode) {
490       return;
491     }
492 
493     BT_DEBUG_ASSERT(event.event_code() ==
494                     hci_spec::kRemoteNameRequestCompleteEventCode);
495 
496     self->requesting_names_.erase(id);
497     Peer* const peer = self->cache_->FindById(id);
498     if (!peer) {
499       return;
500     }
501 
502     auto params =
503         event.view<pw::bluetooth::emboss::RemoteNameRequestCompleteEventView>();
504     emboss::support::ReadOnlyContiguousBuffer name =
505         params.remote_name().BackingStorage();
506     const unsigned char* name_end = std::find(name.begin(), name.end(), '\0');
507     std::string name_string(reinterpret_cast<const char*>(name.begin()),
508                             reinterpret_cast<const char*>(name_end));
509     peer->RegisterName(std::move(name_string),
510                        Peer::NameSource::kNameDiscoveryProcedure);
511   };
512 
513   auto cmd_id =
514       cmd_->SendExclusiveCommand(std::move(packet),
515                                  std::move(cb),
516                                  hci_spec::kRemoteNameRequestCompleteEventCode,
517                                  {hci_spec::kInquiry});
518   if (cmd_id) {
519     requesting_names_.insert(id);
520   }
521 }
522 
RequestDiscoverable(DiscoverableCallback callback)523 void BrEdrDiscoveryManager::RequestDiscoverable(DiscoverableCallback callback) {
524   BT_DEBUG_ASSERT(callback);
525 
526   auto self = weak_self_.GetWeakPtr();
527   auto result_cb = [self, cb = callback.share()](const hci::Result<>& result) {
528     cb(result, (result.is_ok() ? self->AddDiscoverableSession() : nullptr));
529   };
530 
531   auto update_inspect =
532       fit::defer([self]() { self->UpdateInspectProperties(); });
533 
534   if (!pending_discoverable_.empty()) {
535     pending_discoverable_.push(std::move(result_cb));
536     bt_log(INFO,
537            "gap-bredr",
538            "discoverable mode starting: %lu pending",
539            pending_discoverable_.size());
540     return;
541   }
542 
543   // If we're already discoverable, just add a session.
544   if (!discoverable_.empty()) {
545     result_cb(fit::ok());
546     return;
547   }
548 
549   pending_discoverable_.push(std::move(result_cb));
550   SetInquiryScan();
551 }
552 
SetInquiryScan()553 void BrEdrDiscoveryManager::SetInquiryScan() {
554   bool enable = !discoverable_.empty() || !pending_discoverable_.empty();
555   bt_log(INFO,
556          "gap-bredr",
557          "%sabling inquiry scan: %lu sessions, %lu pending",
558          (enable ? "en" : "dis"),
559          discoverable_.size(),
560          pending_discoverable_.size());
561 
562   auto self = weak_self_.GetWeakPtr();
563   auto scan_enable_cb = [self](auto, const hci::EventPacket& event) {
564     if (!self.is_alive()) {
565       return;
566     }
567 
568     auto status = event.ToResult();
569     auto resolve_pending = fit::defer([self, &status]() {
570       while (!self->pending_discoverable_.empty()) {
571         auto cb = std::move(self->pending_discoverable_.front());
572         self->pending_discoverable_.pop();
573         cb(status);
574       }
575     });
576 
577     if (bt_is_error(status, WARN, "gap-bredr", "read scan enable failed")) {
578       return;
579     }
580 
581     bool enable =
582         !self->discoverable_.empty() || !self->pending_discoverable_.empty();
583     auto params = event.return_params<hci_spec::ReadScanEnableReturnParams>();
584     uint8_t scan_type = params->scan_enable;
585     bool enabled =
586         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
587 
588     if (enable == enabled) {
589       bt_log(INFO,
590              "gap-bredr",
591              "inquiry scan already %s",
592              (enable ? "enabled" : "disabled"));
593       return;
594     }
595 
596     if (enable) {
597       scan_type |= static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
598     } else {
599       scan_type &= ~static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
600     }
601 
602     auto write_enable = hci::EmbossCommandPacket::New<
603         pw::bluetooth::emboss::WriteScanEnableCommandWriter>(
604         hci_spec::kWriteScanEnable);
605     auto write_enable_view = write_enable.view_t();
606     write_enable_view.scan_enable().inquiry().Write(
607         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry));
608     write_enable_view.scan_enable().page().Write(
609         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kPage));
610     resolve_pending.cancel();
611     self->cmd_->SendCommand(
612         std::move(write_enable), [self](auto, const hci::EventPacket& event) {
613           if (!self.is_alive()) {
614             return;
615           }
616 
617           // Warn if the command failed
618           hci_is_error(event, WARN, "gap-bredr", "write scan enable failed");
619 
620           while (!self->pending_discoverable_.empty()) {
621             auto cb = std::move(self->pending_discoverable_.front());
622             self->pending_discoverable_.pop();
623             cb(event.ToResult());
624           }
625           self->UpdateInspectProperties();
626         });
627   };
628 
629   auto read_enable = hci::EmbossCommandPacket::New<
630       pw::bluetooth::emboss::ReadScanEnableCommandWriter>(
631       hci_spec::kReadScanEnable);
632   cmd_->SendCommand(std::move(read_enable), std::move(scan_enable_cb));
633 }
634 
WriteInquiryScanSettings(uint16_t interval,uint16_t window,bool interlaced)635 void BrEdrDiscoveryManager::WriteInquiryScanSettings(uint16_t interval,
636                                                      uint16_t window,
637                                                      bool interlaced) {
638   // TODO(jamuraa): add a callback for success or failure?
639   auto write_activity = hci::EmbossCommandPacket::New<
640       pw::bluetooth::emboss::WriteInquiryScanActivityCommandWriter>(
641       hci_spec::kWriteInquiryScanActivity);
642   auto activity_params = write_activity.view_t();
643   activity_params.inquiry_scan_interval().Write(interval);
644   activity_params.inquiry_scan_window().Write(window);
645 
646   cmd_->SendCommand(
647       std::move(write_activity), [](auto id, const hci::EventPacket& event) {
648         if (hci_is_error(event,
649                          WARN,
650                          "gap-bredr",
651                          "write inquiry scan activity failed")) {
652           return;
653         }
654         bt_log(TRACE, "gap-bredr", "inquiry scan activity updated");
655       });
656 
657   auto write_type = hci::EmbossCommandPacket::New<
658       pw::bluetooth::emboss::WriteInquiryScanTypeCommandWriter>(
659       hci_spec::kWriteInquiryScanType);
660   auto type_params = write_type.view_t();
661   type_params.inquiry_scan_type().Write(
662       interlaced ? pw::bluetooth::emboss::InquiryScanType::INTERLACED
663                  : pw::bluetooth::emboss::InquiryScanType::STANDARD);
664 
665   cmd_->SendCommand(
666       std::move(write_type), [](auto id, const hci::EventPacket& event) {
667         if (hci_is_error(
668                 event, WARN, "gap-bredr", "write inquiry scan type failed")) {
669           return;
670         }
671         bt_log(TRACE, "gap-bredr", "inquiry scan type updated");
672       });
673 }
674 
675 std::unique_ptr<BrEdrDiscoverySession>
AddDiscoverySession()676 BrEdrDiscoveryManager::AddDiscoverySession() {
677   bt_log(TRACE, "gap-bredr", "adding discovery session");
678 
679   // Cannot use make_unique here since BrEdrDiscoverySession has a private
680   // constructor.
681   std::unique_ptr<BrEdrDiscoverySession> session(
682       new BrEdrDiscoverySession(weak_self_.GetWeakPtr()));
683   BT_DEBUG_ASSERT(discovering_.find(session.get()) == discovering_.end());
684   discovering_.insert(session.get());
685   bt_log(INFO,
686          "gap-bredr",
687          "new discovery session: %lu sessions active",
688          discovering_.size());
689   UpdateInspectProperties();
690   return session;
691 }
692 
RemoveDiscoverySession(BrEdrDiscoverySession * session)693 void BrEdrDiscoveryManager::RemoveDiscoverySession(
694     BrEdrDiscoverySession* session) {
695   bt_log(TRACE, "gap-bredr", "removing discovery session");
696 
697   auto removed = discovering_.erase(session);
698   // TODO(fxbug.dev/42145646): Cancel the running inquiry with StopInquiry()
699   // instead.
700   if (removed) {
701     zombie_discovering_.insert(session);
702   }
703   UpdateInspectProperties();
704 }
705 
706 std::unique_ptr<BrEdrDiscoverableSession>
AddDiscoverableSession()707 BrEdrDiscoveryManager::AddDiscoverableSession() {
708   bt_log(TRACE, "gap-bredr", "adding discoverable session");
709 
710   // Cannot use make_unique here since BrEdrDiscoverableSession has a private
711   // constructor.
712   std::unique_ptr<BrEdrDiscoverableSession> session(
713       new BrEdrDiscoverableSession(weak_self_.GetWeakPtr()));
714   BT_DEBUG_ASSERT(discoverable_.find(session.get()) == discoverable_.end());
715   discoverable_.insert(session.get());
716   bt_log(INFO,
717          "gap-bredr",
718          "new discoverable session: %lu sessions active",
719          discoverable_.size());
720   return session;
721 }
722 
RemoveDiscoverableSession(BrEdrDiscoverableSession * session)723 void BrEdrDiscoveryManager::RemoveDiscoverableSession(
724     BrEdrDiscoverableSession* session) {
725   bt_log(DEBUG, "gap-bredr", "removing discoverable session");
726   discoverable_.erase(session);
727   if (discoverable_.empty()) {
728     SetInquiryScan();
729   }
730   UpdateInspectProperties();
731 }
732 
InvalidateDiscoverySessions()733 void BrEdrDiscoveryManager::InvalidateDiscoverySessions() {
734   for (auto session : discovering_) {
735     session->NotifyError();
736   }
737   discovering_.clear();
738   UpdateInspectProperties();
739 }
740 
741 }  // namespace bt::gap
742