Add InitiatePairing to NearbySharingService.

PiperOrigin-RevId: 890580038
This commit is contained in:
Francis Tsui
2026-03-27 12:21:02 -07:00
committed by Copybara-Service
parent c855c882c4
commit a189a612f9
6 changed files with 337 additions and 6 deletions
+1
View File
@@ -394,6 +394,7 @@ cc_library(
"//location/nearby/sharing/lib/account:account_manager",
"//location/nearby/sharing/lib/rpc:grpc_async_client_factory",
"//location/nearby/sharing/lib/rpc:sharing_rpc_client",
"//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto",
"//location/nearby/sharing/lib/sync:sync_manager",
"//proto:sharing_enums_cc_proto",
"//sharing/analytics",
+6
View File
@@ -117,6 +117,12 @@ class FakeNearbySharingService : public NearbySharingService {
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
void InitiatePairing(
int64_t share_target_id,
service::proto::BindingRequest::Type binding_type,
absl::AnyInvocable<void(StatusCodes status_codes) &&>
status_codes_callback) override {}
std::string Dump() const override;
bool IsBluetoothPresent() const override { return true; }
bool IsBluetoothPowered() const override { return true; }
+6
View File
@@ -206,6 +206,12 @@ class NearbySharingService {
int64_t share_target_id,
std::function<void(StatusCodes status_codes)> status_codes_callback) = 0;
virtual void InitiatePairing(
int64_t share_target_id,
service::proto::BindingRequest::Type binding_type,
absl::AnyInvocable<void(StatusCodes status_codes) &&>
status_codes_callback) = 0;
// Checks to make sure visibility setting is valid and updates the service's
// visibility if so.
virtual void SetVisibility(
+104 -2
View File
@@ -33,6 +33,7 @@
#include "location/nearby/sharing/lib/account/account_manager.h"
#include "location/nearby/sharing/lib/rpc/sharing_rpc_client.h"
#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h"
#include "absl/base/nullability.h"
#include "absl/container/flat_hash_map.h"
#include "absl/functional/any_invocable.h"
@@ -113,10 +114,12 @@ using ::absl::Milliseconds;
using ::location::nearby::proto::sharing::OSType;
using ::location::nearby::proto::sharing::ResponseToIntroduction;
using ::location::nearby::proto::sharing::SessionStatus;
using ::nearby::sharing::api::SharingPlatform;
using ::nearby::sharing::api::IdentityRpcClient;
using ::nearby::sharing::api::SharingPlatform;
using ::nearby::sharing::proto::DataUsage;
using ::nearby::sharing::proto::DeviceVisibility;
using ::nearby::sharing::service::proto::BindingRequest;
using ::nearby::sharing::service::proto::BindingResponse;
using ::nearby::sharing::service::proto::ConnectionResponseFrame;
using ::nearby::sharing::service::proto::IntroductionFrame;
@@ -948,6 +951,33 @@ void NearbySharingServiceImpl::DoCancel(
std::move(status_codes_callback)(StatusCodes::kOk);
}
void NearbySharingServiceImpl::InitiatePairing(
int64_t share_target_id, BindingRequest::Type binding_type,
absl::AnyInvocable<void(StatusCodes status_codes) &&>
status_codes_callback) {
RunOnNearbySharingServiceThread(
"api_initiate_pairing",
[this, share_target_id, binding_type,
status_codes_callback = std::move(status_codes_callback)]() mutable {
LOG(INFO) << "InitiatePairing is called";
OutgoingShareSession* session =
outgoing_targets_manager_.GetOutgoingShareSession(share_target_id);
if (!session) {
LOG(WARNING) << "InitiatePairing invoked for unknown share target";
std::move(status_codes_callback)(StatusCodes::kInvalidArgument);
return;
}
if (binding_type != BindingRequest::FILESYNC) {
LOG(WARNING) << __func__ << "Only FileSync bindings are supported.";
std::move(status_codes_callback)(StatusCodes::kInvalidArgument);
return;
}
// Start connection without attachments will initiate pairing.
std::move(status_codes_callback)(
ConnectOutgoingSessionOnServiceThread(*session));
});
}
void NearbySharingServiceImpl::SetVisibility(
proto::DeviceVisibility visibility, absl::Duration expiration,
absl::AnyInvocable<void(StatusCodes status_code) &&> callback) {
@@ -2571,7 +2601,79 @@ void NearbySharingServiceImpl::BeginOutgoingPairing(
OutgoingShareSession& session) {
VLOG(1) << __func__ << ": Preparing to initiate pairing with "
<< session.share_target().id;
// TODO(ftsui): Implement this.
// Verify that remote really authenticated with self share certificate.
if (!session.self_share()) {
LOG(WARNING) << __func__ << ": Not self share, skipping pairing.";
session.Abort(TransferMetadata::Status::kDeviceAuthenticationFailed);
return;
}
// Call InitiateBinding rpc.
sync_manager_.AsyncInitiateSyncBinding(
[this, share_target_id = session.share_target().id](
absl::StatusOr<std::string> binding_status) {
LOG(INFO) << __func__ << ": Sync binding rpc completed.";
OnInitiateSyncBindingResponse(share_target_id,
std::move(binding_status));
});
}
void NearbySharingServiceImpl::OnInitiateSyncBindingResponse(
int64_t share_target_id, absl::StatusOr<std::string> binding_status) {
RunOnNearbySharingServiceThread(
"start_peer_binding",
[this, share_target_id, binding_status = std::move(binding_status)]() {
OutgoingShareSession* session =
outgoing_targets_manager_.GetOutgoingShareSession(share_target_id);
if (!session || !session->IsConnected()) {
LOG(WARNING) << __func__
<< ": Session not connected, stop binding to: "
<< share_target_id;
return;
}
if (binding_status.ok()) {
std::string binding_id = binding_status.value();
LOG(INFO) << __func__
<< ": Sync binding rpc succeeded: id=" << binding_id;
session->StartPeerBinding(
binding_id, BindingRequest::FILESYNC,
[this, share_target_id,
binding_id](BindingResponse::Status status) {
OnPeerSyncBindingComplete(share_target_id, binding_id, status);
});
} else {
LOG(INFO) << __func__ << ": Sync binding rpc failed.";
session->Abort(TransferMetadata::Status::kFailed);
}
});
}
void NearbySharingServiceImpl::OnPeerSyncBindingComplete(
int64_t share_target_id, absl::string_view binding_id,
BindingResponse::Status status) {
OutgoingShareSession* session =
outgoing_targets_manager_.GetOutgoingShareSession(share_target_id);
if (!session || !session->IsConnected()) {
LOG(WARNING) << __func__ << ": Session not connected, stop binding to: "
<< share_target_id;
return;
}
if (status != BindingResponse::SUCCESS) {
LOG(INFO) << __func__ << ": Sync binding response failed.";
session->Abort(TransferMetadata::Status::kFailed);
return;
}
sync::SyncBinding binding;
binding.set_binding_id(binding_id);
binding.set_source_name(session->share_target().device_name);
// Set default destination directory to Downloads/`device_name`.
FilePath destination_path{settings_->GetCustomSavePath()};
destination_path.append(FilePath(session->share_target().device_name));
binding.set_destination_directory(destination_path.ToString());
sync_manager_.AddSyncBinding(binding);
session->UpdateTransferMetadata(
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kComplete)
.build());
}
void NearbySharingServiceImpl::OnReceivedIntroduction(
+12
View File
@@ -34,6 +34,7 @@
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "absl/types/span.h"
@@ -155,6 +156,10 @@ class NearbySharingServiceImpl
void Cancel(int64_t share_target_id,
std::function<void(StatusCodes status_codes)>
status_codes_callback) override;
void InitiatePairing(int64_t share_target_id,
service::proto::BindingRequest::Type binding_type,
absl::AnyInvocable<void(StatusCodes status_codes) &&>
status_codes_callback) override;
void SetVisibility(
proto::DeviceVisibility visibility, absl::Duration expiration,
absl::AnyInvocable<void(StatusCodes status_code) &&> callback) override;
@@ -401,6 +406,13 @@ class NearbySharingServiceImpl
bool OutgoingSessionAccept(OutgoingShareSession& session);
void OnIncomingFilesMetadataUpdated(int64_t share_target_id,
TransferMetadata metadata, bool success);
// Called when InitiateBinding rpc returns.
void OnInitiateSyncBindingResponse(
int64_t share_target_id, absl::StatusOr<std::string> binding_status);
// Called when Bindings response frame is received from the peer.
void OnPeerSyncBindingComplete(
int64_t share_target_id, absl::string_view binding_id,
service::proto::BindingResponse::Status status);
// Notify all registered send surfaces of share target state changes.
void NotifyShareTargetDiscovered(const ShareTarget& share_target);
+208 -4
View File
@@ -115,6 +115,7 @@ using ::nearby::sharing::service::proto::PairedKeyResultFrame;
using ::nearby::sharing::service::proto::TextMetadata;
using ::nearby::sharing::service::proto::V1Frame;
using ::testing::_;
using ::protobuf_matchers::EqualsProto;
using ::testing::InSequence;
using ::testing::NiceMock;
using ::testing::Return;
@@ -842,19 +843,22 @@ class NearbySharingServiceImplTest : public testing::Test {
int64_t SetUpOutgoingShareTarget(
MockTransferUpdateCallback& transfer_callback,
MockShareTargetDiscoveredCallback& discovery_callback) {
MockShareTargetDiscoveredCallback& discovery_callback,
bool for_self_share = false) {
SetUpKeyVerification(
/*is_incoming=*/false, PairedKeyResultFrame::SUCCESS);
fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
GetToken());
fake_nearby_connections_manager_->set_nearby_connection(connection_.get());
return DiscoverShareTarget(transfer_callback, discovery_callback);
return DiscoverShareTarget(transfer_callback, discovery_callback,
for_self_share);
}
int64_t DiscoverShareTarget(
MockTransferUpdateCallback& transfer_callback,
MockShareTargetDiscoveredCallback& discovery_callback) {
MockShareTargetDiscoveredCallback& discovery_callback,
bool for_self_share = false) {
SetLanConnected(true);
// Start discovering, to ensure a discovery listener is registered.
@@ -876,7 +880,7 @@ class NearbySharingServiceImplTest : public testing::Test {
std::move(endpoint_info));
FlushTesting();
ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
/*success=*/true);
/*success=*/true, for_self_share);
return discovered_target_id;
}
@@ -4896,5 +4900,205 @@ TEST_F(NearbySharingServiceImplTest, NotifyLogoutSucceededWithCredentialError) {
FlushTesting();
}
TEST_F(NearbySharingServiceImplTest, InitiatePairingNotSelfShare) {
MockTransferUpdateCallback transfer_callback;
MockShareTargetDiscoveredCallback discovery_callback;
int64_t target_id = SetUpOutgoingShareTarget(
transfer_callback, discovery_callback, /*for_self_share=*/false);
ScopedSendSurface s(service_.get(), &transfer_callback);
absl::Notification notification;
ExpectTransferUpdates(transfer_callback, target_id,
{TransferMetadata::Status::kConnecting,
TransferMetadata::Status::kDeviceAuthenticationFailed},
[&] { notification.Notify(); });
absl::Notification pairing_notification;
NearbySharingServiceImpl::StatusCodes pairing_result;
EXPECT_CALL(*mock_app_info_, SetActiveFlag());
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());
// Wait for the transfer updates.
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
}
TEST_F(NearbySharingServiceImplTest, InitiatePairingBindingRpcFailed) {
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;
ExpectTransferUpdates(transfer_callback, target_id,
{TransferMetadata::Status::kConnecting,
TransferMetadata::Status::kFailed},
[&] { notification.Notify(); });
absl::Notification pairing_notification;
NearbySharingServiceImpl::StatusCodes pairing_result;
EXPECT_CALL(*mock_app_info_, SetActiveFlag());
nearby_identity_client_.SetInitiateBindingResponses(
{absl::InternalError("Binding RPC failed")});
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());
// Wait for the transfer updates.
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
}
TEST_F(NearbySharingServiceImplTest,
InitiatePairingPeerBindingResponseTimeout) {
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;
ExpectTransferUpdates(transfer_callback, target_id,
{TransferMetadata::Status::kConnecting,
TransferMetadata::Status::kAwaitingRemoteAcceptance,
TransferMetadata::Status::kFailed},
[&] { notification.Notify(); });
absl::Notification pairing_notification;
NearbySharingServiceImpl::StatusCodes pairing_result;
EXPECT_CALL(*mock_app_info_, SetActiveFlag());
constexpr absl::string_view kBindingId = "binding_id";
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.
if (!ExpectPairedKeyEncryptionFrame()) {
return;
}
if (!ExpectPairedKeyResultFrame()) {
return;
}
// Check BindingRequest frame sent to the remote device.
std::unique_ptr<Frame> 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);
// BindingResponse frame timeout.
FastForward(absl::Seconds(60));
// Wait for the transfer updates.
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
}
TEST_F(NearbySharingServiceImplTest, InitiatePairingSuccess) {
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;
ExpectTransferUpdates(transfer_callback, target_id,
{TransferMetadata::Status::kConnecting,
TransferMetadata::Status::kAwaitingRemoteAcceptance,
TransferMetadata::Status::kComplete},
[&] { notification.Notify(); });
absl::Notification pairing_notification;
NearbySharingServiceImpl::StatusCodes pairing_result;
EXPECT_CALL(*mock_app_info_, SetActiveFlag());
constexpr absl::string_view kBindingId = "binding_id";
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.
if (!ExpectPairedKeyEncryptionFrame()) {
return;
}
if (!ExpectPairedKeyResultFrame()) {
return;
}
// Check BindingRequest frame sent to the remote device.
std::unique_ptr<Frame> 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<uint8_t> result_bytes(binding_response_frame.ByteSizeLong());
binding_response_frame.SerializeToArray(result_bytes.data(),
result_bytes.size());
ReceiveMessageFromConnection(std::move(result_bytes));
// Wait for the transfer updates.
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
std::optional<nearby::sharing::sync::SyncBindingPrefs> 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());
EXPECT_THAT(binding->sync_bindings(0), EqualsProto(expected_binding));
}
} // namespace NearbySharingServiceUnitTests
} // namespace nearby::sharing