mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
nearby_sharing_app.cc can now accept files from samsung quickshare
This commit is contained in:
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user