xref: /aosp_15_r20/external/pigweed/pw_bluetooth_sapphire/host/gap/bredr_discovery_manager.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/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   PW_CHECK(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   PW_DCHECK(cache_);
108   PW_DCHECK(cmd_.is_alive());
109 
110   result_handler_id_ = cmd_->AddEventHandler(
111       hci_spec::kInquiryResultEventCode,
112       fit::bind_member<&BrEdrDiscoveryManager::InquiryResult>(this));
113   PW_DCHECK(result_handler_id_);
114   rssi_handler_id_ = cmd_->AddEventHandler(
115       hci_spec::kInquiryResultWithRSSIEventCode,
116       cpp20::bind_front(&BrEdrDiscoveryManager::InquiryResultWithRssi, this));
117   PW_DCHECK(rssi_handler_id_);
118   eir_handler_id_ = cmd_->AddEventHandler(
119       hci_spec::kExtendedInquiryResultEventCode,
120       cpp20::bind_front(&BrEdrDiscoveryManager::ExtendedInquiryResult, this));
121   PW_DCHECK(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   PW_DCHECK(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::CommandPacket::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 hci::EventPacket& 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 =
191       hci::CommandPacket::New<pw::bluetooth::emboss::InquiryCommandWriter>(
192           hci_spec::kInquiry);
193   auto view = inquiry.view_t();
194   view.lap().Write(pw::bluetooth::emboss::InquiryAccessCode::GIAC);
195   view.inquiry_length().Write(kInquiryLengthDefault);
196   view.num_responses().Write(0);
197 
198   cmd_->SendExclusiveCommand(
199       std::move(inquiry),
200       [self](auto, const hci::EventPacket& event) {
201         if (!self.is_alive()) {
202           return;
203         }
204         auto status = event.ToResult();
205         if (bt_is_error(status, WARN, "gap-bredr", "inquiry error")) {
206           // Failure of some kind, signal error to the sessions.
207           self->InvalidateDiscoverySessions();
208 
209           // Fallthrough for callback to pending sessions.
210         }
211 
212         // Resolve the request if the controller sent back a Command Complete or
213         // Status event.
214         // TODO(fxbug.dev/42062242): Make it impossible for Command Complete to
215         // happen here and remove handling for it.
216         if (event.event_code() == hci_spec::kCommandStatusEventCode ||
217             event.event_code() == hci_spec::kCommandCompleteEventCode) {
218           // Inquiry started, make sessions for our waiting callbacks.
219           while (!self->pending_discovery_.empty()) {
220             auto callback = std::move(self->pending_discovery_.front());
221             self->pending_discovery_.pop();
222             callback(status,
223                      (status.is_ok() ? self->AddDiscoverySession() : nullptr));
224           }
225           return;
226         }
227 
228         PW_DCHECK(event.event_code() == 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   PW_DCHECK(result_handler_id_);
246   bt_log(TRACE, "gap-bredr", "cancelling inquiry");
247 
248   const hci::CommandPacket inq_cancel =
249       hci::CommandPacket::New<pw::bluetooth::emboss::InquiryCancelCommandView>(
250           hci_spec::kInquiryCancel);
251   cmd_->SendCommand(
252       std::move(inq_cancel), [](int64_t, const hci::EventPacket& event) {
253         // Warn if the command failed.
254         HCI_IS_ERROR(event, WARN, "gap-bredr", "inquiry cancel failed");
255       });
256 }
257 
InquiryResult(const hci::EventPacket & event)258 hci::CommandChannel::EventCallbackResult BrEdrDiscoveryManager::InquiryResult(
259     const hci::EventPacket& event) {
260   PW_DCHECK(event.event_code() == hci_spec::kInquiryResultEventCode);
261   std::unordered_set<Peer*> peers;
262 
263   auto view = event.view<pw::bluetooth::emboss::InquiryResultEventView>();
264   for (int i = 0; i < view.num_responses().Read(); i++) {
265     const auto response = view.responses()[i];
266     DeviceAddress addr(DeviceAddress::Type::kBREDR,
267                        DeviceAddressBytes{response.bd_addr()});
268     Peer* peer = AddOrUpdateConnectablePeer(cache_, addr);
269     peer->MutBrEdr().SetInquiryData(response);
270     peers.insert(peer);
271   }
272 
273   NotifyPeersUpdated(peers);
274 
275   return hci::CommandChannel::EventCallbackResult::kContinue;
276 }
277 
278 hci::CommandChannel::EventCallbackResult
InquiryResultWithRssi(const hci::EventPacket & event)279 BrEdrDiscoveryManager::InquiryResultWithRssi(const hci::EventPacket& 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::EventPacket & event)288 BrEdrDiscoveryManager::ExtendedInquiryResult(const hci::EventPacket& event) {
289   bt_log(TRACE, "gap-bredr", "ExtendedInquiryResult received");
290   const auto result =
291       event.view<pw::bluetooth::emboss::ExtendedInquiryResultEventView>();
292 
293   DeviceAddress addr(DeviceAddress::Type::kBREDR,
294                      DeviceAddressBytes(result.bd_addr()));
295   Peer* peer = AddOrUpdateConnectablePeer(cache_, addr);
296   peer->MutBrEdr().SetInquiryData(result);
297 
298   NotifyPeersUpdated({peer});
299   return hci::CommandChannel::EventCallbackResult::kContinue;
300 }
301 
UpdateEIRResponseData(std::string name,hci::ResultFunction<> callback)302 void BrEdrDiscoveryManager::UpdateEIRResponseData(
303     std::string name, hci::ResultFunction<> callback) {
304   DataType name_type = DataType::kCompleteLocalName;
305   size_t name_size = name.size();
306   if (name.size() >= (hci_spec::kExtendedInquiryResponseMaxNameBytes)) {
307     name_type = DataType::kShortenedLocalName;
308     name_size = hci_spec::kExtendedInquiryResponseMaxNameBytes;
309   }
310   auto self = weak_self_.GetWeakPtr();
311 
312   auto write_eir = hci::CommandPacket::New<
313       pw::bluetooth::emboss::WriteExtendedInquiryResponseCommandWriter>(
314       hci_spec::kWriteExtendedInquiryResponse);
315   auto write_eir_params = write_eir.view_t();
316   write_eir_params.fec_required().Write(0x00);
317 
318   // Create MutableBufferView of BackingStorage
319   unsigned char* eir_data =
320       write_eir_params.extended_inquiry_response().BackingStorage().data();
321   MutableBufferView eir_response_buf =
322       MutableBufferView(eir_data, hci_spec::kExtendedInquiryResponseBytes);
323   eir_response_buf.Fill(0);
324   eir_response_buf[0] = name_size + 1;
325   eir_response_buf[1] = static_cast<uint8_t>(name_type);
326   eir_response_buf.mutable_view(2).Write(
327       reinterpret_cast<const uint8_t*>(name.data()), name_size);
328 
329   self->cmd_->SendCommand(
330       std::move(write_eir),
331       [self, local_name = std::move(name), cb = std::move(callback)](
332           auto, const hci::EventPacket& event) mutable {
333         if (!HCI_IS_ERROR(event, WARN, "gap", "write EIR failed")) {
334           self->local_name_ = std::move(local_name);
335         }
336         cb(event.ToResult());
337       });
338 }
339 
UpdateLocalName(std::string name,hci::ResultFunction<> callback)340 void BrEdrDiscoveryManager::UpdateLocalName(std::string name,
341                                             hci::ResultFunction<> callback) {
342   auto self = weak_self_.GetWeakPtr();
343 
344   auto write_name = hci::CommandPacket::New<
345       pw::bluetooth::emboss::WriteLocalNameCommandWriter>(
346       hci_spec::kWriteLocalName);
347   auto write_name_view = write_name.view_t();
348   auto local_name = write_name_view.local_name().BackingStorage();
349   size_t name_size = std::min(name.size(), hci_spec::kMaxNameLength);
350 
351   // Use ContiguousBuffer instead of constructing LocalName view in case of
352   // invalid view being created when name is not large enough for the view
353   auto name_buf = emboss::support::ReadOnlyContiguousBuffer(&name);
354   local_name.CopyFrom(name_buf, name_size);
355 
356   cmd_->SendCommand(
357       std::move(write_name),
358       [self, name_as_str = std::move(name), cb = std::move(callback)](
359           auto, const hci::EventPacket& event) mutable {
360         if (HCI_IS_ERROR(event, WARN, "gap", "set local name failed")) {
361           cb(event.ToResult());
362           return;
363         }
364         // If the WriteLocalName command was successful, update the extended
365         // inquiry data.
366         self->UpdateEIRResponseData(std::move(name_as_str), std::move(cb));
367       });
368 }
369 
AttachInspect(inspect::Node & parent,std::string name)370 void BrEdrDiscoveryManager::AttachInspect(inspect::Node& parent,
371                                           std::string name) {
372   auto node = parent.CreateChild(name);
373   inspect_properties_.Initialize(std::move(node));
374   UpdateInspectProperties();
375 }
376 
Initialize(inspect::Node new_node)377 void BrEdrDiscoveryManager::InspectProperties::Initialize(
378     inspect::Node new_node) {
379   discoverable_sessions = new_node.CreateUint("discoverable_sessions", 0);
380   pending_discoverable_sessions =
381       new_node.CreateUint("pending_discoverable", 0);
382   discoverable_sessions_count =
383       new_node.CreateUint("discoverable_sessions_count", 0);
384   last_discoverable_length_sec =
385       new_node.CreateUint("last_discoverable_length_sec", 0);
386 
387   discovery_sessions = new_node.CreateUint("discovery_sessions", 0);
388   last_discovery_length_sec =
389       new_node.CreateUint("last_discovery_length_sec", 0);
390   discovery_sessions_count = new_node.CreateUint("discovery_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     discovery_sessions_count.Add(1);
422     pw::chrono::SystemClock::duration length =
423         now - inquiry_started_time.value();
424     last_discovery_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::CommandPacket::New<
465       pw::bluetooth::emboss::RemoteNameRequestCommandWriter>(
466       hci_spec::kRemoteNameRequest);
467   auto params = packet.view_t();
468   PW_DCHECK(peer->bredr());
469   PW_DCHECK(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::EventPacket& 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     PW_DCHECK(event.event_code() ==
494               hci_spec::kRemoteNameRequestCompleteEventCode);
495 
496     self->requesting_names_.erase(id);
497     Peer* const cached_peer = self->cache_->FindById(id);
498     if (!cached_peer) {
499       return;
500     }
501 
502     auto event_view =
503         event.view<pw::bluetooth::emboss::RemoteNameRequestCompleteEventView>();
504     emboss::support::ReadOnlyContiguousBuffer name =
505         event_view.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     cached_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   PW_DCHECK(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: %zu 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: %zu sessions, %zu 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 enabling =
582         !self->discoverable_.empty() || !self->pending_discoverable_.empty();
583     const auto params = event.view<
584         pw::bluetooth::emboss::ReadScanEnableCommandCompleteEventView>();
585     uint8_t scan_type = params.scan_enable().BackingStorage().ReadUInt();
586     bool enabled =
587         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
588 
589     if (enabling == enabled) {
590       bt_log(INFO,
591              "gap-bredr",
592              "inquiry scan already %s",
593              (enabling ? "enabled" : "disabled"));
594       return;
595     }
596 
597     if (enabling) {
598       scan_type |= static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
599     } else {
600       scan_type &= ~static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry);
601     }
602 
603     auto write_enable = hci::CommandPacket::New<
604         pw::bluetooth::emboss::WriteScanEnableCommandWriter>(
605         hci_spec::kWriteScanEnable);
606     auto write_enable_view = write_enable.view_t();
607     write_enable_view.scan_enable().inquiry().Write(
608         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kInquiry));
609     write_enable_view.scan_enable().page().Write(
610         scan_type & static_cast<uint8_t>(hci_spec::ScanEnableBit::kPage));
611     resolve_pending.cancel();
612     self->cmd_->SendCommand(
613         std::move(write_enable),
614         [self](auto, const hci::EventPacket& response) {
615           if (!self.is_alive()) {
616             return;
617           }
618 
619           // Warn if the command failed
620           HCI_IS_ERROR(response, WARN, "gap-bredr", "write scan enable failed");
621 
622           while (!self->pending_discoverable_.empty()) {
623             auto cb = std::move(self->pending_discoverable_.front());
624             self->pending_discoverable_.pop();
625             cb(response.ToResult());
626           }
627           self->UpdateInspectProperties();
628         });
629   };
630 
631   auto read_enable = hci::CommandPacket::New<
632       pw::bluetooth::emboss::ReadScanEnableCommandWriter>(
633       hci_spec::kReadScanEnable);
634   cmd_->SendCommand(std::move(read_enable), std::move(scan_enable_cb));
635 }
636 
WriteInquiryScanSettings(uint16_t interval,uint16_t window,bool interlaced)637 void BrEdrDiscoveryManager::WriteInquiryScanSettings(uint16_t interval,
638                                                      uint16_t window,
639                                                      bool interlaced) {
640   // TODO(jamuraa): add a callback for success or failure?
641   auto write_activity = hci::CommandPacket::New<
642       pw::bluetooth::emboss::WriteInquiryScanActivityCommandWriter>(
643       hci_spec::kWriteInquiryScanActivity);
644   auto activity_params = write_activity.view_t();
645   activity_params.inquiry_scan_interval().Write(interval);
646   activity_params.inquiry_scan_window().Write(window);
647 
648   cmd_->SendCommand(
649       std::move(write_activity), [](auto, const hci::EventPacket& event) {
650         if (HCI_IS_ERROR(event,
651                          WARN,
652                          "gap-bredr",
653                          "write inquiry scan activity failed")) {
654           return;
655         }
656         bt_log(TRACE, "gap-bredr", "inquiry scan activity updated");
657       });
658 
659   auto write_type = hci::CommandPacket::New<
660       pw::bluetooth::emboss::WriteInquiryScanTypeCommandWriter>(
661       hci_spec::kWriteInquiryScanType);
662   auto type_params = write_type.view_t();
663   type_params.inquiry_scan_type().Write(
664       interlaced ? pw::bluetooth::emboss::InquiryScanType::INTERLACED
665                  : pw::bluetooth::emboss::InquiryScanType::STANDARD);
666 
667   cmd_->SendCommand(
668       std::move(write_type), [](auto, const hci::EventPacket& event) {
669         if (HCI_IS_ERROR(
670                 event, WARN, "gap-bredr", "write inquiry scan type failed")) {
671           return;
672         }
673         bt_log(TRACE, "gap-bredr", "inquiry scan type updated");
674       });
675 }
676 
677 std::unique_ptr<BrEdrDiscoverySession>
AddDiscoverySession()678 BrEdrDiscoveryManager::AddDiscoverySession() {
679   bt_log(TRACE, "gap-bredr", "adding discovery session");
680 
681   // Cannot use make_unique here since BrEdrDiscoverySession has a private
682   // constructor.
683   std::unique_ptr<BrEdrDiscoverySession> session(
684       new BrEdrDiscoverySession(weak_self_.GetWeakPtr()));
685   PW_DCHECK(discovering_.find(session.get()) == discovering_.end());
686   discovering_.insert(session.get());
687   bt_log(INFO,
688          "gap-bredr",
689          "new discovery session: %zu sessions active",
690          discovering_.size());
691   UpdateInspectProperties();
692   return session;
693 }
694 
RemoveDiscoverySession(BrEdrDiscoverySession * session)695 void BrEdrDiscoveryManager::RemoveDiscoverySession(
696     BrEdrDiscoverySession* session) {
697   bt_log(TRACE, "gap-bredr", "removing discovery session");
698 
699   auto removed = discovering_.erase(session);
700   // TODO(fxbug.dev/42145646): Cancel the running inquiry with StopInquiry()
701   // instead.
702   if (removed) {
703     zombie_discovering_.insert(session);
704   }
705   UpdateInspectProperties();
706 }
707 
708 std::unique_ptr<BrEdrDiscoverableSession>
AddDiscoverableSession()709 BrEdrDiscoveryManager::AddDiscoverableSession() {
710   bt_log(TRACE, "gap-bredr", "adding discoverable session");
711 
712   // Cannot use make_unique here since BrEdrDiscoverableSession has a private
713   // constructor.
714   std::unique_ptr<BrEdrDiscoverableSession> session(
715       new BrEdrDiscoverableSession(weak_self_.GetWeakPtr()));
716   PW_DCHECK(discoverable_.find(session.get()) == discoverable_.end());
717   discoverable_.insert(session.get());
718   bt_log(INFO,
719          "gap-bredr",
720          "new discoverable session: %zu sessions active",
721          discoverable_.size());
722   return session;
723 }
724 
RemoveDiscoverableSession(BrEdrDiscoverableSession * session)725 void BrEdrDiscoveryManager::RemoveDiscoverableSession(
726     BrEdrDiscoverableSession* session) {
727   bt_log(DEBUG, "gap-bredr", "removing discoverable session");
728   discoverable_.erase(session);
729   if (discoverable_.empty()) {
730     SetInquiryScan();
731   }
732   UpdateInspectProperties();
733 }
734 
InvalidateDiscoverySessions()735 void BrEdrDiscoveryManager::InvalidateDiscoverySessions() {
736   for (auto session : discovering_) {
737     session->NotifyError();
738   }
739   discovering_.clear();
740   UpdateInspectProperties();
741 }
742 
743 }  // namespace bt::gap
744