From 33a6a483203acfd37269032bd4216bdd62206feb Mon Sep 17 00:00:00 2001 From: Francis Tsui Date: Thu, 4 Jun 2026 19:17:50 -0700 Subject: [PATCH] Update save path for file sync transfers. PiperOrigin-RevId: 927012760 --- sharing/BUILD | 1 + sharing/fake_nearby_connections_manager.cc | 7 + sharing/fake_nearby_connections_manager.h | 14 +- sharing/nearby_sharing_service_impl.cc | 22 ++ sharing/nearby_sharing_service_impl_test.cc | 283 ++++++++++++++++++-- 5 files changed, 304 insertions(+), 23 deletions(-) diff --git a/sharing/BUILD b/sharing/BUILD index e66e1ce5..2af1bf4a 100644 --- a/sharing/BUILD +++ b/sharing/BUILD @@ -649,6 +649,7 @@ cc_test( ":nearby_connection_impl", ":nearby_sharing_service", ":share_session", + ":share_session_usage", ":test_support", ":transfer_metadata", ":transfer_metadata_matchers", diff --git a/sharing/fake_nearby_connections_manager.cc b/sharing/fake_nearby_connections_manager.cc index e69fbb7d..4fff1842 100644 --- a/sharing/fake_nearby_connections_manager.cc +++ b/sharing/fake_nearby_connections_manager.cc @@ -32,6 +32,7 @@ #include "internal/base/file_path.h" #include "sharing/common/nearby_share_enums.h" #include "sharing/internal/public/logging.h" +#include "sharing/nearby_connection.h" #include "sharing/nearby_connections_manager.h" #include "sharing/nearby_connections_types.h" #include "sharing/proto/enums.pb.h" @@ -204,6 +205,12 @@ void FakeNearbyConnectionsManager::UpgradeBandwidth( upgrade_bandwidth_endpoint_ids_.insert(std::string(endpoint_id)); } +void FakeNearbyConnectionsManager::OverrideSavePath( + absl::string_view endpoint_id, const FilePath& custom_save_path) { + absl::MutexLock lock(endpoints_mutex_); + custom_save_paths_[endpoint_id] = custom_save_path; +} + void FakeNearbyConnectionsManager::OnEndpointFound( absl::string_view endpoint_id, std::unique_ptr info) { diff --git a/sharing/fake_nearby_connections_manager.h b/sharing/fake_nearby_connections_manager.h index 94b3dddb..eabde28b 100644 --- a/sharing/fake_nearby_connections_manager.h +++ b/sharing/fake_nearby_connections_manager.h @@ -27,6 +27,7 @@ #include #include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" @@ -76,7 +77,7 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { 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 {} + const FilePath& custom_save_path) override; absl::flat_hash_set GetAndClearUnknownFilePathsToDelete() override; // Testing methods @@ -131,6 +132,14 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { return it->second; } + std::optional custom_save_path(absl::string_view endpoint_id) { + absl::MutexLock lock(endpoints_mutex_); + auto it = custom_save_paths_.find(endpoint_id); + if (it == custom_save_paths_.end()) return std::nullopt; + + return it->second; + } + bool has_incoming_payloads() { absl::MutexLock lock(incoming_payloads_mutex_); return !incoming_payloads_.empty(); @@ -177,6 +186,9 @@ class FakeNearbyConnectionsManager : public NearbyConnectionsManager { // Maps endpoint_id to endpoint_info. std::map> connection_endpoint_infos_ ABSL_GUARDED_BY(endpoints_mutex_); + // Maps endpoint_id to custom_save_path. + absl::flat_hash_map custom_save_paths_ + ABSL_GUARDED_BY(endpoints_mutex_); std::map> payload_status_listeners_; diff --git a/sharing/nearby_sharing_service_impl.cc b/sharing/nearby_sharing_service_impl.cc index 49f16308..27575d31 100644 --- a/sharing/nearby_sharing_service_impl.cc +++ b/sharing/nearby_sharing_service_impl.cc @@ -2703,6 +2703,28 @@ void NearbySharingServiceImpl::OnReceivedIntroduction( return; } FilePath save_path{settings_->GetCustomSavePath()}; + // If transfer is for file sync, override the save path to the custom save + // path. + if (frame.use_case() == IntroductionFrame::FILE_SYNC) { + if (!session.certificate().has_value() || + session.certificate()->binding_id().empty()) { + LOG(ERROR) << __func__ + << ": Binding id is empty for file sync session."; + Fail(session, TransferMetadata::Status::kRejected); + return; + } + std::optional binding = + sync_manager_.GetSyncBinding(session.certificate()->binding_id()); + if (!binding.has_value()) { + LOG(ERROR) << __func__ + << ": Sync binding not found for binding id: " + << session.certificate()->binding_id(); + Fail(session, TransferMetadata::Status::kRejected); + return; + } + save_path = FilePath(binding->destination_directory()); + session.set_session_usage(ShareSessionUsage::kFileSync); + } // Override save path for this connection. // This must be called before the transfer is accepted and payloads are being // received. diff --git a/sharing/nearby_sharing_service_impl_test.cc b/sharing/nearby_sharing_service_impl_test.cc index 491a56ef..23d3c1e8 100644 --- a/sharing/nearby_sharing_service_impl_test.cc +++ b/sharing/nearby_sharing_service_impl_test.cc @@ -88,6 +88,7 @@ #include "sharing/proto/enums.pb.h" #include "sharing/proto/rpc_resources.pb.h" #include "sharing/proto/wire_format.pb.h" +#include "sharing/share_session_usage.h" #include "sharing/share_target.h" #include "sharing/share_target_discovered_callback.h" #include "sharing/text_attachment.h" @@ -273,11 +274,13 @@ std::unique_ptr GetTextPayload(int64_t payload_id, std::vector(text.begin(), text.end())); } -std::unique_ptr GetValidIntroductionFrame() { +std::unique_ptr GetValidIntroductionFrame( + IntroductionFrame::SharingUseCase use_case) { IntroductionFrame* introduction_frame = IntroductionFrame::default_instance().New(); auto text_metadatas = introduction_frame->mutable_text_metadata(); introduction_frame->set_start_transfer(true); + introduction_frame->set_use_case(use_case); for (int i = 1; i <= 3; ++i) { nearby::sharing::service::proto::TextMetadata* text_metadata = @@ -665,10 +668,9 @@ class NearbySharingServiceImplTest : public testing::Test { EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising()); } - void ProcessLatestPublicCertificateDecryption(size_t expected_num_calls, - bool success, - bool for_self_share = false, - uint8_t vendor_id = 0) { + void ProcessLatestPublicCertificateDecryption( + size_t expected_num_calls, bool success, bool for_self_share = false, + uint8_t vendor_id = 0, absl::string_view binding_id = "") { // Ensure that all pending mojo messages are processed and the certificate // manager state is as expected up to this point. std::vector< @@ -688,6 +690,9 @@ class NearbySharingServiceImplTest : public testing::Test { DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS, GetNearbyShareTestNotBefore(), vendor_id); cert.set_for_self_share(for_self_share); + if (!binding_id.empty()) { + cert.set_binding_id(binding_id); + } std::move(calls.back().callback)( NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate( cert, GetNearbyShareTestEncryptedMetadataKey())); @@ -744,13 +749,17 @@ class NearbySharingServiceImplTest : public testing::Test { return advertisement->ToEndpointInfo(); } - void SetUpIntroductionFrameDecoder(bool return_empty_introduction_frame) { - std::unique_ptr frame; - if (return_empty_introduction_frame) { - frame = GetEmptyIntroductionFrame(); - } else { - frame = GetValidIntroductionFrame(); - } + void SetUpEmptyIntroductionFrameDecoder() { + std::unique_ptr frame = GetEmptyIntroductionFrame(); + std::vector bytes(frame->ByteSizeLong()); + frame->SerializeToArray(bytes.data(), bytes.size()); + ReceiveMessageFromConnection(std::move(bytes)); + } + + void SetUpIntroductionFrameDecoder( + IntroductionFrame::SharingUseCase use_case = + IntroductionFrame::NEARBY_SHARE) { + std::unique_ptr frame = GetValidIntroductionFrame(use_case); std::vector bytes(frame->ByteSizeLong()); frame->SerializeToArray(bytes.data(), bytes.size()); ReceiveMessageFromConnection(std::move(bytes)); @@ -775,7 +784,7 @@ class NearbySharingServiceImplTest : public testing::Test { bool for_self_share = false) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); int64_t share_target_id; SetLanConnected(true); @@ -2375,7 +2384,7 @@ TEST_F(NearbySharingServiceImplTest, TEST_F(NearbySharingServiceImplTest, IncomingConnectionEmptyIntroductionFrame) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/true); + SetUpEmptyIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2414,7 +2423,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionValidIntroductionFrameInvalidCertificate) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2464,6 +2473,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionTimedOut) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kTimedOut); }); @@ -2487,6 +2497,7 @@ TEST_F(NearbySharingServiceImplTest, const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kFailed); }); @@ -2614,7 +2625,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionValidIntroductionFrameValidCertificate) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2655,6 +2666,128 @@ TEST_F(NearbySharingServiceImplTest, .has_value()); } +TEST_F(NearbySharingServiceImplTest, + IncomingConnectionValidIntroductionFrameValidCertificateFileSync) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + constexpr absl::string_view kBindingId = "binding_id"; + sync::SyncBinding binding; + binding.set_binding_id(kBindingId); + binding.set_source_name(kDeviceName); + binding.set_destination_directory( + FilePath("Downloads").append(FilePath(kDeviceName)).ToString()); + binding.set_source_device_type(sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE); + service_->sync_manager().AddSyncBinding(binding); + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_FALSE(metadata.is_final_status()); + EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation, + metadata.status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kFileSync); + EXPECT_TRUE(share_target.is_incoming); + EXPECT_TRUE(share_target.is_known); + EXPECT_TRUE(container.HasAttachments()); + EXPECT_EQ(container.GetTextAttachments().size(), 3u); + EXPECT_EQ(container.GetFileAttachments().size(), 1u); + EXPECT_EQ(share_target.device_name, kDeviceName); + EXPECT_EQ(share_target.type, kDeviceType); + EXPECT_NE(share_target.device_id, kEndpointId); + EXPECT_EQ(share_target.full_name, kTestMetadataFullName); + EXPECT_FALSE(share_target.for_self_share); + EXPECT_FALSE(metadata.is_self_share()); + EXPECT_TRUE(metadata.token().has_value()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, kBindingId); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); + + EXPECT_TRUE( + fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId) + .has_value()); + ASSERT_TRUE(fake_nearby_connections_manager_->custom_save_path(kEndpointId) + .has_value()); + EXPECT_EQ(fake_nearby_connections_manager_->custom_save_path(kEndpointId) + ->ToString(), + binding.destination_directory()); +} + +TEST_F(NearbySharingServiceImplTest, + IncomingIntroductionFrameCertificateEmptyBindingId) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_EQ(TransferMetadata::Status::kRejected, metadata.status()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, /*binding_id=*/""); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + +TEST_F(NearbySharingServiceImplTest, + IncomingIntroductionFrameFileSyncBindingNotFound) { + fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, + GetToken()); + SetUpIntroductionFrameDecoder(IntroductionFrame::FILE_SYNC); + + constexpr absl::string_view kBindingId = "binding_id"; + + SetLanConnected(true); + NiceMock callback; + absl::Notification notification; + EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([¬ification](const ShareTarget& share_target, + const AttachmentContainer& container, + TransferMetadata metadata) { + EXPECT_EQ(TransferMetadata::Status::kRejected, metadata.status()); + notification.Notify(); + }); + + SetUpKeyVerification(/*is_incoming=*/true, PairedKeyResultFrame::SUCCESS); + SetUpForegroundReceiveSurface(callback); + ScopedReceiveSurface r(service_.get(), &callback); + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + StartIncomingConnection(); + ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1, + /*success=*/true, + /*for_self_share=*/false, + /*vendor_id=*/0, kBindingId); + EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout)); +} + TEST_F(NearbySharingServiceImplTest, AcceptInvalidShareTarget) { absl::Notification notification; service_->Accept( @@ -2711,6 +2844,7 @@ TEST_F(NearbySharingServiceImplTest, const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_FALSE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kInProgress); progress_notification.Notify(); }); @@ -2785,6 +2919,7 @@ TEST_F(NearbySharingServiceImplTest, AcceptValidShareTargetPayloadFailed) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kFailed); ASSERT_TRUE(container.HasAttachments()); EXPECT_EQ(container.GetFileAttachments().size(), 1u); @@ -2831,6 +2966,7 @@ TEST_F(NearbySharingServiceImplTest, AcceptValidShareTargetPayloadCancelled) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kCancelled); ASSERT_TRUE(container.HasAttachments()); EXPECT_EQ(container.GetFileAttachments().size(), 1u); @@ -2883,6 +3019,7 @@ TEST_F(NearbySharingServiceImplTest, RejectValidShareTarget) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_TRUE(metadata.is_final_status()); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kRejected); }); @@ -2909,7 +3046,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionKeyVerificationRunnerStatusUnable) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -2952,7 +3089,7 @@ TEST_F(NearbySharingServiceImplTest, IncomingConnectionKeyVerificationRunnerStatusUnableLowPower) { fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId, GetToken()); - SetUpIntroductionFrameDecoder(/*return_empty_introduction_frame=*/false); + SetUpIntroductionFrameDecoder(); SetLanConnected(true); NiceMock callback; @@ -3654,6 +3791,7 @@ TEST_F(NearbySharingServiceImplTest, CancelReceiverInitiator) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(metadata.status(), TransferMetadata::Status::kCancelled); }); EXPECT_FALSE( @@ -3702,6 +3840,7 @@ TEST_F(NearbySharingServiceImplTest, CancelReceiverNoninitiator) { const AttachmentContainer& container, TransferMetadata metadata) { EXPECT_EQ(target_id, share_target.id); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kSharing); EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status()); notification.Notify(); }); @@ -4661,8 +4800,7 @@ TEST_F(NearbySharingServiceImplTest, LoginAndLogoutShouldResetSettings) { ASSERT_TRUE(service_->GetAccountManager()->GetCurrentAccount().has_value()); EXPECT_EQ(service_->GetAccountManager()->GetCurrentAccount()->id, kTestAccountId); - device_id = - preference_manager_.GetString(PrefNames::kDeviceId, ""); + device_id = preference_manager_.GetString(PrefNames::kDeviceId, ""); EXPECT_FALSE(device_id.empty()); EXPECT_EQ(device_id.size(), 10u); for (const char c : device_id) EXPECT_TRUE(std::isalnum(c)); @@ -4679,8 +4817,7 @@ TEST_F(NearbySharingServiceImplTest, LoginAndLogoutShouldResetSettings) { EXPECT_TRUE(service_->GetSettings()->GetIsAnalyticsEnabled()); EXPECT_FALSE(service_->GetAccountManager()->GetCurrentAccount().has_value()); EXPECT_TRUE(sharing_service_task_runner_->SyncWithTimeout(kTaskWaitTimeout)); - device_id = - preference_manager_.GetString(PrefNames::kDeviceId, ""); + device_id = preference_manager_.GetString(PrefNames::kDeviceId, ""); EXPECT_TRUE(device_id.empty()); } @@ -5097,5 +5234,107 @@ TEST_F(NearbySharingServiceImplTest, InitiatePairingSuccess) { EXPECT_THAT(binding->sync_bindings(0), EqualsProto(expected_binding)); } +TEST_F(NearbySharingServiceImplTest, + InitiatePairingSuccessCheckUsageAndBindingId) { + MockTransferUpdateCallback transfer_callback; + MockShareTargetDiscoveredCallback discovery_callback; + int64_t target_id = SetUpOutgoingShareTarget( + transfer_callback, discovery_callback, /*for_self_share=*/true); + ScopedSendSurface s(service_.get(), &transfer_callback); + absl::Notification notification; + + constexpr absl::string_view kBindingId = "binding_id"; + + EXPECT_CALL(transfer_callback, + OnTransferUpdate(testing::_, testing::_, testing::_)) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), TransferMetadata::Status::kConnecting); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kUnknown); + EXPECT_TRUE(metadata.binding_id().empty()); + }) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), + TransferMetadata::Status::kAwaitingRemoteAcceptance); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kPairing); + EXPECT_TRUE(metadata.binding_id().empty()); + }) + .WillOnce([&](const ShareTarget& share_target, + const AttachmentContainer& container, + const TransferMetadata& metadata) { + EXPECT_EQ(share_target.id, target_id); + EXPECT_EQ(metadata.status(), TransferMetadata::Status::kComplete); + EXPECT_EQ(metadata.usage(), ShareSessionUsage::kPairing); + EXPECT_EQ(metadata.binding_id(), kBindingId); + notification.Notify(); + }); + + absl::Notification pairing_notification; + NearbySharingServiceImpl::StatusCodes pairing_result; + EXPECT_CALL(*mock_app_info_, SetActiveFlag()); + google::nearby::identity::v1::InitiateBindingResponse response; + response.set_binding_id(kBindingId); + nearby_identity_client_.SetInitiateBindingResponses({response}); + service_->InitiatePairing( + target_id, service::proto::BindingRequest::FILESYNC, + [&](NearbySharingServiceImpl::StatusCodes status_code) { + pairing_result = status_code; + pairing_notification.Notify(); + }); + EXPECT_TRUE( + pairing_notification.WaitForNotificationWithTimeout(kTaskWaitTimeout)); + EXPECT_EQ(pairing_result, NearbySharingServiceImpl::StatusCodes::kOk); + + FlushTesting(); + // Verify data sent to the remote device so far. + EXPECT_TRUE(ExpectPairedKeyEncryptionFrame()); + EXPECT_TRUE(ExpectPairedKeyResultFrame()); + + // Check BindingRequest frame sent to the remote device. + std::unique_ptr frame = GetWrittenFrame(); + ASSERT_TRUE(frame->has_v1()); + EXPECT_EQ(frame->v1().type(), service::proto::V1Frame::BINDINGS); + EXPECT_EQ(frame->v1().bindings().binding_request().binding_id(), kBindingId); + EXPECT_EQ(frame->v1().bindings().binding_request().type(), + service::proto::BindingRequest::FILESYNC); + + preference_manager_.SetString(PrefNames::kCustomSavePath, "Downloads"); + Frame binding_response_frame; + binding_response_frame.set_version(Frame::V1); + binding_response_frame.mutable_v1()->set_type( + service::proto::V1Frame::BINDINGS); + binding_response_frame.mutable_v1() + ->mutable_bindings() + ->mutable_binding_response() + ->set_status(service::proto::BindingResponse::SUCCESS); + std::vector result_bytes(binding_response_frame.ByteSizeLong()); + binding_response_frame.SerializeToArray(result_bytes.data(), + result_bytes.size()); + ReceiveMessageFromConnection(std::move(result_bytes)); + + // Verify that connection is closed. + EXPECT_FALSE( + fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId) + .has_value()); + + std::optional binding = + preference_manager_.GetSyncBindingValue(); + ASSERT_TRUE(binding.has_value()); + EXPECT_EQ(binding->sync_bindings().size(), 1); + sync::SyncBinding expected_binding; + expected_binding.set_binding_id(kBindingId); + expected_binding.set_source_name(kDeviceName); + expected_binding.set_destination_directory( + FilePath("Downloads").append(FilePath(kDeviceName)).ToString()); + expected_binding.set_source_device_type( + sync::SyncBinding::SOURCE_DEVICE_TYPE_PHONE); + EXPECT_THAT(binding->sync_bindings(0), EqualsProto(expected_binding)); +} + } // namespace NearbySharingServiceUnitTests } // namespace nearby::sharing