From 183fdacb034a2f993d695f282ced52a7e06bef2b Mon Sep 17 00:00:00 2001 From: Francis Tsui Date: Fri, 6 Feb 2026 10:14:24 -0800 Subject: [PATCH] Set file save path on a per session basis. PiperOrigin-RevId: 866512037 --- connections/core.cc | 5 ++++ connections/core.h | 4 ++-- connections/implementation/client_proxy.cc | 21 +++++++++++++++++ connections/implementation/client_proxy.h | 8 +++++++ .../implementation/client_proxy_test.cc | 23 +++++++++++++++---- connections/implementation/payload_manager.cc | 10 +++++--- connections/implementation/payload_manager.h | 6 ++++- sharing/fake_nearby_connections_manager.cc | 5 ---- sharing/fake_nearby_connections_manager.h | 5 ++-- sharing/fake_nearby_connections_service.h | 4 ++++ sharing/nearby_connections_manager.h | 3 +++ sharing/nearby_connections_manager_impl.cc | 7 ++++++ sharing/nearby_connections_manager_impl.h | 2 ++ .../nearby_connections_manager_impl_test.cc | 8 +++++++ sharing/nearby_connections_service.h | 4 ++++ sharing/nearby_connections_service_impl.cc | 5 ++++ sharing/nearby_connections_service_impl.h | 3 +++ sharing/nearby_sharing_service_impl.cc | 8 ++++++- 18 files changed, 113 insertions(+), 18 deletions(-) diff --git a/connections/core.cc b/connections/core.cc index 61b77422..827715cc 100644 --- a/connections/core.cc +++ b/connections/core.cc @@ -207,6 +207,11 @@ void Core::SetCustomSavePath(absl::string_view path, ResultCallback callback) { router_->SetCustomSavePath(&client_, path, std::move(callback)); } +void Core::OverrideSavePath(absl::string_view endpoint_id, + absl::string_view path) { + client_.OverrideSavePath(endpoint_id, path); +} + std::string Core::Dump() { return client_.Dump(); } // V3 diff --git a/connections/core.h b/connections/core.h index d159767a..69edb815 100644 --- a/connections/core.h +++ b/connections/core.h @@ -19,7 +19,6 @@ #include #include #include -#include #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -29,7 +28,6 @@ #include "connections/implementation/client_proxy.h" #include "connections/implementation/service_controller_router.h" #include "connections/listeners.h" -#include "connections/medium_selector.h" #include "connections/out_of_band_connection_metadata.h" #include "connections/params.h" #include "connections/payload.h" @@ -254,6 +252,8 @@ class Core { // // path - The path where the received files will be saved to. void SetCustomSavePath(absl::string_view path, ResultCallback callback); + // Override the save path for payloads from a specific endpoint. + void OverrideSavePath(absl::string_view endpoint_id, absl::string_view path); // Gets the local endpoint generated by Nearby Connections. std::string GetLocalEndpointId() { return client_.GetLocalEndpointId(); } diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index 7c303b83..d53e1a18 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -196,6 +196,27 @@ std::string ClientProxy::GetConnectionToken(const std::string& endpoint_id) { return {}; } +bool ClientProxy::OverrideSavePath(absl::string_view endpoint_id, + absl::string_view path) { + MutexLock lock(&mutex_); + ConnectionPair* item = LookupConnection(endpoint_id); + if (item != nullptr) { + item->first.save_path = path; + return true; + } + return false; +} + +std::string ClientProxy::GetSavePath( + absl::string_view endpoint_id) const { + MutexLock lock(&mutex_); + const ConnectionPair* item = LookupConnection(endpoint_id); + if (item != nullptr) { + return item->first.save_path; + } + return ""; +} + std::optional ClientProxy::GetBluetoothMacAddress( const std::string& endpoint_id) { auto item = bluetooth_mac_addresses_.find(endpoint_id); diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index c567f574..05406a27 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -77,6 +77,13 @@ class ClientProxy final { std::string GetLocalEndpointId(); std::string GetLocalEndpointInfo() { return local_endpoint_info_; } + // Override the base for received file attachments from a specific endpoint. + // Returns true if the endpoint is found and the path is overridden. + bool OverrideSavePath(absl::string_view endpoint_id, absl::string_view path); + // Get the save path for a specific endpoint. Returns empty string if + // not set. + std::string GetSavePath(absl::string_view endpoint_id) const; + analytics::AnalyticsRecorder& GetAnalyticsRecorder() const { return *analytics_recorder_; } @@ -394,6 +401,7 @@ class ClientProxy final { std::optional os_info; std::int32_t safe_to_disconnect_version; std::int32_t remote_multiplex_socket_bitmask; + std::string save_path; }; using ConnectionPair = std::pair; diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index d87c6b7b..172a58d8 100644 --- a/connections/implementation/client_proxy_test.cc +++ b/connections/implementation/client_proxy_test.cc @@ -21,7 +21,6 @@ #include #include -#include "base/casts.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" @@ -65,6 +64,7 @@ using ::location::nearby::connections::OsInfo; using ::location::nearby::proto::connections::CLIENT_SESSION; using ::location::nearby::proto::connections::START_CLIENT_SESSION; using ::location::nearby::proto::connections::STOP_CLIENT_SESSION; +using ::testing::IsEmpty; using ::testing::MockFunction; using ::testing::StrictMock; @@ -1400,9 +1400,7 @@ TEST_F(ClientProxyTest, GetLocalDeviceWorksWithDeviceProvider) { MockDeviceProvider provider; client1()->RegisterDeviceProvider(&provider); ASSERT_NE(client1()->GetLocalDeviceProvider(), nullptr); - EXPECT_CALL(*(absl::down_cast( - client1()->GetLocalDeviceProvider())), - GetLocalDevice); + EXPECT_CALL(provider, GetLocalDevice); client1()->GetLocalDevice(); } @@ -1612,6 +1610,23 @@ TEST_F(ClientProxyTest, NotLoadClientInfoFromPreferencesOnExpired) { false); } +TEST_F(ClientProxyTest, OverrideSavePath) { + Endpoint advertising_endpoint = + StartAdvertising(client1(), advertising_connection_listener_); + OnAdvertisingConnectionInitiated(client1(), advertising_endpoint); + + client1()->OverrideSavePath(advertising_endpoint.id, "/tmp/test_path"); + EXPECT_EQ(client1()->GetSavePath(advertising_endpoint.id), "/tmp/test_path"); +} + +TEST_F(ClientProxyTest, GetSavePathDefaultsToEmpty) { + Endpoint advertising_endpoint = + StartAdvertising(client1(), advertising_connection_listener_); + OnAdvertisingConnectionInitiated(client1(), advertising_endpoint); + + EXPECT_THAT(client1()->GetSavePath(advertising_endpoint.id), IsEmpty()); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/connections/implementation/payload_manager.cc b/connections/implementation/payload_manager.cc index 2ff3e716..9dbf2521 100644 --- a/connections/implementation/payload_manager.cc +++ b/connections/implementation/payload_manager.cc @@ -792,9 +792,12 @@ PayloadTransferFrame::PayloadChunk PayloadManager::CreatePayloadChunk( ErrorOr PayloadManager::CreateIncomingPayload(const PayloadTransferFrame& frame, - const std::string& endpoint_id) { + const std::string& endpoint_id, + const std::string& save_path) { ErrorOr> result = - CreateIncomingInternalPayload(frame, custom_save_path_); + CreateIncomingInternalPayload(frame, save_path.empty() + ? custom_save_path_ + : save_path); if (result.has_error()) { return {result.error()}; } @@ -1340,7 +1343,8 @@ void PayloadManager::ProcessDataPacket( }); ErrorOr result = - CreateIncomingPayload(payload_transfer_frame, from_endpoint_id); + CreateIncomingPayload(payload_transfer_frame, from_endpoint_id, + to_client->GetSavePath(from_endpoint_id)); if (result.has_error()) { LOG(WARNING) << "PayloadManager failed to create InternalPayload from " "PayloadTransferFrame with payload_id=" diff --git a/connections/implementation/payload_manager.h b/connections/implementation/payload_manager.h index 838af4f8..4ba438c5 100644 --- a/connections/implementation/payload_manager.h +++ b/connections/implementation/payload_manager.h @@ -323,9 +323,13 @@ class PayloadManager : public EndpointManager::FrameProcessor { LAST_CHUNK) != 0); } + // Creates an incoming payload and returns a handle to it. + // If `save_path` is empty, the payload will be saved to the default save + // path set in `SetCustomSavePath()`. ErrorOr CreateIncomingPayload( const location::nearby::connections::PayloadTransferFrame& frame, - const std::string& endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_); + const std::string& endpoint_id, + const std::string& save_path) ABSL_LOCKS_EXCLUDED(mutex_); Payload::Id CreateOutgoingPayload(Payload payload, const EndpointIds& endpoint_ids) diff --git a/sharing/fake_nearby_connections_manager.cc b/sharing/fake_nearby_connections_manager.cc index b152fabb..e69fbb7d 100644 --- a/sharing/fake_nearby_connections_manager.cc +++ b/sharing/fake_nearby_connections_manager.cc @@ -311,11 +311,6 @@ void FakeNearbyConnectionsManager::HandleStopAdvertisingCallback( capture_next_stop_advertising_callback_ = false; } -void FakeNearbyConnectionsManager::SetCustomSavePath( - absl::string_view custom_save_path) { - custom_save_path_ = custom_save_path; -} - absl::flat_hash_set FakeNearbyConnectionsManager::GetAndClearUnknownFilePathsToDelete() { absl::flat_hash_set file_paths_to_delete = file_paths_to_delete_; diff --git a/sharing/fake_nearby_connections_manager.h b/sharing/fake_nearby_connections_manager.h index 9131b7c9..d62415c0 100644 --- a/sharing/fake_nearby_connections_manager.h +++ b/sharing/fake_nearby_connections_manager.h @@ -74,7 +74,9 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { std::optional> GetRawAuthenticationToken( absl::string_view endpoint_id) override; void UpgradeBandwidth(absl::string_view endpoint_id) override; - void SetCustomSavePath(absl::string_view custom_save_path) override; + void SetCustomSavePath(absl::string_view custom_save_path) override {} + void OverrideSavePath(absl::string_view endpoint_id, + const FilePath& custom_save_path) override {} absl::flat_hash_set GetAndClearUnknownFilePathsToDelete() override; // Testing methods @@ -170,7 +172,6 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { ConnectionsCallback pending_stop_advertising_callback_; bool capture_next_start_advertising_callback_ = false; ConnectionsCallback pending_start_advertising_callback_; - std::string custom_save_path_; absl::Mutex endpoints_mutex_; // Maps endpoint_id to endpoint_info. diff --git a/sharing/fake_nearby_connections_service.h b/sharing/fake_nearby_connections_service.h index d94363b1..cbe21c02 100644 --- a/sharing/fake_nearby_connections_service.h +++ b/sharing/fake_nearby_connections_service.h @@ -111,6 +111,10 @@ class FakeNearbyConnectionsService : public NearbyConnectionsService { std::function callback), (override)); + MOCK_METHOD(void, OverrideSavePath, + (absl::string_view endpoint_id, absl::string_view path), + (override)); + MOCK_METHOD(std::string, Dump, (), (const, override)); }; diff --git a/sharing/nearby_connections_manager.h b/sharing/nearby_connections_manager.h index 179c7718..27ecb953 100644 --- a/sharing/nearby_connections_manager.h +++ b/sharing/nearby_connections_manager.h @@ -156,6 +156,9 @@ class NearbyConnectionsManager { // Sets a custom save path. virtual void SetCustomSavePath(absl::string_view custom_save_path) = 0; + // Overrides the save path for transfers from a specific endpoint. + virtual void OverrideSavePath(absl::string_view endpoint_id, + const FilePath& custom_save_path) = 0; // Gets the file paths to delete and clear the hash set. virtual absl::flat_hash_set diff --git a/sharing/nearby_connections_manager_impl.cc b/sharing/nearby_connections_manager_impl.cc index 10457dca..16ae5a39 100644 --- a/sharing/nearby_connections_manager_impl.cc +++ b/sharing/nearby_connections_manager_impl.cc @@ -972,6 +972,13 @@ void NearbyConnectionsManagerImpl::SetCustomSavePath( }); } +void NearbyConnectionsManagerImpl::OverrideSavePath( + absl::string_view endpoint_id, const FilePath& custom_save_path) { + MutexLock lock(&mutex_); + nearby_connections_service_->OverrideSavePath(endpoint_id, + custom_save_path.ToString()); +} + absl::flat_hash_set NearbyConnectionsManagerImpl::GetUnknownFilePathsToDelete() { MutexLock lock(&mutex_); diff --git a/sharing/nearby_connections_manager_impl.h b/sharing/nearby_connections_manager_impl.h index bac82acf..ff6f064f 100644 --- a/sharing/nearby_connections_manager_impl.h +++ b/sharing/nearby_connections_manager_impl.h @@ -87,6 +87,8 @@ class NearbyConnectionsManagerImpl : public NearbyConnectionsManager { absl::string_view endpoint_id) override; void UpgradeBandwidth(absl::string_view endpoint_id) override; void SetCustomSavePath(absl::string_view custom_save_path) override; + void OverrideSavePath(absl::string_view endpoint_id, + const FilePath& custom_save_path) override; absl::flat_hash_set GetAndClearUnknownFilePathsToDelete() override; std::string Dump() const override; diff --git a/sharing/nearby_connections_manager_impl_test.cc b/sharing/nearby_connections_manager_impl_test.cc index 563f4346..325a9762 100644 --- a/sharing/nearby_connections_manager_impl_test.cc +++ b/sharing/nearby_connections_manager_impl_test.cc @@ -2050,5 +2050,13 @@ TEST_F(NearbyConnectionsManagerImplTest, ProcessUnknownFilePathsToDelete) { nearby_connections_manager_->GetAndClearUnknownFilePathsToDelete(); } +TEST_F(NearbyConnectionsManagerImplTest, OverrideSavePath) { + EXPECT_CALL(*nearby_connections_, + OverrideSavePath(kRemoteEndpointId, "/tmp/test")); + + nearby_connections_manager_->OverrideSavePath(kRemoteEndpointId, + FilePath("/tmp/test")); +} + } // namespace NearbyConnectionsManagerUnitTests } // namespace nearby::sharing diff --git a/sharing/nearby_connections_service.h b/sharing/nearby_connections_service.h index 51a727fe..acbdd96b 100644 --- a/sharing/nearby_connections_service.h +++ b/sharing/nearby_connections_service.h @@ -137,6 +137,10 @@ class NearbyConnectionsService { virtual void SetCustomSavePath( absl::string_view path, std::function callback) = 0; + // Overrides the save path for transfers from a specific endpoint. + virtual void OverrideSavePath(absl::string_view endpoint_id, + absl::string_view path) = 0; + virtual std::string Dump() const = 0; }; diff --git a/sharing/nearby_connections_service_impl.cc b/sharing/nearby_connections_service_impl.cc index 7fec3154..b0d3a89d 100644 --- a/sharing/nearby_connections_service_impl.cc +++ b/sharing/nearby_connections_service_impl.cc @@ -381,6 +381,11 @@ void NearbyConnectionsServiceImpl::SetCustomSavePath( ->SetCustomSavePath(path, BuildResultCallback(callback)); } +void NearbyConnectionsServiceImpl::OverrideSavePath( + absl::string_view endpoint_id, absl::string_view path) { + GetService(service_handle_)->OverrideSavePath(endpoint_id, path); +} + std::string NearbyConnectionsServiceImpl::Dump() const { return GetService(service_handle_)->Dump(); } diff --git a/sharing/nearby_connections_service_impl.h b/sharing/nearby_connections_service_impl.h index 54bf1ab9..cdcfcf30 100644 --- a/sharing/nearby_connections_service_impl.h +++ b/sharing/nearby_connections_service_impl.h @@ -90,6 +90,9 @@ class NearbyConnectionsServiceImpl : public NearbyConnectionsService { void SetCustomSavePath(absl::string_view path, std::function callback) override; + void OverrideSavePath(absl::string_view endpoint_id, + absl::string_view path) override; + std::string Dump() const override; private: diff --git a/sharing/nearby_sharing_service_impl.cc b/sharing/nearby_sharing_service_impl.cc index f8a8a96e..ebbcb722 100644 --- a/sharing/nearby_sharing_service_impl.cc +++ b/sharing/nearby_sharing_service_impl.cc @@ -2520,13 +2520,19 @@ void NearbySharingServiceImpl::OnReceivedIntroduction( Fail(*session, *status); return; } + FilePath save_path{settings_->GetCustomSavePath()}; + // Override save path for this connection. + // This must be called before the transfer is accepted and payloads are being + // received. + nearby_connections_manager_->OverrideSavePath(session->endpoint_id(), + save_path); // Log analytics event of receiving introduction. analytics_recorder_.NewReceiveIntroduction( session->session_id(), session->share_target(), /*referrer_package=*/std::nullopt, session->os_type()); - if (IsOutOfStorage(device_info_, FilePath{settings_->GetCustomSavePath()}, + if (IsOutOfStorage(device_info_, save_path, session->attachment_container().GetStorageSize())) { Fail(*session, TransferMetadata::Status::kNotEnoughSpace); LOG(WARNING) << __func__