diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index 4a067677..75e60714 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -30,6 +30,7 @@ #include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h" #include "connections/implementation/mediums/ble_v2/ble_utils.h" #include "connections/implementation/mediums/ble_v2/bloom_filter.h" +#include "connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.h" #include "connections/implementation/mediums/bluetooth_radio.h" #include "connections/implementation/mediums/utils.h" #include "connections/power_level.h" @@ -230,6 +231,96 @@ bool BleV2::IsAdvertising(const std::string& service_id) const { return IsAdvertisingLocked(service_id); } +bool BleV2::StartLegacyAdvertising( + const std::string& input_service_id, const std::string& local_endpoint_id, + const std::string& fast_advertisement_service_uuid) { + NEARBY_LOGS(INFO) << "StartLegacyAdvertising: " << input_service_id.c_str() + << ", local_endpoint_id: " << local_endpoint_id.c_str(); + MutexLock lock(&mutex_); + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) << "Can't start BLE v2 legacy advertising because " + "Bluetooth was never turned on"; + return false; + } + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) + << "Can't turn on BLE v2 legacy advertising. BLE is not available."; + return false; + } + // Checking to avoid conflicts to the bool StartAdvertsing invokes. + // TODO(hais) remove this if check after deprecating bool StartAdvertising. + if (IsAdvertisingLocked(input_service_id)) { + NEARBY_LOGS(INFO) << "Failed to BLE v2 legacy device advertise as ble is " + "already advertising."; + return false; + } + std::string service_id = input_service_id + "-Legacy"; + if (service_ids_to_advertising_sessions_.find(service_id) != + service_ids_to_advertising_sessions_.end()) { + NEARBY_LOGS(INFO) << "Already started legacy device advertising for " + << service_id; + return false; + } + + std::unique_ptr + legacy_device_advertizing_session = medium_.StartAdvertising( + CreateAdvertisingDataForLegacyDevice(), + {.tx_power_level = TxPowerLevel::kMedium, .is_connectable = true}, + api::ble_v2::BleMedium::AdvertisingCallback{ + .start_advertising_result = + [this, &service_id](absl::Status status) mutable { + AssumeHeld(mutex_); + if (status.ok()) { + NEARBY_LOGS(INFO) + << "BLE V2 advertising for legacy device started " + "successfully for service ID " + << &service_id; + } else { + NEARBY_LOGS(ERROR) << "BLE V2 advertising for legacy " + "device failed for service ID " + << &service_id << ": " << status; + service_ids_to_advertising_sessions_.erase(service_id); + } + }, + }); + if (legacy_device_advertizing_session == nullptr) { + NEARBY_LOGS(ERROR) << "Failed to turn on BLE v2 advertising for legacy " + "device for service ID " + << service_id; + return false; + } + + service_ids_to_advertising_sessions_.insert( + {std::string(service_id), std::move(legacy_device_advertizing_session)}); + return true; +} + +bool BleV2::StopLegacyAdvertising(const std::string& input_service_id) { + NEARBY_LOGS(INFO) << "StopLegacyAdvertising:" << input_service_id.c_str(); + MutexLock lock(&mutex_); + + std::string service_id = input_service_id + "-Legacy"; + auto legacy_device_advertising_session = + service_ids_to_advertising_sessions_.find(service_id); + if (legacy_device_advertising_session == + service_ids_to_advertising_sessions_.end()) { + NEARBY_LOGS(INFO) << "Can't find session to turn off legacy device BLE " + "advertisingfor this service ID: " + << service_id; + return false; + } + absl::Status status = + legacy_device_advertising_session->second->stop_advertising(); + + if (!status.ok()) { + NEARBY_LOGS(WARNING) << "StopLegacyAdvertising error: " << status; + } + service_ids_to_advertising_sessions_.erase(legacy_device_advertising_session); + NEARBY_LOGS(INFO) << "Removed advertising-session for " << service_id; + return status.ok(); +} + bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, DiscoveredPeripheralCallback callback) { MutexLock lock(&mutex_); @@ -763,6 +854,19 @@ ByteArray BleV2::CreateAdvertisementHeader( advertisement_hash, psm)); } +api::ble_v2::BleAdvertisementData +BleV2::CreateAdvertisingDataForLegacyDevice() { + BleAdvertisementData advertising_data; + advertising_data.is_extended_advertisement = false; + + ByteArray encoded_bytes{ + mediums::DiscoveredPeripheralTracker::kDummyAdvertisementValue}; + + advertising_data.service_data.insert( + {mediums::bleutils::kCopresenceServiceUuid, encoded_bytes}); + return advertising_data; +} + bool BleV2::StartAdvertisingLocked(const std::string& service_id) { const auto it = advertising_infos_.find(service_id); if (it == advertising_infos_.end()) { diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 86a494b7..1b6017f7 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -78,6 +78,18 @@ class BleV2 final { bool IsAdvertising(const std::string& service_id) const ABSL_LOCKS_EXCLUDED(mutex_); + // Use dummy bytes to do ble advertising, only for legacy devices. + // Returns true, if data is successfully set, and false otherwise. + bool StartLegacyAdvertising( + const std::string& service_id, const std::string& local_endpoint_id, + const std::string& fast_advertisement_service_uuid) + ABSL_LOCKS_EXCLUDED(mutex_); + + // (TODO:hais) update this after ble_v2 async api refactor. + // Stop Ble advertising with dummy bytes for legagy device. + bool StopLegacyAdvertising(const std::string& service_id) + ABSL_LOCKS_EXCLUDED(mutex_); + // Enables BLE scanning for a service ID. Will report any discoverable // advertisement data through a callback. // Returns true, if the scanning is successfully enabled, false otherwise. @@ -167,6 +179,10 @@ class BleV2 final { ByteArray CreateAdvertisementHeader(int psm, bool extended_advertisement_advertised) ABSL_SHARED_LOCKS_REQUIRED(mutex_); + + // For devices that don't have extended nor gatt adverting. + api::ble_v2::BleAdvertisementData CreateAdvertisingDataForLegacyDevice(); + bool StartAdvertisingLocked(const std::string& service_id) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); bool StartFastAdvertisingLocked( @@ -216,6 +232,11 @@ class BleV2 final { absl::flat_hash_map> service_ids_to_scanning_sessions_ ABSL_GUARDED_BY(mutex_); + // Save advertising sessions by service id, used by the async StartAdvertising + // method. + absl::flat_hash_map< + std::string, std::unique_ptr> + service_ids_to_advertising_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 3aee2b40..96c9f5a0 100644 --- a/connections/implementation/mediums/ble_v2_test.cc +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -47,6 +47,8 @@ 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"; +constexpr absl::string_view kLocalEndpointId = "local_endpoint_id"; +constexpr absl::string_view kFastAdvertisementServiceUuid{"\xf3\xfe"}; class BleV2Test : public testing::TestWithParam { public: @@ -578,6 +580,126 @@ TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) { env_.Stop(); } +TEST_F(BleV2Test, CanStartAndStopLegacyAdvertising) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BleV2 ble_a{radio_a}; + radio_a.Enable(); + std::string service_id(kServiceIDA); + EXPECT_TRUE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + EXPECT_FALSE(ble_a.IsAdvertising(service_id)); + EXPECT_TRUE(ble_a.StopLegacyAdvertising(service_id)); + env_.Stop(); +} + +TEST_F(BleV2Test, CanNotStartLegacyAdvertisingWhenRadioNotEnabled) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BleV2 ble_a{radio_a}; + radio_a.Disable(); + std::string service_id(kServiceIDA); + EXPECT_FALSE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + env_.Stop(); +} + +TEST_F(BleV2Test, CanNotStopLegacyAdvertisingForNonExistingServiceId) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}}); + env_.Start(); + BluetoothRadio radio_a; + BleV2 ble_a{radio_a}; + radio_a.Enable(); + std::string service_id(kServiceIDA); + EXPECT_FALSE(ble_a.StopLegacyAdvertising(service_id)); + EXPECT_TRUE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble_a.StopLegacyAdvertising(service_id)); + env_.Stop(); +} + +TEST_F(BleV2Test, StartLegacyAdvertisingBlockedByRegularAdvertising) { + env_.Start(); + BluetoothRadio radio_a; + BleV2 ble_a{radio_a}; + radio_a.Enable(); + std::string service_id(kServiceIDA); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + + ble_a.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*is_fast_advertisement=*/false); + EXPECT_TRUE(ble_a.IsAdvertising(service_id)); + EXPECT_FALSE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + ble_a.StopAdvertising(std::string(kServiceIDA)); + env_.Stop(); +} + +TEST_F(BleV2Test, DuplicateStartLegacyAdvertisingReturnsFalse) { + env_.Start(); + BluetoothRadio radio_a; + BleV2 ble_a{radio_a}; + radio_a.Enable(); + std::string service_id(kServiceIDA); + EXPECT_TRUE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + EXPECT_FALSE( + ble_a.StartLegacyAdvertising(service_id, std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble_a.StopLegacyAdvertising(service_id)); + env_.Stop(); +} + +TEST_F(BleV2Test, HandleLegacyAdvertising) { + env_.SetFeatureFlags( + {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true, + .enable_invoking_legacy_device_discovered_cb = 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 legacy_device_found_latch(1); + + ble_b.StartLegacyAdvertising(std::string(kServiceIDA), + std::string(kLocalEndpointId), + std::string(kFastAdvertisementServiceUuid)); + std::string legacy_service_id("NearbySharing"); + EXPECT_TRUE(ble_a.StartScanning( + legacy_service_id, PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [](BleV2Peripheral peripheral, const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + FAIL() << "Legacy device shouldn't be reported here."; + }, + .legacy_device_discovered_cb = + [&legacy_device_found_latch]() { + legacy_device_found_latch.CountDown(); + }, + })); + + EXPECT_TRUE(legacy_device_found_latch.Await(kWaitDuration).result()); + ble_b.StopAdvertising(std::string(kServiceIDA)); + EXPECT_TRUE(ble_a.StopScanning(legacy_service_id)); + env_.Stop(); +} + TEST_F(BleV2Test, CanStartAsyncScanning) { env_.SetFeatureFlags( {FeatureFlags{.enable_ble_v2_async_scanning_advertising = true}});