nearby_sharing_app.cc can now accept files from samsung quickshare

This commit is contained in:
Lasan Mahaliyana
2026-03-03 22:14:20 +05:30
parent f9a0e7deb8
commit 87e04e96fa
4 changed files with 612 additions and 56 deletions
+2
View File
@@ -163,9 +163,11 @@ cc_binary(
deps = [
":nearby_sharing_service_linux",
"//internal/platform:base",
"//internal/platform/implementation:account_manager",
"//sharing:attachments",
"//sharing:types",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
],
)
+157 -2
View File
@@ -19,14 +19,16 @@
#include <chrono>
#include <vector>
#include "absl/time/time.h"
#include "internal/platform/implementation/account_manager.h"
#include "sharing/linux/nearby_sharing_service_linux.h"
#include "sharing/attachment_container.h"
#include "sharing/file_attachment.h"
#include "sharing/text_attachment.h"
#include "sharing/share_target.h"
#include "sharing/text_attachment.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_update_callback.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
@@ -94,6 +96,9 @@ class MyTransferUpdateCallback : public TransferUpdateCallback {
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "║10. Sync credentials │11. Credential status ║" << std::endl;
std::cout << "║12. Visibility: Everyone │13. Visibility: Contacts ║" << std::endl;
std::cout << "║14. Visibility: Hidden │ ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
@@ -155,6 +160,9 @@ class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback {
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "║10. Sync credentials │11. Credential status ║" << std::endl;
std::cout << "║12. Visibility: Everyone │13. Visibility: Contacts ║" << std::endl;
std::cout << "║14. Visibility: Hidden │ ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
@@ -175,6 +183,8 @@ class NearbySharingApp {
}
void StartAsReceiver() {
PrepareCredentialFlow(/*for_receiver=*/true);
std::cout << "\n=== Starting as Receiver (Foreground) ===" << std::endl;
service_->RegisterReceiveSurface(
@@ -183,6 +193,8 @@ class NearbySharingApp {
Advertisement::BlockedVendorId::kNone,
[this](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
receive_surface_state_ =
NearbySharingService::ReceiveSurfaceState::kForeground;
std::cout << "Successfully registered as receiver!" << std::endl;
// Display QR code URL
@@ -200,6 +212,7 @@ class NearbySharingApp {
std::cout << "│ Scan this with your phone to connect! │" << std::endl;
std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl;
}
ForceCredentialSync("receiver surface registration");
} else {
std::cout << "Failed to register as receiver: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
@@ -210,6 +223,8 @@ class NearbySharingApp {
}
void StartAsSender() {
PrepareCredentialFlow(/*for_receiver=*/false);
std::cout << "\n=== Starting as Sender (Foreground) ===" << std::endl;
service_->RegisterSendSurface(
@@ -220,6 +235,7 @@ class NearbySharingApp {
false, // disable_wifi_hotspot
[this](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
send_surface_state_ = NearbySharingService::SendSurfaceState::kForeground;
std::cout << "Successfully registered as sender!" << std::endl;
// Display QR code URL
@@ -237,6 +253,7 @@ class NearbySharingApp {
std::cout << "│ Scan this with your phone to connect! │" << std::endl;
std::cout << "└──────────────────────────────────────────────────────────────┘" << std::endl;
}
ForceCredentialSync("sender surface registration");
} else {
std::cout << "Failed to register as sender: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
@@ -411,6 +428,46 @@ class NearbySharingApp {
std::cout << "======================" << std::endl;
}
void SyncCredentialsNow() { ForceCredentialSync("manual menu request"); }
void PrintCredentialStatus() {
std::cout << "\n=== Credential Flow Status ===" << std::endl;
auto* account_manager = service_->GetAccountManager();
if (account_manager == nullptr) {
std::cout << "Account manager: unavailable on this service implementation."
<< std::endl;
} else if (account_manager->GetCurrentAccount().has_value()) {
const auto& account = *account_manager->GetCurrentAccount();
std::cout << "Signed in account: " << account.email << std::endl;
std::cout << "Account id: " << account.id << std::endl;
} else {
std::cout << "Signed in account: none" << std::endl;
}
std::cout << "Certificate manager pointer: "
<< (service_->GetCertificateManager() != nullptr ? "available"
: "null")
<< std::endl;
std::cout << "Service dump:\n" << service_->Dump() << std::endl;
std::cout << "==============================" << std::endl;
}
void SetVisibilityEveryone() {
ApplyVisibility(proto::DeviceVisibility::DEVICE_VISIBILITY_EVERYONE,
absl::Minutes(15), "EVERYONE");
}
void SetVisibilityContacts() {
ApplyVisibility(proto::DeviceVisibility::DEVICE_VISIBILITY_ALL_CONTACTS,
absl::ZeroDuration(), "ALL_CONTACTS");
}
void SetVisibilityHidden() {
ApplyVisibility(proto::DeviceVisibility::DEVICE_VISIBILITY_HIDDEN,
absl::ZeroDuration(), "HIDDEN");
}
void Shutdown() {
std::cout << "\n=== Shutting Down ===" << std::endl;
service_->Shutdown([](NearbySharingService::StatusCodes status) {
@@ -420,9 +477,84 @@ class NearbySharingApp {
}
private:
void ApplyVisibility(proto::DeviceVisibility visibility,
absl::Duration expiration,
const std::string& label) {
service_->SetVisibility(
visibility, expiration,
[label](NearbySharingService::StatusCodes status) mutable {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Visibility updated to " << label << std::endl;
} else {
std::cout << "Failed to set visibility to " << label << ": "
<< NearbySharingService::StatusCodeToString(status)
<< std::endl;
}
});
}
void PrepareCredentialFlow(bool for_receiver) {
auto* account_manager = service_->GetAccountManager();
bool has_account = account_manager != nullptr &&
account_manager->GetCurrentAccount().has_value();
// Match NearbySharingServiceImpl behavior: outgoing paths are contacts-based
// when account data is available; receiver mode generally uses everyone.
if (for_receiver) {
SetVisibilityEveryone();
} else if (has_account) {
SetVisibilityContacts();
} else {
SetVisibilityEveryone();
}
}
void ForceCredentialSync(const std::string& reason) {
std::cout << "[Credential flow] Requesting sync via service hooks ("
<< reason << ")." << std::endl;
// Keep visibility explicitly valid for cert-backed advertising.
SetVisibilityEveryone();
// NearbySharingServiceImpl forces private cert upload from
// RegisterReceiveSurface when visibility is not hidden. Re-registering
// the current receive surface is enough to trigger that path.
auto state = receive_surface_state_.value_or(
NearbySharingService::ReceiveSurfaceState::kBackground);
service_->RegisterReceiveSurface(
transfer_callback_.get(), state, Advertisement::BlockedVendorId::kNone,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "[Credential flow] Receive surface refreshed for sync."
<< std::endl;
} else {
std::cout << "[Credential flow] Failed to refresh receive surface: "
<< NearbySharingService::StatusCodeToString(status)
<< std::endl;
}
});
// Discovery-triggered public cert download is internal to the full service.
if (send_surface_state_.has_value()) {
service_->RegisterSendSurface(
transfer_callback_.get(), discovery_callback_.get(),
*send_surface_state_, Advertisement::BlockedVendorId::kNone,
/*disable_wifi_hotspot=*/false,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "[Credential flow] Send surface refreshed for scan "
"side sync."
<< std::endl;
}
});
}
}
std::unique_ptr<NearbySharingServiceLinux> service_;
std::unique_ptr<MyTransferUpdateCallback> transfer_callback_;
std::unique_ptr<MyShareTargetDiscoveredCallback> discovery_callback_;
std::optional<NearbySharingService::ReceiveSurfaceState> receive_surface_state_;
std::optional<NearbySharingService::SendSurfaceState> send_surface_state_;
};
void PrintMenu() {
@@ -434,6 +566,9 @@ void PrintMenu() {
std::cout << "║ 3. List devices │ 8. Cancel transfer ║" << std::endl;
std::cout << "║ 4. Send file │ 9. Print status ║" << std::endl;
std::cout << "║ 5. Send text │ 0. Exit ║" << std::endl;
std::cout << "║10. Sync credentials │11. Credential status ║" << std::endl;
std::cout << "║12. Visibility: Everyone │13. Visibility: Contacts ║" << std::endl;
std::cout << "║14. Visibility: Hidden │ ║" << std::endl;
std::cout << "╚═════════════════════════════════════════════════════════╝" << std::endl;
std::cout << "Choice: ";
}
@@ -522,6 +657,26 @@ int main(int argc, char* argv[]) {
case 9:
app.PrintStatus();
break;
case 10:
app.SyncCredentialsNow();
break;
case 11:
app.PrintCredentialStatus();
break;
case 12:
app.SetVisibilityEveryone();
break;
case 13:
app.SetVisibilityContacts();
break;
case 14:
app.SetVisibilityHidden();
break;
case 0:
running = false;
+430 -54
View File
@@ -16,10 +16,12 @@
#include "sharing/proto/enums.pb.h"
#include <algorithm>
#include <array>
#include <cstdint>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
@@ -37,6 +39,7 @@
#include "internal/platform/file.h"
#include "internal/platform/logging.h"
#include "sharing/certificates/common.h"
#include "sharing/proto/wire_format.pb.h"
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
@@ -86,6 +89,231 @@ TransferMetadata::Status StatusFromPayloadStatus(
return TransferMetadata::Status::kUnknown;
}
using Frame = nearby::sharing::service::proto::Frame;
using V1Frame = nearby::sharing::service::proto::V1Frame;
using IntroductionFrame = nearby::sharing::service::proto::IntroductionFrame;
using ConnectionResponseFrame =
nearby::sharing::service::proto::ConnectionResponseFrame;
using PairedKeyEncryptionFrame =
nearby::sharing::service::proto::PairedKeyEncryptionFrame;
using PairedKeyResultFrame = nearby::sharing::service::proto::PairedKeyResultFrame;
constexpr size_t kPairedKeySignedDataSize = 72;
constexpr size_t kPairedKeySecretIdHashSize = 6;
bool IsControlFrameType(V1Frame::FrameType type) {
switch (type) {
case V1Frame::INTRODUCTION:
case V1Frame::RESPONSE:
case V1Frame::PAIRED_KEY_ENCRYPTION:
case V1Frame::PAIRED_KEY_RESULT:
case V1Frame::CANCEL:
return true;
default:
return false;
}
}
bool TryParseControlFrame(const connections::Payload& payload, Frame* frame) {
if (!frame || payload.GetType() != connections::PayloadType::kBytes) {
return false;
}
std::string bytes = payload.AsBytes().string_data();
if (!frame->ParseFromString(bytes) || !frame->has_v1() ||
frame->version() != Frame::V1 || !frame->v1().has_type()) {
return false;
}
return IsControlFrameType(frame->v1().type());
}
bool SendFramePayload(connections::Core* core, const std::string& endpoint_id,
auto& transfer_state, const Frame& frame) {
if (core == nullptr) {
return false;
}
std::string serialized;
if (!frame.SerializeToString(&serialized)) {
LOG(ERROR) << "Failed to serialize protocol frame.";
return false;
}
connections::Payload payload{nearby::ByteArray(serialized)};
transfer_state.control_payload_ids.insert(payload.GetId());
std::vector<std::string> endpoints{endpoint_id};
core->SendPayload(endpoints, std::move(payload), [](connections::Status) {});
return true;
}
Frame BuildPairedKeyEncryptionFrame() {
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::PAIRED_KEY_ENCRYPTION);
PairedKeyEncryptionFrame* encryption_frame =
v1_frame->mutable_paired_key_encryption();
std::vector<uint8_t> secret_hash = GenerateRandomBytes(kPairedKeySecretIdHashSize);
std::vector<uint8_t> signed_data = GenerateRandomBytes(kPairedKeySignedDataSize);
encryption_frame->set_secret_id_hash(secret_hash.data(), secret_hash.size());
encryption_frame->set_signed_data(signed_data.data(), signed_data.size());
return frame;
}
Frame BuildPairedKeyResultFrame(PairedKeyResultFrame::Status status) {
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::PAIRED_KEY_RESULT);
PairedKeyResultFrame* result_frame = v1_frame->mutable_paired_key_result();
result_frame->set_status(status);
result_frame->set_os_type(
location::nearby::proto::sharing::OSType::UNKNOWN_OS_TYPE);
return frame;
}
Frame BuildConnectionResponseFrame(ConnectionResponseFrame::Status status) {
Frame frame;
frame.set_version(Frame::V1);
V1Frame* v1_frame = frame.mutable_v1();
v1_frame->set_type(V1Frame::RESPONSE);
v1_frame->mutable_connection_response()->set_status(status);
return frame;
}
bool PrepareOutgoingIntroductionAndPayloads(auto& transfer_state,
IntroductionFrame* introduction) {
if (introduction == nullptr) {
return false;
}
transfer_state.pending_outgoing_payloads.clear();
transfer_state.attachment_payload_ids.clear();
transfer_state.completed_attachment_payload_ids.clear();
transfer_state.expected_attachment_payload_count = 0;
introduction->set_start_transfer(true);
const AttachmentContainer& attachments = transfer_state.attachments;
for (const auto& file : attachments.GetFileAttachments()) {
if (!file.file_path().has_value()) {
continue;
}
int64_t payload_id = connections::Payload::GenerateId();
auto* metadata = introduction->add_file_metadata();
metadata->set_id(file.id());
metadata->set_name(std::string(file.file_name()));
metadata->set_type(file.type());
metadata->set_payload_id(payload_id);
metadata->set_size(file.size());
metadata->set_mime_type(std::string(file.mime_type()));
metadata->set_parent_folder(std::string(file.parent_folder()));
nearby::InputFile input_file(file.file_path()->ToString());
transfer_state.pending_outgoing_payloads.emplace_back(
payload_id, std::string(file.parent_folder()),
std::string(file.file_name()), std::move(input_file));
transfer_state.attachment_payload_ids.insert(payload_id);
}
for (const auto& text : attachments.GetTextAttachments()) {
int64_t payload_id = connections::Payload::GenerateId();
auto* metadata = introduction->add_text_metadata();
metadata->set_id(text.id());
metadata->set_text_title(std::string(text.text_title()));
metadata->set_type(text.type());
metadata->set_payload_id(payload_id);
metadata->set_size(text.size());
std::string body = std::string(text.text_body());
transfer_state.pending_outgoing_payloads.emplace_back(
payload_id, nearby::ByteArray(body));
transfer_state.attachment_payload_ids.insert(payload_id);
}
transfer_state.expected_attachment_payload_count =
transfer_state.attachment_payload_ids.size();
return true;
}
void PopulateIncomingAttachmentsFromIntroduction(
auto& transfer_state, const IntroductionFrame& introduction) {
AttachmentContainer::Builder builder;
builder.ReserveAttachmentsCount(
introduction.text_metadata_size(), introduction.file_metadata_size(),
introduction.wifi_credentials_metadata_size());
transfer_state.text_attachment_id_by_payload_id.clear();
transfer_state.file_attachment_id_by_payload_id.clear();
transfer_state.attachment_payload_ids.clear();
transfer_state.completed_attachment_payload_ids.clear();
transfer_state.expected_attachment_payload_count = 0;
for (const auto& file : introduction.file_metadata()) {
builder.AddFileAttachment(FileAttachment(
file.id(), file.size(), file.name(), file.mime_type(), file.type(),
file.parent_folder()));
transfer_state.file_attachment_id_by_payload_id[file.payload_id()] =
file.id();
transfer_state.attachment_payload_ids.insert(file.payload_id());
}
for (const auto& text : introduction.text_metadata()) {
builder.AddTextAttachment(TextAttachment(
text.id(), text.type(), text.text_title(), text.size()));
transfer_state.text_attachment_id_by_payload_id[text.payload_id()] =
text.id();
transfer_state.attachment_payload_ids.insert(text.payload_id());
}
transfer_state.expected_attachment_payload_count =
transfer_state.attachment_payload_ids.size();
std::unique_ptr<AttachmentContainer> incoming = builder.Build();
transfer_state.attachments = std::move(*incoming);
}
void UpdateIncomingAttachmentPayload(auto& transfer_state,
connections::Payload& payload) {
if (payload.GetType() == connections::PayloadType::kBytes) {
auto text_it = transfer_state.text_attachment_id_by_payload_id.find(
payload.GetId());
if (text_it == transfer_state.text_attachment_id_by_payload_id.end()) {
return;
}
std::string body = payload.AsBytes().string_data();
auto& texts = transfer_state.attachments.GetTextAttachments();
for (int i = 0; i < texts.size(); ++i) {
if (texts[i].id() == text_it->second) {
transfer_state.attachments.GetMutableTextAttachment(i).set_text_body(
body);
return;
}
}
return;
}
if (payload.GetType() != connections::PayloadType::kFile) {
return;
}
auto file_it =
transfer_state.file_attachment_id_by_payload_id.find(payload.GetId());
if (file_it == transfer_state.file_attachment_id_by_payload_id.end()) {
return;
}
nearby::InputFile* input_file = payload.AsFile();
if (input_file == nullptr) {
return;
}
auto& files = transfer_state.attachments.GetFileAttachments();
for (int i = 0; i < files.size(); ++i) {
if (files[i].id() == file_it->second) {
transfer_state.attachments.GetMutableFileAttachment(i).set_file_path(
nearby::FilePath(input_file->GetFilePath()));
return;
}
}
}
} // namespace
NearbySharingServiceLinux::NearbySharingServiceLinux()
@@ -614,7 +842,7 @@ void NearbySharingServiceLinux::StopAdvertising() {
return;
}
is_advertising_ = false;
core_->StopAdvertising([this](connections::Status status) {
core_->StopAdvertising([](connections::Status status) {
static_cast<void>(status);
});
}
@@ -944,45 +1172,27 @@ void NearbySharingServiceLinux::HandleConnectionAccepted(
return;
}
TransferState& transfer_state = transfer_it->second;
auto share_target = GetShareTarget(endpoint_id);
if (share_target) {
TransferMetadata metadata = TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(0)
.set_total_attachments_count(
transfer_it->second.attachments.GetAttachmentCount())
.build();
NotifyTransferUpdate(*share_target, transfer_it->second, metadata);
TransferMetadata::Status status = is_incoming
? TransferMetadata::Status::kInProgress
: TransferMetadata::Status::kAwaitingRemoteAcceptance;
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(status)
.set_progress(0)
.set_total_attachments_count(
transfer_state.attachments.GetAttachmentCount())
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
}
if (!is_incoming) {
const AttachmentContainer& attachments = transfer_it->second.attachments;
std::unique_ptr<connections::Payload> payload;
if (!attachments.GetTextAttachments().empty()) {
std::string text =
std::string(attachments.GetTextAttachments()[0].text_body());
payload = std::make_unique<connections::Payload>(ByteArray(text));
} else if (!attachments.GetFileAttachments().empty()) {
const auto& file_attachment = attachments.GetFileAttachments()[0];
if (file_attachment.file_path().has_value()) {
std::string file_path = file_attachment.file_path()->ToString();
nearby::InputFile input_file(file_path);
payload = std::make_unique<connections::Payload>(
std::string(file_attachment.parent_folder()),
std::string(file_attachment.file_name()), std::move(input_file));
}
}
if (payload) {
std::vector<std::string> endpoints;
endpoints.push_back(endpoint_id);
core_->SendPayload(
endpoints, std::move(*payload),
[this](connections::Status status) {
if (!status.Ok()) {
is_transferring_ = false;
}
});
// Start minimal Nearby Share frame exchange: PAIRED_KEY_ENCRYPTION.
if (!transfer_state.paired_key_encryption_sent) {
Frame frame = BuildPairedKeyEncryptionFrame();
if (SendFramePayload(core_.get(), endpoint_id, transfer_state, frame)) {
transfer_state.paired_key_encryption_sent = true;
}
}
@@ -1023,8 +1233,7 @@ connections::PayloadListener NearbySharingServiceLinux::MakePayloadListener(
static_cast<void>(is_incoming);
connections::PayloadListener listener;
listener.payload_cb =
[this, is_incoming](absl::string_view endpoint_id,
connections::Payload payload) {
[this](absl::string_view endpoint_id, connections::Payload payload) {
auto transfer_it = active_transfers_.find(std::string(endpoint_id));
if (transfer_it == active_transfers_.end()) {
return;
@@ -1034,17 +1243,145 @@ connections::PayloadListener NearbySharingServiceLinux::MakePayloadListener(
return;
}
TransferMetadata metadata = TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(0)
.build();
NotifyTransferUpdate(*share_target, transfer_it->second, metadata);
TransferState& transfer_state = transfer_it->second;
Frame frame;
if (TryParseControlFrame(payload, &frame)) {
transfer_state.control_payload_ids.insert(payload.GetId());
const V1Frame& v1 = frame.v1();
switch (v1.type()) {
case V1Frame::PAIRED_KEY_ENCRYPTION: {
transfer_state.paired_key_encryption_received = true;
if (!transfer_state.paired_key_encryption_sent) {
Frame local_encryption = BuildPairedKeyEncryptionFrame();
if (SendFramePayload(core_.get(), std::string(endpoint_id),
transfer_state, local_encryption)) {
transfer_state.paired_key_encryption_sent = true;
}
}
if (!transfer_state.paired_key_result_sent) {
Frame result = BuildPairedKeyResultFrame(
PairedKeyResultFrame::UNABLE);
if (SendFramePayload(core_.get(), std::string(endpoint_id),
transfer_state, result)) {
transfer_state.paired_key_result_sent = true;
}
}
break;
}
case V1Frame::PAIRED_KEY_RESULT:
transfer_state.paired_key_result_received = true;
break;
case V1Frame::INTRODUCTION: {
transfer_state.introduction_received = true;
if (transfer_state.is_incoming && v1.has_introduction()) {
PopulateIncomingAttachmentsFromIntroduction(
transfer_state, v1.introduction());
if (!transfer_state.connection_response_sent) {
Frame response =
BuildConnectionResponseFrame(ConnectionResponseFrame::ACCEPT);
if (SendFramePayload(core_.get(), std::string(endpoint_id),
transfer_state, response)) {
transfer_state.connection_response_sent = true;
transfer_state.connection_response_accepted = true;
}
}
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kInProgress)
.set_progress(0)
.set_total_attachments_count(
transfer_state.attachments.GetAttachmentCount())
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
}
break;
}
case V1Frame::RESPONSE: {
if (!transfer_state.is_incoming && v1.has_connection_response()) {
transfer_state.connection_response_received = true;
transfer_state.connection_response_accepted =
v1.connection_response().status() ==
ConnectionResponseFrame::ACCEPT;
if (!transfer_state.connection_response_accepted) {
TransferMetadata::Status rejected_status =
TransferMetadata::Status::kRejected;
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(rejected_status)
.set_progress(0)
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
active_transfers_.erase(transfer_it);
if (active_transfers_.empty()) {
is_transferring_ = false;
}
return;
}
}
break;
}
case V1Frame::CANCEL: {
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kCancelled)
.set_progress(0)
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
active_transfers_.erase(transfer_it);
if (active_transfers_.empty()) {
is_transferring_ = false;
}
return;
}
default:
break;
}
// Sender-side progression:
if (!transfer_state.is_incoming &&
transfer_state.paired_key_encryption_sent &&
transfer_state.paired_key_encryption_received &&
transfer_state.paired_key_result_sent &&
transfer_state.paired_key_result_received &&
!transfer_state.introduction_sent) {
Frame introduction_frame;
introduction_frame.set_version(Frame::V1);
V1Frame* out_v1 = introduction_frame.mutable_v1();
out_v1->set_type(V1Frame::INTRODUCTION);
if (PrepareOutgoingIntroductionAndPayloads(
transfer_state, out_v1->mutable_introduction()) &&
SendFramePayload(core_.get(), std::string(endpoint_id),
transfer_state, introduction_frame)) {
transfer_state.introduction_sent = true;
}
}
if (!transfer_state.is_incoming && transfer_state.introduction_sent &&
transfer_state.connection_response_received &&
transfer_state.connection_response_accepted &&
!transfer_state.attachment_payloads_sent) {
transfer_state.attachment_payloads_sent = true;
std::vector<std::string> endpoints{std::string(endpoint_id)};
for (auto& data_payload : transfer_state.pending_outgoing_payloads) {
transfer_state.attachment_payload_ids.insert(data_payload.GetId());
core_->SendPayload(endpoints, std::move(data_payload),
[](connections::Status) {});
}
transfer_state.pending_outgoing_payloads.clear();
}
return;
}
// Non-control payloads are attachment payloads.
UpdateIncomingAttachmentPayload(transfer_state, payload);
};
listener.payload_progress_cb =
[this, is_incoming](absl::string_view endpoint_id,
const connections::PayloadProgressInfo& info) {
auto transfer_it = active_transfers_.find(std::string(endpoint_id));
[this](absl::string_view endpoint_id,
const connections::PayloadProgressInfo& info) {
std::string endpoint_id_string(endpoint_id);
auto transfer_it = active_transfers_.find(endpoint_id_string);
if (transfer_it == active_transfers_.end()) {
return;
}
@@ -1053,22 +1390,61 @@ connections::PayloadListener NearbySharingServiceLinux::MakePayloadListener(
return;
}
float progress = 0.0f;
if (info.total_bytes > 0) {
progress = static_cast<float>(info.bytes_transferred) /
static_cast<float>(info.total_bytes);
TransferState& transfer_state = transfer_it->second;
if (transfer_state.control_payload_ids.contains(info.payload_id)) {
return;
}
if (transfer_state.expected_attachment_payload_count == 0) {
return;
}
if (!transfer_state.attachment_payload_ids.contains(info.payload_id)) {
return;
}
size_t completed = transfer_state.completed_attachment_payload_ids.size();
float progress = static_cast<float>(completed) /
static_cast<float>(
transfer_state.expected_attachment_payload_count);
TransferMetadata::Status status = TransferMetadata::Status::kInProgress;
if (info.status == connections::PayloadProgressInfo::Status::kInProgress) {
if (info.total_bytes > 0) {
float payload_progress = static_cast<float>(info.bytes_transferred) /
static_cast<float>(info.total_bytes);
progress =
(static_cast<float>(completed) + payload_progress) /
static_cast<float>(transfer_state.expected_attachment_payload_count);
}
} else if (info.status ==
connections::PayloadProgressInfo::Status::kSuccess) {
transfer_state.completed_attachment_payload_ids.insert(info.payload_id);
completed = transfer_state.completed_attachment_payload_ids.size();
progress = static_cast<float>(completed) /
static_cast<float>(
transfer_state.expected_attachment_payload_count);
status = completed >= transfer_state.expected_attachment_payload_count
? TransferMetadata::Status::kComplete
: TransferMetadata::Status::kInProgress;
} else {
status = StatusFromPayloadStatus(info.status);
}
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(StatusFromPayloadStatus(info.status))
.set_progress(progress)
.set_status(status)
.set_progress(std::min(progress, 1.0f))
.set_transferred_bytes(info.bytes_transferred)
.set_total_attachments_count(
static_cast<int>(
transfer_state.expected_attachment_payload_count))
.build();
NotifyTransferUpdate(*share_target, transfer_it->second, metadata);
NotifyTransferUpdate(*share_target, transfer_state, metadata);
if (TransferMetadata::IsFinalStatus(metadata.status())) {
if (metadata.status() == TransferMetadata::Status::kComplete) {
core_->DisconnectFromEndpoint(endpoint_id_string,
[](connections::Status) {});
}
active_transfers_.erase(transfer_it);
if (active_transfers_.empty()) {
is_transferring_ = false;
@@ -15,6 +15,7 @@
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_SHARING_SERVICE_LINUX_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_SHARING_SERVICE_LINUX_H_
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
@@ -145,6 +146,28 @@ class NearbySharingServiceLinux : public NearbySharingService {
nearby::sharing::AttachmentContainer attachments;
TransferUpdateCallback* callback = nullptr;
bool is_incoming = false;
// Minimal Nearby Share frame protocol state.
bool paired_key_encryption_sent = false;
bool paired_key_encryption_received = false;
bool paired_key_result_sent = false;
bool paired_key_result_received = false;
bool introduction_sent = false;
bool introduction_received = false;
bool connection_response_sent = false;
bool connection_response_received = false;
bool connection_response_accepted = false;
bool attachment_payloads_sent = false;
// Payload routing state.
std::unordered_set<int64_t> control_payload_ids;
std::unordered_set<int64_t> attachment_payload_ids;
std::unordered_set<int64_t> completed_attachment_payload_ids;
std::unordered_map<int64_t, int64_t> text_attachment_id_by_payload_id;
std::unordered_map<int64_t, int64_t> file_attachment_id_by_payload_id;
size_t expected_attachment_payload_count = 0;
bool transfer_complete_notified = false;
std::vector<connections::Payload> pending_outgoing_payloads;
};
struct ParsedAdvertisement {