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/low_energy_discovery_manager.h"
16
17 #include <gmock/gmock.h>
18
19 #include <unordered_set>
20 #include <vector>
21
22 #include "pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/macros.h"
25 #include "pw_bluetooth_sapphire/internal/host/gap/peer.h"
26 #include "pw_bluetooth_sapphire/internal/host/gap/peer_cache.h"
27 #include "pw_bluetooth_sapphire/internal/host/hci/fake_local_address_delegate.h"
28 #include "pw_bluetooth_sapphire/internal/host/hci/legacy_low_energy_scanner.h"
29 #include "pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
30 #include "pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
31 #include "pw_bluetooth_sapphire/internal/host/testing/fake_peer.h"
32 #include "pw_bluetooth_sapphire/internal/host/testing/inspect.h"
33 #include "pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
34
35 namespace bt::gap {
36 namespace {
37
38 using namespace inspect::testing;
39 using bt::testing::FakeController;
40 using bt::testing::FakePeer;
41 using PauseToken = LowEnergyDiscoveryManager::PauseToken;
42
43 using TestingBase = bt::testing::FakeDispatcherControllerTest<FakeController>;
44
45 const DeviceAddress kAddress0(DeviceAddress::Type::kLEPublic, {0});
46 const DeviceAddress kAddrAlias0(DeviceAddress::Type::kBREDR, kAddress0.value());
47 const DeviceAddress kAddress1(DeviceAddress::Type::kLERandom, {1});
48 const DeviceAddress kAddress2(DeviceAddress::Type::kLEPublic, {2});
49 const DeviceAddress kAddress3(DeviceAddress::Type::kLEPublic, {3});
50 const DeviceAddress kAddress4(DeviceAddress::Type::kLEPublic, {4});
51 const DeviceAddress kAddress5(DeviceAddress::Type::kLEPublic, {5});
52
53 constexpr uint16_t kServiceDataUuid = 0x1234;
54
55 constexpr pw::chrono::SystemClock::duration kTestScanPeriod =
56 std::chrono::seconds(10);
57
58 const char* kInspectNodeName = "low_energy_discovery_manager";
59
60 class LowEnergyDiscoveryManagerTest : public TestingBase {
61 public:
62 LowEnergyDiscoveryManagerTest() = default;
63 ~LowEnergyDiscoveryManagerTest() override = default;
64
SetUp()65 void SetUp() override {
66 TestingBase::SetUp();
67
68 scan_enabled_ = false;
69
70 FakeController::Settings settings;
71 settings.ApplyLegacyLEConfig();
72 test_device()->set_settings(settings);
73
74 // TODO(armansito): Now that the hci::LowEnergyScanner is injected into
75 // |discovery_manager_| rather than constructed by it, a fake implementation
76 // could be injected directly. Consider providing fake behavior here in this
77 // harness rather than using a FakeController.
78 scanner_ = std::make_unique<hci::LegacyLowEnergyScanner>(
79 &fake_address_delegate_, transport()->GetWeakPtr(), dispatcher());
80 discovery_manager_ = std::make_unique<LowEnergyDiscoveryManager>(
81 scanner_.get(), &peer_cache_, dispatcher());
82 discovery_manager_->AttachInspect(inspector_.GetRoot(), kInspectNodeName);
83
84 test_device()->set_scan_state_callback(
85 std::bind(&LowEnergyDiscoveryManagerTest::OnScanStateChanged,
86 this,
87 std::placeholders::_1));
88 }
89
TearDown()90 void TearDown() override {
91 if (discovery_manager_) {
92 discovery_manager_ = nullptr;
93 }
94 scanner_ = nullptr;
95 test_device()->Stop();
96 TestingBase::TearDown();
97 }
98
99 protected:
discovery_manager() const100 LowEnergyDiscoveryManager* discovery_manager() const {
101 return discovery_manager_.get();
102 }
103
104 // Deletes |discovery_manager_|.
DeleteDiscoveryManager()105 void DeleteDiscoveryManager() { discovery_manager_ = nullptr; }
106
107 #ifndef NINSPECT
InspectHierarchy() const108 inspect::Hierarchy InspectHierarchy() const {
109 return inspect::ReadFromVmo(inspector_.DuplicateVmo()).take_value();
110 }
111
InspectProperties() const112 std::vector<inspect::PropertyValue> InspectProperties() const {
113 auto hierarchy = InspectHierarchy();
114 auto children = hierarchy.take_children();
115 PW_CHECK(children.size() == 1u);
116 return children.front().node_ptr()->take_properties();
117 }
118 #endif // NINSPECT
119
peer_cache()120 PeerCache* peer_cache() { return &peer_cache_; }
121
122 // Returns the last reported scan state of the FakeController.
scan_enabled() const123 bool scan_enabled() const { return scan_enabled_; }
124
125 // The scan states that the FakeController has transitioned through.
scan_states() const126 const std::vector<bool> scan_states() const { return scan_states_; }
127
128 // Sets a callback that will run when the scan state transitions |count|
129 // times.
set_scan_state_handler(size_t count,fit::closure callback)130 void set_scan_state_handler(size_t count, fit::closure callback) {
131 scan_state_callbacks_[count] = std::move(callback);
132 }
133
134 // Called by FakeController when the scan state changes.
OnScanStateChanged(bool enabled)135 void OnScanStateChanged(bool enabled) {
136 auto scan_type = test_device()->le_scan_state().scan_type;
137 bt_log(DEBUG,
138 "gap-test",
139 "FakeController scan state: %s %s",
140 enabled ? "enabled" : "disabled",
141 scan_type == pw::bluetooth::emboss::LEScanType::ACTIVE ? "active"
142 : "passive");
143 scan_enabled_ = enabled;
144 scan_states_.push_back(enabled);
145
146 auto iter = scan_state_callbacks_.find(scan_states_.size());
147 if (iter != scan_state_callbacks_.end()) {
148 iter->second();
149 }
150 }
151
152 // Registers the following fake peers with the FakeController:
153 //
154 // Peer 0:
155 // - Connectable, not scannable;
156 // - General discoverable;
157 // - UUIDs: 0x180d, 0x180f;
158 // - Service Data UUIDs: kServiceDataUuid;
159 // - has name: "Device 0"
160 //
161 // Peer 1:
162 // - Connectable, not scannable;
163 // - Limited discoverable;
164 // - UUIDs: 0x180d;
165 // - has name: "Device 1"
166 //
167 // Peer 2:
168 // - Not connectable, not scannable;
169 // - General discoverable;
170 // - UUIDs: none;
171 // - has name: "Device 2"
172 //
173 // Peer 3:
174 // - Not discoverable;
AddFakePeers()175 void AddFakePeers() {
176 // Peer 0
177 const StaticByteBuffer kAdvData0(
178 // Flags
179 0x02,
180 0x01,
181 0x02,
182
183 // Complete 16-bit service UUIDs
184 0x05,
185 0x03,
186 0x0d,
187 0x18,
188 0x0f,
189 0x18,
190
191 // 16-bit service data UUID
192 0x03,
193 DataType::kServiceData16Bit,
194 LowerBits(kServiceDataUuid),
195 UpperBits(kServiceDataUuid),
196
197 // Complete local name
198 0x09,
199 0x09,
200 'D',
201 'e',
202 'v',
203 'i',
204 'c',
205 'e',
206 ' ',
207 '0');
208 auto fake_peer =
209 std::make_unique<FakePeer>(kAddress0, dispatcher(), true, true);
210 fake_peer->set_advertising_data(kAdvData0);
211 test_device()->AddPeer(std::move(fake_peer));
212
213 // Peer 1
214 const StaticByteBuffer kAdvData1(
215 // Flags
216 0x02,
217 0x01,
218 0x01,
219
220 // Complete 16-bit service UUIDs
221 0x03,
222 0x03,
223 0x0d,
224 0x18);
225 fake_peer = std::make_unique<FakePeer>(kAddress1, dispatcher(), true, true);
226 fake_peer->set_advertising_data(kAdvData1);
227 test_device()->AddPeer(std::move(fake_peer));
228
229 // Peer 2
230 const StaticByteBuffer kAdvData2(
231 // Flags
232 0x02,
233 0x01,
234 0x02,
235
236 // Complete local name
237 0x09,
238 0x09,
239 'D',
240 'e',
241 'v',
242 'i',
243 'c',
244 'e',
245 ' ',
246 '2');
247 fake_peer =
248 std::make_unique<FakePeer>(kAddress2, dispatcher(), false, false);
249 fake_peer->set_advertising_data(kAdvData2);
250 test_device()->AddPeer(std::move(fake_peer));
251
252 // Peer 3
253 const StaticByteBuffer kAdvData3(
254 // Flags
255 0x02,
256 0x01,
257 0x00,
258
259 // Complete local name
260 0x09,
261 0x09,
262 'D',
263 'e',
264 'v',
265 'i',
266 'c',
267 'e',
268 ' ',
269 '3');
270 fake_peer =
271 std::make_unique<FakePeer>(kAddress3, dispatcher(), false, false);
272 fake_peer->set_advertising_data(kAdvData3);
273 test_device()->AddPeer(std::move(fake_peer));
274 }
275
276 // Creates and returns a discovery session.
StartDiscoverySession(bool active=true)277 std::unique_ptr<LowEnergyDiscoverySession> StartDiscoverySession(
278 bool active = true) {
279 std::unique_ptr<LowEnergyDiscoverySession> session;
280 discovery_manager()->StartDiscovery(active, [&](auto cb_session) {
281 PW_CHECK(cb_session);
282 session = std::move(cb_session);
283 });
284
285 RunUntilIdle();
286 PW_CHECK(session);
287 return session;
288 }
289
290 private:
291 PeerCache peer_cache_{dispatcher()};
292 hci::FakeLocalAddressDelegate fake_address_delegate_{dispatcher()};
293 std::unique_ptr<hci::LegacyLowEnergyScanner> scanner_;
294 std::unique_ptr<LowEnergyDiscoveryManager> discovery_manager_;
295
296 bool scan_enabled_;
297 std::vector<bool> scan_states_;
298 std::unordered_map<size_t, fit::closure> scan_state_callbacks_;
299
300 inspect::Inspector inspector_;
301
302 BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyDiscoveryManagerTest);
303 };
304
305 using GAP_LowEnergyDiscoveryManagerTest = LowEnergyDiscoveryManagerTest;
306
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryAndStop)307 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryAndStop) {
308 std::unique_ptr<LowEnergyDiscoverySession> session;
309 discovery_manager()->StartDiscovery(
310 /*active=*/true,
311 [&session](auto cb_session) { session = std::move(cb_session); });
312
313 RunUntilIdle();
314
315 // The test fixture will be notified of the change in scan state before we
316 // receive the session.
317 EXPECT_TRUE(scan_enabled());
318 RunUntilIdle();
319
320 ASSERT_TRUE(session);
321 EXPECT_TRUE(session->alive());
322
323 session->Stop();
324 EXPECT_FALSE(session->alive());
325
326 RunUntilIdle();
327 EXPECT_FALSE(scan_enabled());
328 }
329
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryAndStopByDeleting)330 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryAndStopByDeleting) {
331 // Start discovery but don't acquire ownership of the received session. This
332 // should immediately terminate the session.
333 std::unique_ptr<LowEnergyDiscoverySession> session;
334 discovery_manager()->StartDiscovery(
335 /*active=*/true,
336 [&session](auto cb_session) { session = std::move(cb_session); });
337
338 RunUntilIdle();
339
340 // The test fixture will be notified of the change in scan state before we
341 // receive the session.
342 EXPECT_TRUE(scan_enabled());
343 RunUntilIdle();
344
345 ASSERT_TRUE(session);
346 EXPECT_TRUE(session->alive());
347
348 session = nullptr;
349
350 RunUntilIdle();
351 EXPECT_FALSE(scan_enabled());
352 }
353
TEST_F(LowEnergyDiscoveryManagerTest,Destructor)354 TEST_F(LowEnergyDiscoveryManagerTest, Destructor) {
355 // Start discovery with a session, delete the manager and ensure that the
356 // session is inactive with the error callback called.
357 std::unique_ptr<LowEnergyDiscoverySession> session;
358 discovery_manager()->StartDiscovery(
359 /*active=*/true,
360 [&session](auto cb_session) { session = std::move(cb_session); });
361
362 RunUntilIdle();
363
364 EXPECT_TRUE(scan_enabled());
365
366 ASSERT_TRUE(session);
367 EXPECT_TRUE(session->alive());
368
369 size_t num_errors = 0u;
370 session->set_error_callback([&num_errors]() { num_errors++; });
371
372 EXPECT_EQ(0u, num_errors);
373 DeleteDiscoveryManager();
374 EXPECT_EQ(1u, num_errors);
375 EXPECT_FALSE(session->alive());
376 }
377
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryAndStopInCallback)378 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryAndStopInCallback) {
379 // Start discovery but don't acquire ownership of the received session. This
380 // should terminate the session when |session| goes out of scope.
381 discovery_manager()->StartDiscovery(/*active=*/true, [](auto) {});
382
383 RunUntilIdle();
384 ASSERT_EQ(2u, scan_states().size());
385 EXPECT_TRUE(scan_states()[0]);
386 EXPECT_FALSE(scan_states()[1]);
387 }
388
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryFailure)389 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryFailure) {
390 test_device()->SetDefaultResponseStatus(
391 hci_spec::kLESetScanEnable,
392 pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
393
394 // |session| should contain nullptr.
395 discovery_manager()->StartDiscovery(
396 /*active=*/true, [](auto session) { EXPECT_FALSE(session); });
397
398 RunUntilIdle();
399 EXPECT_FALSE(scan_enabled());
400 }
401
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWhileScanning)402 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryWhileScanning) {
403 std::vector<std::unique_ptr<LowEnergyDiscoverySession>> sessions;
404
405 constexpr size_t kExpectedSessionCount = 5;
406 size_t cb_count = 0u;
407 auto cb = [&cb_count, &sessions](auto session) {
408 sessions.push_back(std::move(session));
409 cb_count++;
410 };
411
412 discovery_manager()->StartDiscovery(/*active=*/true, cb);
413
414 RunUntilIdle();
415 EXPECT_TRUE(scan_enabled());
416 EXPECT_EQ(1u, sessions.size());
417
418 // Add the rest of the sessions. These are expected to succeed immediately but
419 // the callbacks should be called asynchronously.
420 for (size_t i = 1u; i < kExpectedSessionCount; i++) {
421 discovery_manager()->StartDiscovery(/*active=*/true, cb);
422 }
423
424 RunUntilIdle();
425 EXPECT_TRUE(scan_enabled());
426 EXPECT_EQ(kExpectedSessionCount, sessions.size());
427
428 // Remove one session from the list. Scan should continue.
429 sessions.pop_back();
430 RunUntilIdle();
431 EXPECT_TRUE(scan_enabled());
432
433 // Remove all but one session from the list. Scan should continue.
434 sessions.erase(sessions.begin() + 1, sessions.end());
435 RunUntilIdle();
436 EXPECT_TRUE(scan_enabled());
437 EXPECT_EQ(1u, sessions.size());
438
439 // Remove the last session.
440 sessions.clear();
441 RunUntilIdle();
442 EXPECT_FALSE(scan_enabled());
443 }
444
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWhilePendingStart)445 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryWhilePendingStart) {
446 std::vector<std::unique_ptr<LowEnergyDiscoverySession>> sessions;
447
448 constexpr size_t kExpectedSessionCount = 5;
449 size_t cb_count = 0u;
450 auto cb = [&cb_count, &sessions](auto session) {
451 sessions.push_back(std::move(session));
452 cb_count++;
453 };
454
455 for (size_t i = 0u; i < kExpectedSessionCount; i++) {
456 discovery_manager()->StartDiscovery(/*active=*/true, cb);
457 }
458
459 RunUntilIdle();
460 EXPECT_TRUE(scan_enabled());
461 EXPECT_EQ(kExpectedSessionCount, sessions.size());
462
463 // Remove all sessions. This should stop the scan.
464 sessions.clear();
465 RunUntilIdle();
466 EXPECT_FALSE(scan_enabled());
467 }
468
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWhilePendingStartAndStopInCallback)469 TEST_F(LowEnergyDiscoveryManagerTest,
470 StartDiscoveryWhilePendingStartAndStopInCallback) {
471 constexpr size_t kExpectedSessionCount = 5;
472 size_t cb_count = 0u;
473 std::unique_ptr<LowEnergyDiscoverySession> session;
474 auto cb = [&cb_count, &session](auto cb_session) {
475 cb_count++;
476 if (cb_count == kExpectedSessionCount) {
477 // Hold on to only the last session object. The rest should get deleted
478 // within the callback.
479 session = std::move(cb_session);
480 }
481 };
482
483 for (size_t i = 0u; i < kExpectedSessionCount; i++) {
484 discovery_manager()->StartDiscovery(/*active=*/true, cb);
485 }
486
487 RunUntilIdle();
488 EXPECT_TRUE(scan_enabled());
489 EXPECT_TRUE(session);
490
491 RunUntilIdle();
492 EXPECT_EQ(kExpectedSessionCount, cb_count);
493 EXPECT_TRUE(scan_enabled());
494
495 // Deleting the only remaning session should stop the scan.
496 session = nullptr;
497 RunUntilIdle();
498 EXPECT_FALSE(scan_enabled());
499 }
500
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWhilePendingStop)501 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryWhilePendingStop) {
502 std::unique_ptr<LowEnergyDiscoverySession> session;
503
504 discovery_manager()->StartDiscovery(
505 /*active=*/true,
506 [&session](auto cb_session) { session = std::move(cb_session); });
507
508 RunUntilIdle();
509 EXPECT_TRUE(scan_enabled());
510 EXPECT_TRUE(session);
511
512 // Stop the session. This should issue a request to stop the ongoing scan but
513 // the request will remain pending until we run the message loop.
514 session = nullptr;
515
516 // Request a new session. The discovery manager should restart the scan after
517 // the ongoing one stops.
518 discovery_manager()->StartDiscovery(
519 /*active=*/true,
520 [&session](auto cb_session) { session = std::move(cb_session); });
521
522 // Discovery should stop and start again.
523 RunUntilIdle();
524 ASSERT_EQ(3u, scan_states().size());
525 EXPECT_TRUE(scan_states()[0]);
526 EXPECT_FALSE(scan_states()[1]);
527 EXPECT_TRUE(scan_states()[2]);
528 }
529
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryFailureManyPending)530 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryFailureManyPending) {
531 test_device()->SetDefaultResponseStatus(
532 hci_spec::kLESetScanEnable,
533 pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
534
535 constexpr size_t kExpectedSessionCount = 5;
536 size_t cb_count = 0u;
537 auto cb = [&cb_count](auto session) {
538 // |session| should contain nullptr as the request will fail.
539 EXPECT_FALSE(session);
540 cb_count++;
541 };
542
543 for (size_t i = 0u; i < kExpectedSessionCount; i++) {
544 discovery_manager()->StartDiscovery(/*active=*/true, cb);
545 }
546
547 RunUntilIdle();
548 EXPECT_FALSE(scan_enabled());
549 }
550
TEST_F(LowEnergyDiscoveryManagerTest,ScanPeriodRestart)551 TEST_F(LowEnergyDiscoveryManagerTest, ScanPeriodRestart) {
552 constexpr size_t kNumScanStates = 3;
553
554 discovery_manager()->set_scan_period(kTestScanPeriod);
555
556 std::unique_ptr<LowEnergyDiscoverySession> session;
557 discovery_manager()->StartDiscovery(
558 /*active=*/true,
559 [&session](auto cb_session) { session = std::move(cb_session); });
560
561 // We should observe the scan state become enabled -> disabled -> enabled.
562 RunUntilIdle();
563 EXPECT_TRUE(scan_enabled());
564
565 // End the scan period.
566 RunFor(kTestScanPeriod);
567 ASSERT_EQ(kNumScanStates, scan_states().size());
568 EXPECT_TRUE(scan_states()[0]);
569 EXPECT_FALSE(scan_states()[1]);
570 EXPECT_TRUE(scan_states()[2]);
571 }
572
TEST_F(LowEnergyDiscoveryManagerTest,ScanPeriodRestartFailure)573 TEST_F(LowEnergyDiscoveryManagerTest, ScanPeriodRestartFailure) {
574 constexpr size_t kNumScanStates = 2;
575
576 discovery_manager()->set_scan_period(kTestScanPeriod);
577
578 std::unique_ptr<LowEnergyDiscoverySession> session;
579 bool session_error = false;
580 discovery_manager()->StartDiscovery(/*active=*/true, [&](auto cb_session) {
581 session = std::move(cb_session);
582 session->set_error_callback([&session_error] { session_error = true; });
583 });
584
585 // The controller will fail to restart scanning after scanning stops at the
586 // end of the period. The scan state will transition twice (-> enabled ->
587 // disabled).
588 set_scan_state_handler(kNumScanStates, [this] {
589 test_device()->SetDefaultResponseStatus(
590 hci_spec::kLESetScanEnable,
591 pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
592 });
593
594 RunUntilIdle();
595 EXPECT_TRUE(scan_enabled());
596
597 // End the scan period. The scan should not restart.
598 RunFor(kTestScanPeriod);
599
600 ASSERT_EQ(kNumScanStates, scan_states().size());
601 EXPECT_TRUE(scan_states()[0]);
602 EXPECT_FALSE(scan_states()[1]);
603 EXPECT_TRUE(session_error);
604 }
605
TEST_F(LowEnergyDiscoveryManagerTest,ScanPeriodRestartRemoveSession)606 TEST_F(LowEnergyDiscoveryManagerTest, ScanPeriodRestartRemoveSession) {
607 constexpr size_t kNumScanStates = 4;
608
609 discovery_manager()->set_scan_period(kTestScanPeriod);
610
611 std::unique_ptr<LowEnergyDiscoverySession> session;
612 discovery_manager()->StartDiscovery(
613 /*active=*/true,
614 [&session](auto cb_session) { session = std::move(cb_session); });
615
616 // We should observe 3 scan state transitions (-> enabled -> disabled ->
617 // enabled).
618 set_scan_state_handler(kNumScanStates - 1, [this, &session] {
619 ASSERT_TRUE(session);
620 EXPECT_TRUE(scan_enabled());
621
622 // At this point the fake controller has updated its state but the discovery
623 // manager has not processed the restarted scan. We should be able to remove
624 // the current session and the state should ultimately become disabled.
625 session->Stop();
626 });
627
628 RunUntilIdle();
629 EXPECT_TRUE(scan_enabled());
630
631 // End the scan period.
632 RunFor(kTestScanPeriod);
633 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true, false));
634 }
635
TEST_F(LowEnergyDiscoveryManagerTest,ScanPeriodRemoveSessionDuringRestart)636 TEST_F(LowEnergyDiscoveryManagerTest, ScanPeriodRemoveSessionDuringRestart) {
637 constexpr size_t kNumScanStates = 2;
638
639 // Set a very short scan period for the sake of the test.
640 discovery_manager()->set_scan_period(kTestScanPeriod);
641
642 std::unique_ptr<LowEnergyDiscoverySession> session;
643 discovery_manager()->StartDiscovery(
644 /*active=*/true,
645 [&session](auto cb_session) { session = std::move(cb_session); });
646
647 // The controller will fail to restart scanning after scanning stops at the
648 // end of the period. The scan state will transition twice (-> enabled ->
649 // disabled).
650 set_scan_state_handler(kNumScanStates, [this, &session] {
651 ASSERT_TRUE(session);
652 EXPECT_FALSE(scan_enabled());
653
654 // Stop the session before the discovery manager processes the event. It
655 // should detect this and discontinue the scan.
656 session->Stop();
657 });
658
659 RunUntilIdle();
660 EXPECT_TRUE(scan_enabled());
661
662 // End the scan period.
663 RunFor(kTestScanPeriod);
664 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false));
665 }
666
TEST_F(LowEnergyDiscoveryManagerTest,ScanPeriodRestartRemoveAndAddSession)667 TEST_F(LowEnergyDiscoveryManagerTest, ScanPeriodRestartRemoveAndAddSession) {
668 constexpr size_t kNumScanPeriodRestartStates = 3;
669 constexpr size_t kTotalNumStates = 5;
670
671 // Set a very short scan period for the sake of the test.
672 discovery_manager()->set_scan_period(kTestScanPeriod);
673
674 std::unique_ptr<LowEnergyDiscoverySession> session;
675 auto cb = [&session](auto cb_session) { session = std::move(cb_session); };
676 discovery_manager()->StartDiscovery(/*active=*/true, cb);
677
678 // We should observe 3 scan state transitions (-> enabled -> disabled ->
679 // enabled).
680 set_scan_state_handler(kNumScanPeriodRestartStates, [this, &session, cb] {
681 ASSERT_TRUE(session);
682 EXPECT_TRUE(scan_enabled());
683
684 // At this point the fake controller has updated its state but the discovery
685 // manager has not processed the restarted scan. We should be able to remove
686 // the current session and create a new one and the state should update
687 // accordingly.
688 session->Stop();
689 discovery_manager()->StartDiscovery(/*active=*/true, cb);
690 });
691
692 RunUntilIdle();
693 EXPECT_TRUE(scan_enabled());
694
695 // End the scan period.
696 RunFor(kTestScanPeriod);
697
698 // Scan should have been disabled and re-enabled.
699 ASSERT_EQ(kTotalNumStates, scan_states().size());
700 EXPECT_TRUE(scan_states()[0]);
701 EXPECT_FALSE(scan_states()[1]);
702 EXPECT_TRUE(scan_states()[2]);
703 }
704
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWithFilters)705 TEST_F(LowEnergyDiscoveryManagerTest, StartDiscoveryWithFilters) {
706 AddFakePeers();
707
708 std::vector<std::unique_ptr<LowEnergyDiscoverySession>> sessions;
709
710 // Set a short scan period so that we that we process events for multiple scan
711 // periods during the test.
712 discovery_manager()->set_scan_period(std::chrono::milliseconds(200));
713
714 // Session 0 is interested in performing general discovery.
715 std::unordered_set<DeviceAddress> peers_session0;
716 LowEnergyDiscoverySession::PeerFoundCallback result_cb =
717 [&peers_session0](const auto& peer) {
718 peers_session0.insert(peer.address());
719 };
720 sessions.push_back(StartDiscoverySession());
721 sessions[0]->filter()->SetGeneralDiscoveryFlags();
722 sessions[0]->SetResultCallback(std::move(result_cb));
723
724 // Session 1 is interested in performing limited discovery.
725 std::unordered_set<DeviceAddress> peers_session1;
726 result_cb = [&peers_session1](const auto& peer) {
727 peers_session1.insert(peer.address());
728 };
729 sessions.push_back(StartDiscoverySession());
730 sessions[1]->filter()->set_flags(
731 static_cast<uint8_t>(AdvFlag::kLELimitedDiscoverableMode));
732 sessions[1]->SetResultCallback(std::move(result_cb));
733
734 // Session 2 is interested in peers with UUID 0x180d.
735 std::unordered_set<DeviceAddress> peers_session2;
736 result_cb = [&peers_session2](const auto& peer) {
737 peers_session2.insert(peer.address());
738 };
739 sessions.push_back(StartDiscoverySession());
740
741 uint16_t uuid = 0x180d;
742 sessions[2]->filter()->set_service_uuids({UUID(uuid)});
743 sessions[2]->SetResultCallback(std::move(result_cb));
744
745 // Session 3 is interested in peers whose names contain "Device".
746 std::unordered_set<DeviceAddress> peers_session3;
747 result_cb = [&peers_session3](const auto& peer) {
748 peers_session3.insert(peer.address());
749 };
750 sessions.push_back(StartDiscoverySession());
751 sessions[3]->filter()->set_name_substring("Device");
752 sessions[3]->SetResultCallback(std::move(result_cb));
753
754 // Session 4 is interested in non-connectable peers.
755 std::unordered_set<DeviceAddress> peers_session4;
756 result_cb = [&peers_session4](const auto& peer) {
757 peers_session4.insert(peer.address());
758 };
759 sessions.push_back(StartDiscoverySession());
760 sessions[4]->filter()->set_connectable(false);
761 sessions[4]->SetResultCallback(std::move(result_cb));
762
763 // Session 5 is interested in peers with UUID 0x180d and service data UUID
764 // 0x1234.
765 std::unordered_set<DeviceAddress> peers_session5;
766 result_cb = [&peers_session5](const auto& peer) {
767 peers_session5.insert(peer.address());
768 };
769 sessions.push_back(StartDiscoverySession());
770
771 sessions[5]->filter()->set_service_uuids({UUID(uuid)});
772 sessions[5]->filter()->set_service_data_uuids({UUID(kServiceDataUuid)});
773 sessions[5]->SetResultCallback(std::move(result_cb));
774
775 RunUntilIdle();
776
777 EXPECT_EQ(6u, sessions.size());
778
779 // At this point all sessions should have processed all peers at least once.
780
781 // Session 0: Should have seen all peers except for peer 3, which is
782 // non-discoverable.
783 EXPECT_EQ(3u, peers_session0.size());
784 EXPECT_THAT(peers_session0, ::testing::Contains(kAddress0));
785 EXPECT_THAT(peers_session0, ::testing::Contains(kAddress1));
786 EXPECT_THAT(peers_session0, ::testing::Contains(kAddress2));
787
788 // Session 1: Should have only seen peer 1.
789 EXPECT_EQ(1u, peers_session1.size());
790 EXPECT_THAT(peers_session1, ::testing::Contains(kAddress1));
791
792 // Session 2: Should have only seen peers 0 and 1
793 EXPECT_EQ(2u, peers_session2.size());
794 EXPECT_THAT(peers_session2, ::testing::Contains(kAddress0));
795 EXPECT_THAT(peers_session2, ::testing::Contains(kAddress1));
796
797 // Session 3: Should have only seen peers 0, 2, and 3
798 EXPECT_EQ(3u, peers_session3.size());
799 EXPECT_THAT(peers_session3, ::testing::Contains(kAddress0));
800 EXPECT_THAT(peers_session3, ::testing::Contains(kAddress2));
801 EXPECT_THAT(peers_session3, ::testing::Contains(kAddress3));
802
803 // Session 4: Should have seen peers 2 and 3
804 EXPECT_EQ(2u, peers_session4.size());
805 EXPECT_THAT(peers_session4, ::testing::Contains(kAddress2));
806 EXPECT_THAT(peers_session4, ::testing::Contains(kAddress3));
807
808 // Session 5: Should only see peer 0.
809 EXPECT_EQ(1u, peers_session5.size());
810 EXPECT_THAT(peers_session5, ::testing::Contains(kAddress0));
811 }
812
TEST_F(LowEnergyDiscoveryManagerTest,StartDiscoveryWithFiltersCachedPeerNotifications)813 TEST_F(LowEnergyDiscoveryManagerTest,
814 StartDiscoveryWithFiltersCachedPeerNotifications) {
815 AddFakePeers();
816
817 std::vector<std::unique_ptr<LowEnergyDiscoverySession>> sessions;
818
819 // Set a long scan period to make sure that the FakeController sends
820 // advertising reports only once.
821 discovery_manager()->set_scan_period(std::chrono::seconds(20));
822
823 // Session 0 is interested in performing general discovery.
824 std::unordered_set<DeviceAddress> peers_session0;
825 LowEnergyDiscoverySession::PeerFoundCallback result_cb =
826 [&peers_session0](const auto& peer) {
827 peers_session0.insert(peer.address());
828 };
829 sessions.push_back(StartDiscoverySession());
830 sessions[0]->filter()->SetGeneralDiscoveryFlags();
831 sessions[0]->SetResultCallback(std::move(result_cb));
832
833 RunUntilIdle();
834 ASSERT_EQ(3u, peers_session0.size());
835
836 // Session 1 is interested in performing limited discovery.
837 std::unordered_set<DeviceAddress> peers_session1;
838 result_cb = [&peers_session1](const auto& peer) {
839 peers_session1.insert(peer.address());
840 };
841 sessions.push_back(StartDiscoverySession());
842 sessions[1]->filter()->set_flags(
843 static_cast<uint8_t>(AdvFlag::kLELimitedDiscoverableMode));
844 sessions[1]->SetResultCallback(std::move(result_cb));
845
846 // Session 2 is interested in peers with UUID 0x180d.
847 std::unordered_set<DeviceAddress> peers_session2;
848 result_cb = [&peers_session2](const auto& peer) {
849 peers_session2.insert(peer.address());
850 };
851 sessions.push_back(StartDiscoverySession());
852
853 uint16_t uuid = 0x180d;
854 sessions[2]->filter()->set_service_uuids({UUID(uuid)});
855 sessions[2]->SetResultCallback(std::move(result_cb));
856
857 // Session 3 is interested in peers whose names contain "Device".
858 std::unordered_set<DeviceAddress> peers_session3;
859 result_cb = [&peers_session3](const auto& peer) {
860 peers_session3.insert(peer.address());
861 };
862 sessions.push_back(StartDiscoverySession());
863 sessions[3]->filter()->set_name_substring("Device");
864 sessions[3]->SetResultCallback(std::move(result_cb));
865
866 // Session 4 is interested in non-connectable peers.
867 std::unordered_set<DeviceAddress> peers_session4;
868 result_cb = [&peers_session4](const auto& peer) {
869 peers_session4.insert(peer.address());
870 };
871 sessions.push_back(StartDiscoverySession());
872 sessions[4]->filter()->set_connectable(false);
873 sessions[4]->SetResultCallback(std::move(result_cb));
874
875 EXPECT_EQ(5u, sessions.size());
876
877 #define EXPECT_CONTAINS(addr, dev_list) \
878 EXPECT_TRUE(dev_list.find(addr) != dev_list.end())
879 // At this point all sessions should have processed all peers at least once
880 // without running the message loop; results for Sessions 1, 2, 3, and 4
881 // should have come from the cache.
882
883 // Session 0: Should have seen all peers except for peer 3, which is
884 // non-discoverable.
885 EXPECT_EQ(3u, peers_session0.size());
886 EXPECT_CONTAINS(kAddress0, peers_session0);
887 EXPECT_CONTAINS(kAddress1, peers_session0);
888 EXPECT_CONTAINS(kAddress2, peers_session0);
889
890 // Session 1: Should have only seen peer 1.
891 EXPECT_EQ(1u, peers_session1.size());
892 EXPECT_CONTAINS(kAddress1, peers_session1);
893
894 // Session 2: Should have only seen peers 0 and 1
895 EXPECT_EQ(2u, peers_session2.size());
896 EXPECT_CONTAINS(kAddress0, peers_session2);
897 EXPECT_CONTAINS(kAddress1, peers_session2);
898
899 // Session 3: Should have only seen peers 0, 2, and 3
900 EXPECT_EQ(3u, peers_session3.size());
901 EXPECT_CONTAINS(kAddress0, peers_session3);
902 EXPECT_CONTAINS(kAddress2, peers_session3);
903 EXPECT_CONTAINS(kAddress3, peers_session3);
904
905 // Session 4: Should have seen peers 2 and 3
906 EXPECT_EQ(2u, peers_session4.size());
907 EXPECT_CONTAINS(kAddress2, peers_session4);
908 EXPECT_CONTAINS(kAddress3, peers_session4);
909
910 #undef EXPECT_CONTAINS
911 }
912
TEST_F(LowEnergyDiscoveryManagerTest,DirectedAdvertisingEventFromUnknownPeer)913 TEST_F(LowEnergyDiscoveryManagerTest, DirectedAdvertisingEventFromUnknownPeer) {
914 auto fake_peer = std::make_unique<FakePeer>(kAddress0,
915 dispatcher(),
916 /*connectable=*/true,
917 /*scannable=*/false);
918 fake_peer->set_directed_advertising_enabled(true);
919 test_device()->AddPeer(std::move(fake_peer));
920
921 int connectable_count = 0;
922 discovery_manager()->set_peer_connectable_callback(
923 [&](auto) { connectable_count++; });
924 discovery_manager()->set_scan_period(kTestScanPeriod);
925
926 auto active_session = StartDiscoverySession();
927 int active_count = 0;
928 active_session->SetResultCallback([&](auto&) { active_count++; });
929
930 auto passive_session = StartDiscoverySession(/*active=*/false);
931 int passive_count = 0;
932 passive_session->SetResultCallback([&](auto&) { passive_count++; });
933
934 RunUntilIdle();
935 ASSERT_TRUE(active_session);
936 ASSERT_TRUE(passive_session);
937 EXPECT_EQ(0, connectable_count);
938 EXPECT_EQ(0, active_count);
939 EXPECT_EQ(0, passive_count);
940 }
941
TEST_F(LowEnergyDiscoveryManagerTest,DirectedAdvertisingEventFromKnownNonConnectablePeer)942 TEST_F(LowEnergyDiscoveryManagerTest,
943 DirectedAdvertisingEventFromKnownNonConnectablePeer) {
944 auto fake_peer = std::make_unique<FakePeer>(kAddress0,
945 dispatcher(),
946 /*connectable=*/false,
947 /*scannable=*/false);
948 fake_peer->set_directed_advertising_enabled(true);
949 test_device()->AddPeer(std::move(fake_peer));
950 Peer* peer = peer_cache()->NewPeer(kAddress0, /*connectable=*/false);
951 ASSERT_TRUE(peer);
952
953 int connectable_count = 0;
954 discovery_manager()->set_peer_connectable_callback(
955 [&](auto) { connectable_count++; });
956 discovery_manager()->set_scan_period(kTestScanPeriod);
957
958 auto active_session = StartDiscoverySession();
959 int active_count = 0;
960 active_session->SetResultCallback([&](auto&) { active_count++; });
961
962 auto passive_session = StartDiscoverySession(/*active=*/false);
963 int passive_count = 0;
964 passive_session->SetResultCallback([&](auto&) { passive_count++; });
965
966 RunFor(kTestScanPeriod);
967 ASSERT_TRUE(active_session);
968 ASSERT_TRUE(passive_session);
969 EXPECT_EQ(0, connectable_count);
970 EXPECT_EQ(0, active_count);
971 EXPECT_EQ(1, passive_count);
972 }
973
TEST_F(LowEnergyDiscoveryManagerTest,DirectedAdvertisingEventFromKnownConnectablePeer)974 TEST_F(LowEnergyDiscoveryManagerTest,
975 DirectedAdvertisingEventFromKnownConnectablePeer) {
976 auto fake_peer = std::make_unique<FakePeer>(kAddress0,
977 dispatcher(),
978 /*connectable=*/true,
979 /*scannable=*/false);
980 fake_peer->set_directed_advertising_enabled(true);
981 test_device()->AddPeer(std::move(fake_peer));
982 Peer* peer = peer_cache()->NewPeer(kAddress0, /*connectable=*/true);
983 ASSERT_TRUE(peer);
984
985 int connectable_count = 0;
986 discovery_manager()->set_peer_connectable_callback([&](Peer* callback_peer) {
987 ASSERT_TRUE(callback_peer);
988 EXPECT_TRUE(callback_peer->le());
989 EXPECT_EQ(peer, callback_peer);
990 connectable_count++;
991 });
992 discovery_manager()->set_scan_period(kTestScanPeriod);
993
994 auto active_session = StartDiscoverySession();
995 int active_count = 0;
996 active_session->SetResultCallback([&](auto&) { active_count++; });
997
998 auto passive_session = StartDiscoverySession(/*active=*/false);
999 int passive_count = 0;
1000 passive_session->SetResultCallback([&](auto&) { passive_count++; });
1001
1002 RunFor(kTestScanPeriod);
1003 ASSERT_TRUE(active_session);
1004 ASSERT_TRUE(passive_session);
1005 // Connectable callback will be notified at the start of each scan period.
1006 EXPECT_EQ(2, connectable_count);
1007 EXPECT_EQ(0, active_count);
1008 EXPECT_EQ(1, passive_count);
1009 }
1010
TEST_F(LowEnergyDiscoveryManagerTest,ScanResultUpgradesKnownBrEdrPeerToDualMode)1011 TEST_F(LowEnergyDiscoveryManagerTest,
1012 ScanResultUpgradesKnownBrEdrPeerToDualMode) {
1013 Peer* peer = peer_cache()->NewPeer(kAddrAlias0, /*connectable=*/true);
1014 ASSERT_TRUE(peer);
1015 ASSERT_EQ(peer, peer_cache()->FindByAddress(kAddress0));
1016 ASSERT_EQ(TechnologyType::kClassic, peer->technology());
1017
1018 AddFakePeers();
1019
1020 discovery_manager()->set_scan_period(kTestScanPeriod);
1021
1022 std::unordered_set<DeviceAddress> addresses_found;
1023 LowEnergyDiscoverySession::PeerFoundCallback result_cb =
1024 [&addresses_found](const auto& peer) {
1025 addresses_found.insert(peer.address());
1026 };
1027 auto session = StartDiscoverySession();
1028 session->filter()->SetGeneralDiscoveryFlags();
1029 session->SetResultCallback(std::move(result_cb));
1030
1031 RunUntilIdle();
1032
1033 ASSERT_EQ(3u, addresses_found.size());
1034 EXPECT_TRUE(addresses_found.find(kAddrAlias0) != addresses_found.end());
1035 EXPECT_EQ(TechnologyType::kDualMode, peer->technology());
1036 }
1037
TEST_F(LowEnergyDiscoveryManagerTest,StartAndDisablePassiveScan)1038 TEST_F(LowEnergyDiscoveryManagerTest, StartAndDisablePassiveScan) {
1039 ASSERT_FALSE(test_device()->le_scan_state().enabled);
1040
1041 auto session = StartDiscoverySession(/*active=*/false);
1042 RunUntilIdle();
1043 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1044 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::PASSIVE,
1045 test_device()->le_scan_state().scan_type);
1046 EXPECT_FALSE(discovery_manager()->discovering());
1047
1048 session.reset();
1049 RunUntilIdle();
1050 EXPECT_FALSE(test_device()->le_scan_state().enabled);
1051 }
1052
TEST_F(LowEnergyDiscoveryManagerTest,StartAndDisablePassiveScanQuickly)1053 TEST_F(LowEnergyDiscoveryManagerTest, StartAndDisablePassiveScanQuickly) {
1054 ASSERT_FALSE(test_device()->le_scan_state().enabled);
1055
1056 // Session will be destroyed in callback, stopping scan.
1057 discovery_manager()->StartDiscovery(
1058 /*active=*/false, [&](auto cb_session) { PW_CHECK(cb_session); });
1059 RunUntilIdle();
1060
1061 EXPECT_FALSE(test_device()->le_scan_state().enabled);
1062 EXPECT_EQ(2u, scan_states().size());
1063
1064 // This should not result in a request to stop scan because both pending
1065 // requests will be processed at the same time, and second call to
1066 // StartDiscovery() retains its session.
1067 discovery_manager()->StartDiscovery(
1068 /*active=*/false, [&](auto cb_session) { PW_CHECK(cb_session); });
1069 std::unique_ptr<LowEnergyDiscoverySession> session;
1070 discovery_manager()->StartDiscovery(/*active=*/false, [&](auto cb_session) {
1071 PW_CHECK(cb_session);
1072 session = std::move(cb_session);
1073 });
1074 RunUntilIdle();
1075 EXPECT_EQ(3u, scan_states().size());
1076
1077 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1078 }
1079
TEST_F(LowEnergyDiscoveryManagerTest,EnablePassiveScanDuringActiveScanAndDisableActiveScanCausesDowngrade)1080 TEST_F(LowEnergyDiscoveryManagerTest,
1081 EnablePassiveScanDuringActiveScanAndDisableActiveScanCausesDowngrade) {
1082 auto active_session = StartDiscoverySession();
1083 ASSERT_TRUE(active_session);
1084 ASSERT_TRUE(test_device()->le_scan_state().enabled);
1085 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1086 test_device()->le_scan_state().scan_type);
1087
1088 // The scan state should transition to enabled.
1089 ASSERT_EQ(1u, scan_states().size());
1090 EXPECT_TRUE(scan_states()[0]);
1091
1092 // Enabling passive scans should not disable the active scan.
1093 auto passive_session = StartDiscoverySession(false);
1094 RunUntilIdle();
1095 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1096 test_device()->le_scan_state().scan_type);
1097 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1098 EXPECT_EQ(1u, scan_states().size());
1099
1100 // Stopping the active session should fall back to passive scan.
1101 active_session = nullptr;
1102 RunUntilIdle();
1103 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1104 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::PASSIVE,
1105 test_device()->le_scan_state().scan_type);
1106 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true));
1107 }
1108
TEST_F(LowEnergyDiscoveryManagerTest,DisablePassiveScanDuringActiveScan)1109 TEST_F(LowEnergyDiscoveryManagerTest, DisablePassiveScanDuringActiveScan) {
1110 auto active_session = StartDiscoverySession();
1111 ASSERT_TRUE(active_session);
1112 ASSERT_TRUE(test_device()->le_scan_state().enabled);
1113 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1114 test_device()->le_scan_state().scan_type);
1115
1116 // The scan state should transition to enabled.
1117 ASSERT_EQ(1u, scan_states().size());
1118 EXPECT_TRUE(scan_states()[0]);
1119
1120 // Enabling passive scans should not disable the active scan.
1121 auto passive_session = StartDiscoverySession(false);
1122 RunUntilIdle();
1123 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1124 test_device()->le_scan_state().scan_type);
1125 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1126 EXPECT_EQ(1u, scan_states().size());
1127
1128 // Disabling the passive scan should not disable the active scan.
1129 passive_session.reset();
1130 RunUntilIdle();
1131 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1132 test_device()->le_scan_state().scan_type);
1133 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1134 EXPECT_EQ(1u, scan_states().size());
1135
1136 // Stopping the active session should stop scans.
1137 active_session = nullptr;
1138 RunUntilIdle();
1139 EXPECT_FALSE(test_device()->le_scan_state().enabled);
1140 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false));
1141 }
1142
TEST_F(LowEnergyDiscoveryManagerTest,StartActiveScanDuringPassiveScan)1143 TEST_F(LowEnergyDiscoveryManagerTest, StartActiveScanDuringPassiveScan) {
1144 auto passive_session = StartDiscoverySession(false);
1145 RunUntilIdle();
1146 ASSERT_TRUE(test_device()->le_scan_state().enabled);
1147 ASSERT_EQ(pw::bluetooth::emboss::LEScanType::PASSIVE,
1148 test_device()->le_scan_state().scan_type);
1149
1150 // The scan state should transition to enabled.
1151 ASSERT_EQ(1u, scan_states().size());
1152 EXPECT_TRUE(scan_states()[0]);
1153
1154 // Starting discovery should turn off the passive scan and initiate an active
1155 // scan.
1156 auto active_session = StartDiscoverySession();
1157 EXPECT_TRUE(active_session);
1158 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1159 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1160 test_device()->le_scan_state().scan_type);
1161 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true));
1162 }
1163
TEST_F(LowEnergyDiscoveryManagerTest,StartActiveScanWhileStartingPassiveScan)1164 TEST_F(LowEnergyDiscoveryManagerTest, StartActiveScanWhileStartingPassiveScan) {
1165 std::unique_ptr<LowEnergyDiscoverySession> passive_session;
1166 discovery_manager()->StartDiscovery(/*active=*/false, [&](auto cb_session) {
1167 PW_CHECK(cb_session);
1168 passive_session = std::move(cb_session);
1169 });
1170 ASSERT_FALSE(passive_session);
1171
1172 std::unique_ptr<LowEnergyDiscoverySession> active_session;
1173 discovery_manager()->StartDiscovery(/*active=*/true, [&](auto cb_session) {
1174 PW_CHECK(cb_session);
1175 active_session = std::move(cb_session);
1176 });
1177 ASSERT_FALSE(active_session);
1178
1179 // Scan should not be enabled yet.
1180 EXPECT_FALSE(test_device()->le_scan_state().enabled);
1181 EXPECT_TRUE(scan_states().empty());
1182
1183 // Process all the requests. We should observe multiple state transitions:
1184 // -> enabled (passive) -> disabled -> enabled (active)
1185 RunUntilIdle();
1186 ASSERT_TRUE(test_device()->le_scan_state().enabled);
1187 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1188 test_device()->le_scan_state().scan_type);
1189 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true));
1190 }
1191
1192 // Emulate a number of connectable and non-connectable advertisers in both
1193 // undirected connectable and directed connectable modes. This test is to ensure
1194 // that the only peers notified during a passive scan are from connectable peers
1195 // that are already in the cache.
TEST_F(LowEnergyDiscoveryManagerTest,PeerConnectableCallbackOnlyHandlesEventsFromKnownConnectableDevices)1196 TEST_F(LowEnergyDiscoveryManagerTest,
1197 PeerConnectableCallbackOnlyHandlesEventsFromKnownConnectableDevices) {
1198 // Address 0: undirected connectable; added to cache below
1199 {
1200 auto peer = std::make_unique<FakePeer>(kAddress0,
1201 dispatcher(),
1202 /*connectable=*/true,
1203 /*scannable=*/true);
1204 test_device()->AddPeer(std::move(peer));
1205 }
1206 // Address 1: undirected connectable; NOT in cache
1207 {
1208 auto peer = std::make_unique<FakePeer>(kAddress1,
1209 dispatcher(),
1210 /*connectable=*/true,
1211 /*scannable=*/true);
1212 test_device()->AddPeer(std::move(peer));
1213 }
1214 // Address 2: not connectable; added to cache below
1215 {
1216 auto peer = std::make_unique<FakePeer>(kAddress2,
1217 dispatcher(),
1218 /*connectable=*/false,
1219 /*scannable=*/false);
1220 test_device()->AddPeer(std::move(peer));
1221 }
1222 // Address 3: not connectable but directed advertising (NOTE: although a
1223 // directed advertising PDU is inherently connectable, it is theoretically
1224 // possible for the peer_cache() to be in this state, even if unlikely in
1225 // practice).
1226 //
1227 // added to cache below
1228 {
1229 auto peer = std::make_unique<FakePeer>(kAddress3,
1230 dispatcher(),
1231 /*connectable=*/false,
1232 /*scannable=*/false);
1233 peer->set_directed_advertising_enabled(true);
1234 test_device()->AddPeer(std::move(peer));
1235 }
1236 // Address 4: directed connectable; added to cache below
1237 {
1238 auto peer = std::make_unique<FakePeer>(kAddress4,
1239 dispatcher(),
1240 /*connectable=*/true,
1241 /*scannable=*/false);
1242 peer->set_directed_advertising_enabled(true);
1243 test_device()->AddPeer(std::move(peer));
1244 }
1245 // Address 5: directed connectable; NOT in cache
1246 {
1247 auto peer = std::make_unique<FakePeer>(kAddress5,
1248 dispatcher(),
1249 /*connectable=*/true,
1250 /*scannable=*/false);
1251 peer->set_directed_advertising_enabled(true);
1252 test_device()->AddPeer(std::move(peer));
1253 }
1254
1255 // Add cache entries for addresses 0, 2, 3, and 4. The callback should only
1256 // run for addresses 0 and 4 as the only known connectable peers. All other
1257 // advertisements should be ignored.
1258 auto address0_id =
1259 peer_cache()->NewPeer(kAddress0, /*connectable=*/true)->identifier();
1260 peer_cache()->NewPeer(kAddress2, /*connectable=*/false);
1261 peer_cache()->NewPeer(kAddress3, /*connectable=*/false);
1262 auto address4_id =
1263 peer_cache()->NewPeer(kAddress4, /*connectable=*/true)->identifier();
1264 EXPECT_EQ(4u, peer_cache()->count());
1265
1266 int count = 0;
1267 discovery_manager()->set_peer_connectable_callback([&](Peer* peer) {
1268 ASSERT_TRUE(peer);
1269 auto id = peer->identifier();
1270 count++;
1271 EXPECT_TRUE(id == address0_id || id == address4_id) << id.ToString();
1272 });
1273 auto session = StartDiscoverySession(/*active=*/false);
1274 RunUntilIdle();
1275 EXPECT_EQ(2, count);
1276
1277 // No new remote peer cache entries should have been created.
1278 EXPECT_EQ(4u, peer_cache()->count());
1279 }
1280
TEST_F(LowEnergyDiscoveryManagerTest,PassiveScanPeriodRestart)1281 TEST_F(LowEnergyDiscoveryManagerTest, PassiveScanPeriodRestart) {
1282 discovery_manager()->set_scan_period(kTestScanPeriod);
1283 auto session = StartDiscoverySession(/*active=*/false);
1284
1285 // The scan state should transition to enabled.
1286 RunUntilIdle();
1287 EXPECT_TRUE(scan_enabled());
1288 ASSERT_EQ(1u, scan_states().size());
1289 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1290
1291 // End the scan period by advancing time.
1292 RunFor(kTestScanPeriod);
1293 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1294 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::PASSIVE,
1295 test_device()->le_scan_state().scan_type);
1296 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true));
1297 }
1298
TEST_F(LowEnergyDiscoveryManagerTest,PauseActiveDiscoveryTwiceKeepsScanningDisabledUntilBothPauseTokensDestroyed)1299 TEST_F(
1300 LowEnergyDiscoveryManagerTest,
1301 PauseActiveDiscoveryTwiceKeepsScanningDisabledUntilBothPauseTokensDestroyed) {
1302 auto session = StartDiscoverySession();
1303 EXPECT_TRUE(scan_enabled());
1304
1305 std::optional<PauseToken> pause_0 = discovery_manager()->PauseDiscovery();
1306 RunUntilIdle();
1307 EXPECT_FALSE(scan_enabled());
1308 EXPECT_TRUE(discovery_manager()->discovering());
1309
1310 std::optional<PauseToken> pause_1 = discovery_manager()->PauseDiscovery();
1311 RunUntilIdle();
1312 EXPECT_FALSE(scan_enabled());
1313 EXPECT_TRUE(discovery_manager()->discovering());
1314
1315 pause_0.reset();
1316 RunUntilIdle();
1317 EXPECT_FALSE(scan_enabled());
1318 EXPECT_TRUE(discovery_manager()->discovering());
1319
1320 pause_1.reset();
1321 RunUntilIdle();
1322 EXPECT_TRUE(scan_enabled());
1323 EXPECT_TRUE(discovery_manager()->discovering());
1324 }
1325
TEST_F(LowEnergyDiscoveryManagerTest,EnablePassiveScanAfterPausing)1326 TEST_F(LowEnergyDiscoveryManagerTest, EnablePassiveScanAfterPausing) {
1327 std::optional<PauseToken> pause = discovery_manager()->PauseDiscovery();
1328 RunUntilIdle();
1329 EXPECT_FALSE(scan_enabled());
1330
1331 std::unique_ptr<LowEnergyDiscoverySession> session;
1332 discovery_manager()->StartDiscovery(/*active=*/false, [&](auto cb_session) {
1333 session = std::move(cb_session);
1334 });
1335 RunUntilIdle();
1336 EXPECT_FALSE(scan_enabled());
1337 EXPECT_FALSE(session);
1338
1339 pause.reset();
1340 RunUntilIdle();
1341 EXPECT_TRUE(scan_enabled());
1342 }
1343
TEST_F(LowEnergyDiscoveryManagerTest,StartActiveScanAfterPausing)1344 TEST_F(LowEnergyDiscoveryManagerTest, StartActiveScanAfterPausing) {
1345 std::optional<PauseToken> pause = discovery_manager()->PauseDiscovery();
1346 RunUntilIdle();
1347 EXPECT_FALSE(scan_enabled());
1348
1349 std::unique_ptr<LowEnergyDiscoverySession> session;
1350 discovery_manager()->StartDiscovery(/*active=*/true, [&](auto cb_session) {
1351 session = std::move(cb_session);
1352 });
1353 RunUntilIdle();
1354 EXPECT_FALSE(scan_enabled());
1355 EXPECT_FALSE(session);
1356
1357 pause.reset();
1358 RunUntilIdle();
1359 EXPECT_TRUE(scan_enabled());
1360 EXPECT_TRUE(session);
1361 }
1362
TEST_F(LowEnergyDiscoveryManagerTest,PauseDiscoveryJustBeforeScanComplete)1363 TEST_F(LowEnergyDiscoveryManagerTest, PauseDiscoveryJustBeforeScanComplete) {
1364 discovery_manager()->set_scan_period(kTestScanPeriod);
1365
1366 auto session = StartDiscoverySession();
1367 EXPECT_TRUE(scan_enabled());
1368
1369 // Pause discovery in FakeController scan state callback to ensure it is
1370 // called just before kComplete status is received. This will be the 2nd scan
1371 // state change because it is started above and then stopped by the scan
1372 // period ending below.
1373 std::optional<PauseToken> pause;
1374 set_scan_state_handler(
1375 2, [this, &pause]() { pause = discovery_manager()->PauseDiscovery(); });
1376
1377 RunFor(kTestScanPeriod);
1378 EXPECT_TRUE(pause.has_value());
1379 EXPECT_EQ(scan_states().size(), 2u);
1380 EXPECT_FALSE(scan_enabled());
1381 }
1382
TEST_F(LowEnergyDiscoveryManagerTest,PauseDiscoveryJustBeforeScanStopped)1383 TEST_F(LowEnergyDiscoveryManagerTest, PauseDiscoveryJustBeforeScanStopped) {
1384 auto session = StartDiscoverySession();
1385 EXPECT_TRUE(scan_enabled());
1386
1387 // Pause discovery in FakeController scan state callback to ensure it is
1388 // called just before kStopped status is received. This will be the 2nd scan
1389 // state change because it is started above and then stopped by the session
1390 // being destroyed below.
1391 std::optional<PauseToken> pause;
1392 set_scan_state_handler(
1393 2, [this, &pause]() { pause = discovery_manager()->PauseDiscovery(); });
1394
1395 session.reset();
1396 RunUntilIdle();
1397 EXPECT_TRUE(pause.has_value());
1398 EXPECT_EQ(scan_states().size(), 2u);
1399 EXPECT_FALSE(scan_enabled());
1400 }
1401
TEST_F(LowEnergyDiscoveryManagerTest,PauseJustBeforeScanActive)1402 TEST_F(LowEnergyDiscoveryManagerTest, PauseJustBeforeScanActive) {
1403 // Pause discovery in FakeController scan state callback to ensure it is
1404 // called just before kActive status is received. This will be the first scan
1405 // state change.
1406 std::optional<PauseToken> pause;
1407 set_scan_state_handler(
1408 1, [this, &pause]() { pause = discovery_manager()->PauseDiscovery(); });
1409
1410 std::unique_ptr<LowEnergyDiscoverySession> session;
1411 discovery_manager()->StartDiscovery(/*active=*/true, [&](auto cb_session) {
1412 session = std::move(cb_session);
1413 });
1414
1415 // The scan should be canceled.
1416 RunUntilIdle();
1417 EXPECT_FALSE(session);
1418 EXPECT_TRUE(pause.has_value());
1419 EXPECT_EQ(scan_states().size(), 2u);
1420 EXPECT_FALSE(scan_enabled());
1421 EXPECT_FALSE(discovery_manager()->discovering());
1422
1423 // Resume discovery.
1424 pause.reset();
1425 RunUntilIdle();
1426 EXPECT_TRUE(session);
1427 EXPECT_TRUE(scan_enabled());
1428 EXPECT_TRUE(discovery_manager()->discovering());
1429 }
1430
TEST_F(LowEnergyDiscoveryManagerTest,PauseJustBeforeScanPassive)1431 TEST_F(LowEnergyDiscoveryManagerTest, PauseJustBeforeScanPassive) {
1432 // Pause discovery in FakeController scan state callback to ensure it is
1433 // called just before kPassive status is received. This will be the first scan
1434 // state change.
1435 std::optional<PauseToken> pause;
1436 set_scan_state_handler(
1437 1, [this, &pause]() { pause = discovery_manager()->PauseDiscovery(); });
1438
1439 std::unique_ptr<LowEnergyDiscoverySession> session;
1440 discovery_manager()->StartDiscovery(/*active=*/false, [&](auto cb_session) {
1441 session = std::move(cb_session);
1442 });
1443
1444 // The scan should be canceled.
1445 RunUntilIdle();
1446 EXPECT_FALSE(session);
1447 EXPECT_TRUE(pause.has_value());
1448 EXPECT_EQ(scan_states().size(), 2u);
1449 EXPECT_FALSE(scan_enabled());
1450
1451 // Resume scan.
1452 pause.reset();
1453 RunUntilIdle();
1454 EXPECT_TRUE(scan_enabled());
1455 }
1456
TEST_F(LowEnergyDiscoveryManagerTest,StartActiveScanWhilePassiveScanStoppingBetweenScanPeriods)1457 TEST_F(LowEnergyDiscoveryManagerTest,
1458 StartActiveScanWhilePassiveScanStoppingBetweenScanPeriods) {
1459 discovery_manager()->set_scan_period(kTestScanPeriod);
1460
1461 auto passive_session = StartDiscoverySession(/*active=*/false);
1462
1463 std::unique_ptr<LowEnergyDiscoverySession> active_session;
1464 set_scan_state_handler(2, [this, &active_session]() {
1465 discovery_manager()->StartDiscovery(
1466 /*active=*/true, [&active_session](auto session) {
1467 active_session = std::move(session);
1468 });
1469 });
1470 RunFor(kTestScanPeriod);
1471 EXPECT_TRUE(test_device()->le_scan_state().enabled);
1472 EXPECT_EQ(pw::bluetooth::emboss::LEScanType::ACTIVE,
1473 test_device()->le_scan_state().scan_type);
1474 EXPECT_THAT(scan_states(), ::testing::ElementsAre(true, false, true));
1475 }
1476
TEST_F(LowEnergyDiscoveryManagerTest,StopSessionInsideOfResultCallbackDoesNotCrash)1477 TEST_F(LowEnergyDiscoveryManagerTest,
1478 StopSessionInsideOfResultCallbackDoesNotCrash) {
1479 auto session = StartDiscoverySession(/*active=*/false);
1480 auto result_cb = [&session](const auto&) { session->Stop(); };
1481 session->SetResultCallback(std::move(result_cb));
1482 RunUntilIdle();
1483
1484 AddFakePeers();
1485 RunUntilIdle();
1486 }
1487
TEST_F(LowEnergyDiscoveryManagerTest,PeerChangesFromNonConnectableToConnectable)1488 TEST_F(LowEnergyDiscoveryManagerTest,
1489 PeerChangesFromNonConnectableToConnectable) {
1490 test_device()->AddPeer(std::make_unique<FakePeer>(
1491 kAddress0, dispatcher(), /*connectable=*/false));
1492
1493 std::unique_ptr<LowEnergyDiscoverySession> session;
1494 discovery_manager()->StartDiscovery(
1495 /*active=*/true,
1496 [&session](auto cb_session) { session = std::move(cb_session); });
1497
1498 RunUntilIdle();
1499 EXPECT_TRUE(scan_enabled());
1500 auto peer = peer_cache()->FindByAddress(kAddress0);
1501 ASSERT_TRUE(peer);
1502 EXPECT_FALSE(peer->connectable());
1503
1504 // Make peer connectable.
1505 test_device()->RemovePeer(kAddress0);
1506 test_device()->AddPeer(std::make_unique<FakePeer>(
1507 kAddress0, dispatcher(), /*connectable=*/true));
1508
1509 RunUntilIdle();
1510 peer = peer_cache()->FindByAddress(kAddress0);
1511 ASSERT_TRUE(peer);
1512 EXPECT_TRUE(peer->connectable());
1513
1514 // Ensure peer stays connectable after non-connectable advertisement.
1515 test_device()->RemovePeer(kAddress0);
1516 test_device()->AddPeer(std::make_unique<FakePeer>(
1517 kAddress0, dispatcher(), /*connectable=*/false));
1518
1519 RunUntilIdle();
1520 peer = peer_cache()->FindByAddress(kAddress0);
1521 ASSERT_TRUE(peer);
1522 EXPECT_TRUE(peer->connectable());
1523 }
1524
1525 #ifndef NINSPECT
TEST_F(LowEnergyDiscoveryManagerTest,Inspect)1526 TEST_F(LowEnergyDiscoveryManagerTest, Inspect) {
1527 // Ensure node exists before testing properties.
1528 ASSERT_THAT(InspectHierarchy(),
1529 AllOf(ChildrenMatch(ElementsAre(NodeMatches(
1530 AllOf(NameMatches(std::string(kInspectNodeName))))))));
1531 EXPECT_THAT(InspectProperties(),
1532 UnorderedElementsAre(StringIs("state", "Idle"),
1533 IntIs("paused", 0),
1534 UintIs("failed_count", 0u),
1535 DoubleIs("scan_interval_ms", 0.0),
1536 DoubleIs("scan_window_ms", 0.0)));
1537
1538 std::unique_ptr<LowEnergyDiscoverySession> passive_session;
1539 discovery_manager()->StartDiscovery(/*active=*/false, [&](auto cb_session) {
1540 PW_CHECK(cb_session);
1541 passive_session = std::move(cb_session);
1542 });
1543 EXPECT_THAT(InspectProperties(),
1544 ::testing::IsSupersetOf(
1545 {StringIs("state", "Starting"),
1546 DoubleIs("scan_interval_ms", ::testing::Gt(0.0)),
1547 DoubleIs("scan_window_ms", ::testing::Gt(0.0))}));
1548
1549 RunUntilIdle();
1550 EXPECT_THAT(InspectProperties(),
1551 ::testing::IsSupersetOf(
1552 {StringIs("state", "Passive"),
1553 DoubleIs("scan_interval_ms", ::testing::Gt(0.0)),
1554 DoubleIs("scan_window_ms", ::testing::Gt(0.0))}));
1555
1556 {
1557 auto pause_token = discovery_manager()->PauseDiscovery();
1558 EXPECT_THAT(InspectProperties(),
1559 ::testing::IsSupersetOf(
1560 {StringIs("state", "Stopping"), IntIs("paused", 1)}));
1561 }
1562
1563 auto active_session = StartDiscoverySession();
1564 EXPECT_THAT(InspectProperties(),
1565 ::testing::IsSupersetOf(
1566 {StringIs("state", "Active"),
1567 DoubleIs("scan_interval_ms", ::testing::Gt(0.0)),
1568 DoubleIs("scan_window_ms", ::testing::Gt(0.0))}));
1569
1570 passive_session.reset();
1571 active_session.reset();
1572 EXPECT_THAT(InspectProperties(),
1573 ::testing::IsSupersetOf({StringIs("state", "Stopping")}));
1574 RunUntilIdle();
1575 EXPECT_THAT(InspectProperties(),
1576 ::testing::IsSupersetOf({StringIs("state", "Idle")}));
1577
1578 // Cause discovery to fail.
1579 test_device()->SetDefaultResponseStatus(
1580 hci_spec::kLESetScanEnable,
1581 pw::bluetooth::emboss::StatusCode::COMMAND_DISALLOWED);
1582 discovery_manager()->StartDiscovery(
1583 /*active=*/true, [](auto session) { EXPECT_FALSE(session); });
1584 RunUntilIdle();
1585 EXPECT_THAT(InspectProperties(),
1586 ::testing::IsSupersetOf({UintIs("failed_count", 1u)}));
1587 }
1588 #endif // NINSPECT
1589
TEST_F(LowEnergyDiscoveryManagerTest,SetResultCallbackIgnoresRemovedPeers)1590 TEST_F(LowEnergyDiscoveryManagerTest, SetResultCallbackIgnoresRemovedPeers) {
1591 auto fake_peer_0 = std::make_unique<FakePeer>(kAddress0, dispatcher());
1592 test_device()->AddPeer(std::move(fake_peer_0));
1593 Peer* peer_0 = peer_cache()->NewPeer(kAddress0, /*connectable=*/true);
1594 PeerId peer_id_0 = peer_0->identifier();
1595
1596 auto fake_peer_1 = std::make_unique<FakePeer>(kAddress1, dispatcher());
1597 test_device()->AddPeer(std::move(fake_peer_1));
1598 Peer* peer_1 = peer_cache()->NewPeer(kAddress1, /*connectable=*/true);
1599 PeerId peer_id_1 = peer_1->identifier();
1600
1601 // Start active session so that results get cached.
1602 auto session = StartDiscoverySession(/*active=*/true);
1603
1604 std::unordered_map<PeerId, int> result_counts;
1605 session->SetResultCallback(
1606 [&](const Peer& peer) { result_counts[peer.identifier()]++; });
1607 RunUntilIdle();
1608 EXPECT_EQ(result_counts[peer_id_0], 1);
1609 EXPECT_EQ(result_counts[peer_id_1], 1);
1610
1611 // Remove peer_0 to make the cached result stale. The result callback should
1612 // not be called again for peer_0.
1613 ASSERT_TRUE(peer_cache()->RemoveDisconnectedPeer(peer_0->identifier()));
1614 session->SetResultCallback(
1615 [&](const Peer& peer) { result_counts[peer.identifier()]++; });
1616 RunUntilIdle();
1617 EXPECT_EQ(result_counts[peer_id_0], 1);
1618 EXPECT_EQ(result_counts[peer_id_1], 2);
1619 }
1620
1621 } // namespace
1622 } // namespace bt::gap
1623