mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
removed ipc server and daemon service. added dbus sharing service
This commit is contained in:
+13
-34
@@ -6,8 +6,7 @@ cc_binary(
|
||||
name = "nearby_sharing_daemon",
|
||||
srcs = ["main.cc"],
|
||||
deps = [
|
||||
":daemon_service",
|
||||
":ipc_server",
|
||||
":nearby_sharing_dbus_service",
|
||||
"//connections/implementation/flags:connections_flags",
|
||||
"//internal/flags:nearby_flags",
|
||||
"//sharing:nearby_sharing_service",
|
||||
@@ -15,14 +14,17 @@ cc_binary(
|
||||
"//sharing/linux:linux_sharing_platform",
|
||||
"//sharing/proto:enums_cc_proto",
|
||||
"@com_google_absl//absl/time",
|
||||
"@nlohmann_json//:json",
|
||||
"@sdbus_cpp",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "daemon_service",
|
||||
srcs = ["daemon_service.cc"],
|
||||
hdrs = ["daemon_service.h"],
|
||||
name = "nearby_sharing_dbus_service",
|
||||
srcs = ["nearby_sharing_dbus_service.cc"],
|
||||
hdrs = [
|
||||
"nearby_sharing_dbus_service.h",
|
||||
"nearby_sharing_server.h",
|
||||
],
|
||||
deps = [
|
||||
"//internal/base:file_path",
|
||||
"//sharing:attachments",
|
||||
@@ -30,43 +32,20 @@ cc_library(
|
||||
"//sharing:transfer_metadata",
|
||||
"//sharing:types",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@nlohmann_json//:json",
|
||||
"@sdbus_cpp",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "daemon_service_test",
|
||||
srcs = ["daemon_service_test.cc"],
|
||||
name = "nearby_sharing_dbus_service_test",
|
||||
srcs = ["nearby_sharing_dbus_service_test.cc"],
|
||||
deps = [
|
||||
":daemon_service",
|
||||
":nearby_sharing_dbus_service",
|
||||
"//internal/platform/implementation/linux:linux",
|
||||
"//sharing:attachments",
|
||||
"//sharing:nearby_sharing_service",
|
||||
"//sharing:transfer_metadata",
|
||||
"//sharing:types",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
"@nlohmann_json//:json",
|
||||
"@sdbus_cpp",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "ipc_server_test",
|
||||
srcs = ["ipc_server_test.cc"],
|
||||
deps = [
|
||||
":ipc_server",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@nlohmann_json//:json",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "ipc_server",
|
||||
hdrs = ["ipc_server.h"],
|
||||
srcs = ["ipc_server.cc"],
|
||||
deps = [
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@nlohmann_json//:json",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
#include "sharing/linux/daemon/daemon_service.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "sharing/advertisement.h"
|
||||
#include "sharing/file_attachment.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace {
|
||||
|
||||
nlohmann::json CommandResult(std::string command, bool ok,
|
||||
std::string message) {
|
||||
return nlohmann::json{{"event", "command_result"},
|
||||
{"command", std::move(command)},
|
||||
{"ok", ok},
|
||||
{"message", std::move(message)}};
|
||||
}
|
||||
|
||||
std::string StatusCodeToString(NearbySharingService::StatusCodes status) {
|
||||
return NearbySharingService::StatusCodeToString(status);
|
||||
}
|
||||
|
||||
template <typename Invoker>
|
||||
NearbySharingService::StatusCodes WaitForStatus(Invoker invoker) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::optional<NearbySharingService::StatusCodes> status;
|
||||
invoker([&](NearbySharingService::StatusCodes callback_status) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
status = callback_status;
|
||||
}
|
||||
cv.notify_one();
|
||||
});
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return status.has_value(); });
|
||||
return *status;
|
||||
}
|
||||
|
||||
std::unique_ptr<AttachmentContainer> CreateFileAttachments(
|
||||
const std::string& file_path) {
|
||||
AttachmentContainer::Builder builder;
|
||||
builder.AddFileAttachment(FileAttachment(FilePath(file_path)));
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
nlohmann::json ShareTargetToJson(const ShareTarget& share_target) {
|
||||
return nlohmann::json{
|
||||
{"id", share_target.id},
|
||||
{"device_name", share_target.device_name},
|
||||
{"type", static_cast<int>(share_target.type)},
|
||||
{"is_incoming", share_target.is_incoming},
|
||||
{"is_known", share_target.is_known},
|
||||
{"device_id", share_target.device_id},
|
||||
{"for_self_share", share_target.for_self_share},
|
||||
{"vendor_id", share_target.vendor_id},
|
||||
{"receive_disabled", share_target.receive_disabled},
|
||||
};
|
||||
}
|
||||
|
||||
nlohmann::json TransferMetadataToJson(
|
||||
const TransferMetadata& transfer_metadata,
|
||||
const AttachmentContainer& attachment_container) {
|
||||
nlohmann::json result{
|
||||
{"status", TransferMetadata::StatusToString(transfer_metadata.status())},
|
||||
{"progress", transfer_metadata.progress()},
|
||||
{"transferred_bytes", transfer_metadata.transferred_bytes()},
|
||||
{"total_bytes", attachment_container.GetTotalAttachmentsSize()},
|
||||
{"transfer_speed", transfer_metadata.transfer_speed()},
|
||||
{"estimated_time_remaining",
|
||||
transfer_metadata.estimated_time_remaining()},
|
||||
{"total_attachments_count", transfer_metadata.total_attachments_count()},
|
||||
{"transferred_attachments_count",
|
||||
transfer_metadata.transferred_attachments_count()},
|
||||
{"is_final_status", transfer_metadata.is_final_status()},
|
||||
{"is_self_share", transfer_metadata.is_self_share()},
|
||||
{"binding_id", transfer_metadata.binding_id()},
|
||||
};
|
||||
if (transfer_metadata.token().has_value()) {
|
||||
result["token"] = *transfer_metadata.token();
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_id().has_value()) {
|
||||
result["in_progress_attachment_id"] =
|
||||
*transfer_metadata.in_progress_attachment_id();
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_transferred_bytes()
|
||||
.has_value()) {
|
||||
result["in_progress_attachment_transferred_bytes"] =
|
||||
*transfer_metadata.in_progress_attachment_transferred_bytes();
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_total_bytes().has_value()) {
|
||||
result["in_progress_attachment_total_bytes"] =
|
||||
*transfer_metadata.in_progress_attachment_total_bytes();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DaemonService::DaemonService(NearbySharingService& service,
|
||||
EventSink event_sink)
|
||||
: service_(service),
|
||||
event_sink_(std::move(event_sink)),
|
||||
send_transfer_callback_(*this, /*receive_mode=*/false),
|
||||
receive_transfer_callback_(*this, /*receive_mode=*/true),
|
||||
discovery_callback_(*this) {}
|
||||
|
||||
DaemonService::~DaemonService() { Shutdown(); }
|
||||
|
||||
nlohmann::json DaemonService::HandleCommand(const nlohmann::json& request) {
|
||||
if (!request.is_object() || !request.contains("command") ||
|
||||
!request["command"].is_string()) {
|
||||
return CommandResult("", false, "missing string field: command");
|
||||
}
|
||||
std::string command = request["command"].get<std::string>();
|
||||
if (command == "status") {
|
||||
return Status();
|
||||
}
|
||||
if (command == "start_receive") {
|
||||
return StartReceive();
|
||||
}
|
||||
if (command == "stop_receive") {
|
||||
return StopReceive();
|
||||
}
|
||||
if (command == "start_discovery") {
|
||||
return StartDiscovery();
|
||||
}
|
||||
if (command == "stop_discovery") {
|
||||
return StopDiscovery();
|
||||
}
|
||||
if (command == "send_file") {
|
||||
return SendFile(request);
|
||||
}
|
||||
if (command == "accept") {
|
||||
return Accept(request);
|
||||
}
|
||||
if (command == "reject") {
|
||||
return Reject(request);
|
||||
}
|
||||
if (command == "cancel") {
|
||||
return Cancel(request);
|
||||
}
|
||||
if (command == "shutdown") {
|
||||
Shutdown();
|
||||
return CommandResult(command, true, "daemon service shut down");
|
||||
}
|
||||
return CommandResult(command, false, "unknown command");
|
||||
}
|
||||
|
||||
void DaemonService::Shutdown() {
|
||||
bool stop_receive = false;
|
||||
bool stop_discovery = false;
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (shutdown_) {
|
||||
return;
|
||||
}
|
||||
shutdown_ = true;
|
||||
stop_receive = receive_registered_;
|
||||
stop_discovery = discovery_registered_;
|
||||
receive_registered_ = false;
|
||||
discovery_registered_ = false;
|
||||
targets_.clear();
|
||||
active_transfers_.clear();
|
||||
}
|
||||
|
||||
if (stop_receive) {
|
||||
WaitForStatus([&](auto callback) {
|
||||
service_.UnregisterReceiveSurface(&receive_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
if (stop_discovery) {
|
||||
WaitForStatus([&](auto callback) {
|
||||
service_.UnregisterSendSurface(&send_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
WaitForStatus([&](auto callback) { service_.Shutdown(std::move(callback)); });
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::StartReceive() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (receive_registered_) {
|
||||
return CommandResult("start_receive", true, "receive already started");
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand("start_receive", [&](auto callback) {
|
||||
service_.RegisterReceiveSurface(
|
||||
&receive_transfer_callback_,
|
||||
NearbySharingService::ReceiveSurfaceState::kForeground,
|
||||
Advertisement::BlockedVendorId::kNone, std::move(callback));
|
||||
});
|
||||
if (result["ok"].get<bool>()) {
|
||||
absl::MutexLock lock(lock_);
|
||||
receive_registered_ = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::StopReceive() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (!receive_registered_) {
|
||||
return CommandResult("stop_receive", true, "receive already stopped");
|
||||
}
|
||||
receive_registered_ = false;
|
||||
}
|
||||
|
||||
return InvokeStatusCommand("stop_receive", [&](auto callback) {
|
||||
service_.UnregisterReceiveSurface(&receive_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::StartDiscovery() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (discovery_registered_) {
|
||||
return CommandResult("start_discovery", true, "discovery already started");
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand("start_discovery", [&](auto callback) {
|
||||
service_.RegisterSendSurface(
|
||||
&send_transfer_callback_, &discovery_callback_,
|
||||
NearbySharingService::SendSurfaceState::kForeground,
|
||||
Advertisement::BlockedVendorId::kNone,
|
||||
/*disable_wifi_hotspot=*/false, std::move(callback));
|
||||
});
|
||||
if (result["ok"].get<bool>()) {
|
||||
absl::MutexLock lock(lock_);
|
||||
discovery_registered_ = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::StopDiscovery() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (!discovery_registered_) {
|
||||
return CommandResult("stop_discovery", true, "discovery already stopped");
|
||||
}
|
||||
discovery_registered_ = false;
|
||||
targets_.clear();
|
||||
}
|
||||
|
||||
return InvokeStatusCommand("stop_discovery", [&](auto callback) {
|
||||
service_.UnregisterSendSurface(&send_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::SendFile(const nlohmann::json& request) {
|
||||
nlohmann::json error;
|
||||
std::optional<int64_t> share_target_id = GetRequiredTargetId(request, error);
|
||||
if (!share_target_id.has_value()) {
|
||||
error["command"] = "send_file";
|
||||
return error;
|
||||
}
|
||||
if (!request.contains("path") || !request["path"].is_string()) {
|
||||
return CommandResult("send_file", false, "missing string field: path");
|
||||
}
|
||||
|
||||
std::string path = request["path"].get<std::string>();
|
||||
std::error_code file_error;
|
||||
if (!std::filesystem::is_regular_file(path, file_error)) {
|
||||
return CommandResult("send_file", false, "path is not a regular file");
|
||||
}
|
||||
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (targets_.find(*share_target_id) == targets_.end()) {
|
||||
return CommandResult("send_file", false, "unknown share_target_id");
|
||||
}
|
||||
}
|
||||
|
||||
auto attachments = CreateFileAttachments(path);
|
||||
return InvokeStatusCommand("send_file", [&](auto callback) {
|
||||
service_.SendAttachments(*share_target_id, std::move(attachments),
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::Accept(const nlohmann::json& request) {
|
||||
nlohmann::json error;
|
||||
std::optional<int64_t> share_target_id = GetRequiredTargetId(request, error);
|
||||
if (!share_target_id.has_value()) {
|
||||
error["command"] = "accept";
|
||||
return error;
|
||||
}
|
||||
return InvokeStatusCommand("accept", [&](auto callback) {
|
||||
service_.Accept(*share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::Reject(const nlohmann::json& request) {
|
||||
nlohmann::json error;
|
||||
std::optional<int64_t> share_target_id = GetRequiredTargetId(request, error);
|
||||
if (!share_target_id.has_value()) {
|
||||
error["command"] = "reject";
|
||||
return error;
|
||||
}
|
||||
return InvokeStatusCommand("reject", [&](auto callback) {
|
||||
service_.Reject(*share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::Cancel(const nlohmann::json& request) {
|
||||
nlohmann::json error;
|
||||
std::optional<int64_t> share_target_id = GetRequiredTargetId(request, error);
|
||||
if (!share_target_id.has_value()) {
|
||||
error["command"] = "cancel";
|
||||
return error;
|
||||
}
|
||||
return InvokeStatusCommand("cancel", [&](auto callback) {
|
||||
service_.Cancel(*share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::Status() {
|
||||
nlohmann::json targets = nlohmann::json::array();
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
for (const auto& [id, target] : targets_) {
|
||||
static_cast<void>(id);
|
||||
targets.push_back(ShareTargetToJson(target));
|
||||
}
|
||||
return nlohmann::json{{"event", "command_result"},
|
||||
{"command", "status"},
|
||||
{"ok", true},
|
||||
{"receive_registered", receive_registered_},
|
||||
{"discovery_registered", discovery_registered_},
|
||||
{"is_transferring", service_.IsTransferring()},
|
||||
{"is_scanning", service_.IsScanning()},
|
||||
{"bluetooth_present", service_.IsBluetoothPresent()},
|
||||
{"bluetooth_powered", service_.IsBluetoothPowered()},
|
||||
{"lan_connected", service_.IsLanConnected()},
|
||||
{"targets", std::move(targets)}};
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonService::OnTransferUpdate(
|
||||
bool receive_mode, const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
active_transfers_[share_target.id] = share_target;
|
||||
if (TransferMetadata::IsFinalStatus(transfer_metadata.status())) {
|
||||
active_transfers_.erase(share_target.id);
|
||||
}
|
||||
}
|
||||
|
||||
std::string event =
|
||||
receive_mode &&
|
||||
transfer_metadata.status() ==
|
||||
TransferMetadata::Status::kAwaitingLocalConfirmation
|
||||
? "incoming_transfer"
|
||||
: "transfer_update";
|
||||
Emit(nlohmann::json{
|
||||
{"event", event},
|
||||
{"direction", receive_mode ? "receive" : "send"},
|
||||
{"share_target", ShareTargetToJson(share_target)},
|
||||
{"transfer",
|
||||
TransferMetadataToJson(transfer_metadata, attachment_container)},
|
||||
});
|
||||
}
|
||||
|
||||
void DaemonService::OnShareTargetDiscovered(const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_[share_target.id] = share_target;
|
||||
}
|
||||
Emit(nlohmann::json{{"event", "target_discovered"},
|
||||
{"share_target", ShareTargetToJson(share_target)}});
|
||||
}
|
||||
|
||||
void DaemonService::OnShareTargetUpdated(const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_[share_target.id] = share_target;
|
||||
}
|
||||
Emit(nlohmann::json{{"event", "target_updated"},
|
||||
{"share_target", ShareTargetToJson(share_target)}});
|
||||
}
|
||||
|
||||
void DaemonService::OnShareTargetLost(const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_.erase(share_target.id);
|
||||
}
|
||||
Emit(nlohmann::json{{"event", "target_lost"},
|
||||
{"share_target", ShareTargetToJson(share_target)}});
|
||||
}
|
||||
|
||||
nlohmann::json DaemonService::InvokeStatusCommand(
|
||||
const std::string& command,
|
||||
std::function<void(std::function<void(NearbySharingService::StatusCodes)>)>
|
||||
invoker) {
|
||||
NearbySharingService::StatusCodes status = WaitForStatus(std::move(invoker));
|
||||
bool ok = status == NearbySharingService::StatusCodes::kOk;
|
||||
return CommandResult(command, ok, StatusCodeToString(status));
|
||||
}
|
||||
|
||||
std::optional<int64_t> DaemonService::GetRequiredTargetId(
|
||||
const nlohmann::json& request, nlohmann::json& error) const {
|
||||
if (!request.contains("share_target_id") ||
|
||||
!request["share_target_id"].is_number_integer()) {
|
||||
error = CommandResult("", false, "missing integer field: share_target_id");
|
||||
return std::nullopt;
|
||||
}
|
||||
return request["share_target_id"].get<int64_t>();
|
||||
}
|
||||
|
||||
void DaemonService::Emit(nlohmann::json event) {
|
||||
if (event_sink_) {
|
||||
event_sink_(std::move(event));
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonService::TransferCallback::OnTransferUpdate(
|
||||
const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) {
|
||||
daemon_.OnTransferUpdate(receive_mode_, share_target, attachment_container,
|
||||
transfer_metadata);
|
||||
}
|
||||
|
||||
void DaemonService::DiscoveryCallback::OnShareTargetDiscovered(
|
||||
const ShareTarget& share_target) {
|
||||
daemon_.OnShareTargetDiscovered(share_target);
|
||||
}
|
||||
|
||||
void DaemonService::DiscoveryCallback::OnShareTargetLost(
|
||||
const ShareTarget& share_target) {
|
||||
daemon_.OnShareTargetLost(share_target);
|
||||
}
|
||||
|
||||
void DaemonService::DiscoveryCallback::OnShareTargetUpdated(
|
||||
const ShareTarget& share_target) {
|
||||
daemon_.OnShareTargetUpdated(share_target);
|
||||
}
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
@@ -1,172 +0,0 @@
|
||||
#ifndef SHARING_LINUX_DAEMON_DAEMON_SERVICE_H_
|
||||
#define SHARING_LINUX_DAEMON_DAEMON_SERVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "sharing/attachment_container.h"
|
||||
#include "sharing/nearby_sharing_service.h"
|
||||
#include "sharing/share_target.h"
|
||||
#include "sharing/share_target_discovered_callback.h"
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_update_callback.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
|
||||
// Owns the daemon-facing state and translates JSON IPC commands into
|
||||
// NearbySharingService operations. This class does not own `service`; callers
|
||||
// must keep the service alive for the lifetime of DaemonService.
|
||||
class DaemonService {
|
||||
public:
|
||||
// Receives asynchronous daemon events, such as discovered targets and
|
||||
// transfer updates, serialized as JSON objects.
|
||||
using EventSink = std::function<void(const nlohmann::json& event)>;
|
||||
|
||||
// Creates a daemon adapter around `service` and publishes asynchronous events
|
||||
// to `event_sink`.
|
||||
DaemonService(NearbySharingService& service, EventSink event_sink);
|
||||
|
||||
// Stops registered surfaces and shuts down the wrapped Nearby service.
|
||||
~DaemonService();
|
||||
|
||||
DaemonService(const DaemonService&) = delete;
|
||||
DaemonService& operator=(const DaemonService&) = delete;
|
||||
|
||||
// Handles one JSON IPC command and returns a command_result JSON object.
|
||||
// The request must contain a string `command` field.
|
||||
nlohmann::json HandleCommand(const nlohmann::json& request);
|
||||
|
||||
// Idempotently unregisters active surfaces, clears daemon state, and shuts
|
||||
// down the wrapped Nearby service.
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
// Receives send or receive transfer updates from NearbySharingService and
|
||||
// forwards them back into DaemonService with direction context.
|
||||
class TransferCallback final : public TransferUpdateCallback {
|
||||
public:
|
||||
// Creates a transfer callback. `receive_mode` distinguishes receive events
|
||||
// from send events when serializing daemon events.
|
||||
explicit TransferCallback(DaemonService& daemon, bool receive_mode)
|
||||
: daemon_(daemon), receive_mode_(receive_mode) {}
|
||||
|
||||
// Forwards a Nearby transfer update to the daemon event pipeline.
|
||||
void OnTransferUpdate(const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) override;
|
||||
|
||||
private:
|
||||
DaemonService& daemon_;
|
||||
bool receive_mode_;
|
||||
};
|
||||
|
||||
// Receives discovered target lifecycle events from NearbySharingService and
|
||||
// forwards them back into DaemonService.
|
||||
class DiscoveryCallback final : public ShareTargetDiscoveredCallback {
|
||||
public:
|
||||
// Creates a discovery callback bound to `daemon`.
|
||||
explicit DiscoveryCallback(DaemonService& daemon) : daemon_(daemon) {}
|
||||
|
||||
// Records and emits a newly discovered share target.
|
||||
void OnShareTargetDiscovered(const ShareTarget& share_target) override;
|
||||
|
||||
// Removes and emits a lost share target.
|
||||
void OnShareTargetLost(const ShareTarget& share_target) override;
|
||||
|
||||
// Updates and emits an existing share target.
|
||||
void OnShareTargetUpdated(const ShareTarget& share_target) override;
|
||||
|
||||
private:
|
||||
DaemonService& daemon_;
|
||||
};
|
||||
|
||||
// Registers the foreground receive surface used for incoming transfers.
|
||||
nlohmann::json StartReceive();
|
||||
|
||||
// Unregisters the foreground receive surface if it is active.
|
||||
nlohmann::json StopReceive();
|
||||
|
||||
// Registers the foreground send surface used for discovery and outgoing
|
||||
// transfer updates.
|
||||
nlohmann::json StartDiscovery();
|
||||
|
||||
// Unregisters the foreground send surface and clears discovered targets.
|
||||
nlohmann::json StopDiscovery();
|
||||
|
||||
// Sends the regular file named by request field `path` to request field
|
||||
// `share_target_id`.
|
||||
nlohmann::json SendFile(const nlohmann::json& request);
|
||||
|
||||
// Accepts an incoming share identified by request field `share_target_id`.
|
||||
nlohmann::json Accept(const nlohmann::json& request);
|
||||
|
||||
// Rejects an incoming share identified by request field `share_target_id`.
|
||||
nlohmann::json Reject(const nlohmann::json& request);
|
||||
|
||||
// Cancels a transfer identified by request field `share_target_id`.
|
||||
nlohmann::json Cancel(const nlohmann::json& request);
|
||||
|
||||
// Returns current daemon/service state, including registered surfaces and
|
||||
// discovered targets.
|
||||
nlohmann::json Status();
|
||||
|
||||
// Updates active transfer state and emits either `incoming_transfer` or
|
||||
// `transfer_update`.
|
||||
void OnTransferUpdate(bool receive_mode, const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata);
|
||||
|
||||
// Stores and emits a newly discovered target.
|
||||
void OnShareTargetDiscovered(const ShareTarget& share_target);
|
||||
|
||||
// Stores and emits an updated target.
|
||||
void OnShareTargetUpdated(const ShareTarget& share_target);
|
||||
|
||||
// Removes and emits a lost target.
|
||||
void OnShareTargetLost(const ShareTarget& share_target);
|
||||
|
||||
// Runs an asynchronous Nearby operation synchronously and converts its status
|
||||
// callback into a command_result JSON object.
|
||||
nlohmann::json InvokeStatusCommand(
|
||||
const std::string& command,
|
||||
std::function<void(std::function<void(NearbySharingService::StatusCodes)>)>
|
||||
invoker);
|
||||
|
||||
// Extracts request field `share_target_id`; writes a command_result-style
|
||||
// error object and returns nullopt when the field is absent or not integral.
|
||||
std::optional<int64_t> GetRequiredTargetId(const nlohmann::json& request,
|
||||
nlohmann::json& error) const;
|
||||
|
||||
// Publishes an asynchronous event through the configured EventSink.
|
||||
void Emit(nlohmann::json event);
|
||||
|
||||
NearbySharingService& service_;
|
||||
EventSink event_sink_;
|
||||
TransferCallback send_transfer_callback_;
|
||||
TransferCallback receive_transfer_callback_;
|
||||
DiscoveryCallback discovery_callback_;
|
||||
mutable absl::Mutex lock_;
|
||||
std::unordered_map<int64_t, ShareTarget> targets_;
|
||||
std::unordered_map<int64_t, ShareTarget> active_transfers_;
|
||||
bool receive_registered_ = false;
|
||||
bool discovery_registered_ = false;
|
||||
bool shutdown_ = false;
|
||||
};
|
||||
|
||||
// Serializes the subset of ShareTarget fields exposed through daemon IPC.
|
||||
nlohmann::json ShareTargetToJson(const ShareTarget& share_target);
|
||||
|
||||
// Serializes transfer progress and attachment totals exposed through daemon
|
||||
// IPC.
|
||||
nlohmann::json TransferMetadataToJson(
|
||||
const TransferMetadata& transfer_metadata,
|
||||
const AttachmentContainer& attachment_container);
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
|
||||
#endif // SHARING_LINUX_DAEMON_DAEMON_SERVICE_H_
|
||||
@@ -1,306 +0,0 @@
|
||||
#include "sharing/linux/daemon/daemon_service.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <functional>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/functional/any_invocable.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "sharing/attachment_container.h"
|
||||
#include "sharing/nearby_sharing_service.h"
|
||||
#include "sharing/share_target.h"
|
||||
#include "sharing/share_target_discovered_callback.h"
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_metadata_builder.h"
|
||||
#include "sharing/transfer_update_callback.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace {
|
||||
|
||||
ShareTarget MakeTarget(int64_t id, std::string name = "Pixel") {
|
||||
ShareTarget target;
|
||||
target.id = id;
|
||||
target.device_name = std::move(name);
|
||||
return target;
|
||||
}
|
||||
|
||||
std::unique_ptr<AttachmentContainer> MakeAttachments() {
|
||||
AttachmentContainer::Builder builder;
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
TransferMetadata MakeMetadata(TransferMetadata::Status status) {
|
||||
return TransferMetadataBuilder()
|
||||
.set_status(status)
|
||||
.set_progress(status == TransferMetadata::Status::kComplete ? 100 : 25)
|
||||
.set_total_attachments_count(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
class FakeDaemonNearbySharingService final : public NearbySharingService {
|
||||
public:
|
||||
void AddObserver(Observer* observer) override { static_cast<void>(observer); }
|
||||
void RemoveObserver(Observer* observer) override {
|
||||
static_cast<void>(observer);
|
||||
}
|
||||
void Shutdown(std::function<void(StatusCodes)> callback) override {
|
||||
shutdown_called = true;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void RegisterSendSurface(
|
||||
TransferUpdateCallback* transfer_callback,
|
||||
ShareTargetDiscoveredCallback* discovery_callback, SendSurfaceState state,
|
||||
Advertisement::BlockedVendorId blocked_vendor_id,
|
||||
bool disable_wifi_hotspot,
|
||||
absl::AnyInvocable<void(StatusCodes)> callback) override {
|
||||
static_cast<void>(blocked_vendor_id);
|
||||
static_cast<void>(disable_wifi_hotspot);
|
||||
send_transfer_callback = transfer_callback;
|
||||
this->discovery_callback = discovery_callback;
|
||||
send_state = state;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void UnregisterSendSurface(
|
||||
TransferUpdateCallback* transfer_callback,
|
||||
absl::AnyInvocable<void(StatusCodes)> callback) override {
|
||||
if (send_transfer_callback == transfer_callback) {
|
||||
send_transfer_callback = nullptr;
|
||||
discovery_callback = nullptr;
|
||||
}
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void RegisterReceiveSurface(
|
||||
TransferUpdateCallback* transfer_callback, ReceiveSurfaceState state,
|
||||
Advertisement::BlockedVendorId vendor_id,
|
||||
absl::AnyInvocable<void(StatusCodes)> callback) override {
|
||||
static_cast<void>(vendor_id);
|
||||
receive_transfer_callback = transfer_callback;
|
||||
receive_state = state;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void UnregisterReceiveSurface(
|
||||
TransferUpdateCallback* transfer_callback,
|
||||
absl::AnyInvocable<void(StatusCodes)> callback) override {
|
||||
if (receive_transfer_callback == transfer_callback) {
|
||||
receive_transfer_callback = nullptr;
|
||||
}
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void ClearForegroundReceiveSurfaces(
|
||||
absl::AnyInvocable<void(StatusCodes)> callback) override {
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
bool IsTransferring() const override { return false; }
|
||||
bool IsScanning() const override { return discovery_callback != nullptr; }
|
||||
bool IsBluetoothPresent() const override { return true; }
|
||||
bool IsBluetoothPowered() const override { return true; }
|
||||
bool IsExtendedAdvertisingSupported() const override { return true; }
|
||||
bool IsLanConnected() const override { return true; }
|
||||
std::string GetQrCodeUrl() const override { return ""; }
|
||||
void SendAttachments(
|
||||
int64_t share_target_id,
|
||||
std::unique_ptr<AttachmentContainer> attachment_container,
|
||||
std::function<void(StatusCodes)> callback) override {
|
||||
last_send_target_id = share_target_id;
|
||||
last_attachment_count = attachment_container == nullptr
|
||||
? 0
|
||||
: attachment_container->GetAttachmentCount();
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void Accept(int64_t share_target_id,
|
||||
std::function<void(StatusCodes)> callback) override {
|
||||
last_accept_target_id = share_target_id;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void Reject(int64_t share_target_id,
|
||||
std::function<void(StatusCodes)> callback) override {
|
||||
last_reject_target_id = share_target_id;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void Cancel(int64_t share_target_id,
|
||||
std::function<void(StatusCodes)> callback) override {
|
||||
last_cancel_target_id = share_target_id;
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
void InitiatePairing(
|
||||
int64_t share_target_id,
|
||||
service::proto::BindingRequest::Type binding_type,
|
||||
absl::AnyInvocable<void(StatusCodes status_codes) &&> callback) override {
|
||||
static_cast<void>(share_target_id);
|
||||
static_cast<void>(binding_type);
|
||||
std::move(callback)(StatusCodes::kOk);
|
||||
}
|
||||
void SetVisibility(
|
||||
proto::DeviceVisibility visibility, absl::Duration expiration,
|
||||
absl::AnyInvocable<void(StatusCodes status_code) &&> callback) override {
|
||||
static_cast<void>(visibility);
|
||||
static_cast<void>(expiration);
|
||||
std::move(callback)(StatusCodes::kOk);
|
||||
}
|
||||
std::string Dump() const override { return ""; }
|
||||
void UpdateFilePathsInProgress(bool update_file_paths) override {
|
||||
static_cast<void>(update_file_paths);
|
||||
}
|
||||
NearbyShareSettings* GetSettings() override { return nullptr; }
|
||||
NearbyShareCertificateManager* GetCertificateManager() override {
|
||||
return nullptr;
|
||||
}
|
||||
AccountManager* GetAccountManager() override { return nullptr; }
|
||||
Clock& GetClock() override { throw std::logic_error("unused"); }
|
||||
void SetAlternateServiceUuidForDiscovery(uint16_t uuid) override {
|
||||
static_cast<void>(uuid);
|
||||
}
|
||||
SyncManager& sync_manager() override { throw std::logic_error("unused"); }
|
||||
OutgoingTargetsManager& outgoing_targets_manager() override {
|
||||
throw std::logic_error("unused");
|
||||
}
|
||||
void UpdateBackupSavePath(
|
||||
absl::string_view binding_id, absl::string_view save_path,
|
||||
absl::AnyInvocable<void(NearbySharingService::StatusCodes)> callback)
|
||||
override {
|
||||
static_cast<void>(binding_id);
|
||||
static_cast<void>(save_path);
|
||||
callback(StatusCodes::kOk);
|
||||
}
|
||||
|
||||
void FireShareTargetDiscovered(const ShareTarget& target) {
|
||||
discovery_callback->OnShareTargetDiscovered(target);
|
||||
}
|
||||
void FireShareTargetLost(const ShareTarget& target) {
|
||||
discovery_callback->OnShareTargetLost(target);
|
||||
}
|
||||
void FireReceiveTransferUpdate(const ShareTarget& target,
|
||||
const AttachmentContainer& attachments,
|
||||
const TransferMetadata& metadata) {
|
||||
receive_transfer_callback->OnTransferUpdate(target, attachments, metadata);
|
||||
}
|
||||
|
||||
TransferUpdateCallback* send_transfer_callback = nullptr;
|
||||
TransferUpdateCallback* receive_transfer_callback = nullptr;
|
||||
ShareTargetDiscoveredCallback* discovery_callback = nullptr;
|
||||
SendSurfaceState send_state = SendSurfaceState::kUnknown;
|
||||
ReceiveSurfaceState receive_state = ReceiveSurfaceState::kUnknown;
|
||||
bool shutdown_called = false;
|
||||
int64_t last_send_target_id = 0;
|
||||
size_t last_attachment_count = 0;
|
||||
int64_t last_accept_target_id = 0;
|
||||
int64_t last_reject_target_id = 0;
|
||||
int64_t last_cancel_target_id = 0;
|
||||
};
|
||||
|
||||
class DaemonServiceTest : public ::testing::Test {
|
||||
protected:
|
||||
DaemonServiceTest()
|
||||
: daemon_(service_, [this](const nlohmann::json& event) {
|
||||
events_.push_back(event);
|
||||
}) {}
|
||||
|
||||
FakeDaemonNearbySharingService service_;
|
||||
std::vector<nlohmann::json> events_;
|
||||
DaemonService daemon_;
|
||||
};
|
||||
|
||||
TEST_F(DaemonServiceTest, StartDiscoveryPublishesDiscoveredTargets) {
|
||||
nlohmann::json result =
|
||||
daemon_.HandleCommand({{"command", "start_discovery"}});
|
||||
ASSERT_TRUE(result["ok"].get<bool>());
|
||||
|
||||
service_.FireShareTargetDiscovered(MakeTarget(42));
|
||||
|
||||
ASSERT_EQ(events_.size(), 1u);
|
||||
EXPECT_EQ(events_[0]["event"], "target_discovered");
|
||||
EXPECT_EQ(events_[0]["share_target"]["id"], 42);
|
||||
EXPECT_EQ(events_[0]["share_target"]["device_name"], "Pixel");
|
||||
|
||||
nlohmann::json status = daemon_.HandleCommand({{"command", "status"}});
|
||||
ASSERT_TRUE(status["ok"].get<bool>());
|
||||
ASSERT_EQ(status["targets"].size(), 1u);
|
||||
EXPECT_EQ(status["targets"][0]["id"], 42);
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, TargetLostRemovesTargetFromStatus) {
|
||||
ASSERT_TRUE(
|
||||
daemon_.HandleCommand({{"command", "start_discovery"}})["ok"].get<bool>());
|
||||
ShareTarget target = MakeTarget(7);
|
||||
service_.FireShareTargetDiscovered(target);
|
||||
service_.FireShareTargetLost(target);
|
||||
|
||||
ASSERT_EQ(events_.size(), 2u);
|
||||
EXPECT_EQ(events_[1]["event"], "target_lost");
|
||||
|
||||
nlohmann::json status = daemon_.HandleCommand({{"command", "status"}});
|
||||
ASSERT_TRUE(status["ok"].get<bool>());
|
||||
EXPECT_TRUE(status["targets"].empty());
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, ReceiveAwaitingConfirmationPublishesIncomingTransfer) {
|
||||
ASSERT_TRUE(
|
||||
daemon_.HandleCommand({{"command", "start_receive"}})["ok"].get<bool>());
|
||||
|
||||
auto attachments = MakeAttachments();
|
||||
service_.FireReceiveTransferUpdate(
|
||||
MakeTarget(9), *attachments,
|
||||
MakeMetadata(TransferMetadata::Status::kAwaitingLocalConfirmation));
|
||||
|
||||
ASSERT_EQ(events_.size(), 1u);
|
||||
EXPECT_EQ(events_[0]["event"], "incoming_transfer");
|
||||
EXPECT_EQ(events_[0]["direction"], "receive");
|
||||
EXPECT_EQ(events_[0]["share_target"]["id"], 9);
|
||||
EXPECT_EQ(events_[0]["transfer"]["status"], "kAwaitingLocalConfirmation");
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, AcceptRejectAndCancelReturnCommandResults) {
|
||||
EXPECT_TRUE(daemon_.HandleCommand({{"command", "accept"},
|
||||
{"share_target_id", 1}})["ok"]
|
||||
.get<bool>());
|
||||
EXPECT_EQ(service_.last_accept_target_id, 1);
|
||||
EXPECT_TRUE(daemon_.HandleCommand({{"command", "reject"},
|
||||
{"share_target_id", 1}})["ok"]
|
||||
.get<bool>());
|
||||
EXPECT_EQ(service_.last_reject_target_id, 1);
|
||||
EXPECT_TRUE(daemon_.HandleCommand({{"command", "cancel"},
|
||||
{"share_target_id", 1}})["ok"]
|
||||
.get<bool>());
|
||||
EXPECT_EQ(service_.last_cancel_target_id, 1);
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, SendFileRejectsUnknownTarget) {
|
||||
nlohmann::json result = daemon_.HandleCommand(
|
||||
{{"command", "send_file"}, {"share_target_id", 404}, {"path", "/tmp/x"}});
|
||||
EXPECT_FALSE(result["ok"].get<bool>());
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, SendFileToKnownTargetReturnsOkForRegularFile) {
|
||||
ASSERT_TRUE(
|
||||
daemon_.HandleCommand({{"command", "start_discovery"}})["ok"].get<bool>());
|
||||
service_.FireShareTargetDiscovered(MakeTarget(12));
|
||||
|
||||
const std::string path = "/tmp/nearby_daemon_service_test_file";
|
||||
{
|
||||
std::ofstream file(path);
|
||||
file << "hello";
|
||||
}
|
||||
|
||||
nlohmann::json result = daemon_.HandleCommand(
|
||||
{{"command", "send_file"}, {"share_target_id", 12}, {"path", path}});
|
||||
EXPECT_TRUE(result["ok"].get<bool>()) << result.dump();
|
||||
EXPECT_EQ(service_.last_send_target_id, 12);
|
||||
EXPECT_EQ(service_.last_attachment_count, 1u);
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
|
||||
TEST_F(DaemonServiceTest, MalformedCommandReturnsError) {
|
||||
nlohmann::json result = daemon_.HandleCommand({{"path", "/tmp/x"}});
|
||||
EXPECT_FALSE(result["ok"].get<bool>());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace nearby::sharing::linux
|
||||
@@ -1,263 +0,0 @@
|
||||
#include "ipc_server.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
|
||||
nlohmann::json CommandResult(std::string command, bool ok,
|
||||
std::string message) {
|
||||
return nlohmann::json{{"event", "command_result"},
|
||||
{"command", std::move(command)},
|
||||
{"ok", ok},
|
||||
{"message", std::move(message)}};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string IPCServer::Read() {
|
||||
std::string cmd;
|
||||
absl::MutexLock lock(lock_);
|
||||
|
||||
size_t pos = read_buf.find('\n');
|
||||
|
||||
if (pos != std::string::npos) {
|
||||
cmd = read_buf.substr(0, pos);
|
||||
read_buf.erase(0, pos + 1);
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void IPCServer::Stop() {
|
||||
running_.store(false);
|
||||
|
||||
{
|
||||
absl::MutexLock write_lock(write_lock_);
|
||||
if (client_fd_ >= 0) {
|
||||
shutdown(client_fd_, SHUT_RDWR);
|
||||
close(client_fd_);
|
||||
client_fd_ = -1;
|
||||
}
|
||||
|
||||
if (sock_fd_ >= 0) {
|
||||
shutdown(sock_fd_, SHUT_RDWR);
|
||||
close(sock_fd_);
|
||||
sock_fd_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
unlink(SOCK_PATH.data());
|
||||
}
|
||||
|
||||
void IPCServer::InitialiseSock() {
|
||||
sock_fd_ = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd_ == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
unlink(SOCK_PATH.data());
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SOCK_PATH.data(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (bind(sock_fd_, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1) {
|
||||
close(sock_fd_);
|
||||
sock_fd_ = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(sock_fd_, 1) == -1) {
|
||||
close(sock_fd_);
|
||||
sock_fd_ = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
void IPCServer::Recv() {
|
||||
while (running_.load()) {
|
||||
pollfd pfd{};
|
||||
pfd.fd = client_fd_;
|
||||
pfd.events = POLLIN;
|
||||
|
||||
int poll_result = poll(&pfd, 1, 100);
|
||||
|
||||
if (!running_.load()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (poll_result < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (poll_result == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pfd.revents & (POLLNVAL | POLLERR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pfd.revents & (POLLIN | POLLHUP)) {
|
||||
char buf[1024]{};
|
||||
|
||||
ssize_t n = recv(client_fd_, buf, sizeof(buf), 0);
|
||||
|
||||
if (n > 0) {
|
||||
absl::MutexLock lock(lock_);
|
||||
read_buf.append(buf, static_cast<size_t>(n));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IPCServer::StartEventLoop() {
|
||||
started_.store(true);
|
||||
running_.store(true);
|
||||
|
||||
InitialiseSock();
|
||||
|
||||
if (sock_fd_ < 0) {
|
||||
running_.store(false);
|
||||
return;
|
||||
}
|
||||
|
||||
while (running_.load()) {
|
||||
sockaddr_un client_addr{};
|
||||
socklen_t addr_len = sizeof(client_addr);
|
||||
|
||||
int accepted_fd =
|
||||
accept(sock_fd_, reinterpret_cast<sockaddr*>(&client_addr), &addr_len);
|
||||
|
||||
if (!running_.load()) {
|
||||
if (accepted_fd >= 0) {
|
||||
close(accepted_fd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (accepted_fd < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
absl::MutexLock write_lock(write_lock_);
|
||||
client_fd_ = accepted_fd;
|
||||
}
|
||||
|
||||
Recv();
|
||||
|
||||
{
|
||||
absl::MutexLock write_lock(write_lock_);
|
||||
if (client_fd_ >= 0) {
|
||||
close(client_fd_);
|
||||
client_fd_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stop();
|
||||
}
|
||||
|
||||
bool IPCServer::SendLine(std::string_view line) {
|
||||
absl::MutexLock write_lock(write_lock_);
|
||||
if (client_fd_ < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string data(line);
|
||||
if (data.empty() || data.back() != '\n') {
|
||||
data.push_back('\n');
|
||||
}
|
||||
|
||||
size_t total_sent = 0;
|
||||
while (total_sent < data.size()) {
|
||||
ssize_t sent =
|
||||
send(client_fd_, data.data() + total_sent, data.size() - total_sent, 0);
|
||||
if (sent > 0) {
|
||||
total_sent += static_cast<size_t>(sent);
|
||||
continue;
|
||||
}
|
||||
if (sent < 0 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPCServer::SendJson(const nlohmann::json& message) {
|
||||
return SendLine(message.dump());
|
||||
}
|
||||
|
||||
void IPCServer::DispatchOne(const std::string& line) {
|
||||
nlohmann::json request;
|
||||
try {
|
||||
request = nlohmann::json::parse(line);
|
||||
} catch (const nlohmann::json::exception& e) {
|
||||
SendJson(CommandResult("", false, std::string("malformed JSON: ") + e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!request.is_object() || !request.contains("command") ||
|
||||
!request["command"].is_string()) {
|
||||
SendJson(CommandResult("", false, "missing string field: command"));
|
||||
return;
|
||||
}
|
||||
std::string command = request["command"].get<std::string>();
|
||||
|
||||
Handler handler;
|
||||
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
|
||||
auto it = handlers_.find(command);
|
||||
if (it == handlers_.end()) {
|
||||
SendJson(CommandResult(command, false, "unknown command"));
|
||||
return;
|
||||
}
|
||||
|
||||
handler = it->second;
|
||||
}
|
||||
|
||||
handler(request);
|
||||
}
|
||||
void IPCServer::DispatchLoop() {
|
||||
while (true) {
|
||||
std::string line = Read();
|
||||
|
||||
if (line.empty()) {
|
||||
if (!running_.load() && started_.load()) {
|
||||
return;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
continue;
|
||||
}
|
||||
|
||||
DispatchOne(line);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "gtest/gtest_prod.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
constexpr std::string_view SOCK_PATH = "/tmp/nearby_sharing_sock";
|
||||
|
||||
class IPCServer {
|
||||
public:
|
||||
IPCServer() = default;
|
||||
|
||||
~IPCServer() { Stop(); }
|
||||
void Stop();
|
||||
void StartEventLoop();
|
||||
using Handler = std::function<void(const nlohmann::json& request)>;
|
||||
|
||||
void RegisterHandler(std::string command, Handler handler) {
|
||||
absl::MutexLock lock(lock_);
|
||||
handlers_[std::move(command)] = std::move(handler);
|
||||
}
|
||||
|
||||
bool SendLine(std::string_view line);
|
||||
bool SendJson(const nlohmann::json& message);
|
||||
void DispatchLoop();
|
||||
|
||||
private:
|
||||
friend class IPCServerTest;
|
||||
|
||||
void DispatchOne(const std::string& line);
|
||||
void InitialiseSock();
|
||||
void Recv();
|
||||
std::string Read();
|
||||
|
||||
int sock_fd_ = -1;
|
||||
int client_fd_ = -1;
|
||||
sockaddr_un addr{};
|
||||
std::string read_buf;
|
||||
absl::Mutex lock_;
|
||||
absl::Mutex write_lock_;
|
||||
std::unordered_map<std::string, Handler> handlers_;
|
||||
std::atomic<bool> running_{false};
|
||||
std::atomic<bool> started_{false};
|
||||
};
|
||||
@@ -1,452 +0,0 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
|
||||
#include "ipc_server.h"
|
||||
|
||||
class IPCServerTest : public ::testing::Test {
|
||||
protected:
|
||||
static std::string Read(IPCServer& server) {
|
||||
return server.Read();
|
||||
}
|
||||
|
||||
static void DispatchOne(IPCServer& server, const std::string& line) {
|
||||
server.DispatchOne(line);
|
||||
}
|
||||
|
||||
static void SetReadBuffer(IPCServer& server, std::string value) {
|
||||
absl::MutexLock lock(server.lock_);
|
||||
server.read_buf = std::move(value);
|
||||
}
|
||||
|
||||
static void SetRunning(IPCServer& server, bool running) {
|
||||
server.running_.store(running);
|
||||
server.started_.store(true);
|
||||
}
|
||||
|
||||
static bool IsRunning(IPCServer& server) {
|
||||
return server.running_.load();
|
||||
}
|
||||
|
||||
static std::string WaitRead(IPCServer& server) {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
std::string cmd = Read(server);
|
||||
|
||||
if (!cmd.empty()) {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static bool WaitUntilTrue(const std::function<bool()>& condition) {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
if (condition()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
int ConnectClientWithRetry() {
|
||||
int client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
EXPECT_GE(client_fd, 0);
|
||||
|
||||
sockaddr_un addr {};
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, SOCK_PATH.data(), sizeof(addr.sun_path) - 1);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
int result = connect(
|
||||
client_fd,
|
||||
reinterpret_cast<sockaddr*>(&addr),
|
||||
sizeof(addr));
|
||||
|
||||
if (result == 0) {
|
||||
return client_fd;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
ADD_FAILURE() << "connect failed: " << strerror(errno);
|
||||
close(client_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SendAll(int fd, std::string_view data) {
|
||||
size_t total_sent = 0;
|
||||
|
||||
while (total_sent < data.size()) {
|
||||
ssize_t sent = send(
|
||||
fd,
|
||||
data.data() + total_sent,
|
||||
data.size() - total_sent,
|
||||
0);
|
||||
|
||||
ASSERT_GT(sent, 0) << "send failed: " << strerror(errno);
|
||||
|
||||
total_sent += static_cast<size_t>(sent);
|
||||
}
|
||||
}
|
||||
|
||||
void StopAndJoin(IPCServer& server, std::thread& server_thread) {
|
||||
server.Stop();
|
||||
|
||||
if (server_thread.joinable()) {
|
||||
server_thread.join();
|
||||
}
|
||||
|
||||
unlink(SOCK_PATH.data());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(IPCServerTest, ServerCanReadMultipleCommands) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
SendAll(client_fd, "A\nB\nC\n");
|
||||
|
||||
EXPECT_EQ(WaitRead(server), "A");
|
||||
EXPECT_EQ(WaitRead(server), "B");
|
||||
EXPECT_EQ(WaitRead(server), "C");
|
||||
|
||||
close(client_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, PartialCommandIsBufferedUntilDelimiter) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
SendAll(client_fd, "PIN");
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
EXPECT_EQ(Read(server), "");
|
||||
|
||||
SendAll(client_fd, "G\n");
|
||||
|
||||
EXPECT_EQ(WaitRead(server), "PING");
|
||||
|
||||
close(client_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, LargeCommandAcrossMultipleRecvCalls) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
std::string large(8192, 'A');
|
||||
SendAll(client_fd, large + "\n");
|
||||
|
||||
EXPECT_EQ(WaitRead(server), large);
|
||||
|
||||
close(client_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, MultipleCommandsSplitAcrossSends) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
SendAll(client_fd, "A\nB");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
SendAll(client_fd, "\nC\n");
|
||||
|
||||
EXPECT_EQ(WaitRead(server), "A");
|
||||
EXPECT_EQ(WaitRead(server), "B");
|
||||
EXPECT_EQ(WaitRead(server), "C");
|
||||
|
||||
close(client_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, ClientDisconnectDoesNotCrashServer) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
close(client_fd);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
StopAndJoin(server, server_thread);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, ClientCanReconnectAfterDisconnect) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client1_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client1_fd, 0);
|
||||
|
||||
SendAll(client1_fd, "FIRST\n");
|
||||
EXPECT_EQ(WaitRead(server), "FIRST");
|
||||
|
||||
close(client1_fd);
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
int client2_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client2_fd, 0);
|
||||
|
||||
SendAll(client2_fd, "SECOND\n");
|
||||
EXPECT_EQ(WaitRead(server), "SECOND");
|
||||
|
||||
close(client2_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, StopEndsEventLoopThread) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
StopAndJoin(server, server_thread);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, StopWhileClientConnected) {
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
SendAll(client_fd, "PING\n");
|
||||
EXPECT_EQ(WaitRead(server), "PING");
|
||||
|
||||
StopAndJoin(server, server_thread);
|
||||
|
||||
close(client_fd);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, StaleSocketPathIsCleanedUp) {
|
||||
unlink(SOCK_PATH.data());
|
||||
|
||||
{
|
||||
std::ofstream stale_file{SOCK_PATH.data()};
|
||||
ASSERT_TRUE(stale_file.is_open());
|
||||
stale_file << "stale";
|
||||
}
|
||||
|
||||
struct stat before {};
|
||||
ASSERT_EQ(stat(SOCK_PATH.data(), &before), 0);
|
||||
ASSERT_TRUE(S_ISREG(before.st_mode));
|
||||
|
||||
IPCServer server;
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
struct stat after {};
|
||||
ASSERT_EQ(stat(SOCK_PATH.data(), &after), 0);
|
||||
EXPECT_TRUE(S_ISSOCK(after.st_mode));
|
||||
|
||||
close(client_fd);
|
||||
StopAndJoin(server, server_thread);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, DispatchOneCallsRegisteredHandler) {
|
||||
IPCServer server;
|
||||
|
||||
bool called = false;
|
||||
|
||||
server.RegisterHandler("PING", [&](const nlohmann::json& request) {
|
||||
EXPECT_EQ(request.value("command", ""), "PING");
|
||||
called = true;
|
||||
});
|
||||
|
||||
DispatchOne(server, R"({"command":"PING"})");
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, DispatchOnePassesArguments) {
|
||||
IPCServer server;
|
||||
|
||||
std::string received_args;
|
||||
|
||||
server.RegisterHandler("ECHO", [&](const nlohmann::json& request) {
|
||||
received_args = request.value("args", "");
|
||||
});
|
||||
|
||||
DispatchOne(server, R"({"command":"ECHO","args":"hello world"})");
|
||||
|
||||
EXPECT_EQ(received_args, "hello world");
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, DispatchOneHandlesCommandWithoutArgs) {
|
||||
IPCServer server;
|
||||
|
||||
std::string received_args = "not empty";
|
||||
|
||||
server.RegisterHandler("PING", [&](const nlohmann::json& request) {
|
||||
received_args = request.value("args", "");
|
||||
});
|
||||
|
||||
DispatchOne(server, R"({"command":"PING"})");
|
||||
|
||||
EXPECT_EQ(received_args, "");
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, DispatchOneIgnoresUnknownCommand) {
|
||||
IPCServer server;
|
||||
|
||||
bool called = false;
|
||||
|
||||
server.RegisterHandler("PING", [&](const nlohmann::json& request) {
|
||||
called = true;
|
||||
});
|
||||
|
||||
DispatchOne(server, R"({"command":"UNKNOWN","args":"something"})");
|
||||
|
||||
EXPECT_FALSE(called);
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, DispatchLoopDispatchesBufferedCommand) {
|
||||
IPCServer server;
|
||||
|
||||
std::atomic<bool> called{false};
|
||||
std::string received_args;
|
||||
|
||||
server.RegisterHandler("ECHO", [&](const nlohmann::json& request) {
|
||||
received_args = request.value("args", "");
|
||||
called.store(true);
|
||||
});
|
||||
|
||||
SetReadBuffer(server, R"({"command":"ECHO","args":"hello"})"
|
||||
"\n");
|
||||
SetRunning(server, true);
|
||||
|
||||
std::thread dispatch_thread([&server]() {
|
||||
server.DispatchLoop();
|
||||
});
|
||||
|
||||
ASSERT_TRUE(WaitUntilTrue([&]() {
|
||||
return called.load();
|
||||
}));
|
||||
|
||||
SetRunning(server, false);
|
||||
|
||||
dispatch_thread.join();
|
||||
|
||||
EXPECT_EQ(received_args, "hello");
|
||||
}
|
||||
|
||||
TEST_F(IPCServerTest, SocketCommandReachesRegisteredHandler) {
|
||||
IPCServer server;
|
||||
|
||||
std::atomic<bool> called{false};
|
||||
std::string received_args;
|
||||
|
||||
server.RegisterHandler("ECHO", [&](const nlohmann::json& request) {
|
||||
received_args = request.value("args", "");
|
||||
called.store(true);
|
||||
});
|
||||
|
||||
std::thread server_thread([&server]() {
|
||||
server.StartEventLoop();
|
||||
});
|
||||
|
||||
int client_fd = ConnectClientWithRetry();
|
||||
ASSERT_GE(client_fd, 0);
|
||||
|
||||
std::thread dispatch_thread([&server]() {
|
||||
server.DispatchLoop();
|
||||
});
|
||||
|
||||
SendAll(client_fd, R"({"command":"ECHO","args":"from socket"})"
|
||||
"\n");
|
||||
|
||||
ASSERT_TRUE(WaitUntilTrue([&]() {
|
||||
return called.load();
|
||||
}));
|
||||
|
||||
EXPECT_EQ(received_args, "from socket");
|
||||
|
||||
close(client_fd);
|
||||
|
||||
server.Stop();
|
||||
|
||||
if (server_thread.joinable()) {
|
||||
server_thread.join();
|
||||
}
|
||||
|
||||
if (dispatch_thread.joinable()) {
|
||||
dispatch_thread.join();
|
||||
}
|
||||
|
||||
unlink(SOCK_PATH.data());
|
||||
}
|
||||
@@ -8,14 +8,14 @@
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include "absl/time/time.h"
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
#include "sharing/flags/generated/nearby_sharing_feature_flags.h"
|
||||
#include "sharing/linux/daemon/daemon_service.h"
|
||||
#include "sharing/linux/daemon/ipc_server.h"
|
||||
#include "sharing/linux/daemon/nearby_sharing_dbus_service.h"
|
||||
#include "sharing/linux/nearby_noop_analytics_recorder.h"
|
||||
#include "sharing/linux/platform/linux_sharing_platform.h"
|
||||
#include "sharing/nearby_sharing_service.h"
|
||||
@@ -27,13 +27,13 @@ namespace nearby::sharing::linux {
|
||||
namespace {
|
||||
|
||||
std::atomic<bool> g_interrupted = false;
|
||||
IPCServer* g_ipc_server = nullptr;
|
||||
sdbus::IConnection* g_bus = nullptr;
|
||||
|
||||
void HandleSignal(int signal) {
|
||||
static_cast<void>(signal);
|
||||
g_interrupted = true;
|
||||
if (g_ipc_server != nullptr) {
|
||||
g_ipc_server->Stop();
|
||||
if (g_bus != nullptr) {
|
||||
g_bus->leaveEventLoop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,35 +140,26 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
IPCServer ipc_server;
|
||||
nearby::sharing::linux::g_ipc_server = &ipc_server;
|
||||
nearby::sharing::linux::DaemonService daemon(
|
||||
*service, [&](const nlohmann::json& event) { ipc_server.SendJson(event); });
|
||||
auto bus = sdbus::createSessionBusConnection(
|
||||
sdbus::ServiceName("com.google.nearby.sharing"));
|
||||
nearby::sharing::linux::g_bus = bus.get();
|
||||
auto object = sdbus::createObject(
|
||||
*bus, sdbus::ObjectPath("/com/google/nearby/sharing"));
|
||||
nearby::sharing::linux::NearbySharingDbusService dbus_service(
|
||||
*object, *service, [&bus]() { bus->leaveEventLoop(); });
|
||||
dbus_service.EmitStatusChanged();
|
||||
|
||||
const std::string commands[] = {
|
||||
"status", "start_receive", "stop_receive", "start_discovery",
|
||||
"stop_discovery", "send_file", "accept", "reject",
|
||||
"cancel", "shutdown",
|
||||
};
|
||||
for (const std::string& command : commands) {
|
||||
ipc_server.RegisterHandler(command, [&](const nlohmann::json& request) {
|
||||
nlohmann::json result = daemon.HandleCommand(request);
|
||||
ipc_server.SendJson(result);
|
||||
if (request.value("command", "") == "shutdown") {
|
||||
ipc_server.Stop();
|
||||
}
|
||||
});
|
||||
try {
|
||||
bus->enterEventLoop();
|
||||
} catch (const sdbus::Error& error) {
|
||||
std::cerr << "D-Bus event loop failed: " << error.getName() << ": "
|
||||
<< error.getMessage() << std::endl;
|
||||
dbus_service.ShutdownService();
|
||||
nearby::sharing::linux::g_bus = nullptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::thread server_thread([&ipc_server]() { ipc_server.StartEventLoop(); });
|
||||
std::thread dispatch_thread([&ipc_server]() { ipc_server.DispatchLoop(); });
|
||||
|
||||
server_thread.join();
|
||||
ipc_server.Stop();
|
||||
if (dispatch_thread.joinable()) {
|
||||
dispatch_thread.join();
|
||||
}
|
||||
daemon.Shutdown();
|
||||
nearby::sharing::linux::g_ipc_server = nullptr;
|
||||
dbus_service.ShutdownService();
|
||||
nearby::sharing::linux::g_bus = nullptr;
|
||||
return nearby::sharing::linux::g_interrupted ? 130 : 0;
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
#include "absl/time/time.h"
|
||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "internal/flags/nearby_flags.h"
|
||||
#include "sharing/advertisement.h"
|
||||
#include "sharing/attachment_container.h"
|
||||
#include "sharing/common/nearby_share_enums.h"
|
||||
#include "sharing/file_attachment.h"
|
||||
#include "sharing/linux/platform/linux_sharing_platform.h"
|
||||
#include "sharing/linux/nearby_noop_analytics_recorder.h"
|
||||
#include "sharing/flags/generated/nearby_sharing_feature_flags.h"
|
||||
#include "sharing/nearby_sharing_service.h"
|
||||
#include "sharing/nearby_sharing_service_factory.h"
|
||||
#include "sharing/nearby_sharing_settings.h"
|
||||
#include "sharing/share_target.h"
|
||||
#include "sharing/share_target_discovered_callback.h"
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_update_callback.h"
|
||||
#include "sharing/proto/enums.pb.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace sharing {
|
||||
namespace linux {
|
||||
|
||||
}
|
||||
} // namespace sharing
|
||||
} // namespace nearby
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
|
||||
#define __sdbuscpp___home_lasan_Dev_nearby_latest_sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
|
||||
#ifndef __sdbuscpp___sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
|
||||
#define __sdbuscpp___sharing_linux_daemon_nearby_sharing_client_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
#include "sharing/linux/daemon/nearby_sharing_dbus_service.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "internal/base/file_path.h"
|
||||
#include "sharing/advertisement.h"
|
||||
#include "sharing/file_attachment.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace {
|
||||
|
||||
std::string StatusCodeToString(NearbySharingService::StatusCodes status) {
|
||||
return NearbySharingService::StatusCodeToString(status);
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> StatusResult(
|
||||
NearbySharingService::StatusCodes status) {
|
||||
return {status == NearbySharingService::StatusCodes::kOk,
|
||||
StatusCodeToString(status)};
|
||||
}
|
||||
|
||||
std::unique_ptr<AttachmentContainer> CreateFileAttachments(
|
||||
const std::string& file_path) {
|
||||
AttachmentContainer::Builder builder;
|
||||
builder.AddFileAttachment(FileAttachment(FilePath(file_path)));
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
template <typename Invoker>
|
||||
NearbySharingService::StatusCodes WaitForStatus(Invoker invoker) {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
std::optional<NearbySharingService::StatusCodes> status;
|
||||
invoker([&](NearbySharingService::StatusCodes callback_status) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
status = callback_status;
|
||||
}
|
||||
cv.notify_one();
|
||||
});
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [&] { return status.has_value(); });
|
||||
return *status;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DbusDictionary ShareTargetToDbus(const ShareTarget& share_target) {
|
||||
DbusDictionary result;
|
||||
result.emplace("id", sdbus::Variant(share_target.id));
|
||||
result.emplace("device_name", sdbus::Variant(share_target.device_name));
|
||||
result.emplace("type",
|
||||
sdbus::Variant(static_cast<int32_t>(share_target.type)));
|
||||
result.emplace("is_incoming", sdbus::Variant(share_target.is_incoming));
|
||||
result.emplace("is_known", sdbus::Variant(share_target.is_known));
|
||||
result.emplace("device_id", sdbus::Variant(share_target.device_id));
|
||||
result.emplace("for_self_share", sdbus::Variant(share_target.for_self_share));
|
||||
result.emplace("vendor_id",
|
||||
sdbus::Variant(static_cast<int32_t>(share_target.vendor_id)));
|
||||
result.emplace("receive_disabled",
|
||||
sdbus::Variant(share_target.receive_disabled));
|
||||
return result;
|
||||
}
|
||||
|
||||
DbusDictionary TransferMetadataToDbus(
|
||||
const TransferMetadata& transfer_metadata,
|
||||
const AttachmentContainer& attachment_container) {
|
||||
DbusDictionary result;
|
||||
result.emplace("status", sdbus::Variant(TransferMetadata::StatusToString(
|
||||
transfer_metadata.status())));
|
||||
result.emplace(
|
||||
"progress",
|
||||
sdbus::Variant(static_cast<double>(transfer_metadata.progress())));
|
||||
result.emplace("transferred_bytes",
|
||||
sdbus::Variant(static_cast<int64_t>(
|
||||
transfer_metadata.transferred_bytes())));
|
||||
result.emplace(
|
||||
"total_bytes",
|
||||
sdbus::Variant(attachment_container.GetTotalAttachmentsSize()));
|
||||
result.emplace(
|
||||
"transfer_speed",
|
||||
sdbus::Variant(static_cast<int64_t>(transfer_metadata.transfer_speed())));
|
||||
result.emplace("estimated_time_remaining",
|
||||
sdbus::Variant(static_cast<int64_t>(
|
||||
transfer_metadata.estimated_time_remaining())));
|
||||
result.emplace("total_attachments_count",
|
||||
sdbus::Variant(transfer_metadata.total_attachments_count()));
|
||||
result.emplace(
|
||||
"transferred_attachments_count",
|
||||
sdbus::Variant(transfer_metadata.transferred_attachments_count()));
|
||||
result.emplace("is_final_status",
|
||||
sdbus::Variant(transfer_metadata.is_final_status()));
|
||||
result.emplace("is_self_share",
|
||||
sdbus::Variant(transfer_metadata.is_self_share()));
|
||||
result.emplace("binding_id", sdbus::Variant(transfer_metadata.binding_id()));
|
||||
|
||||
if (transfer_metadata.token().has_value()) {
|
||||
result.emplace("token", sdbus::Variant(*transfer_metadata.token()));
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_id().has_value()) {
|
||||
result.emplace(
|
||||
"in_progress_attachment_id",
|
||||
sdbus::Variant(*transfer_metadata.in_progress_attachment_id()));
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_transferred_bytes()
|
||||
.has_value()) {
|
||||
result.emplace(
|
||||
"in_progress_attachment_transferred_bytes",
|
||||
sdbus::Variant(static_cast<int64_t>(
|
||||
*transfer_metadata.in_progress_attachment_transferred_bytes())));
|
||||
}
|
||||
if (transfer_metadata.in_progress_attachment_total_bytes().has_value()) {
|
||||
result.emplace(
|
||||
"in_progress_attachment_total_bytes",
|
||||
sdbus::Variant(static_cast<int64_t>(
|
||||
*transfer_metadata.in_progress_attachment_total_bytes())));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NearbySharingDbusService::TransferCallback::TransferCallback(
|
||||
NearbySharingDbusService& service, bool receive_mode)
|
||||
: service_(service), receive_mode_(receive_mode) {}
|
||||
|
||||
void NearbySharingDbusService::TransferCallback::OnTransferUpdate(
|
||||
const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) {
|
||||
service_.OnTransferUpdate(receive_mode_, share_target, attachment_container,
|
||||
transfer_metadata);
|
||||
}
|
||||
|
||||
NearbySharingDbusService::NearbySharingDbusService(
|
||||
sdbus::IObject& object, NearbySharingService& service,
|
||||
QuitCallback quit_callback)
|
||||
: sharing_adaptor(object),
|
||||
service_(service),
|
||||
quit_callback_(std::move(quit_callback)),
|
||||
send_transfer_callback_(*this, /*receive_mode=*/false),
|
||||
receive_transfer_callback_(*this, /*receive_mode=*/true) {
|
||||
registerAdaptor();
|
||||
service_.AddObserver(this);
|
||||
}
|
||||
|
||||
NearbySharingDbusService::~NearbySharingDbusService() {
|
||||
service_.RemoveObserver(this);
|
||||
ShutdownService();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::EmitStatusChanged() {
|
||||
emitStatusChanged(StatusToDbus());
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::ShutdownService() {
|
||||
bool stop_receive = false;
|
||||
bool stop_discovery = false;
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (shutdown_) {
|
||||
return;
|
||||
}
|
||||
shutdown_ = true;
|
||||
stop_receive = receive_registered_;
|
||||
stop_discovery = discovery_registered_;
|
||||
receive_registered_ = false;
|
||||
discovery_registered_ = false;
|
||||
targets_.clear();
|
||||
}
|
||||
|
||||
if (stop_receive) {
|
||||
WaitForStatus([&](auto callback) {
|
||||
service_.UnregisterReceiveSurface(&receive_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
if (stop_discovery) {
|
||||
WaitForStatus([&](auto callback) {
|
||||
service_.UnregisterSendSurface(&send_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
WaitForStatus([&](auto callback) { service_.Shutdown(std::move(callback)); });
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::StartReceive() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (receive_registered_) {
|
||||
return {true, "receive already started"};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand([&](auto callback) {
|
||||
service_.RegisterReceiveSurface(
|
||||
&receive_transfer_callback_,
|
||||
NearbySharingService::ReceiveSurfaceState::kForeground,
|
||||
Advertisement::BlockedVendorId::kNone, std::move(callback));
|
||||
});
|
||||
if (std::get<0>(result)) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
receive_registered_ = true;
|
||||
}
|
||||
EmitStatusChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::StopReceive() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (!receive_registered_) {
|
||||
return {true, "receive already stopped"};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand([&](auto callback) {
|
||||
service_.UnregisterReceiveSurface(&receive_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
if (std::get<0>(result)) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
receive_registered_ = false;
|
||||
}
|
||||
EmitStatusChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::StartDiscovery() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (discovery_registered_) {
|
||||
return {true, "discovery already started"};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand([&](auto callback) {
|
||||
service_.RegisterSendSurface(
|
||||
&send_transfer_callback_, this,
|
||||
NearbySharingService::SendSurfaceState::kForeground,
|
||||
Advertisement::BlockedVendorId::kNone,
|
||||
/*disable_wifi_hotspot=*/false, std::move(callback));
|
||||
});
|
||||
if (std::get<0>(result)) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
discovery_registered_ = true;
|
||||
}
|
||||
EmitStatusChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::StopDiscovery() {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (!discovery_registered_) {
|
||||
return {true, "discovery already stopped"};
|
||||
}
|
||||
}
|
||||
|
||||
auto result = InvokeStatusCommand([&](auto callback) {
|
||||
service_.UnregisterSendSurface(&send_transfer_callback_,
|
||||
std::move(callback));
|
||||
});
|
||||
if (std::get<0>(result)) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
discovery_registered_ = false;
|
||||
targets_.clear();
|
||||
}
|
||||
EmitStatusChanged();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::SendFile(
|
||||
const int64_t& share_target_id, const std::string& path) {
|
||||
std::error_code file_error;
|
||||
if (!std::filesystem::is_regular_file(path, file_error)) {
|
||||
return {false, "path is not a regular file"};
|
||||
}
|
||||
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
if (targets_.find(share_target_id) == targets_.end()) {
|
||||
return {false, "unknown share_target_id"};
|
||||
}
|
||||
}
|
||||
|
||||
auto attachments = CreateFileAttachments(path);
|
||||
return InvokeStatusCommand([&](auto callback) {
|
||||
service_.SendAttachments(share_target_id, std::move(attachments),
|
||||
std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::Accept(
|
||||
const int64_t& share_target_id) {
|
||||
return InvokeStatusCommand([&](auto callback) {
|
||||
service_.Accept(share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::Reject(
|
||||
const int64_t& share_target_id) {
|
||||
return InvokeStatusCommand([&](auto callback) {
|
||||
service_.Reject(share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::Cancel(
|
||||
const int64_t& share_target_id) {
|
||||
return InvokeStatusCommand([&](auto callback) {
|
||||
service_.Cancel(share_target_id, std::move(callback));
|
||||
});
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::Shutdown() {
|
||||
ShutdownService();
|
||||
if (quit_callback_) {
|
||||
quit_callback_();
|
||||
}
|
||||
return {true, "daemon service shut down"};
|
||||
}
|
||||
|
||||
std::tuple<bool, std::string> NearbySharingDbusService::InvokeStatusCommand(
|
||||
std::function<void(std::function<void(NearbySharingService::StatusCodes)>)>
|
||||
invoker) {
|
||||
return StatusResult(WaitForStatus(std::move(invoker)));
|
||||
}
|
||||
|
||||
DbusDictionary NearbySharingDbusService::StatusToDbus() const {
|
||||
std::vector<DbusDictionary> targets;
|
||||
bool receive_registered = false;
|
||||
bool discovery_registered = false;
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
receive_registered = receive_registered_;
|
||||
discovery_registered = discovery_registered_;
|
||||
targets.reserve(targets_.size());
|
||||
for (const auto& [id, target] : targets_) {
|
||||
static_cast<void>(id);
|
||||
targets.push_back(ShareTargetToDbus(target));
|
||||
}
|
||||
}
|
||||
|
||||
DbusDictionary status;
|
||||
status.emplace("receive_registered", sdbus::Variant(receive_registered));
|
||||
status.emplace("discovery_registered", sdbus::Variant(discovery_registered));
|
||||
status.emplace("is_transferring", sdbus::Variant(service_.IsTransferring()));
|
||||
status.emplace("is_scanning", sdbus::Variant(service_.IsScanning()));
|
||||
status.emplace("bluetooth_present",
|
||||
sdbus::Variant(service_.IsBluetoothPresent()));
|
||||
status.emplace("bluetooth_powered",
|
||||
sdbus::Variant(service_.IsBluetoothPowered()));
|
||||
status.emplace("lan_connected", sdbus::Variant(service_.IsLanConnected()));
|
||||
status.emplace("targets", sdbus::Variant(targets));
|
||||
return status;
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnTransferUpdate(
|
||||
bool receive_mode, const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) {
|
||||
const std::string direction = receive_mode ? "receive" : "send";
|
||||
const DbusDictionary target = ShareTargetToDbus(share_target);
|
||||
const DbusDictionary transfer =
|
||||
TransferMetadataToDbus(transfer_metadata, attachment_container);
|
||||
|
||||
if (receive_mode &&
|
||||
transfer_metadata.status() ==
|
||||
TransferMetadata::Status::kAwaitingLocalConfirmation) {
|
||||
emitIncomingTransfer(direction, target, transfer);
|
||||
} else {
|
||||
emitTransferUpdate(direction, target, transfer);
|
||||
}
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnShareTargetDiscovered(
|
||||
const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_[share_target.id] = share_target;
|
||||
}
|
||||
emitTargetDiscovered(ShareTargetToDbus(share_target));
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnShareTargetLost(
|
||||
const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_.erase(share_target.id);
|
||||
}
|
||||
emitTargetLost(ShareTargetToDbus(share_target));
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnShareTargetUpdated(
|
||||
const ShareTarget& share_target) {
|
||||
{
|
||||
absl::MutexLock lock(lock_);
|
||||
targets_[share_target.id] = share_target;
|
||||
}
|
||||
emitTargetUpdated(ShareTargetToDbus(share_target));
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnHighVisibilityChanged(
|
||||
bool in_high_visibility) {
|
||||
static_cast<void>(in_high_visibility);
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnBluetoothStatusChanged(AdapterState state) {
|
||||
static_cast<void>(state);
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnLanStatusChanged(AdapterState state) {
|
||||
static_cast<void>(state);
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
void NearbySharingDbusService::OnIrrecoverableHardwareErrorReported() {
|
||||
EmitStatusChanged();
|
||||
}
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_
|
||||
#define SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "sharing/attachment_container.h"
|
||||
#include "sharing/linux/daemon/nearby_sharing_server.h"
|
||||
#include "sharing/nearby_sharing_service.h"
|
||||
#include "sharing/share_target.h"
|
||||
#include "sharing/share_target_discovered_callback.h"
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_update_callback.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
|
||||
using DbusDictionary = std::map<std::string, sdbus::Variant>;
|
||||
|
||||
DbusDictionary ShareTargetToDbus(const ShareTarget& share_target);
|
||||
DbusDictionary TransferMetadataToDbus(
|
||||
const TransferMetadata& transfer_metadata,
|
||||
const AttachmentContainer& attachment_container);
|
||||
|
||||
class NearbySharingDbusService final
|
||||
: public com::google::nearby::sharing_adaptor,
|
||||
public ShareTargetDiscoveredCallback,
|
||||
public NearbySharingService::Observer {
|
||||
public:
|
||||
using QuitCallback = std::function<void()>;
|
||||
|
||||
NearbySharingDbusService(sdbus::IObject& object,
|
||||
NearbySharingService& service,
|
||||
QuitCallback quit_callback);
|
||||
~NearbySharingDbusService() override;
|
||||
|
||||
NearbySharingDbusService(const NearbySharingDbusService&) = delete;
|
||||
NearbySharingDbusService& operator=(const NearbySharingDbusService&) = delete;
|
||||
|
||||
void EmitStatusChanged();
|
||||
void ShutdownService();
|
||||
|
||||
void OnShareTargetDiscovered(const ShareTarget& share_target) override;
|
||||
void OnShareTargetLost(const ShareTarget& share_target) override;
|
||||
void OnShareTargetUpdated(const ShareTarget& share_target) override;
|
||||
|
||||
void OnHighVisibilityChanged(bool in_high_visibility) override;
|
||||
void OnBluetoothStatusChanged(AdapterState state) override;
|
||||
void OnLanStatusChanged(AdapterState state) override;
|
||||
void OnIrrecoverableHardwareErrorReported() override;
|
||||
|
||||
private:
|
||||
class TransferCallback final : public TransferUpdateCallback {
|
||||
public:
|
||||
TransferCallback(NearbySharingDbusService& service, bool receive_mode);
|
||||
|
||||
void OnTransferUpdate(const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata) override;
|
||||
|
||||
private:
|
||||
NearbySharingDbusService& service_;
|
||||
bool receive_mode_;
|
||||
};
|
||||
|
||||
std::tuple<bool, std::string> StartReceive() override;
|
||||
std::tuple<bool, std::string> StopReceive() override;
|
||||
std::tuple<bool, std::string> StartDiscovery() override;
|
||||
std::tuple<bool, std::string> StopDiscovery() override;
|
||||
std::tuple<bool, std::string> SendFile(const int64_t& share_target_id,
|
||||
const std::string& path) override;
|
||||
std::tuple<bool, std::string> Accept(const int64_t& share_target_id) override;
|
||||
std::tuple<bool, std::string> Reject(const int64_t& share_target_id) override;
|
||||
std::tuple<bool, std::string> Cancel(const int64_t& share_target_id) override;
|
||||
std::tuple<bool, std::string> Shutdown() override;
|
||||
|
||||
std::tuple<bool, std::string> InvokeStatusCommand(
|
||||
std::function<
|
||||
void(std::function<void(NearbySharingService::StatusCodes)>)>
|
||||
invoker);
|
||||
DbusDictionary StatusToDbus() const;
|
||||
void OnTransferUpdate(bool receive_mode, const ShareTarget& share_target,
|
||||
const AttachmentContainer& attachment_container,
|
||||
const TransferMetadata& transfer_metadata);
|
||||
|
||||
NearbySharingService& service_;
|
||||
QuitCallback quit_callback_;
|
||||
TransferCallback send_transfer_callback_;
|
||||
TransferCallback receive_transfer_callback_;
|
||||
mutable absl::Mutex lock_;
|
||||
std::unordered_map<int64_t, ShareTarget> targets_;
|
||||
bool receive_registered_ = false;
|
||||
bool discovery_registered_ = false;
|
||||
bool shutdown_ = false;
|
||||
};
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
|
||||
#endif // SHARING_LINUX_DAEMON_NEARBY_SHARING_DBUS_SERVICE_H_
|
||||
@@ -0,0 +1,89 @@
|
||||
#include "sharing/linux/daemon/nearby_sharing_dbus_service.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "sharing/attachment_container.h"
|
||||
#include "sharing/common/nearby_share_enums.h"
|
||||
#include "sharing/share_target.h"
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_metadata_builder.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace {
|
||||
|
||||
ShareTarget MakeShareTarget() {
|
||||
ShareTarget target;
|
||||
target.id = 42;
|
||||
target.device_name = "Pixel";
|
||||
target.type = ShareTargetType::kPhone;
|
||||
target.is_incoming = true;
|
||||
target.is_known = true;
|
||||
target.device_id = "device-id";
|
||||
target.for_self_share = true;
|
||||
target.vendor_id = 7;
|
||||
target.receive_disabled = true;
|
||||
return target;
|
||||
}
|
||||
|
||||
TEST(NearbySharingDbusServiceTest, ShareTargetToDbusMapsNearbyFields) {
|
||||
DbusDictionary result = ShareTargetToDbus(MakeShareTarget());
|
||||
|
||||
EXPECT_EQ(result.at("id").get<int64_t>(), 42);
|
||||
EXPECT_EQ(result.at("device_name").get<std::string>(), "Pixel");
|
||||
EXPECT_EQ(result.at("type").get<int32_t>(),
|
||||
static_cast<int32_t>(ShareTargetType::kPhone));
|
||||
EXPECT_TRUE(result.at("is_incoming").get<bool>());
|
||||
EXPECT_TRUE(result.at("is_known").get<bool>());
|
||||
EXPECT_EQ(result.at("device_id").get<std::string>(), "device-id");
|
||||
EXPECT_TRUE(result.at("for_self_share").get<bool>());
|
||||
EXPECT_EQ(result.at("vendor_id").get<int32_t>(), 7);
|
||||
EXPECT_TRUE(result.at("receive_disabled").get<bool>());
|
||||
}
|
||||
|
||||
TEST(NearbySharingDbusServiceTest, TransferMetadataToDbusMapsNearbyFields) {
|
||||
AttachmentContainer::Builder attachments_builder;
|
||||
auto attachments = attachments_builder.Build();
|
||||
TransferMetadata metadata =
|
||||
TransferMetadataBuilder()
|
||||
.set_status(TransferMetadata::Status::kInProgress)
|
||||
.set_progress(25)
|
||||
.set_token(std::string("1234"))
|
||||
.set_is_self_share(true)
|
||||
.set_transferred_bytes(1024)
|
||||
.set_transfer_speed(256)
|
||||
.set_estimated_time_remaining(4)
|
||||
.set_total_attachments_count(2)
|
||||
.set_transferred_attachments_count(1)
|
||||
.set_in_progress_attachment_id(99)
|
||||
.set_in_progress_attachment_transferred_bytes(512)
|
||||
.set_in_progress_attachment_total_bytes(2048)
|
||||
.set_binding_id("binding")
|
||||
.build();
|
||||
|
||||
DbusDictionary result = TransferMetadataToDbus(metadata, *attachments);
|
||||
|
||||
EXPECT_EQ(result.at("status").get<std::string>(), "kInProgress");
|
||||
EXPECT_EQ(result.at("progress").get<double>(), 25);
|
||||
EXPECT_EQ(result.at("transferred_bytes").get<int64_t>(), 1024);
|
||||
EXPECT_EQ(result.at("total_bytes").get<int64_t>(), 0);
|
||||
EXPECT_EQ(result.at("transfer_speed").get<int64_t>(), 256);
|
||||
EXPECT_EQ(result.at("estimated_time_remaining").get<int64_t>(), 4);
|
||||
EXPECT_EQ(result.at("total_attachments_count").get<int>(), 2);
|
||||
EXPECT_EQ(result.at("transferred_attachments_count").get<int>(), 1);
|
||||
EXPECT_FALSE(result.at("is_final_status").get<bool>());
|
||||
EXPECT_TRUE(result.at("is_self_share").get<bool>());
|
||||
EXPECT_EQ(result.at("binding_id").get<std::string>(), "binding");
|
||||
EXPECT_EQ(result.at("token").get<std::string>(), "1234");
|
||||
EXPECT_EQ(result.at("in_progress_attachment_id").get<int64_t>(), 99);
|
||||
EXPECT_EQ(
|
||||
result.at("in_progress_attachment_transferred_bytes").get<int64_t>(),
|
||||
512);
|
||||
EXPECT_EQ(result.at("in_progress_attachment_total_bytes").get<int64_t>(),
|
||||
2048);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace nearby::sharing::linux
|
||||
@@ -3,8 +3,8 @@
|
||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__
|
||||
#define __sdbuscpp___home_lasan_Dev_nearby_latest_sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__
|
||||
#ifndef __sdbuscpp__sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__
|
||||
#define __sdbuscpp__sharing_linux_daemon_nearby_sharing_server_h__adaptor__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <string>
|
||||
|
||||
Reference in New Issue
Block a user