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