added nearby connections API for linux

This commit is contained in:
Lasan Mahaliyana
2026-03-26 20:50:22 +05:30
parent 29e1aa0555
commit ae4a89fa7b
4 changed files with 1014 additions and 14 deletions
+53 -14
View File
@@ -13,7 +13,8 @@
# limitations under the License.
licenses(["notice"])
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("@rules_cc//cc/private/rules_impl:cc_static_library.bzl", "cc_static_library")
cc_library(
name = "linux_sharing_platform",
@@ -46,17 +47,6 @@ cc_library(
],
)
cc_library(
name = "system_clock_shim",
srcs = ["platform/system_clock_shim.cc"],
alwayslink = True,
visibility = ["//visibility:private"],
deps = [
"//internal/platform:types",
"@com_google_absl//absl/time",
],
)
cc_library(
name = "nearby_sharing_api",
srcs = ["nearby_sharing_api.cc"],
@@ -65,8 +55,8 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
":linux_sharing_platform",
":system_clock_shim",
"//internal/base:file_path",
"//internal/platform/implementation/linux:system_clock",
"//sharing:attachments",
"//sharing:nearby_sharing_service",
"//sharing/analytics",
@@ -74,6 +64,25 @@ cc_library(
],
)
cc_library(
name = "nearby_connections_api",
srcs = ["nearby_connections_api.cc"],
hdrs = ["nearby_connections_api.h"],
alwayslink = True,
visibility = ["//visibility:public"],
deps = [
":linux_sharing_platform",
"//internal/base:file_path",
"//sharing/internal/public:nearby_context",
"//sharing:nearby_sharing_service",
],
)
cc_static_library(
name = "nearby_sharing_api_static",
deps = [":nearby_sharing_api"],
)
cc_binary(
name = "nearby_sharing_api_shared",
linkshared = True,
@@ -87,13 +96,43 @@ cc_binary(
],
deps = [
":linux_sharing_platform",
":system_clock_shim",
"//internal/base:file_path",
"//internal/platform/implementation:platform",
"//internal/platform/implementation/linux",
"//internal/platform/implementation/linux:system_clock",
"//sharing:attachments",
"//sharing:nearby_sharing_service",
"//sharing/analytics",
"//sharing/local_device_data",
],
)
cc_binary(
name = "nearby_connections_api_shared",
linkshared = True,
srcs = [
"nearby_connections_api.cc",
"nearby_connections_api.h",
],
visibility = ["//visibility:public"],
linkopts = [
"-Wl,--exclude-libs,ALL",
],
deps = [
":linux_sharing_platform",
"//internal/base:file_path",
"//internal/platform/implementation:platform",
"//internal/platform/implementation/linux",
"//sharing/internal/public:nearby_context",
"//sharing:nearby_sharing_service",
],
)
cc_test(
name = "nearby_connections_api_test",
srcs = ["nearby_connections_api_test.cc"],
deps = [
":nearby_connections_api",
"@com_google_googletest//:gtest_main",
],
)
+649
View File
@@ -0,0 +1,649 @@
#include "sharing/linux/nearby_connections_api.h"
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/time/time.h"
#include "internal/base/file_path.h"
#include "internal/interop/authentication_status.h"
#include "sharing/internal/public/context_impl.h"
#include "sharing/linux/platform/linux_sharing_platform.h"
#include "sharing/nearby_connections_service.h"
#include "sharing/nearby_connections_service_impl.h"
namespace nearby::sharing {
namespace {
using NativeService = nearby::sharing::NearbyConnectionsService;
NearbyConnectionsApi::StatusCode ToFacadeStatus(Status status) {
switch (status) {
case Status::kSuccess:
return NearbyConnectionsApi::StatusCode::kSuccess;
case Status::kError:
return NearbyConnectionsApi::StatusCode::kError;
case Status::kOutOfOrderApiCall:
return NearbyConnectionsApi::StatusCode::kOutOfOrderApiCall;
case Status::kAlreadyHaveActiveStrategy:
return NearbyConnectionsApi::StatusCode::kAlreadyHaveActiveStrategy;
case Status::kAlreadyAdvertising:
return NearbyConnectionsApi::StatusCode::kAlreadyAdvertising;
case Status::kAlreadyDiscovering:
return NearbyConnectionsApi::StatusCode::kAlreadyDiscovering;
case Status::kAlreadyListening:
return NearbyConnectionsApi::StatusCode::kAlreadyListening;
case Status::kEndpointIOError:
return NearbyConnectionsApi::StatusCode::kEndpointIOError;
case Status::kEndpointUnknown:
return NearbyConnectionsApi::StatusCode::kEndpointUnknown;
case Status::kConnectionRejected:
return NearbyConnectionsApi::StatusCode::kConnectionRejected;
case Status::kAlreadyConnectedToEndpoint:
return NearbyConnectionsApi::StatusCode::kAlreadyConnectedToEndpoint;
case Status::kNotConnectedToEndpoint:
return NearbyConnectionsApi::StatusCode::kNotConnectedToEndpoint;
case Status::kBluetoothError:
return NearbyConnectionsApi::StatusCode::kBluetoothError;
case Status::kBleError:
return NearbyConnectionsApi::StatusCode::kBleError;
case Status::kWifiLanError:
return NearbyConnectionsApi::StatusCode::kWifiLanError;
case Status::kPayloadUnknown:
return NearbyConnectionsApi::StatusCode::kPayloadUnknown;
case Status::kReset:
return NearbyConnectionsApi::StatusCode::kReset;
case Status::kTimeout:
return NearbyConnectionsApi::StatusCode::kTimeout;
case Status::kUnknown:
return NearbyConnectionsApi::StatusCode::kUnknown;
case Status::kNextValue:
break;
}
return NearbyConnectionsApi::StatusCode::kUnknown;
}
NearbyConnectionsApi::AuthenticationStatus ToFacadeAuthenticationStatus(
::nearby::AuthenticationStatus status) {
switch (status) {
case ::nearby::AuthenticationStatus::kUnknown:
return NearbyConnectionsApi::AuthenticationStatus::kUnknown;
case ::nearby::AuthenticationStatus::kSuccess:
return NearbyConnectionsApi::AuthenticationStatus::kSuccess;
case ::nearby::AuthenticationStatus::kFailure:
return NearbyConnectionsApi::AuthenticationStatus::kFailure;
}
return NearbyConnectionsApi::AuthenticationStatus::kUnknown;
}
NearbyConnectionsApi::DistanceInfo ToFacadeDistanceInfo(
nearby::sharing::DistanceInfo distance_info) {
switch (distance_info) {
case nearby::sharing::DistanceInfo::kUnknown:
return NearbyConnectionsApi::DistanceInfo::kUnknown;
case nearby::sharing::DistanceInfo::kVeryClose:
return NearbyConnectionsApi::DistanceInfo::kVeryClose;
case nearby::sharing::DistanceInfo::kClose:
return NearbyConnectionsApi::DistanceInfo::kClose;
case nearby::sharing::DistanceInfo::kFar:
return NearbyConnectionsApi::DistanceInfo::kFar;
}
return NearbyConnectionsApi::DistanceInfo::kUnknown;
}
NearbyConnectionsApi::Medium ToFacadeMedium(nearby::sharing::Medium medium) {
switch (medium) {
case nearby::sharing::Medium::kUnknown:
return NearbyConnectionsApi::Medium::kUnknown;
case nearby::sharing::Medium::kMdns:
return NearbyConnectionsApi::Medium::kMdns;
case nearby::sharing::Medium::kBluetooth:
return NearbyConnectionsApi::Medium::kBluetooth;
case nearby::sharing::Medium::kWifiHotspot:
return NearbyConnectionsApi::Medium::kWifiHotspot;
case nearby::sharing::Medium::kBle:
return NearbyConnectionsApi::Medium::kBle;
case nearby::sharing::Medium::kWifiLan:
return NearbyConnectionsApi::Medium::kWifiLan;
case nearby::sharing::Medium::kWifiAware:
return NearbyConnectionsApi::Medium::kWifiAware;
case nearby::sharing::Medium::kNfc:
return NearbyConnectionsApi::Medium::kNfc;
case nearby::sharing::Medium::kWifiDirect:
return NearbyConnectionsApi::Medium::kWifiDirect;
case nearby::sharing::Medium::kWebRtc:
return NearbyConnectionsApi::Medium::kWebRtc;
case nearby::sharing::Medium::kBleL2Cap:
return NearbyConnectionsApi::Medium::kBleL2Cap;
}
return NearbyConnectionsApi::Medium::kUnknown;
}
nearby::sharing::Strategy ToNativeStrategy(
NearbyConnectionsApi::Strategy strategy) {
switch (strategy) {
case NearbyConnectionsApi::Strategy::kP2pCluster:
return nearby::sharing::Strategy::kP2pCluster;
case NearbyConnectionsApi::Strategy::kP2pStar:
return nearby::sharing::Strategy::kP2pStar;
case NearbyConnectionsApi::Strategy::kP2pPointToPoint:
return nearby::sharing::Strategy::kP2pPointToPoint;
}
return nearby::sharing::Strategy::kP2pCluster;
}
nearby::sharing::MediumSelection ToNativeMediumSelection(
const NearbyConnectionsApi::MediumSelection& selection) {
nearby::sharing::MediumSelection native_selection;
native_selection.bluetooth = selection.bluetooth;
native_selection.ble = selection.ble;
native_selection.web_rtc = selection.web_rtc;
native_selection.wifi_lan = selection.wifi_lan;
native_selection.wifi_hotspot = selection.wifi_hotspot;
return native_selection;
}
nearby::sharing::AdvertisingOptions ToNativeAdvertisingOptions(
const NearbyConnectionsApi::AdvertisingOptions& options) {
nearby::sharing::AdvertisingOptions native_options;
native_options.strategy = ToNativeStrategy(options.strategy);
native_options.allowed_mediums =
ToNativeMediumSelection(options.allowed_mediums);
native_options.auto_upgrade_bandwidth = options.auto_upgrade_bandwidth;
native_options.enforce_topology_constraints =
options.enforce_topology_constraints;
native_options.enable_bluetooth_listening =
options.enable_bluetooth_listening;
native_options.enable_webrtc_listening = options.enable_webrtc_listening;
native_options.use_stable_endpoint_id = options.use_stable_endpoint_id;
native_options.force_new_endpoint_id = options.force_new_endpoint_id;
native_options.fast_advertisement_service_uuid =
nearby::sharing::Uuid(options.fast_advertisement_service_uuid);
return native_options;
}
nearby::sharing::DiscoveryOptions ToNativeDiscoveryOptions(
const NearbyConnectionsApi::DiscoveryOptions& options) {
nearby::sharing::DiscoveryOptions native_options;
native_options.strategy = ToNativeStrategy(options.strategy);
native_options.allowed_mediums =
ToNativeMediumSelection(options.allowed_mediums);
if (options.has_fast_advertisement_service_uuid) {
native_options.fast_advertisement_service_uuid =
nearby::sharing::Uuid(options.fast_advertisement_service_uuid.uuid);
}
native_options.is_out_of_band_connection =
options.is_out_of_band_connection;
if (options.has_alternate_service_uuid) {
native_options.alternate_service_uuid = options.alternate_service_uuid;
}
return native_options;
}
nearby::sharing::ConnectionOptions ToNativeConnectionOptions(
const NearbyConnectionsApi::ConnectionOptions& options) {
nearby::sharing::ConnectionOptions native_options;
native_options.allowed_mediums =
ToNativeMediumSelection(options.allowed_mediums);
if (!options.remote_bluetooth_mac_address.empty()) {
native_options.remote_bluetooth_mac_address =
options.remote_bluetooth_mac_address;
}
if (options.has_keep_alive_interval_millis &&
options.keep_alive_interval_millis >= 0) {
native_options.keep_alive_interval =
absl::Milliseconds(options.keep_alive_interval_millis);
}
if (options.has_keep_alive_timeout_millis &&
options.keep_alive_timeout_millis >= 0) {
native_options.keep_alive_timeout =
absl::Milliseconds(options.keep_alive_timeout_millis);
}
native_options.non_disruptive_hotspot_mode =
options.non_disruptive_hotspot_mode;
return native_options;
}
NearbyConnectionsApi::ConnectionInfo ToFacadeConnectionInfo(
const nearby::sharing::ConnectionInfo& info) {
NearbyConnectionsApi::ConnectionInfo facade_info;
facade_info.authentication_token = info.authentication_token;
facade_info.raw_authentication_token = info.raw_authentication_token;
facade_info.endpoint_info = info.endpoint_info;
facade_info.is_incoming_connection = info.is_incoming_connection;
facade_info.connection_layer_status =
ToFacadeStatus(info.connection_layer_status);
facade_info.authentication_status =
ToFacadeAuthenticationStatus(info.authentication_status);
return facade_info;
}
NearbyConnectionsApi::DiscoveredEndpointInfo ToFacadeDiscoveredEndpointInfo(
const nearby::sharing::DiscoveredEndpointInfo& info) {
NearbyConnectionsApi::DiscoveredEndpointInfo facade_info;
facade_info.endpoint_info = info.endpoint_info;
facade_info.service_id = info.service_id;
return facade_info;
}
NearbyConnectionsApi::PayloadStatus ToFacadePayloadStatus(
nearby::sharing::PayloadStatus status) {
switch (status) {
case nearby::sharing::PayloadStatus::kSuccess:
return NearbyConnectionsApi::PayloadStatus::kSuccess;
case nearby::sharing::PayloadStatus::kFailure:
return NearbyConnectionsApi::PayloadStatus::kFailure;
case nearby::sharing::PayloadStatus::kInProgress:
return NearbyConnectionsApi::PayloadStatus::kInProgress;
case nearby::sharing::PayloadStatus::kCanceled:
return NearbyConnectionsApi::PayloadStatus::kCanceled;
}
return NearbyConnectionsApi::PayloadStatus::kFailure;
}
NearbyConnectionsApi::Payload ToFacadePayload(
const nearby::sharing::Payload& payload) {
NearbyConnectionsApi::Payload facade_payload;
facade_payload.id = payload.id;
switch (payload.content.type) {
case nearby::sharing::PayloadContent::Type::kBytes:
facade_payload.type = NearbyConnectionsApi::PayloadType::kBytes;
facade_payload.bytes = payload.content.bytes_payload.bytes;
break;
case nearby::sharing::PayloadContent::Type::kFile:
facade_payload.type = NearbyConnectionsApi::PayloadType::kFile;
facade_payload.file_path =
payload.content.file_payload.file_path.ToString();
facade_payload.parent_folder = payload.content.file_payload.parent_folder;
break;
case nearby::sharing::PayloadContent::Type::kUnknown:
case nearby::sharing::PayloadContent::Type::kStream:
facade_payload.type = NearbyConnectionsApi::PayloadType::kUnknown;
break;
}
return facade_payload;
}
std::unique_ptr<nearby::sharing::Payload> ToNativePayload(
NearbyConnectionsApi::Payload payload) {
switch (payload.type) {
case NearbyConnectionsApi::PayloadType::kBytes:
return std::make_unique<nearby::sharing::Payload>(
payload.id, std::move(payload.bytes));
case NearbyConnectionsApi::PayloadType::kFile:
return std::make_unique<nearby::sharing::Payload>(
payload.id, FilePath(payload.file_path), payload.parent_folder);
case NearbyConnectionsApi::PayloadType::kUnknown:
return nullptr;
}
return nullptr;
}
NearbyConnectionsApi::PayloadTransferUpdate ToFacadePayloadTransferUpdate(
const nearby::sharing::PayloadTransferUpdate& update) {
NearbyConnectionsApi::PayloadTransferUpdate facade_update;
facade_update.payload_id = update.payload_id;
facade_update.status = ToFacadePayloadStatus(update.status);
facade_update.total_bytes = update.total_bytes;
facade_update.bytes_transferred = update.bytes_transferred;
return facade_update;
}
} // namespace
class NearbyConnectionsApi::Impl {
public:
struct ListenerState {
std::mutex mutex;
NearbyConnectionsApi::Listener listener;
};
Impl()
: platform(),
context(platform),
service(std::make_unique<NearbyConnectionsServiceImpl>(
context.GetConnectivityManager(), /*event_logger=*/nullptr)),
listener_state(std::make_shared<ListenerState>()) {}
static NearbyConnectionsApi::Listener CopyListener(
const std::shared_ptr<ListenerState>& listener_state) {
std::scoped_lock lock(listener_state->mutex);
return listener_state->listener;
}
static NativeService::ConnectionListener BuildConnectionListener(
const std::shared_ptr<ListenerState>& listener_state) {
NativeService::ConnectionListener listener;
listener.initiated_cb =
[listener_state](const std::string& endpoint_id,
const nearby::sharing::ConnectionInfo& info) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (!listener_copy.connection_initiated_cb) {
return;
}
listener_copy.connection_initiated_cb(endpoint_id,
ToFacadeConnectionInfo(info));
};
listener.accepted_cb = [listener_state](const std::string& endpoint_id) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.connection_accepted_cb) {
listener_copy.connection_accepted_cb(endpoint_id);
}
};
listener.rejected_cb = [listener_state](const std::string& endpoint_id,
Status status) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.connection_rejected_cb) {
listener_copy.connection_rejected_cb(endpoint_id,
ToFacadeStatus(status));
}
};
listener.disconnected_cb = [listener_state](const std::string& endpoint_id) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.disconnected_cb) {
listener_copy.disconnected_cb(endpoint_id);
}
};
listener.bandwidth_changed_cb =
[listener_state](const std::string& endpoint_id,
nearby::sharing::Medium medium) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.bandwidth_changed_cb) {
listener_copy.bandwidth_changed_cb(endpoint_id,
ToFacadeMedium(medium));
}
};
return listener;
}
static NativeService::DiscoveryListener BuildDiscoveryListener(
const std::shared_ptr<ListenerState>& listener_state) {
NativeService::DiscoveryListener listener;
listener.endpoint_found_cb =
[listener_state](const std::string& endpoint_id,
const nearby::sharing::DiscoveredEndpointInfo& info) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.endpoint_found_cb) {
listener_copy.endpoint_found_cb(
endpoint_id, ToFacadeDiscoveredEndpointInfo(info));
}
};
listener.endpoint_lost_cb = [listener_state](const std::string& endpoint_id) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.endpoint_lost_cb) {
listener_copy.endpoint_lost_cb(endpoint_id);
}
};
listener.endpoint_distance_changed_cb =
[listener_state](const std::string& endpoint_id,
nearby::sharing::DistanceInfo distance_info) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.endpoint_distance_changed_cb) {
listener_copy.endpoint_distance_changed_cb(
endpoint_id, ToFacadeDistanceInfo(distance_info));
}
};
return listener;
}
static NativeService::PayloadListener BuildPayloadListener(
const std::shared_ptr<ListenerState>& listener_state) {
NativeService::PayloadListener listener;
listener.payload_cb = [listener_state](absl::string_view endpoint_id,
nearby::sharing::Payload payload) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.payload_received_cb) {
listener_copy.payload_received_cb(std::string(endpoint_id),
ToFacadePayload(payload));
}
};
listener.payload_progress_cb =
[listener_state](
absl::string_view endpoint_id,
const nearby::sharing::PayloadTransferUpdate& update) {
NearbyConnectionsApi::Listener listener_copy =
CopyListener(listener_state);
if (listener_copy.payload_transfer_update_cb) {
listener_copy.payload_transfer_update_cb(
std::string(endpoint_id), ToFacadePayloadTransferUpdate(update));
}
};
return listener;
}
LinuxSharingPlatform platform;
::nearby::ContextImpl context;
std::unique_ptr<NativeService> service;
std::shared_ptr<ListenerState> listener_state;
};
NearbyConnectionsApi::NearbyConnectionsApi() : impl_(std::make_unique<Impl>()) {}
NearbyConnectionsApi::~NearbyConnectionsApi() = default;
NearbyConnectionsApi::NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept =
default;
NearbyConnectionsApi& NearbyConnectionsApi::operator=(
NearbyConnectionsApi&&) noexcept = default;
void NearbyConnectionsApi::SetListener(Listener listener) {
std::scoped_lock lock(impl_->listener_state->mutex);
impl_->listener_state->listener = std::move(listener);
}
void NearbyConnectionsApi::StartAdvertising(
const std::string& service_id, const std::vector<uint8_t>& endpoint_info,
const AdvertisingOptions& options,
std::function<void(StatusCode)> callback) {
impl_->service->StartAdvertising(
service_id, endpoint_info, ToNativeAdvertisingOptions(options),
Impl::BuildConnectionListener(impl_->listener_state),
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::StopAdvertising(
const std::string& service_id, std::function<void(StatusCode)> callback) {
impl_->service->StopAdvertising(
service_id, [callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::StartDiscovery(
const std::string& service_id, const DiscoveryOptions& options,
std::function<void(StatusCode)> callback) {
impl_->service->StartDiscovery(
service_id, ToNativeDiscoveryOptions(options),
Impl::BuildDiscoveryListener(impl_->listener_state),
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::StopDiscovery(
const std::string& service_id, std::function<void(StatusCode)> callback) {
impl_->service->StopDiscovery(
service_id, [callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::RequestConnection(
const std::string& service_id, const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id, const ConnectionOptions& options,
std::function<void(StatusCode)> callback) {
impl_->service->RequestConnection(
service_id, endpoint_info, endpoint_id, ToNativeConnectionOptions(options),
Impl::BuildConnectionListener(impl_->listener_state),
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::DisconnectFromEndpoint(
const std::string& service_id, const std::string& endpoint_id,
std::function<void(StatusCode)> callback) {
impl_->service->DisconnectFromEndpoint(
service_id, endpoint_id,
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::SendPayload(
const std::string& service_id, const std::vector<std::string>& endpoint_ids,
Payload payload, std::function<void(StatusCode)> callback) {
std::unique_ptr<nearby::sharing::Payload> native_payload =
ToNativePayload(std::move(payload));
if (native_payload == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
impl_->service->SendPayload(
service_id, endpoint_ids, std::move(native_payload),
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::CancelPayload(
const std::string& service_id, int64_t payload_id,
std::function<void(StatusCode)> callback) {
impl_->service->CancelPayload(
service_id, payload_id,
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::InitiateBandwidthUpgrade(
const std::string& service_id, const std::string& endpoint_id,
std::function<void(StatusCode)> callback) {
impl_->service->InitiateBandwidthUpgrade(
service_id, endpoint_id,
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::AcceptConnection(
const std::string& service_id, const std::string& endpoint_id,
std::function<void(StatusCode)> callback) {
impl_->service->AcceptConnection(
service_id, endpoint_id, Impl::BuildPayloadListener(impl_->listener_state),
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::StopAllEndpoints(
std::function<void(StatusCode)> callback) {
impl_->service->StopAllEndpoints(
[callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::SetCustomSavePath(
const std::string& path, std::function<void(StatusCode)> callback) {
impl_->service->SetCustomSavePath(
path, [callback = std::move(callback)](Status status) mutable {
if (callback) {
callback(ToFacadeStatus(status));
}
});
}
void NearbyConnectionsApi::OverrideSavePath(const std::string& endpoint_id,
const std::string& path) {
impl_->service->OverrideSavePath(endpoint_id, path);
}
std::string NearbyConnectionsApi::Dump() const { return impl_->service->Dump(); }
std::string NearbyConnectionsApi::StatusCodeToString(StatusCode status) {
switch (status) {
case StatusCode::kSuccess:
return "Success";
case StatusCode::kError:
return "Error";
case StatusCode::kOutOfOrderApiCall:
return "OutOfOrderApiCall";
case StatusCode::kAlreadyHaveActiveStrategy:
return "AlreadyHaveActiveStrategy";
case StatusCode::kAlreadyAdvertising:
return "AlreadyAdvertising";
case StatusCode::kAlreadyDiscovering:
return "AlreadyDiscovering";
case StatusCode::kAlreadyListening:
return "AlreadyListening";
case StatusCode::kEndpointIOError:
return "EndpointIOError";
case StatusCode::kEndpointUnknown:
return "EndpointUnknown";
case StatusCode::kConnectionRejected:
return "ConnectionRejected";
case StatusCode::kAlreadyConnectedToEndpoint:
return "AlreadyConnectedToEndpoint";
case StatusCode::kNotConnectedToEndpoint:
return "NotConnectedToEndpoint";
case StatusCode::kBluetoothError:
return "BluetoothError";
case StatusCode::kBleError:
return "BleError";
case StatusCode::kWifiLanError:
return "WifiLanError";
case StatusCode::kPayloadUnknown:
return "PayloadUnknown";
case StatusCode::kReset:
return "Reset";
case StatusCode::kTimeout:
return "Timeout";
case StatusCode::kUnknown:
return "Unknown";
}
return "Unknown";
}
} // namespace nearby::sharing
+268
View File
@@ -0,0 +1,268 @@
// Copyright 2026
//
// Thin app-facing API for NearbyConnectionsService on Linux that avoids
// exposing internal Nearby headers to external consumers.
#ifndef SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
#define SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace nearby {
namespace sharing {
class __attribute__((visibility("default"))) NearbyConnectionsApi {
public:
enum class StatusCode {
kSuccess = 0,
kError = 1,
kOutOfOrderApiCall = 2,
kAlreadyHaveActiveStrategy = 3,
kAlreadyAdvertising = 4,
kAlreadyDiscovering = 5,
kAlreadyListening = 6,
kEndpointIOError = 7,
kEndpointUnknown = 8,
kConnectionRejected = 9,
kAlreadyConnectedToEndpoint = 10,
kNotConnectedToEndpoint = 11,
kBluetoothError = 12,
kBleError = 13,
kWifiLanError = 14,
kPayloadUnknown = 15,
kReset = 16,
kTimeout = 17,
kUnknown = 18,
};
enum class Strategy {
kP2pCluster = 0,
kP2pStar = 1,
kP2pPointToPoint = 2,
};
enum class Medium {
kUnknown = 0,
kMdns = 1,
kBluetooth = 2,
kWifiHotspot = 3,
kBle = 4,
kWifiLan = 5,
kWifiAware = 6,
kNfc = 7,
kWifiDirect = 8,
kWebRtc = 9,
kBleL2Cap = 10,
};
enum class DistanceInfo {
kUnknown = 1,
kVeryClose = 2,
kClose = 3,
kFar = 4,
};
enum class AuthenticationStatus {
kUnknown = 0,
kSuccess = 1,
kFailure = 2,
};
enum class PayloadType {
kUnknown = 0,
kBytes = 1,
kFile = 2,
};
enum class PayloadStatus {
kSuccess = 0,
kFailure = 1,
kInProgress = 2,
kCanceled = 3,
};
struct Uuid {
std::string uuid;
};
struct MediumSelection {
bool bluetooth = true;
bool ble = true;
bool web_rtc = true;
bool wifi_lan = true;
bool wifi_hotspot = true;
};
struct AdvertisingOptions {
Strategy strategy = Strategy::kP2pCluster;
MediumSelection allowed_mediums;
bool auto_upgrade_bandwidth = true;
bool enforce_topology_constraints = true;
bool enable_bluetooth_listening = false;
bool enable_webrtc_listening = false;
bool use_stable_endpoint_id = false;
bool force_new_endpoint_id = false;
std::string fast_advertisement_service_uuid;
};
struct DiscoveryOptions {
Strategy strategy = Strategy::kP2pCluster;
MediumSelection allowed_mediums;
bool has_fast_advertisement_service_uuid = false;
Uuid fast_advertisement_service_uuid;
bool is_out_of_band_connection = false;
bool has_alternate_service_uuid = false;
uint16_t alternate_service_uuid = 0;
};
struct ConnectionOptions {
MediumSelection allowed_mediums;
std::vector<uint8_t> remote_bluetooth_mac_address;
bool has_keep_alive_interval_millis = false;
int64_t keep_alive_interval_millis = 0;
bool has_keep_alive_timeout_millis = false;
int64_t keep_alive_timeout_millis = 0;
bool non_disruptive_hotspot_mode = false;
};
struct ConnectionInfo {
std::string authentication_token;
std::vector<uint8_t> raw_authentication_token;
std::vector<uint8_t> endpoint_info;
bool is_incoming_connection = false;
StatusCode connection_layer_status = StatusCode::kUnknown;
AuthenticationStatus authentication_status =
AuthenticationStatus::kUnknown;
};
struct DiscoveredEndpointInfo {
std::vector<uint8_t> endpoint_info;
std::string service_id;
};
struct PayloadTransferUpdate {
int64_t payload_id = 0;
PayloadStatus status = PayloadStatus::kInProgress;
uint64_t total_bytes = 0;
uint64_t bytes_transferred = 0;
};
struct Payload {
int64_t id = 0;
PayloadType type = PayloadType::kUnknown;
std::vector<uint8_t> bytes;
std::string file_path;
std::string parent_folder;
static Payload FromBytes(int64_t id, std::vector<uint8_t> bytes) {
Payload payload;
payload.id = id;
payload.type = PayloadType::kBytes;
payload.bytes = std::move(bytes);
return payload;
}
static Payload FromFile(int64_t id, std::string file_path,
std::string parent_folder = {}) {
Payload payload;
payload.id = id;
payload.type = PayloadType::kFile;
payload.file_path = std::move(file_path);
payload.parent_folder = std::move(parent_folder);
return payload;
}
};
struct Listener {
std::function<void(const std::string&, const DiscoveredEndpointInfo&)>
endpoint_found_cb;
std::function<void(const std::string&)> endpoint_lost_cb;
std::function<void(const std::string&, DistanceInfo)>
endpoint_distance_changed_cb;
std::function<void(const std::string&, const ConnectionInfo&)>
connection_initiated_cb;
std::function<void(const std::string&)> connection_accepted_cb;
std::function<void(const std::string&, StatusCode)> connection_rejected_cb;
std::function<void(const std::string&)> disconnected_cb;
std::function<void(const std::string&, Medium)> bandwidth_changed_cb;
std::function<void(const std::string&, const Payload&)>
payload_received_cb;
std::function<void(const std::string&, const PayloadTransferUpdate&)>
payload_transfer_update_cb;
};
NearbyConnectionsApi();
~NearbyConnectionsApi();
NearbyConnectionsApi(const NearbyConnectionsApi&) = delete;
NearbyConnectionsApi& operator=(const NearbyConnectionsApi&) = delete;
NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept;
NearbyConnectionsApi& operator=(NearbyConnectionsApi&&) noexcept;
void SetListener(Listener listener);
void StartAdvertising(const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const AdvertisingOptions& options,
std::function<void(StatusCode)> callback);
void StopAdvertising(const std::string& service_id,
std::function<void(StatusCode)> callback);
void StartDiscovery(const std::string& service_id,
const DiscoveryOptions& options,
std::function<void(StatusCode)> callback);
void StopDiscovery(const std::string& service_id,
std::function<void(StatusCode)> callback);
void RequestConnection(const std::string& service_id,
const std::vector<uint8_t>& endpoint_info,
const std::string& endpoint_id,
const ConnectionOptions& options,
std::function<void(StatusCode)> callback);
void DisconnectFromEndpoint(const std::string& service_id,
const std::string& endpoint_id,
std::function<void(StatusCode)> callback);
void SendPayload(const std::string& service_id,
const std::vector<std::string>& endpoint_ids,
Payload payload,
std::function<void(StatusCode)> callback);
void CancelPayload(const std::string& service_id, int64_t payload_id,
std::function<void(StatusCode)> callback);
void InitiateBandwidthUpgrade(const std::string& service_id,
const std::string& endpoint_id,
std::function<void(StatusCode)> callback);
void AcceptConnection(const std::string& service_id,
const std::string& endpoint_id,
std::function<void(StatusCode)> callback);
void StopAllEndpoints(std::function<void(StatusCode)> callback);
void SetCustomSavePath(const std::string& path,
std::function<void(StatusCode)> callback);
void OverrideSavePath(const std::string& endpoint_id,
const std::string& path);
std::string Dump() const;
static std::string StatusCodeToString(StatusCode status);
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace sharing
} // namespace nearby
#endif // SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
@@ -0,0 +1,44 @@
#include "sharing/linux/nearby_connections_api.h"
#include <vector>
#include "gtest/gtest.h"
namespace nearby::sharing {
namespace {
TEST(NearbyConnectionsApiTest, PayloadFromBytesSetsFields) {
NearbyConnectionsApi::Payload payload =
NearbyConnectionsApi::Payload::FromBytes(42, {1, 2, 3});
EXPECT_EQ(payload.id, 42);
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kBytes);
EXPECT_EQ(payload.bytes, (std::vector<uint8_t>{1, 2, 3}));
EXPECT_TRUE(payload.file_path.empty());
}
TEST(NearbyConnectionsApiTest, PayloadFromFileSetsFields) {
NearbyConnectionsApi::Payload payload =
NearbyConnectionsApi::Payload::FromFile(7, "/tmp/test.txt", "tmp");
EXPECT_EQ(payload.id, 7);
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kFile);
EXPECT_EQ(payload.file_path, "/tmp/test.txt");
EXPECT_EQ(payload.parent_folder, "tmp");
EXPECT_TRUE(payload.bytes.empty());
}
TEST(NearbyConnectionsApiTest, StatusCodeToStringCoversRepresentativeValues) {
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
NearbyConnectionsApi::StatusCode::kSuccess),
"Success");
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
NearbyConnectionsApi::StatusCode::kConnectionRejected),
"ConnectionRejected");
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
NearbyConnectionsApi::StatusCode::kUnknown),
"Unknown");
}
} // namespace
} // namespace nearby::sharing