From acc38a809bb2b5fab9d6cb51b3c6f923fd876ad6 Mon Sep 17 00:00:00 2001 From: Crisrael Lucero Date: Mon, 17 Jun 2024 11:15:17 -0700 Subject: [PATCH] [Nearby Presence] Update ScanManager to trigger changed and lost events PiperOrigin-RevId: 644072733 --- connections/implementation/mediums/ble_v2.cc | 4 + internal/platform/ble_v2.cc | 1 + internal/platform/implementation/ble_v2.h | 2 + internal/platform/medium_environment.cc | 85 +++++++++++--------- presence/implementation/scan_manager.cc | 84 +++++++++++++++---- presence/implementation/scan_manager.h | 5 ++ presence/implementation/scan_manager_test.cc | 55 ++++++++++++- 7 files changed, 184 insertions(+), 52 deletions(-) diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index d345d59b..c2da1633 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -1090,6 +1090,10 @@ bool BleV2::StartAsyncScanningLocked(absl::string_view service_id, }); }); }, + .advertisement_lost_cb = + [](api::ble_v2::BlePeripheral& peripheral) { + // TODO(b/345514862): Implement. + }, }); service_ids_to_scanning_sessions_.insert( {std::string(service_id), std::move(scanning_session)}); diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc index 8993033f..5ce2d071 100644 --- a/internal/platform/ble_v2.cc +++ b/internal/platform/ble_v2.cc @@ -131,6 +131,7 @@ BleV2Medium::StartScanning(const Uuid& service_uuid, start_scanning_result(status); }, .advertisement_found_cb = std::move(callback.advertisement_found_cb), + .advertisement_lost_cb = std::move(callback.advertisement_lost_cb), }); } diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index ae6f3423..31d2cbbe 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -460,6 +460,8 @@ class BleMedium { absl::AnyInvocable advertisement_found_cb = [](BlePeripheral&, BleAdvertisementData) {}; + absl::AnyInvocable + advertisement_lost_cb = [](BlePeripheral&) {}; }; // Async interface for StartScanning. diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 8ed0d104..67fb5e48 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -315,11 +315,15 @@ void MediumEnvironment::OnBleV2PeripheralStateChanged( NEARBY_LOGS(INFO) << "G3 [Run] OnBleServiceStateChanged [peripheral impl=" << &peripheral << "]; context=" << &context << "; notify=" << enabled; - if (enabled) { - for (auto& element : context.scan_callback_map) { - if (element.first.first == service_id) + + for (auto& element : context.scan_callback_map) { + if (element.first.first == service_id) { + if (enabled) { element.second.advertisement_found_cb(peripheral, ble_advertisement_data); + } else { + element.second.advertisement_lost_cb(peripheral); + } } } } @@ -610,39 +614,48 @@ void MediumEnvironment::UpdateBleV2MediumForAdvertising( auto& context = it->second; context.ble_peripheral = &peripheral; context.advertising = enabled; - if (enabled) { - context.advertisement_data = advertisement_data; - NEARBY_LOGS(INFO) - << "G3 UpdateBleV2MediumForAdvertising: this=" << this - << ", medium=" << &medium << ", medium_context=" << &context - << ", peripheral=" << &peripheral << ", enabled=" << enabled; - for (auto& medium_info : ble_v2_mediums_) { - const api::ble_v2::BleMedium* remote_medium = medium_info.first; - BleV2MediumContext& remote_context = medium_info.second; - // Do not send notification to the same medium. - if (remote_medium == &medium) continue; - // Do not send notification to the medium that is not scanning. - if (!remote_context.scanning) continue; - absl::flat_hash_set remote_scanning_service_uuids; - for (auto& element : remote_context.scan_callback_map) { - remote_scanning_service_uuids.insert(element.first.first); - } - for (auto& remote_scanning_service_uuid : - remote_scanning_service_uuids) { - auto const it = context.advertisement_data.service_data.find( - remote_scanning_service_uuid); - if (it == context.advertisement_data.service_data.end()) continue; - NEARBY_LOGS(INFO) - << "G3 UpdateBleV2MediumForAdvertising, found other medium=" - << remote_medium - << ", remote_medium_context=" << &remote_context - << ", remote_context.peripheral=" - << remote_context.ble_peripheral - << ". Ready to call OnBleV2PeripheralStateChanged."; - OnBleV2PeripheralStateChanged( - enabled, remote_context, remote_scanning_service_uuid, - context.advertisement_data, *context.ble_peripheral); - } + context.advertisement_data = advertisement_data; + + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForAdvertising: this=" << this + << ", medium=" << &medium << ", medium_context=" << &context + << ", peripheral=" << &peripheral << ", enabled=" << enabled; + + for (auto& medium_info : ble_v2_mediums_) { + const api::ble_v2::BleMedium* remote_medium = medium_info.first; + BleV2MediumContext& remote_context = medium_info.second; + + // Do not send notification to the same medium. + if (remote_medium == &medium) continue; + // Do not send notification to the medium that is not scanning. + if (!remote_context.scanning) continue; + + absl::flat_hash_set remote_scanning_service_uuids; + for (auto& element : remote_context.scan_callback_map) { + remote_scanning_service_uuids.insert(element.first.first); + } + + for (auto& remote_scanning_service_uuid : + remote_scanning_service_uuids) { + auto const it = context.advertisement_data.service_data.find( + remote_scanning_service_uuid); + + // Only skip when service data is not found and the medium is + // enabled. Mediums that stop advertising (disabled) pass in empty + // advertisement data but should still be processed. + if (it == context.advertisement_data.service_data.end() + && enabled) continue; + + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForAdvertising, found other medium=" + << remote_medium + << ", remote_medium_context=" << &remote_context + << ", remote_context.peripheral=" + << remote_context.ble_peripheral + << ". Ready to call OnBleV2PeripheralStateChanged."; + OnBleV2PeripheralStateChanged( + enabled, remote_context, remote_scanning_service_uuid, + context.advertisement_data, *context.ble_peripheral); } } }); diff --git a/presence/implementation/scan_manager.cc b/presence/implementation/scan_manager.cc index 112906ab..29f2dcc4 100644 --- a/presence/implementation/scan_manager.cc +++ b/presence/implementation/scan_manager.cc @@ -74,6 +74,15 @@ ScanSessionId ScanManager::StartScan(ScanRequest scan_request, ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { NotifyFoundBle(id, data, address); }); + }, + .advertisement_lost_cb = + [this, id](BlePeripheral& peripheral) { + RunOnServiceControllerThread( + "notify-lost-ble", + [this, id, address = peripheral.GetAddress()]() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + NotifyLostBle(id, address); + }); }}; FetchCredentials(id, scan_request); scan_sessions_.insert( @@ -107,12 +116,14 @@ void ScanManager::StopScan(ScanSessionId id) { void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, absl::string_view remote_address) { - auto advertisement_data = - data.service_data[kPresenceServiceUuid].AsStringView(); auto it = scan_sessions_.find(id); if (it == scan_sessions_.end()) { return; } + + auto advertisement_data = + data.service_data[kPresenceServiceUuid].AsStringView(); + auto advert = it->second.decoder.DecodeAdvertisement(advertisement_data); if (!advert.ok()) { // This advertisement is not relevant to the current element, skip. @@ -123,20 +134,65 @@ void ScanManager::NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, internal::DeviceIdentityMetaData device_identity_metadata; device_identity_metadata.set_bluetooth_mac_address( std::string(remote_address)); - PresenceDevice device(DeviceMotion(), device_identity_metadata, - advert->identity_type); - // Ok if the advertisement is for trusted/private identity. - if (advert->public_credential.ok()) { - device.SetDecryptSharedCredential(*(advert->public_credential)); - } - device.AddExtendedProperties(advert->data_elements); - for (const auto& data_element : advert->data_elements) { - if (data_element.GetType() == DataElement::kActionFieldType) { - device.AddAction(PresenceAction(static_cast( - static_cast(data_element.GetValue()[0])))); + + if (!device_address_to_endpoint_id_map_.contains(remote_address)) { + PresenceDevice device(DeviceMotion(), device_identity_metadata, + advert->identity_type); + // Ok if the advertisement is for trusted/private identity. + if (advert->public_credential.ok()) { + device.SetDecryptSharedCredential(*(advert->public_credential)); } + device.AddExtendedProperties(advert->data_elements); + for (const auto& data_element : advert->data_elements) { + if (data_element.GetType() == DataElement::kActionFieldType) { + device.AddAction(PresenceAction(static_cast( + static_cast(data_element.GetValue()[0])))); + } + } + + device_address_to_endpoint_id_map_.emplace(remote_address, + device.GetEndpointId()); + + it->second.callback.on_discovered_cb(std::move(device)); + } else { + PresenceDevice device( + device_address_to_endpoint_id_map_.at(remote_address)); + device.SetDeviceIdentityMetaData(device_identity_metadata); + // Ok if the advertisement is for trusted/private identity. + if (advert->public_credential.ok()) { + device.SetDecryptSharedCredential(*(advert->public_credential)); + } + device.AddExtendedProperties(advert->data_elements); + for (const auto& data_element : advert->data_elements) { + if (data_element.GetType() == DataElement::kActionFieldType) { + device.AddAction(PresenceAction(static_cast( + static_cast(data_element.GetValue()[0])))); + } + } + + it->second.callback.on_updated_cb(std::move(device)); } - it->second.callback.on_discovered_cb(std::move(device)); + } +} + +void ScanManager::NotifyLostBle(ScanSessionId id, + absl::string_view remote_address) { + auto it = scan_sessions_.find(id); + if (it == scan_sessions_.end()) { + return; + } + + if (device_address_to_endpoint_id_map_.contains(remote_address)) { + internal::DeviceIdentityMetaData device_identity_metadata; + device_identity_metadata.set_bluetooth_mac_address( + std::string(remote_address)); + PresenceDevice device( + device_address_to_endpoint_id_map_.at(remote_address)); + device.SetDeviceIdentityMetaData(device_identity_metadata); + + device_address_to_endpoint_id_map_.erase(remote_address); + + it->second.callback.on_lost_cb(std::move(device)); } } diff --git a/presence/implementation/scan_manager.h b/presence/implementation/scan_manager.h index 6dd1eabf..c0127afb 100644 --- a/presence/implementation/scan_manager.h +++ b/presence/implementation/scan_manager.h @@ -84,6 +84,8 @@ class ScanManager { void NotifyFoundBle(ScanSessionId id, BleAdvertisementData data, absl::string_view remote_address) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void NotifyLostBle(ScanSessionId id, absl::string_view remote_address) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); void FetchCredentials(ScanSessionId id, const ScanRequest& scan_request) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); void UpdateCredentials(ScanSessionId id, IdentityType identity_type, @@ -96,6 +98,9 @@ class ScanManager { CredentialManager* credential_manager_; absl::flat_hash_map scan_sessions_ ABSL_GUARDED_BY(*executor_); + absl::flat_hash_map + device_address_to_endpoint_id_map_ + ABSL_GUARDED_BY(*executor_); SingleThreadExecutor* executor_; }; diff --git a/presence/implementation/scan_manager_test.cc b/presence/implementation/scan_manager_test.cc index c06bf3ab..f58a7c8c 100644 --- a/presence/implementation/scan_manager_test.cc +++ b/presence/implementation/scan_manager_test.cc @@ -56,7 +56,6 @@ using AdvertisingCallback = using ::nearby::SingleThreadExecutor; using CountDownLatch = ::nearby::CountDownLatch; -// using ::testing::UnorderedElementsAre; using ::testing::Contains; class ScanManagerTest : public testing::Test { @@ -104,7 +103,11 @@ class ScanManagerTest : public testing::Test { } }, .on_discovered_cb = - [this](PresenceDevice pd) { found_latch_.CountDown(); }}; + [this](PresenceDevice pd) { found_latch_.CountDown(); }, + .on_updated_cb = + [this](PresenceDevice pd) { updated_latch_.CountDown(); }, + .on_lost_cb = + [this](PresenceDevice pd) { lost_latch_.CountDown(); }}; } std::vector MakeDefaultIdentityTypes() { @@ -120,6 +123,8 @@ class ScanManagerTest : public testing::Test { nearby::MediumEnvironment& env_ = {nearby::MediumEnvironment::Instance()}; CountDownLatch start_latch_{1}; CountDownLatch found_latch_{1}; + CountDownLatch updated_latch_{1}; + CountDownLatch lost_latch_{1}; }; TEST_F(ScanManagerTest, CanStartThenStopScanning) { @@ -213,6 +218,21 @@ TEST_F(ScanManagerTest, PresenceMetadataIsRetained) { found_latch_.CountDown(); } + }, + .on_updated_cb = + [this, &address](PresenceDevice pd) { + if (pd.GetDeviceIdentityMetadata().bluetooth_mac_address() == + address) { + EXPECT_THAT(pd.GetExtendedProperties(), + Contains(DataElement(ActionBit::kNearbyShareAction)) + .Times(1)); + EXPECT_THAT( + pd.GetActions(), + Contains(PresenceAction{(int)ActionBit::kNearbyShareAction}) + .Times(1)); + + updated_latch_.CountDown(); + } }}; // Start scanning ScanRequest scan_request_no_filter = MakeDefaultScanRequest(); @@ -224,6 +244,37 @@ TEST_F(ScanManagerTest, PresenceMetadataIsRetained) { ASSERT_TRUE(mediums.GetBle().IsAvailable()); EXPECT_TRUE(start_latch_.Await().Ok()); EXPECT_TRUE(found_latch_.Await().Ok()); + + // Advertise again to trigger `on_updated_cb` + advertising_session = StartAdvertisingOn(ble2); + + EXPECT_TRUE(updated_latch_.Await().Ok()); + manager.StopScan(scan_session); + EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); +} + +TEST_F(ScanManagerTest, DiscoverThenLoseAdvertisement) { + 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()); + + // Stop advertising to trigger `on_lost_cb` + EXPECT_OK(advertising_session->stop_advertising()); + env_.Sync(); + + EXPECT_TRUE(lost_latch_.Await().Ok()); manager.StopScan(scan_session); EXPECT_EQ(manager.ScanningCallbacksLengthForTest(), 0); }