From 0ab31f63128b099fbf8a5428cdcab2fc796537cf Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Thu, 8 Jun 2023 10:18:21 -0700 Subject: [PATCH] Implement device loss alarms PiperOrigin-RevId: 538822094 --- .../implementation/base_pcp_handler.cc | 51 ++++ connections/implementation/base_pcp_handler.h | 21 ++ .../implementation/base_pcp_handler_test.cc | 227 ++++++++++++++++++ connections/implementation/client_proxy.cc | 1 + connections/implementation/client_proxy.h | 7 + .../implementation/client_proxy_test.cc | 5 + .../implementation/p2p_cluster_pcp_handler.cc | 47 ++-- 7 files changed, 345 insertions(+), 14 deletions(-) diff --git a/connections/implementation/base_pcp_handler.cc b/connections/implementation/base_pcp_handler.cc index b7c63229..11690857 100644 --- a/connections/implementation/base_pcp_handler.cc +++ b/connections/implementation/base_pcp_handler.cc @@ -24,6 +24,7 @@ #include "securegcm/ukey2_handshake.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/escaping.h" +#include "absl/time/time.h" #include "absl/types/span.h" #include "connections/advertising_options.h" #include "connections/connection_options.h" @@ -35,11 +36,16 @@ #include "internal/flags/nearby_flags.h" #include "internal/platform/base64_utils.h" #include "internal/platform/bluetooth_utils.h" +#include "internal/platform/cancelable_alarm.h" #include "internal/platform/logging.h" namespace nearby { namespace connections { +namespace { +constexpr int kEndpointCancelAlarmTimeout = 10; +} // namespace + using ::location::nearby::connections::ConnectionRequestFrame; using ::location::nearby::connections::ConnectionResponseFrame; using ::location::nearby::connections::MediumMetadata; @@ -124,6 +130,7 @@ Status BasePcpHandler::StartAdvertising( client->StartedAdvertising(service_id, GetStrategy(), info.listener, absl::MakeSpan(result.mediums), compatible_advertising_options); + client->UpdateLocalEndpointInfo(info.endpoint_info.string_data()); response.Set({Status::kSuccess}); }); return WaitForResult(absl::StrCat("StartAdvertising(", service_id, ")"), @@ -781,6 +788,48 @@ BasePcpHandler::GetDiscoveredEndpoints( return result; } +namespace { +std::string GetEndpointLostByMediumAlarmKey(absl::string_view endpoint_id, + Medium medium) { + return absl::StrCat(location::nearby::proto::connections::Medium_Name(medium), + "_", endpoint_id); +} +} // namespace + +void BasePcpHandler::StartEndpointLostByMediumAlarms( + ClientProxy* client, location::nearby::proto::connections::Medium medium) { + auto discovered_endpoints_medium = GetDiscoveredEndpoints(medium); + for (const auto discovered_endpoint : discovered_endpoints_medium) { + std::string key = GetEndpointLostByMediumAlarmKey( + discovered_endpoint->endpoint_id, medium); + StopEndpointLostByMediumAlarm(discovered_endpoint->endpoint_id, medium); + endpoint_lost_by_medium_alarms_.emplace( + key, std::make_unique( + absl::StrCat("EndpointLostByMediumAlarm_", key), + [this, discovered_endpoint, key, client]() { + RunOnPcpHandlerThread( + "endpoint-lost-by-medium-alarm", + [this, client, discovered_endpoint, + key]() RUN_ON_PCP_HANDLER_THREAD() { + if (endpoint_lost_by_medium_alarms_.erase(key) != 0) { + OnEndpointLost(client, *discovered_endpoint); + } + }); + }, + absl::Seconds(kEndpointCancelAlarmTimeout), &alarm_executor_)); + } +} + +void BasePcpHandler::StopEndpointLostByMediumAlarm( + absl::string_view endpoint_id, + location::nearby::proto::connections::Medium medium) { + std::string key = GetEndpointLostByMediumAlarmKey(endpoint_id, medium); + if (endpoint_lost_by_medium_alarms_.contains(key)) { + endpoint_lost_by_medium_alarms_[key]->Cancel(); + endpoint_lost_by_medium_alarms_.erase(key); + } +} + mediums::WebrtcPeerId BasePcpHandler::CreatePeerIdFromAdvertisement( const std::string& service_id, const std::string& endpoint_id, const ByteArray& endpoint_info) { @@ -1093,6 +1142,8 @@ void BasePcpHandler::OnEndpointFound( owned_endpoint = discovered_endpoints_.emplace(endpoint_id, std::move(endpoint)) ->second.get(); + StopEndpointLostByMediumAlarm(owned_endpoint->endpoint_id, + owned_endpoint->medium); client->OnEndpointFound( owned_endpoint->service_id, owned_endpoint->endpoint_id, owned_endpoint->endpoint_info, owned_endpoint->medium); diff --git a/connections/implementation/base_pcp_handler.h b/connections/implementation/base_pcp_handler.h index 3be0f25f..4d8f1ec1 100644 --- a/connections/implementation/base_pcp_handler.h +++ b/connections/implementation/base_pcp_handler.h @@ -34,6 +34,7 @@ #include "connections/implementation/pcp.h" #include "connections/implementation/pcp_handler.h" #include "connections/listeners.h" +#include "connections/medium_selector.h" #include "connections/status.h" #include "internal/platform/atomic_boolean.h" #include "internal/platform/byte_array.h" @@ -299,6 +300,15 @@ class BasePcpHandler : public PcpHandler, std::vector GetDiscoveredEndpoints( const location::nearby::proto::connections::Medium medium); + // Start alarms for endpoints lost by their mediums. Used when updating + // discovery options. + void StartEndpointLostByMediumAlarms( + ClientProxy* client, location::nearby::proto::connections::Medium medium); + + void StopEndpointLostByMediumAlarm( + absl::string_view endpoint_id, + location::nearby::proto::connections::Medium medium); + mediums::WebrtcPeerId CreatePeerIdFromAdvertisement( const string& service_id, const string& endpoint_id, const ByteArray& endpoint_info); @@ -308,6 +318,12 @@ class BasePcpHandler : public PcpHandler, return &serial_executor_; } + // Test only. + absl::flat_hash_map>& + GetEndpointLostByMediumAlarms() { + return endpoint_lost_by_medium_alarms_; + } + Mediums* mediums_; EndpointManager* endpoint_manager_; EndpointChannelManager* channel_manager_; @@ -529,6 +545,11 @@ class BasePcpHandler : public PcpHandler, // advertising. ConnectionListener advertising_listener_; + // Mapping from endpoint_id -> CancelableAlarm for triggering endpoint loss + // while discovery options are updated. + absl::flat_hash_map> + endpoint_lost_by_medium_alarms_; + Pcp pcp_; Strategy strategy_{PcpToStrategy(pcp_)}; EncryptionRunner encryption_runner_; diff --git a/connections/implementation/base_pcp_handler_test.cc b/connections/implementation/base_pcp_handler_test.cc index 9a7311d4..e8f1adda 100644 --- a/connections/implementation/base_pcp_handler_test.cc +++ b/connections/implementation/base_pcp_handler_test.cc @@ -189,6 +189,26 @@ class MockPcpHandler : public BasePcpHandler { return BasePcpHandler::GetDiscoveredEndpoints(endpoint_id); } + std::vector GetDiscoveredEndpoints( + location::nearby::proto::connections::Medium medium) { + return BasePcpHandler::GetDiscoveredEndpoints(medium); + } + + absl::flat_hash_map>& + GetEndpointLostByMediumAlarms() { + return BasePcpHandler::GetEndpointLostByMediumAlarms(); + } + + void StartEndpointLostByMediumAlarms( + ClientProxy* client, location::nearby::proto::connections::Medium medium) { + BasePcpHandler::StartEndpointLostByMediumAlarms(client, medium); + } + + void StopEndpointLostByMediumAlarm(absl::string_view endpoint_id, + location::nearby::proto::connections::Medium medium) { + BasePcpHandler::StopEndpointLostByMediumAlarm(endpoint_id, medium); + } + std::vector GetDiscoveryMediums( ClientProxy* client) { auto allowed = client->GetDiscoveryOptions().CompatibleOptions().allowed; @@ -281,6 +301,7 @@ class BasePcpHandlerTest advertising_options, info), Status{Status::kSuccess}); EXPECT_TRUE(client->IsAdvertising()); + EXPECT_EQ(client->GetLocalEndpointInfo(), info.endpoint_info.string_data()); } void StartDiscovery(ClientProxy* client, MockPcpHandler* pcp_handler, @@ -819,6 +840,212 @@ TEST_F(BasePcpHandlerTest, InjectEndpoint) { env_.Stop(); } +TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) { + env_.Start(); + std::string service_id{"service"}; + std::string endpoint_id{"ABCD"}; + ClientProxy client; + Mediums m; + EndpointChannelManager ecm; + EndpointManager em(&ecm); + BwuManager bwu(m, em, ecm, {}, {}); + MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu); + BooleanMediumSelector allowed{ + .bluetooth = true, + }; + DiscoveryOptions discovery_options{ + { + Strategy::kP2pPointToPoint, + allowed, + }, + false, // auto_upgrade_bandwidth; + false, // enforce_topology_constraints; + }; + EXPECT_CALL(pcp_handler, StartDiscoveryImpl) + .WillOnce(Return(MockPcpHandler::StartOperationResult{ + .status = {Status::kSuccess}, + .mediums = allowed.GetMediums(true), + })); + EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, + {}), + Status{Status::kSuccess}); + EXPECT_TRUE(client.IsDiscovering()); + + EXPECT_CALL(pcp_handler, InjectEndpointImpl) + .WillOnce(Invoke([&pcp_handler, &endpoint_id]( + ClientProxy* client, const std::string& service_id, + const OutOfBandConnectionMetadata& metadata) { + pcp_handler.OnEndpointFound( + client, + std::make_shared(MockDiscoveredEndpoint{ + { + endpoint_id, + /*endpoint_info=*/ByteArray{"ABCD"}, + service_id, + Medium::BLUETOOTH, + WebRtcState::kUndefined, + }, + MockContext{nullptr}, + })); + return Status{Status::kSuccess}; + })); + pcp_handler.InjectEndpoint( + &client, service_id, + OutOfBandConnectionMetadata{ + .medium = Medium::BLUETOOTH, + .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), + }); + EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + pcp_handler.StopEndpointLostByMediumAlarm(endpoint_id, Medium::BLUETOOTH); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + env_.Stop(); +} + +TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) { + env_.Start(); + std::string service_id{"service"}; + std::string endpoint_id{"ABCD"}; + ClientProxy client; + Mediums m; + EndpointChannelManager ecm; + EndpointManager em(&ecm); + BwuManager bwu(m, em, ecm, {}, {}); + MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu); + BooleanMediumSelector allowed{ + .bluetooth = true, + }; + DiscoveryOptions discovery_options{ + { + Strategy::kP2pPointToPoint, + allowed, + }, + false, // auto_upgrade_bandwidth; + false, // enforce_topology_constraints; + }; + EXPECT_CALL(pcp_handler, StartDiscoveryImpl) + .WillOnce(Return(MockPcpHandler::StartOperationResult{ + .status = {Status::kSuccess}, + .mediums = allowed.GetMediums(true), + })); + EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, + {}), + Status{Status::kSuccess}); + EXPECT_TRUE(client.IsDiscovering()); + + EXPECT_CALL(pcp_handler, InjectEndpointImpl) + .WillOnce(Invoke([&pcp_handler, &endpoint_id]( + ClientProxy* client, const std::string& service_id, + const OutOfBandConnectionMetadata& metadata) { + pcp_handler.OnEndpointFound( + client, + std::make_shared(MockDiscoveredEndpoint{ + { + endpoint_id, + /*endpoint_info=*/ByteArray{"ABCD"}, + service_id, + Medium::BLUETOOTH, + WebRtcState::kUndefined, + }, + MockContext{nullptr}, + })); + return Status{Status::kSuccess}; + })); + pcp_handler.InjectEndpoint( + &client, service_id, + OutOfBandConnectionMetadata{ + .medium = Medium::BLUETOOTH, + .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), + }); + EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + absl::SleepFor(absl::Seconds(11)); + EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + env_.Stop(); +} + +TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) { + env_.Start(); + std::string service_id{"service"}; + std::string endpoint_id{"ABCD"}; + ClientProxy client; + Mediums m; + EndpointChannelManager ecm; + EndpointManager em(&ecm); + BwuManager bwu(m, em, ecm, {}, {}); + MockPcpHandler pcp_handler(&m, &em, &ecm, &bwu); + BooleanMediumSelector allowed{ + .bluetooth = true, + }; + DiscoveryOptions discovery_options{ + { + Strategy::kP2pPointToPoint, + allowed, + }, + false, // auto_upgrade_bandwidth; + false, // enforce_topology_constraints; + }; + EXPECT_CALL(pcp_handler, StartDiscoveryImpl) + .WillOnce(Return(MockPcpHandler::StartOperationResult{ + .status = {Status::kSuccess}, + .mediums = allowed.GetMediums(true), + })); + EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, + {}), + Status{Status::kSuccess}); + EXPECT_TRUE(client.IsDiscovering()); + + bool first_call = true; + EXPECT_CALL(pcp_handler, InjectEndpointImpl).Times(2) + .WillRepeatedly(Invoke([&pcp_handler, &endpoint_id, &first_call]( + ClientProxy* client, const std::string& service_id, + const OutOfBandConnectionMetadata& metadata) { + ByteArray endpoint_info; + if (first_call) { + endpoint_info = ByteArray("ABCD"); + } else { + endpoint_info = ByteArray("ABCDE"); + } + first_call = false; + pcp_handler.OnEndpointFound( + client, + std::make_shared(MockDiscoveredEndpoint{ + { + endpoint_id, + endpoint_info, + service_id, + Medium::BLUETOOTH, + WebRtcState::kUndefined, + }, + MockContext{nullptr}, + })); + return Status{Status::kSuccess}; + })); + pcp_handler.InjectEndpoint( + &client, service_id, + OutOfBandConnectionMetadata{ + .medium = Medium::BLUETOOTH, + .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), + }); + EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + pcp_handler.InjectEndpoint( + &client, service_id, + OutOfBandConnectionMetadata{ + .medium = Medium::BLUETOOTH, + .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), + }); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + env_.Stop(); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index 902d202f..7e3dfa1e 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -163,6 +163,7 @@ void ClientProxy::StoppedAdvertising() { if (IsAdvertising()) { advertising_info_.Clear(); analytics_recorder_->OnStopAdvertising(); + local_endpoint_info_.clear(); } // advertising_options_ is purposefully not cleared here. OnSessionComplete(); diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index 4e1ade14..654f01d6 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -64,6 +64,7 @@ class ClientProxy final { std::int64_t GetClientId() const; std::string GetLocalEndpointId(); + std::string GetLocalEndpointInfo() { return local_endpoint_info_; } analytics::AnalyticsRecorder& GetAnalyticsRecorder() const { return *analytics_recorder_; @@ -102,6 +103,11 @@ class ClientProxy final { bool IsDiscovering() const; std::string GetDiscoveryServiceId() const; + void UpdateLocalEndpointInfo(absl::string_view endpoint_info) { + MutexLock lock(&mutex_); + local_endpoint_info_ = std::string(endpoint_info); + } + // Proxies to the client's DiscoveryListener::OnEndpointFound() callback. void OnEndpointFound(const std::string& service_id, const std::string& endpoint_id, @@ -295,6 +301,7 @@ class ClientProxy final { mutable RecursiveMutex mutex_; std::int64_t client_id_; std::string local_endpoint_id_; + std::string local_endpoint_info_; // If currently is advertising in high visibility mode is true: high power and // Bluetooth Classic enabled. When high_visibility_mode_ is true, the endpoint // id is stable for 30s. When high_visibility_mode_ is false, the endpoint id diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index b7b209cd..ed728f0c 100644 --- a/connections/implementation/client_proxy_test.cc +++ b/connections/implementation/client_proxy_test.cc @@ -981,6 +981,11 @@ TEST_F(ClientProxyTest, GetLocalDeviceWorksWithDeviceProvider) { client1_.GetLocalDevice(); } +TEST_F(ClientProxyTest, TestGetSetLocalEndpointInfo) { + client1_.UpdateLocalEndpointInfo("endpoint_info"); + EXPECT_EQ(client1_.GetLocalEndpointInfo(), "endpoint_info"); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/connections/implementation/p2p_cluster_pcp_handler.cc b/connections/implementation/p2p_cluster_pcp_handler.cc index 26c18129..18d81cc5 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.cc +++ b/connections/implementation/p2p_cluster_pcp_handler.cc @@ -14,6 +14,7 @@ #include "connections/implementation/p2p_cluster_pcp_handler.h" +#include #include #include #include @@ -29,6 +30,7 @@ #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/mediums/utils.h" #include "connections/implementation/wifi_lan_endpoint_channel.h" +#include "connections/power_level.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/nsd_service_info.h" #include "internal/platform/types.h" @@ -485,6 +487,10 @@ void P2pClusterPcpHandler::BlePeripheralDiscoveredHandler( BleEndpointState(advertisement.GetEndpointId(), advertisement.GetEndpointInfo())); + StopEndpointLostByMediumAlarm( + advertisement.GetEndpointId(), + location::nearby::proto::connections::Medium::BLE); + // Report the discovered endpoint to the client. NEARBY_LOGS(INFO) << "Found BleAdvertisement " << absl::BytesToHexString(advertisement_bytes.data()) @@ -493,15 +499,15 @@ void P2pClusterPcpHandler::BlePeripheralDiscoveredHandler( << ", and endpoint_info=" << absl::BytesToHexString( advertisement.GetEndpointInfo().data()) - << ").", - OnEndpointFound( - client, std::make_shared(BleEndpoint{ - {advertisement.GetEndpointId(), - advertisement.GetEndpointInfo(), service_id, - location::nearby::proto::connections::Medium::BLE, - advertisement.GetWebRtcState()}, - peripheral, - })); + << ")."; + OnEndpointFound( + client, + std::make_shared(BleEndpoint{ + {advertisement.GetEndpointId(), advertisement.GetEndpointInfo(), + service_id, location::nearby::proto::connections::Medium::BLE, + advertisement.GetWebRtcState()}, + peripheral, + })); // Make sure we can connect to this device via Classic Bluetooth. std::string remote_bluetooth_mac_address = @@ -522,6 +528,9 @@ void P2pClusterPcpHandler::BlePeripheralDiscoveredHandler( return; } + StopEndpointLostByMediumAlarm( + advertisement.GetEndpointId(), + location::nearby::proto::connections::Medium::BLUETOOTH); OnEndpointFound( client, std::make_shared(BluetoothEndpoint{ @@ -665,6 +674,9 @@ void P2pClusterPcpHandler::BleV2PeripheralDiscoveredHandler( << absl::BytesToHexString( advertisement.GetEndpointInfo().data()) << ")."; + StopEndpointLostByMediumAlarm( + advertisement.GetEndpointId(), + location::nearby::proto::connections::Medium::BLE); OnEndpointFound( client, std::make_shared(BleV2Endpoint{ @@ -695,6 +707,9 @@ void P2pClusterPcpHandler::BleV2PeripheralDiscoveredHandler( ble_endpoint_state.bt = true; found_endpoints_in_ble_discover_cb_[peripheral_id] = ble_endpoint_state; + StopEndpointLostByMediumAlarm( + advertisement.GetEndpointId(), + location::nearby::proto::connections::Medium::BLUETOOTH); OnEndpointFound( client, std::make_shared(BluetoothEndpoint{ @@ -849,6 +864,9 @@ void P2pClusterPcpHandler::WifiLanServiceDiscoveredHandler( << absl::BytesToHexString( wifi_lan_service_info.GetEndpointInfo().data()) << ")."; + StopEndpointLostByMediumAlarm( + wifi_lan_service_info.GetEndpointId(), + location::nearby::proto::connections::Medium::WIFI_LAN); OnEndpointFound( client, std::make_shared(WifiLanEndpoint{ @@ -1154,8 +1172,8 @@ P2pClusterPcpHandler::StartBluetoothAdvertising( socket.GetRemoteDevice().GetName(); auto channel = std::make_unique( - service_id, /*channel_name=*/remote_device_name, - socket); + service_id, + /*channel_name=*/remote_device_name, socket); ByteArray remote_device_info{remote_device_name}; OnIncomingConnection( @@ -1182,7 +1200,8 @@ P2pClusterPcpHandler::StartBluetoothAdvertising( << service_id; } - // Generate a BluetoothDeviceName with which to become Bluetooth discoverable. + // Generate a BluetoothDeviceName with which to become Bluetooth + // discoverable. // TODO(b/169550050): Implement UWBAddress. std::string device_name(BluetoothDeviceName( kBluetoothDeviceNameVersion, GetPcp(), local_endpoint_id, service_id_hash, @@ -1658,8 +1677,8 @@ P2pClusterPcpHandler::StartBleV2Advertising( advertisement_bytes = ByteArray(BleAdvertisement( kBleAdvertisementVersion, GetPcp(), service_id_hash, local_endpoint_id, - local_endpoint_info, bluetooth_mac_address, /*uwb_address=*/ByteArray{}, - web_rtc_state)); + local_endpoint_info, bluetooth_mac_address, + /*uwb_address=*/ByteArray{}, web_rtc_state)); } if (advertisement_bytes.Empty()) { NEARBY_LOGS(WARNING) << "In StartBleAdvertising("