diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 15490a29..219afcb7 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -57,6 +57,7 @@ cc_library( "//internal/platform:comm", "//internal/platform:types", "//internal/platform:uuid", + "//internal/platform/implementation:comm", "//proto/mediums:web_rtc_signaling_frames_cc_proto", # TODO: Support WebRTC "@com_google_absl//absl/container:btree", @@ -64,6 +65,7 @@ cc_library( "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", + "@com_google_absl//absl/status", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index 03174d6e..4a067677 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -20,6 +20,7 @@ #include #include +#include "absl/status/status.h" #include "absl/strings/escaping.h" #include "absl/time/time.h" #include "absl/types/optional.h" @@ -33,8 +34,11 @@ #include "connections/implementation/mediums/utils.h" #include "connections/power_level.h" #include "internal/flags/nearby_flags.h" +#include "internal/platform/ble_v2.h" #include "internal/platform/byte_array.h" #include "internal/platform/cancelable_alarm.h" +#include "internal/platform/feature_flags.h" +#include "internal/platform/implementation/ble_v2.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" @@ -61,8 +65,17 @@ BleV2::BleV2(BluetoothRadio& radio) BleV2::~BleV2() { // Destructor is not taking locks, but methods it is calling are. - while (!scanned_service_ids_.empty()) { - StopScanning(*scanned_service_ids_.begin()); + if (FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising) { + // If using asynchronous scanning, check the corresponding map. + while (!service_ids_to_scanning_sessions_.empty()) { + StopScanning(service_ids_to_scanning_sessions_.begin()->first); + } + } else { + while (!scanned_service_ids_.empty()) { + StopScanning(*scanned_service_ids_.begin()); + } } while (!advertising_infos_.empty()) { StopAdvertising(advertising_infos_.begin()->first); @@ -249,6 +262,12 @@ bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, service_id, std::move(callback), mediums::bleutils::kCopresenceServiceUuid); + if (FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising) { + return StartAsyncScanningLocked(service_id, power_level); + } + // Check if scan has been activated, if yes, no need to notify client // to scan again. if (!scanned_service_ids_.empty()) { @@ -320,6 +339,11 @@ bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, bool BleV2::StopScanning(const std::string& service_id) { MutexLock lock(&mutex_); + if (FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising) { + return StopAsyncScanningLocked(service_id); + } if (!IsScanningLocked(service_id)) { NEARBY_LOGS(INFO) << "Can't turn off BLE scanning because we never " @@ -329,6 +353,7 @@ bool BleV2::StopScanning(const std::string& service_id) { discovered_peripheral_tracker_.StopTracking(service_id); NEARBY_LOGS(INFO) << "Turned off BLE scanning with service id=" << service_id; + scanned_service_ids_.erase(service_id); // If still has scanner, don't stop the client scanning. @@ -506,6 +531,13 @@ bool BleV2::IsAdvertisingLocked(const std::string& service_id) const { } bool BleV2::IsScanningLocked(const std::string& service_id) const { + if (FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising) { + // If using asynchronous scanning, check the corresponding map. + auto it = service_ids_to_scanning_sessions_.find(service_id); + return it != service_ids_to_scanning_sessions_.end(); + } return scanned_service_ids_.contains(service_id); } @@ -888,6 +920,121 @@ bool BleV2::StartGattAdvertisingLocked( return true; } +bool BleV2::StartAsyncScanningLocked(absl::string_view service_id, + PowerLevel power_level) { + CHECK(FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising); + + // Use the asynchronous StartScanning method instead of the synchronous one. + // Note: using FeatureFlags instead of NearbyFlags as there is no Mendel + // experiment associated with this change, which was driven by ChromeOS. + + // Should we check if the map is not empty, and forego scanning if true? + // If so, do we store a nullptr instead of a scanning session ptr? + + auto scanning_session = medium_.StartScanning( + mediums::bleutils::kCopresenceServiceUuid, + PowerLevelToTxPowerLevel(power_level), + api::ble_v2::BleMedium::ScanningCallback{ + .start_scanning_result = + [this, &service_id](absl::Status status) mutable { + // The `mutex_` is already held here. Use + // `AssumeHeld` to tell the thread + // annotation static analysis that + // `mutex_` is already exclusively + // locked. + // Note: unsure if this is always the case, but when I + // attempted to acquire the lock here I hit a deadlock. + AssumeHeld(mutex_); + if (status.ok()) { + NEARBY_LOGS(INFO) << "BLE V2 async StartScanning started " + "successfully for service ID" + << &service_id; + } else { + NEARBY_LOGS(ERROR) << "BLE V2 async StartScanning " + "failed for service ID" + << &service_id << ": " << status; + service_ids_to_scanning_sessions_.erase(service_id); + } + }, + .advertisement_found_cb = + [this](api::ble_v2::BlePeripheral& peripheral, + BleAdvertisementData advertisement_data) { + RunOnBleThread([this, &peripheral, advertisement_data]() { + MutexLock lock(&mutex_); + BleV2Peripheral proxy(medium_, peripheral); + discovered_peripheral_tracker_.ProcessFoundBleAdvertisement( + std::move(proxy), advertisement_data, + [this](BleV2Peripheral proxy, int num_slots, int psm, + const std::vector& + interesting_service_ids, + mediums::AdvertisementReadResult& + advertisement_read_result) { + // The `mutex_` is already held here. Use + // `AssumeHeld` to tell the thread + // annotation static analysis that + // `mutex_` is already exclusively + // locked. + AssumeHeld(mutex_); + ProcessFetchGattAdvertisementsRequest( + std::move(proxy), num_slots, psm, + interesting_service_ids, advertisement_read_result); + }); + }); + }, + }); + service_ids_to_scanning_sessions_.insert( + {std::string(service_id), std::move(scanning_session)}); + NEARBY_LOGS(INFO) << "Requested to start BLE scanning with service id=" + << service_id << " size " + << service_ids_to_scanning_sessions_.size(); + + if (lost_alarm_ != nullptr && lost_alarm_->IsValid()) { + // We only use one lost alarm, which will check all service IDs for lost + // advertisements. + return true; + } + + absl::Duration peripheral_lost_timeout = + absl::Milliseconds(NearbyFlags::GetInstance().GetInt64Flag( + config_package_nearby::nearby_connections_feature:: + kBlePeripheralLostTimeoutMillis)); + // Set up lost alarm. + lost_alarm_ = std::make_unique( + "BLE.StartScanning() onLost", + [this]() { + MutexLock lock(&mutex_); + discovered_peripheral_tracker_.ProcessLostGattAdvertisements(); + }, + peripheral_lost_timeout, &alarm_executor_, /*is_recurring=*/true); + return true; +} + +bool BleV2::StopAsyncScanningLocked(absl::string_view service_id) { + CHECK(FeatureFlags::GetInstance() + .GetFlags() + .enable_ble_v2_async_scanning_advertising); + // If using asynchronous scanning, check the corresponding map. + auto scanning_session = service_ids_to_scanning_sessions_.find(service_id); + if (scanning_session == service_ids_to_scanning_sessions_.end()) { + NEARBY_LOGS(INFO) << "Can't turn off async BLE scanning because we never " + "started scanning for this service ID."; + return false; + } + absl::Status status = scanning_session->second->stop_scanning(); + if (!status.ok()) { + NEARBY_LOGS(WARNING) << "StopAsyncScanningLocked error: " << status; + } + service_ids_to_scanning_sessions_.erase(scanning_session); + + NEARBY_LOGS(INFO) << "Turned off BLE client scanning"; + if (lost_alarm_->IsValid()) { + lost_alarm_->Cancel(); + } + return true; +} + TxPowerLevel BleV2::PowerLevelToTxPowerLevel(PowerLevel power_level) { switch (power_level) { case PowerLevel::kHighPower: diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 0040dbd0..86a494b7 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -182,6 +182,13 @@ class BleV2 final { const ByteArray& medium_advertisement_bytes, bool extended_advertisement_advertised) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Called by StartScanning when using the async methods. + bool StartAsyncScanningLocked(absl::string_view service_id, + PowerLevel power_level) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Called by StartScanning when using the async methods. + bool StopAsyncScanningLocked(absl::string_view service_id) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); api::ble_v2::TxPowerLevel PowerLevelToTxPowerLevel(PowerLevel power_level); @@ -204,6 +211,11 @@ class BleV2 final { absl::flat_hash_set hosted_gatt_characteristics_ ABSL_GUARDED_BY(mutex_); absl::flat_hash_set scanned_service_ids_ ABSL_GUARDED_BY(mutex_); + // This map has the same purpose as the set above, but is used only by + // the async StartScanning method. + absl::flat_hash_map> + service_ids_to_scanning_sessions_ ABSL_GUARDED_BY(mutex_); std::unique_ptr lost_alarm_; mediums::DiscoveredPeripheralTracker discovered_peripheral_tracker_ ABSL_GUARDED_BY(mutex_){medium_.IsExtendedAdvertisementsAvailable()}; diff --git a/connections/implementation/mediums/ble_v2_test.cc b/connections/implementation/mediums/ble_v2_test.cc index d1d1ca5e..3aee2b40 100644 --- a/connections/implementation/mediums/ble_v2_test.cc +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -46,6 +46,7 @@ constexpr absl::string_view kServiceIDA = constexpr absl::string_view kServiceIDB = "com.google.location.nearby.apps.test.b"; constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d"; +constexpr absl::string_view kAdvertisementStringB = "\x01\x02\x03\x04"; class BleV2Test : public testing::TestWithParam { public: @@ -577,6 +578,353 @@ TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) { env_.Stop(); } +TEST_F(BleV2Test, CanStartAsyncScanning) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + EXPECT_TRUE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + })); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + ble_b.StopAdvertising(std::string(kServiceIDA)); + EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA))); + env_.Stop(); +} + +TEST_F(BleV2Test, StartAsyncScanningWithPlatformErrors) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + // Disable radio a to simulate platform error. + radio_a.Disable(); + EXPECT_FALSE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + })); + + radio_a.Enable(); + EXPECT_TRUE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + })); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + ble_b.StopAdvertising(std::string(kServiceIDA)); + + EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA))); + + // Should return false the second time, as we removed service ID from the map. + EXPECT_FALSE(ble_a.StopScanning(std::string(kServiceIDA))); + env_.Stop(); +} + +TEST_F(BleV2Test, StartAsyncScanningDiscoverAndLostPeripheral) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + EXPECT_TRUE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch]( + BleV2Peripheral peripheral, const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { lost_latch.CountDown(); }, + })); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_b.StopAdvertising(std::string(kServiceIDA))); + + // Wait for a while (2 times delay) to let the alarm occur twice and + // `ProcessLostGattAdvertisements` twice to lost periperal. + SystemClock::Sleep(absl::Milliseconds(kPeripheralLostTimeoutInMillis) * 2); + + EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); + + EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA))); + env_.Stop(); +} + +TEST_F(BleV2Test, + StartAsyncScanningDiscoverButNoPeripheralLostAfterStopScanning) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + EXPECT_TRUE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch]( + BleV2Peripheral peripheral, const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { lost_latch.CountDown(); }, + })); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + + EXPECT_TRUE(ble_b.StopAdvertising(std::string(kServiceIDA))); + EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA))); + + // Don't receive lost peripheral callback because we have stopped scanning and + // cancelled the alarm. + EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); + + env_.Stop(); +} + +TEST_F(BleV2Test, CanStartStopMultipleAsyncScanningWithDifferentServiceIds) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_scanner; + BluetoothRadio radio_advertiser_a; + BluetoothRadio radio_advertiser_b; + BleV2 ble_scanner(radio_scanner); + BleV2 ble_advertiser_a(radio_advertiser_a); + BleV2 ble_advertiser_b(radio_advertiser_b); + radio_scanner.Enable(); + radio_advertiser_a.Enable(); + radio_advertiser_b.Enable(); + ByteArray advertisement_bytes_a((std::string(kAdvertisementString))); + ByteArray advertisement_bytes_b((std::string(kAdvertisementStringB))); + CountDownLatch found_latch_a(1); + CountDownLatch found_latch_b(1); + + ble_advertiser_a.StartAdvertising( + std::string(kServiceIDA), advertisement_bytes_a, PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + ble_advertiser_b.StartAdvertising( + std::string(kServiceIDB), advertisement_bytes_b, PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + ble_scanner.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch_a](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDA); + EXPECT_FALSE(fast_advertisement); + found_latch_a.CountDown(); + }, + }); + + ble_scanner.StartScanning( + std::string(kServiceIDB), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch_b](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDB); + EXPECT_FALSE(fast_advertisement); + found_latch_b.CountDown(); + }, + }); + + EXPECT_TRUE(found_latch_a.Await(kWaitDuration).result()); + EXPECT_TRUE(found_latch_b.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_scanner.StopScanning(std::string(kServiceIDA))); + EXPECT_TRUE(ble_scanner.StopScanning(std::string(kServiceIDB))); + env_.Stop(); +} + +TEST_F(BleV2Test, StartMultipleAsyncScanningDiscoverAndLostPeripheral) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_scanner; + BluetoothRadio radio_advertiser_a; + BluetoothRadio radio_advertiser_b; + BleV2 ble_scanner(radio_scanner); + BleV2 ble_advertiser_a(radio_advertiser_a); + BleV2 ble_advertiser_b(radio_advertiser_b); + radio_scanner.Enable(); + radio_advertiser_a.Enable(); + radio_advertiser_b.Enable(); + ByteArray advertisement_bytes_a((std::string(kAdvertisementString))); + ByteArray advertisement_bytes_b((std::string(kAdvertisementStringB))); + CountDownLatch found_latch_a(1); + CountDownLatch found_latch_b(1); + CountDownLatch lost_latch_a(1); + CountDownLatch lost_latch_b(1); + + ble_advertiser_a.StartAdvertising( + std::string(kServiceIDA), advertisement_bytes_a, PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + ble_advertiser_b.StartAdvertising( + std::string(kServiceIDB), advertisement_bytes_b, PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + + ble_scanner.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch_a](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDA); + EXPECT_FALSE(fast_advertisement); + found_latch_a.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch_a](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDA); + EXPECT_FALSE(fast_advertisement); + lost_latch_a.CountDown(); + }, + }); + + ble_scanner.StartScanning( + std::string(kServiceIDB), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch_b](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDB); + EXPECT_FALSE(fast_advertisement); + found_latch_b.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch_b](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_EQ(service_id, kServiceIDB); + EXPECT_FALSE(fast_advertisement); + lost_latch_b.CountDown(); + }, + }); + + EXPECT_TRUE(found_latch_a.Await(kWaitDuration).result()); + EXPECT_TRUE(found_latch_b.Await(kWaitDuration).result()); + + EXPECT_TRUE(ble_advertiser_a.StopAdvertising(std::string(kServiceIDA))); + + // Wait for a while (2 times delay) to let the alarm occur twice and + // `ProcessLostGattAdvertisements` twice to lost periperal. + SystemClock::Sleep(absl::Milliseconds(kPeripheralLostTimeoutInMillis) * 2); + + EXPECT_TRUE(lost_latch_a.Await(kWaitDuration).result()); + + EXPECT_TRUE(ble_advertiser_b.StopAdvertising(std::string(kServiceIDB))); + + // Wait for a while (2 times delay) to let the alarm occur twice and + // `ProcessLostGattAdvertisements` twice to lost periperal. + SystemClock::Sleep(absl::Milliseconds(kPeripheralLostTimeoutInMillis) * 2); + + EXPECT_TRUE(lost_latch_b.Await(kWaitDuration).result()); + + EXPECT_TRUE(ble_scanner.StopScanning(std::string(kServiceIDA))); + EXPECT_TRUE(ble_scanner.StopScanning(std::string(kServiceIDB))); + env_.Stop(); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/connections/implementation/service_controller_router.cc b/connections/implementation/service_controller_router.cc index a9883f81..fd455c55 100644 --- a/connections/implementation/service_controller_router.cc +++ b/connections/implementation/service_controller_router.cc @@ -31,6 +31,7 @@ #include "connections/v3/connections_device.h" #include "connections/v3/listening_result.h" #include "internal/flags/nearby_flags.h" +#include "internal/platform/feature_flags.h" #include "internal/platform/logging.h" // TODO(b/285657711): Add tests for uncovered logic, even if trivial. @@ -97,6 +98,10 @@ ServiceControllerRouter::ServiceControllerRouter(bool enable_ble_v2) NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature::kEnableBleV2, enable_ble_v2); + // CrOS uses the async methods for Scanning and Advertising, and has + // no support for the sync version of those methods. + const_cast(FeatureFlags::GetInstance()) + .SetFlags({.enable_ble_v2_async_scanning_advertising = true}); } } diff --git a/internal/platform/feature_flags.h b/internal/platform/feature_flags.h index 544a0d90..2b77f6a3 100644 --- a/internal/platform/feature_flags.h +++ b/internal/platform/feature_flags.h @@ -62,6 +62,9 @@ class FeatureFlags { // requested service id before attempting to connect over rfcomm. SDP fails // on Windows when connecting to FP service id but the rfcomm is successful. bool skip_service_discovery_before_connecting_to_rfcomm = false; + // Controls enable or disable the use of async methods for StartScanning, + // StopScanning, StartAdvertising, and StopAdvertising for BLE V2. + bool enable_ble_v2_async_scanning_advertising = false; std::int32_t min_nc_version_supports_safe_to_disconnect = 1; std::int32_t min_nc_version_supports_auto_reconnect = 3; diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index f1a31b14..ae6f3423 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -463,9 +463,9 @@ class BleMedium { }; // Async interface for StartScanning. - // Result status will be passed to start_advertising_result callback. - // To stop advertising, invoke the stop_advertising callback in - // AdvertisingSession. + // Result status will be passed to start_scanning_result callback. + // To stop scanning, invoke the stop_scanning callback in + // ScanningSession. virtual std::unique_ptr StartScanning( const Uuid& service_uuid, TxPowerLevel tx_power_level, ScanningCallback callback) = 0;