// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "presence/implementation/scan_manager.h" #include #include #include #include #include #include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/time/time.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" #include "presence/implementation/advertisement_factory.h" #include "presence/implementation/base_broadcast_request.h" #include "presence/implementation/credential_manager_impl.h" #include "presence/implementation/mediums/ble.h" #include "presence/implementation/mediums/mediums.h" namespace nearby { namespace presence { namespace { using AdvertisingSession = ::nearby::api::ble_v2::BleMedium::AdvertisingSession; using AdvertisingCallback = ::nearby::api::ble_v2::BleMedium::AdvertisingCallback; using ::nearby::SingleThreadExecutor; using CountDownLatch = ::nearby::CountDownLatch; // using ::testing::UnorderedElementsAre; using ::testing::Contains; class ScanManagerTest : public testing::Test { protected: void SetUp() override { env_.Start(); } void TearDown() override { executor_.Shutdown(); env_.Stop(); } std::unique_ptr StartAdvertisingOn(Ble& ble) { PresenceBroadcast::BroadcastSection section = { .identity = internal::IDENTITY_TYPE_PUBLIC, .extended_properties = MakeDefaultExtendedProperties(), .account_name = "Test account"}; PresenceBroadcast presence_request = {.sections = {section}}; BroadcastRequest input = {.tx_power = 30, .variant = presence_request}; absl::StatusOr request = BaseBroadcastRequest::Create(input); EXPECT_OK(request); absl::StatusOr advertisement = AdvertisementFactory().CreateAdvertisement(request.value()); EXPECT_OK(advertisement); std::unique_ptr session = ble.StartAdvertising( advertisement.value(), PowerMode::kLowPower, AdvertisingCallback{.start_advertising_result = [](absl::Status) {}}); env_.Sync(); return session; } ScanRequest MakeDefaultScanRequest() { std::vector> filters = {PresenceScanFilter{ .scan_type = ScanType::kPresenceScan, .extended_properties = MakeDefaultExtendedProperties(), }}; return { .account_name = "Test account", .identity_types = MakeDefaultIdentityTypes(), .scan_filters = filters, .use_ble = true, .scan_type = ScanType::kPresenceScan, .power_mode = PowerMode::kBalanced, .scan_only_when_screen_on = true, }; } ScanCallback MakeDefaultScanCallback() { return {.start_scan_cb = [this](absl::Status status) { if (status.ok()) { start_latch_.CountDown(); } }, .on_discovered_cb = [this](PresenceDevice pd) { found_latch_.CountDown(); }}; } std::vector MakeDefaultIdentityTypes() { return { nearby::internal::IdentityType::IDENTITY_TYPE_PUBLIC, }; } std::vector MakeDefaultExtendedProperties() { return {DataElement(ActionBit::kPresenceManagerAction)}; } SingleThreadExecutor executor_; CredentialManagerImpl credential_manager_{&executor_}; nearby::MediumEnvironment& env_ = {nearby::MediumEnvironment::Instance()}; CountDownLatch start_latch_{1}; CountDownLatch found_latch_{1}; }; TEST_F(ScanManagerTest, CanStartThenStopScanning) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); // Set up advertiser nearby::BluetoothAdapter server_adapter; Ble ble2(server_adapter); std::unique_ptr advertising_session = StartAdvertisingOn(ble2); // Start scanning ScanSessionId scan_session = manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback()); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); EXPECT_TRUE(start_latch_.Await().Ok()); EXPECT_TRUE(found_latch_.Await().Ok()); manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); } TEST_F(ScanManagerTest, CannotStopScanTwice) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); ScanSessionId scan_session = manager.StartScan(MakeDefaultScanRequest(), MakeDefaultScanCallback()); NEARBY_LOGS(INFO) << "Start scan"; EXPECT_TRUE(start_latch_.Await().Ok()); // Ensure that we have started scanning before we try to stop. env_.Sync(); NEARBY_LOGS(INFO) << "Stop scan"; manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); NEARBY_LOGS(INFO) << "Stop scan again"; manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); } TEST_F(ScanManagerTest, TestNoFilter) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); // Set up advertiser nearby::BluetoothAdapter server_adapter; Ble ble2(server_adapter); std::unique_ptr advertising_session = StartAdvertisingOn(ble2); // Start scanning ScanRequest scan_request_no_filter = MakeDefaultScanRequest(); scan_request_no_filter.scan_filters.clear(); ScanSessionId scan_session = manager.StartScan(scan_request_no_filter, MakeDefaultScanCallback()); ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1); ASSERT_TRUE(mediums.GetBle().IsAvailable()); EXPECT_TRUE(start_latch_.Await().Ok()); EXPECT_TRUE(found_latch_.Await().Ok()); manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); } TEST_F(ScanManagerTest, PresenceMetadataIsRetained) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); // Set up advertiser nearby::BluetoothAdapter server_adapter; Ble ble2(server_adapter); std::unique_ptr advertising_session = StartAdvertisingOn(ble2); std::string address = server_adapter.GetMacAddress(); ScanCallback callback = { .start_scan_cb = [this](absl::Status status) { if (status.ok()) { start_latch_.CountDown(); } }, .on_discovered_cb = [this, &address](PresenceDevice pd) { if (pd.GetMetadata().bluetooth_mac_address() == address) { EXPECT_THAT( pd.GetExtendedProperties(), Contains( DataElement(DataElement::kPublicIdentityFieldType, "")) .Times(1)); // MakeDefaultScanRequest() used kPresenceManagerAction for // broadcasting. Thus verify DE and action got it recorded. EXPECT_THAT( pd.GetExtendedProperties(), Contains(DataElement(ActionBit::kPresenceManagerAction)) .Times(1)); EXPECT_THAT(pd.GetActions(), Contains(PresenceAction{ (int)ActionBit::kPresenceManagerAction}) .Times(1)); found_latch_.CountDown(); } }}; // Start scanning ScanRequest scan_request_no_filter = MakeDefaultScanRequest(); scan_request_no_filter.scan_filters.clear(); auto scan_session = manager.StartScan(scan_request_no_filter, std::move(callback)); ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 1); ASSERT_TRUE(mediums.GetBle().IsAvailable()); EXPECT_TRUE(start_latch_.Await().Ok()); EXPECT_TRUE(found_latch_.Await().Ok()); manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); } TEST_F(ScanManagerTest, StopOneSessionFromAnotherDeadlock) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); CountDownLatch start_latch2{1}; CountDownLatch found_latch2{1}; // Start scanning std::vector extended_properties_mismatch = { DataElement(ActionBit::kInstantTetheringAction)}; std::vector> mismatch_filters = {PresenceScanFilter{ .scan_type = ScanType::kPresenceScan, .extended_properties = extended_properties_mismatch, }}; ScanRequest scan_request_mismatch = { .account_name = "Test account", .identity_types = MakeDefaultIdentityTypes(), .scan_filters = mismatch_filters, .use_ble = true, .scan_type = ScanType::kPresenceScan, .power_mode = PowerMode::kBalanced, .scan_only_when_screen_on = true, }; // we use scan_request_mismatch so this session's discovery doesn't get // triggered. ScanSessionId scan_session = manager.StartScan(scan_request_mismatch, MakeDefaultScanCallback()); ScanCallback scanning_callback2 = {.start_scan_cb = [&](absl::Status status) { if (status.ok()) { start_latch2.CountDown(); } }, .on_discovered_cb = [&](PresenceDevice pd) { NEARBY_LOGS(INFO) << "scansession2 found"; found_latch2.CountDown(); manager.StopScan(scan_session); }}; ScanSessionId scan_session2 = manager.StartScan( MakeDefaultScanRequest(), std::move(scanning_callback2)); ASSERT_EQ(manager.ScanningCallbacksLengthForTest(), 2); // Set up advertiser nearby::BluetoothAdapter server_adapter; Ble ble2(server_adapter); std::unique_ptr advertising_session = StartAdvertisingOn(ble2); EXPECT_TRUE(found_latch2.Await().Ok()); // Session was stopped before, this should not be able to stop successfully. manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 1); ASSERT_TRUE(mediums.GetBle().IsAvailable()); manager.StopScan(scan_session2); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); } // Receive a BLE advertisement after StopScan. `on_discovered_cb` // must not be called. TEST_F(ScanManagerTest, NoDeviceFoundAfterStopScan) { Mediums mediums; ScanManager manager(mediums, credential_manager_, executor_); CountDownLatch start_scan_latch{1}; nearby::BluetoothAdapter server_adapter; Ble ble2(server_adapter); std::atomic_bool stopped = false; ScanSessionId scan_session = manager.StartScan( MakeDefaultScanRequest(), ScanCallback{.start_scan_cb = [&start_scan_latch](absl::Status status) { if (status.ok()) { start_scan_latch.CountDown(); } }, .on_discovered_cb = [&](PresenceDevice pd) { EXPECT_FALSE(stopped); }}); start_scan_latch.Await(); manager.StopScan(scan_session); stopped = true; std::unique_ptr advertising_session = StartAdvertisingOn(ble2); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); executor_.Shutdown(); } } // namespace } // namespace presence } // namespace nearby