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