refactored linux sharing platform

This commit is contained in:
Lasan Mahaliyana
2026-06-19 05:06:02 +05:30
parent be227f8466
commit 14fbeb67c4
28 changed files with 1242 additions and 5237 deletions
-292
View File
@@ -1,292 +0,0 @@
# Nearby Sharing Linux - Known Limitations and Compatibility
## Authentication Failure Issue
### Problem
When trying to connect between the Linux implementation and Android Nearby Share, you may see:
```
[NS_AUTH] send-side ConnectionFailure
AUTH_FAILURE
```
### Root Cause
The current Linux implementation (`nearby_sharing_service_linux`) is a **simplified version** that:
- ✅ Implements device discovery (BLE advertising/scanning)
- ✅ Implements connection establishment (via Nearby Connections)
- ✅ Implements basic payload transfer
-**Does NOT implement the full Nearby Sharing protocol**
The full Nearby Sharing protocol (used by Android/ChromeOS) requires:
1. **Introduction Frame Exchange**
- Sender sends an introduction with file metadata
- Receiver parses and displays file info before accepting
2. **Paired Key Verification**
- Security handshake using certificates
- PIN/Token verification for untrusted devices
- Requires `NearbyShareCertificateManager`
3. **Protocol Frames**
- Structured message format (protobuf-based)
- Control frames for connection, progress, cancellation
- Metadata exchange before file transfer
4. **Certificate Management**
- Public/Private certificate pairs
- Contact-based sharing
- Visibility controls
### What the Linux Implementation Lacks
```cpp
// Missing in nearby_sharing_service_linux.cc:
// 1. Certificate Manager (returns nullptr)
NearbyShareCertificateManager* GetCertificateManager() {
return nullptr; // ❌ Not implemented
}
// 2. Contact Manager (returns nullptr)
NearbyShareContactManager* GetContactManager() {
return nullptr; // ❌ Not implemented
}
// 3. Settings Manager (returns nullptr)
NearbyShareSettings* GetSettings() {
return nullptr; // ❌ Not implemented
}
// 4. No introduction frame exchange
// 5. No paired key verification
// 6. No protocol frame parsing
```
## Workarounds
### Option 1: Linux-to-Linux Only
The current implementation **DOES work** for Linux-to-Linux transfers:
```bash
# Device 1
./nearby_sharing_app
> Choose 1 (Start as Receiver)
# Device 2
./nearby_sharing_app
> Choose 2 (Start as Sender)
> Choose 4 (Send file)
```
Both devices use the same simplified protocol, so they can communicate.
### Option 2: Use the Full Implementation
To communicate with Android/ChromeOS, you need the **full Nearby Sharing implementation** which is located in:
```
sharing/nearby_sharing_service_impl.h
sharing/nearby_sharing_service_impl.cc
```
This is the complete implementation used by ChromeOS and includes all the protocol handling.
**However**, this requires:
- Platform-specific implementations (preferences, UI, etc.)
- Certificate storage and management
- Contact synchronization
- More complex setup
### Option 3: Implement Protocol Handlers
Add the missing protocol components to `nearby_sharing_service_linux`:
#### A. Add Introduction Frame Support
```cpp
// sharing/linux/nearby_sharing_service_linux.cc
#include "sharing/proto/wire_format.pb.h"
void NearbySharingServiceLinux::SendIntroductionFrame(
const std::string& endpoint_id,
const AttachmentContainer& attachments) {
// Build introduction message
nearby::sharing::service::proto::V1Frame frame;
frame.set_type(nearby::sharing::service::proto::V1Frame::INTRODUCTION);
auto* intro = frame.mutable_introduction();
// Add file metadata
for (const auto& file : attachments.GetFileAttachments()) {
auto* file_meta = intro->add_file_metadata();
file_meta->set_name(std::string(file.file_name()));
file_meta->set_size(file.size());
file_meta->set_mime_type(std::string(file.mime_type()));
file_meta->set_type(file.type());
}
// Add text metadata
for (const auto& text : attachments.GetTextAttachments()) {
auto* text_meta = intro->add_text_metadata();
text_meta->set_text_title(std::string(text.text_title()));
text_meta->set_size(text.size());
text_meta->set_type(text.type());
}
// Serialize and send
std::string serialized;
frame.SerializeToString(&serialized);
auto payload = std::make_unique<connections::Payload>(
ByteArray(serialized));
std::vector<std::string> endpoints = {endpoint_id};
core_->SendPayload(endpoints, std::move(*payload), [](auto status) {});
}
```
#### B. Add Certificate Stubs
```cpp
// For basic interop without real security:
class SimpleCertificateManager : public NearbyShareCertificateManager {
public:
// Return a dummy certificate
std::optional<NearbySharePrivateCertificate> GetPrivateCertificate() {
// Create minimal valid certificate
return CreateDummyCertificate();
}
};
```
#### C. Handle Paired Key Verification
```cpp
void NearbySharingServiceLinux::HandleConnectionAccepted(...) {
// Send connection response frame
nearby::sharing::service::proto::V1Frame frame;
frame.set_type(V1Frame::CONNECTION_RESPONSE);
auto* response = frame.mutable_connection_response();
response->set_status(ConnectionResponseFrame::ACCEPT);
// Send frame...
// Then send introduction
SendIntroductionFrame(endpoint_id, attachments);
// Wait for response before sending payloads
}
```
## Full Protocol Flow (What's Missing)
### Android/ChromeOS Protocol:
```
Sender Receiver
| |
| 1. Request Connection |
|------------------------------ >|
| |
| 2. Connection Initiated |
|< -----------------------------|
| |
| 3. Accept Connection |
|------------------------------ >|
| |
| 4. Send Introduction Frame | ← MISSING IN LINUX
| (file metadata, etc) |
|------------------------------ >|
| |
| | 5. Parse Introduction
| | 6. Show UI to user
| | 7. User accepts
| |
| 8. Send Connection Response | ← MISSING IN LINUX
| (ACCEPT/REJECT) |
|< -----------------------------|
| |
| 9. Paired Key Frame (optional) | ← MISSING IN LINUX
|< -----------------------------|
| |
| 10. Send Payloads |
|============================== >|
| |
```
### Linux Implementation (Current):
```
Sender Receiver
| |
| 1. Request Connection |
|------------------------------ >|
| |
| 2. Accept Connection |
|< -----------------------------|
| |
| 3. Send Payloads (DIRECTLY) | ← Android rejects this
|============================== >|
| |
```
## Recommended Solution
For **production use** with Android/ChromeOS devices:
1. **Use nearby_sharing_service_impl.h** - The full implementation
2. **Implement platform layer** - Provide Linux implementations of:
- PreferenceManager
- Context
- UI callbacks
- Certificate storage
Example structure:
```cpp
// Create Linux platform implementation
class LinuxNearbyShareContext : public Context { ... };
class LinuxPreferenceManager : public PreferenceManager { ... };
// Use full service implementation
auto service = NearbySharingServiceFactory::CreateNearbySharingService(
preference_manager,
notification_delegate,
context,
account_manager,
device_info);
```
For **testing/development** between Linux devices:
Continue using `nearby_sharing_service_linux` - it works fine for Linux-to-Linux transfers since both sides use the same simplified protocol.
## References
- Full implementation: `sharing/nearby_sharing_service_impl.{h,cc}`
- Protocol definitions: `sharing/proto/wire_format.proto`
- Certificate manager: `sharing/certificates/nearby_share_certificate_manager.h`
- Incoming frames reader: `sharing/incoming_frames_reader.{h,cc}`
- Outgoing frames writer: (implicitly in share sessions)
## Summary
| Feature | Linux Implementation | Full Implementation |
|---------|---------------------|---------------------|
| Discovery | ✅ Working | ✅ Working |
| Advertising | ✅ Working | ✅ Working |
| Connection | ✅ Basic | ✅ Full protocol |
| Introduction frames | ❌ Missing | ✅ Implemented |
| Certificates | ❌ Stub only | ✅ Full PKI |
| Android compat | ❌ Auth fails | ✅ Full compat |
| Linux-to-Linux | ✅ Working | ✅ Working |
| File transfer | ✅ Basic | ✅ Full |
| Progress tracking | ✅ Basic | ✅ Detailed |
The auth failure you're seeing is **expected behavior** - the Android side is correctly rejecting connections that don't follow the full Nearby Sharing protocol.
+35 -100
View File
@@ -19,12 +19,13 @@ load("@rules_cc//cc/private/rules_impl:cc_static_library.bzl", "cc_static_librar
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
refresh_compile_commands(
name = "refresh_compile_commands_sharing",
name = "refresh_compile_commands",
# Specify the targets of interest.
# For example, specify a dict of targets and any flags required to build.
targets = {
":linux_fast_init": "",
":fast_init": "",
":nearby_connections_manager": "",
},
# No need to add flags already in .bazelrc. They're automatically picked up.
# If you don't need flags, a list of targets is also okay, as is a single target string.
@@ -35,7 +36,7 @@ refresh_compile_commands(
cc_library(
name = "linux_fast_init",
name = "fast_init",
hdrs = [
"nearby_fast_init_ble_beacon.h",
"nearby_fast_init_manager.h",
@@ -50,29 +51,55 @@ cc_library(
]
)
cc_binary(
name = "fast_init_test",
srcs = ["nearby_fast_init.cc"],
deps = [":linux_fast_init"]
deps = [":fast_init"],
)
cc_binary(
name = "nearby_connections",
srcs = [
"nearby_connections.cc",
],
deps = [
":nearby_connections_manager",
":fast_init",
"//connections:core",
"//internal/platform/implementation/linux:linux"
]
)
cc_library(
name = "linux_sharing_platform",
srcs = ["platform/linux_sharing_platform.cc"],
srcs = [
"platform/linux_account_manager.cc",
"platform/linux_account_manager.h",
"platform/linux_platform_components.cc",
"platform/linux_platform_components.h",
"platform/linux_preference_manager.cc",
"platform/linux_preference_manager.h",
"platform/linux_sharing_platform.cc",
"platform/platform_util.cc",
"platform/platform_util.h",
],
hdrs = ["platform/linux_sharing_platform.h"],
visibility = ["//visibility:private"],
visibility = ["//visibility:public"],
deps = [
":fast_init",
"//internal/base:file_path",
"//internal/base:files",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:mac_address",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/implementation:account_manager",
"//internal/platform/implementation:platform",
"//internal/platform/implementation:signin_attempt",
"//internal/platform/implementation:types",
"//internal/platform/implementation/linux",
"//location/nearby/sharing/lib/account:account_manager",
"//location/nearby/sharing/lib/sync:sync_binding_prefs_cc_proto",
"//sharing/internal/api:platform",
"//sharing/internal/public:pref_names",
"//sharing/proto:share_cc_proto",
@@ -86,95 +113,3 @@ cc_library(
"@nlohmann_json//:json",
],
)
cc_library(
name = "nearby_sharing_api",
srcs = ["nearby_sharing_api.cc"],
hdrs = ["nearby_sharing_api.h"],
alwayslink = True,
visibility = ["//visibility:public"],
deps = [
":linux_sharing_platform",
"//internal/base:file_path",
"//internal/platform/implementation/linux:system_clock",
"//sharing:attachments",
"//sharing:nearby_sharing_service",
"//sharing/analytics",
"//sharing/local_device_data",
],
)
cc_library(
name = "nearby_connections_api",
srcs = ["nearby_connections_api.cc"],
hdrs = ["nearby_connections_api.h"],
alwayslink = True,
visibility = ["//visibility:public"],
deps = [
"//internal/platform:base",
":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,
srcs = [
"nearby_sharing_api.cc",
"nearby_sharing_api.h",
],
visibility = ["//visibility:public"],
linkopts = [
"-Wl,--exclude-libs,ALL",
],
deps = [
"//internal/platform:base",
":linux_sharing_platform",
"//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",
],
)
-592
View File
@@ -1,592 +0,0 @@
# Nearby Sharing Service Linux - Architecture & Implementation Guide
## Table of Contents
1. [Architecture Overview](#architecture-overview)
2. [How It Works](#how-it-works)
3. [Implementation Guide](#implementation-guide)
4. [Code Examples](#code-examples)
5. [Best Practices](#best-practices)
## Architecture Overview
### Component Hierarchy
```
NearbySharingServiceLinux
├── Connections Core (nearby connections layer)
│ ├── ServiceControllerRouter
│ └── Medium Management (BLE, WiFi)
├── Observers (UI/App notifications)
├── Send Surfaces (outgoing transfers)
│ ├── Transfer Callbacks
│ └── Discovery Callbacks
├── Receive Surfaces (incoming transfers)
│ └── Transfer Callbacks
└── Active Transfers
├── Endpoint Mapping
├── Transfer State
└── Attachment Container
```
### Key Classes
**NearbySharingServiceLinux**: Main service class
- Manages discovery, advertising, and transfers
- Built on top of Nearby Connections Core
- Handles lifecycle of send/receive surfaces
**TransferUpdateCallback**: Interface for transfer notifications
- Called on status changes (connecting, in-progress, complete)
- Provides progress information
- Reports errors and completion
**ShareTargetDiscoveredCallback**: Interface for discovery notifications
- Called when devices are found
- Called when devices are lost
- Called when device info updates
**AttachmentContainer**: Container for files and text
- Manages multiple attachments
- Supports files, text, and WiFi credentials
- Handles attachment lifecycle
## How It Works
### 1. Discovery & Advertising Flow
#### Sender (Discovers devices):
```
RegisterSendSurface (Foreground)
StartDiscoveryIfNeeded()
core_->StartDiscovery()
[BLE Scanning Starts]
endpoint_found_cb → ParseAdvertisement()
ShareTarget created
OnShareTargetDiscovered() callback
```
#### Receiver (Advertises availability):
```
RegisterReceiveSurface (Foreground)
StartAdvertisingIfNeeded()
BuildAdvertisement()
core_->StartAdvertising()
[BLE Advertising Starts]
[Visible to nearby senders]
```
### 2. Connection Establishment
```
Sender Receiver
| |
| RequestConnection() |
|------------------------------->|
| | connection_initiated_cb
| | (auto or manual accept)
| connection_initiated_cb |
|<-------------------------------|
| |
| AcceptConnection() | AcceptConnection()
|------------------------------->|
|<-------------------------------|
| |
| connection_accepted_cb | connection_accepted_cb
| |
[Connected - Ready for transfer]
```
### 3. File Transfer Flow
```
Sender Receiver
| |
| SendAttachments() |
| - Create AttachmentContainer |
| - Add FileAttachment |
| |
| RequestConnection() |
|------------------------------->|
| Status: kAwaitingLocalConfirmation
| |
| | Accept()
| |
| AcceptConnection() | AcceptConnection()
| + PayloadListener | + PayloadListener
| |
| Status: kConnecting |
| |
| Send Payloads |
|=============================> |
| (File data chunks) |
| |
| Status: kInProgress |
| Progress: 0% → 100% |
| |
| payload_progress_cb | payload_progress_cb
| |
| Status: kComplete |
| |
```
### 4. Advertisement Format
The service creates custom BLE advertisements with device information:
```
Byte Layout:
[0] Header Byte
- Bits 7-5: Version (3 bits)
- Bit 4: Visibility (0=visible, 1=hidden)
- Bits 3-1: Device Type (3 bits)
- Bit 0: Reserved
[1-2] Salt (2 random bytes)
[3-16] Metadata Key (14 bytes - for encryption)
[17+] TLV Fields (Type-Length-Value)
- Vendor ID (1 byte)
- QR Code data (variable)
- Other metadata
[N+] Device Name (optional, UTF-8)
```
**Device Types:**
- 0: Unknown
- 1: Phone
- 2: Tablet
- 3: Laptop
- 4: Unknown
### 5. State Management
```cpp
struct TransferState {
AttachmentContainer attachments; // Files/text being transferred
TransferUpdateCallback* callback; // Where to send updates
bool is_incoming; // Direction of transfer
};
// Mappings
endpoint_to_target_ // endpoint_id → ShareTarget
target_id_to_endpoint_ // share_target_id → endpoint_id
active_transfers_ // endpoint_id → TransferState
```
## Implementation Guide
### Step 1: Create Service Instance
```cpp
#include "sharing/linux/nearby_sharing_service_linux.h"
// Create service with custom device name
NearbySharingServiceLinux service("MyLinuxDevice");
// Or let it auto-detect from system
NearbySharingServiceLinux service;
```
### Step 2: Implement Callbacks
```cpp
class MyTransferCallback : public TransferUpdateCallback {
public:
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
// Handle transfer status changes
switch (transfer_metadata.status()) {
case TransferMetadata::Status::kAwaitingLocalConfirmation:
// Incoming transfer - need to accept/reject
HandleIncomingRequest(share_target);
break;
case TransferMetadata::Status::kInProgress:
// Show progress
UpdateProgress(transfer_metadata.progress());
break;
case TransferMetadata::Status::kComplete:
// Transfer done - access attachments
HandleCompletedTransfer(attachment_container);
break;
case TransferMetadata::Status::kFailed:
// Handle error
HandleError();
break;
}
}
};
class MyDiscoveryCallback : public ShareTargetDiscoveredCallback {
public:
void OnShareTargetDiscovered(const ShareTarget& share_target) override {
// New device found
devices_.push_back(share_target);
NotifyUI();
}
void OnShareTargetLost(const ShareTarget& share_target) override {
// Device went away
RemoveDevice(share_target.id);
}
void OnShareTargetUpdated(const ShareTarget& share_target) override {
// Device info changed
UpdateDevice(share_target);
}
};
```
### Step 3: Register Surfaces
```cpp
MyTransferCallback transfer_callback;
MyDiscoveryCallback discovery_callback;
// To receive files
service.RegisterReceiveSurface(
&transfer_callback,
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
[](auto status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Now advertising to nearby devices" << std::endl;
}
});
// To send files
service.RegisterSendSurface(
&transfer_callback,
&discovery_callback,
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
false, // don't disable wifi hotspot
[](auto status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Now scanning for nearby devices" << std::endl;
}
});
```
### Step 4: Send Content
```cpp
// Send a file
void SendFile(int64_t target_id, const std::string& file_path) {
auto container = std::make_unique<AttachmentContainer>();
FileAttachment attachment(FilePath(file_path));
container->AddFileAttachment(std::move(attachment));
service.SendAttachments(target_id, std::move(container),
[](auto status) {
std::cout << "Send status: "
<< NearbySharingService::StatusCodeToString(status)
<< std::endl;
});
}
// Send text
void SendText(int64_t target_id, const std::string& text) {
auto container = std::make_unique<AttachmentContainer>();
TextAttachment attachment(
TextAttachment::Type::TEXT,
text,
std::nullopt, // no title
std::nullopt // no mime type
);
container->AddTextAttachment(std::move(attachment));
service.SendAttachments(target_id, std::move(container),
[](auto status) { /* ... */ });
}
```
### Step 5: Handle Incoming Transfers
```cpp
void HandleIncomingRequest(const ShareTarget& target) {
// Show confirmation dialog to user
std::cout << "Accept file from " << target.device_name << "? (y/n): ";
char choice;
std::cin >> choice;
if (choice == 'y') {
service.Accept(target.id, [](auto status) {
std::cout << "Accepted!" << std::endl;
});
} else {
service.Reject(target.id, [](auto status) {
std::cout << "Rejected!" << std::endl;
});
}
}
void HandleCompletedTransfer(const AttachmentContainer& container) {
// Process received files
for (const auto& file : container.GetFileAttachments()) {
std::cout << "Received: " << file.file_name() << std::endl;
if (file.file_path().has_value()) {
std::cout << "Saved to: " << file.file_path()->string() << std::endl;
}
}
// Process received text
for (const auto& text : container.GetTextAttachments()) {
std::cout << "Received text: " << text.text_body() << std::endl;
}
}
```
## Code Examples
### Example 1: Simple File Sender
```cpp
#include "sharing/linux/nearby_sharing_service_linux.h"
#include "sharing/file_attachment.h"
#include <thread>
int main() {
NearbySharingServiceLinux service("FileSender");
// Setup callbacks
class SimpleCallback : public TransferUpdateCallback {
void OnTransferUpdate(...) override {
std::cout << "Progress: " << transfer_metadata.progress() * 100 << "%" << std::endl;
}
} transfer_cb;
class SimpleDiscovery : public ShareTargetDiscoveredCallback {
int64_t target_id = -1;
void OnShareTargetDiscovered(const ShareTarget& t) override {
target_id = t.id;
std::cout << "Found: " << t.device_name << std::endl;
}
void OnShareTargetLost(...) override {}
void OnShareTargetUpdated(...) override {}
} discovery_cb;
// Start scanning
service.RegisterSendSurface(&transfer_cb, &discovery_cb,
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone, false, [](auto) {});
// Wait for discovery
std::this_thread::sleep_for(std::chrono::seconds(5));
if (discovery_cb.target_id != -1) {
// Send file
auto container = std::make_unique<AttachmentContainer>();
container->AddFileAttachment(FileAttachment(FilePath("/path/to/file.txt")));
service.SendAttachments(discovery_cb.target_id, std::move(container), [](auto) {});
// Wait for completion
std::this_thread::sleep_for(std::chrono::seconds(10));
}
return 0;
}
```
### Example 2: Auto-Accepting Receiver
```cpp
class AutoAcceptCallback : public TransferUpdateCallback {
public:
AutoAcceptCallback(NearbySharingServiceLinux* service) : service_(service) {}
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
// Auto-accept all incoming transfers
if (transfer_metadata.status() == TransferMetadata::Status::kAwaitingLocalConfirmation) {
service_->Accept(share_target.id, [](auto) {});
}
// Save received files
if (transfer_metadata.status() == TransferMetadata::Status::kComplete) {
for (const auto& file : attachment_container.GetFileAttachments()) {
std::cout << "Saved: " << file.file_name() << std::endl;
}
}
}
private:
NearbySharingServiceLinux* service_;
};
int main() {
NearbySharingServiceLinux service("AutoReceiver");
AutoAcceptCallback callback(&service);
service.RegisterReceiveSurface(&callback,
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone, [](auto) {});
// Keep running
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
```
## Best Practices
### 1. Callback Lifetime Management
```cpp
// DON'T: Callbacks going out of scope
void BadExample() {
MyTransferCallback callback; // Stack allocated
service.RegisterSendSurface(&callback, ...);
// callback destroyed when function exits!
}
// DO: Keep callbacks alive
class App {
MyTransferCallback callback_; // Member variable
void Setup() {
service.RegisterSendSurface(&callback_, ...);
}
};
```
### 2. Error Handling
```cpp
service.SendAttachments(target_id, container,
[this](NearbySharingService::StatusCodes status) {
switch (status) {
case StatusCodes::kOk:
// Success
break;
case StatusCodes::kInvalidArgument:
// Bad target_id or empty container
LogError("Invalid arguments");
break;
case StatusCodes::kNoAvailableConnectionMedium:
// Bluetooth/WiFi not available
NotifyUserToEnableBluetooth();
break;
default:
LogError("Transfer failed");
break;
}
});
```
### 3. Resource Cleanup
```cpp
class ProperCleanup {
public:
~ProperCleanup() {
// Unregister surfaces before destroying callbacks
service_.UnregisterSendSurface(&transfer_callback_, [](auto) {});
service_.UnregisterReceiveSurface(&transfer_callback_, [](auto) {});
// Shutdown service
service_.Shutdown([](auto) {});
}
private:
NearbySharingServiceLinux service_;
MyTransferCallback transfer_callback_;
};
```
### 4. Thread Safety
```cpp
// The service is NOT thread-safe
// All calls should be from the same thread or synchronized
class ThreadSafeApp {
public:
void SendFromAnyThread(int64_t target_id, const std::string& file) {
task_runner_.PostTask([this, target_id, file]() {
// All service calls happen on same thread
auto container = std::make_unique<AttachmentContainer>();
container->AddFileAttachment(FileAttachment(FilePath(file)));
service_.SendAttachments(target_id, std::move(container), [](auto) {});
});
}
private:
NearbySharingServiceLinux service_;
TaskRunner task_runner_; // Your threading implementation
};
```
### 5. State Tracking
```cpp
class StatefulApp {
public:
void OnTransferUpdate(...) override {
current_state_ = transfer_metadata.status();
// Track progress
if (transfer_metadata.status() == Status::kInProgress) {
progress_map_[share_target.id] = transfer_metadata.progress();
}
// Cleanup on completion
if (TransferMetadata::IsFinalStatus(transfer_metadata.status())) {
progress_map_.erase(share_target.id);
}
}
private:
TransferMetadata::Status current_state_;
std::unordered_map<int64_t, float> progress_map_;
};
```
## Troubleshooting
### Discovery Not Working
- Check Bluetooth is enabled: `IsBluetoothPowered()`
- Verify sender is in foreground state
- Ensure receiver is advertising
- Check for permission issues
### Transfers Failing
- Verify file paths are valid and accessible
- Check available disk space on receiver
- Ensure stable Bluetooth connection
- Monitor transfer callbacks for specific error status
### Connection Issues
- Devices must be within Bluetooth range (~10m)
- Minimize interference from other BLE devices
- Ensure both devices support required BLE features
- Check firewall settings for WiFi Direct
## Performance Tips
1. **Use appropriate surface states**: Background mode when not actively transferring
2. **Unregister when not needed**: Stop scanning/advertising to save battery
3. **Batch small files**: Combine into zip for better efficiency
4. **Monitor transfer progress**: Cancel stalled transfers
5. **Handle errors gracefully**: Retry with exponential backoff
-300
View File
@@ -1,300 +0,0 @@
# Nearby Sharing Linux - Quick Reference
## Quick Start
### Build
```bash
bazel build //sharing/linux:simple_example
bazel build //sharing/linux:nearby_sharing_app
```
### Run Simple Example
```bash
# Terminal 1 (Receiver)
./bazel-bin/sharing/linux/simple_example receiver
# Terminal 2 (Sender)
./bazel-bin/sharing/linux/simple_example sender "Hello World!"
```
### Run Full App
```bash
./bazel-bin/sharing/linux/nearby_sharing_app [device_name]
```
## API Cheat Sheet
### Include Headers
```cpp
#include "sharing/linux/nearby_sharing_service_linux.h"
#include "sharing/attachment_container.h"
#include "sharing/file_attachment.h"
#include "sharing/text_attachment.h"
#include "sharing/share_target.h"
#include "sharing/transfer_metadata.h"
```
### Create Service
```cpp
using namespace nearby::sharing::linux;
NearbySharingServiceLinux service("DeviceName");
```
### Implement Callbacks
```cpp
// Transfer updates
class MyCallback : public TransferUpdateCallback {
void OnTransferUpdate(const ShareTarget& target,
const AttachmentContainer& attachments,
const TransferMetadata& metadata) override {
// Handle status changes
}
};
// Device discovery
class MyDiscovery : public ShareTargetDiscoveredCallback {
void OnShareTargetDiscovered(const ShareTarget& target) override { }
void OnShareTargetLost(const ShareTarget& target) override { }
void OnShareTargetUpdated(const ShareTarget& target) override { }
};
```
### Register to Receive
```cpp
service.RegisterReceiveSurface(
&transfer_callback,
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
[](auto status) { /* callback */ });
```
### Register to Send
```cpp
service.RegisterSendSurface(
&transfer_callback,
&discovery_callback,
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
false, // disable_wifi_hotspot
[](auto status) { /* callback */ });
```
### Send File
```cpp
auto container = std::make_unique<AttachmentContainer>();
container->AddFileAttachment(FileAttachment(FilePath("/path/to/file")));
service.SendAttachments(target_id, std::move(container), [](auto) {});
```
### Send Text
```cpp
auto container = std::make_unique<AttachmentContainer>();
container->AddTextAttachment(TextAttachment(
TextAttachment::Type::TEXT, "message", std::nullopt, std::nullopt));
service.SendAttachments(target_id, std::move(container), [](auto) {});
```
### Accept/Reject/Cancel
```cpp
service.Accept(target_id, [](auto status) {});
service.Reject(target_id, [](auto status) {});
service.Cancel(target_id, [](auto status) {});
```
### Check Status
```cpp
bool scanning = service.IsScanning();
bool transferring = service.IsTransferring();
bool bt_present = service.IsBluetoothPresent();
bool bt_powered = service.IsBluetoothPowered();
```
### Shutdown
```cpp
service.Shutdown([](auto status) {});
```
## Transfer Statuses
| Status | Meaning | Action |
|--------|---------|--------|
| `kConnecting` | Establishing connection | Wait |
| `kAwaitingLocalConfirmation` | Need to accept/reject | Call Accept() or Reject() |
| `kAwaitingRemoteAcceptance` | Waiting for remote | Wait |
| `kInProgress` | Transferring data | Show progress |
| `kComplete` | Success | Access attachments |
| `kFailed` | Error occurred | Check logs |
| `kRejected` | User rejected | Retry or cancel |
| `kCancelled` | Transfer cancelled | Cleanup |
| `kTimedOut` | Connection timeout | Retry |
## Status Codes
| Code | Meaning |
|------|---------|
| `kOk` | Success |
| `kError` | General error |
| `kOutOfOrderApiCall` | API called incorrectly |
| `kTransferAlreadyInProgress` | Can't start new transfer |
| `kNoAvailableConnectionMedium` | No Bluetooth/WiFi |
| `kInvalidArgument` | Bad parameters |
## Common Patterns
### Auto-Accept Pattern
```cpp
class AutoAccept : public TransferUpdateCallback {
void OnTransferUpdate(...) override {
if (metadata.status() == Status::kAwaitingLocalConfirmation) {
service_->Accept(target.id, [](auto) {});
}
}
};
```
### Progress Tracking Pattern
```cpp
void OnTransferUpdate(...) override {
if (metadata.status() == Status::kInProgress) {
int percent = metadata.progress() * 100;
uint64_t bytes = metadata.transferred_bytes();
std::cout << percent << "% (" << bytes << " bytes)" << std::endl;
}
}
```
### Device Selection Pattern
```cpp
std::vector<ShareTarget> devices;
void OnShareTargetDiscovered(const ShareTarget& target) override {
devices.push_back(target);
std::cout << devices.size() << ". " << target.device_name << std::endl;
}
void SendToDevice(size_t index) {
if (index < devices.size()) {
SendFile(devices[index].id, file_path);
}
}
```
### Error Handling Pattern
```cpp
service.SendAttachments(target_id, container,
[](NearbySharingService::StatusCodes status) {
if (status != StatusCodes::kOk) {
std::cerr << "Error: "
<< NearbySharingService::StatusCodeToString(status)
<< std::endl;
return;
}
std::cout << "Transfer initiated" << std::endl;
});
```
## Debugging Tips
### Enable Verbose Logging
```cpp
// Set environment variable
export NEARBY_LOGS=VERBOSE
```
### Check Bluetooth
```cpp
if (!service.IsBluetoothPresent()) {
std::cout << "No Bluetooth adapter found" << std::endl;
}
if (!service.IsBluetoothPowered()) {
std::cout << "Bluetooth is off" << std::endl;
}
```
### Dump Service State
```cpp
std::cout << service.Dump() << std::endl;
// Output: "NearbySharingServiceLinux advertising=true scanning=false ..."
```
### Monitor Callbacks
```cpp
void OnTransferUpdate(...) override {
std::cout << "[Transfer] " << target.device_name
<< " - " << TransferMetadata::StatusToString(metadata.status())
<< " - " << (metadata.progress() * 100) << "%" << std::endl;
}
```
## File Locations
- **Service**: `sharing/linux/nearby_sharing_service_linux.{h,cc}`
- **Simple Example**: `sharing/linux/simple_example.cc`
- **Full App**: `sharing/linux/nearby_sharing_app.cc`
- **README**: `sharing/linux/README.md`
- **Implementation Guide**: `sharing/linux/IMPLEMENTATION_GUIDE.md`
- **BUILD**: `sharing/linux/BUILD`
## Common Issues
### "No devices found"
- Ensure receiver is running and advertising
- Check Bluetooth is enabled on both devices
- Verify devices are within range (~10m)
- Try restarting Bluetooth
### "Transfer failed"
- Check file permissions
- Verify disk space
- Ensure stable connection
- Check firewall settings
### "Invalid argument"
- Verify target_id is valid
- Ensure container has attachments
- Check surface is registered
### Callback not called
- Verify callback lifetime (must outlive service)
- Check registration was successful
- Ensure main thread/event loop is running
## Example Workflows
### Send File Workflow
```
1. Create service
2. Create callbacks
3. RegisterSendSurface (foreground)
4. Wait for OnShareTargetDiscovered
5. Create AttachmentContainer
6. Add FileAttachment
7. SendAttachments(target_id, container)
8. Wait for kComplete in OnTransferUpdate
```
### Receive File Workflow
```
1. Create service
2. Create callback
3. RegisterReceiveSurface (foreground)
4. Wait for kAwaitingLocalConfirmation
5. Call Accept(target_id)
6. Wait for kInProgress updates
7. Wait for kComplete
8. Access files from AttachmentContainer
```
## Performance Notes
- **Scanning**: Consumes battery, stop when not needed
- **Advertising**: Minimal impact
- **Transfer**: WiFi Direct faster than Bluetooth
- **File Size**: Large files (>100MB) benefit from WiFi
- **Small Files**: Bluetooth sufficient for <10MB
## Links
- [README.md](README.md) - Overview and features
- [IMPLEMENTATION_GUIDE.md](IMPLEMENTATION_GUIDE.md) - Detailed architecture
- [nearby_sharing_service.h](../nearby_sharing_service.h) - Base interface
-104
View File
@@ -1,104 +0,0 @@
# Nearby Sharing for Linux
This directory contains:
- A Linux-facing Nearby Sharing library (`NearbySharingApi`)
- A sample CLI app (`nearby_sharing_app`)
- A Qt/QML tray sample app (`qml_tray_app`)
## Scope and Intended Use
This project is primarily intended to be used as a reusable library/API.
The sample applications are still supported and will continue to be supported because they are used in real day-to-day device sharing workflows.
## Current Status
The current implementation works with the reverse-engineered certificate manager currently used in this project.
Compatibility can still break if Google changes certificate manager behavior/protocol details. That component is closed source, so upstream changes can be difficult to inspect and adapt to quickly.
## Test Coverage and Session Notes
- Verified: single-file sharing flow.
- Not fully verified: multiple transfers in one app lifetime.
- Current practical testing pattern: restart the application before each new transfer.
For this README, a "session" means one process lifetime (app start to app exit).
After one transfer, some endpoints may close and internal state can reset/change. Multi-transfer handling in one live session is still under investigation.
## Known Issues
- Linux hotspot startup can be slow.
- Connecting to a hotspot started on another device can be slow on Linux.
- Android-initiated connection formation can be very slow.
The Android/Linux connection latency issue still needs deeper investigation. One possible cause is connection/negotiation behavior that Linux does not currently handle well.
## Wi-Fi Direct Status
Wi-Fi Direct is theoretically possible but not implemented yet.
Reason: NetworkManager does not natively support creating Wi-Fi Direct Group Owners in the way this project needs.
## Installation
### 1. Install the shared library
From the repository root:
```bash
./sharing/linux/install_nearby_sharing_service.sh
```
This installs:
- `libnearby_sharing_api_shared.so`
- `sharing/linux/nearby_sharing_api.h`
### 2. Build and run the CLI sample app (optional)
From the repository root:
```bash
bazel build //sharing/linux:nearby_sharing_app
./bazel-bin/sharing/linux/nearby_sharing_app
# Optional custom device name
./bazel-bin/sharing/linux/nearby_sharing_app "MyDeviceName"
```
### 3. Build and run the tray sample app (optional)
From `sharing/linux/qml_tray_app`:
```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_PREFIX=/usr/local
cmake --build build -j
./build/nearby_qml_file_tray_app
```
### 4. Install launcher entry (`.desktop`) for the tray app
From `sharing/linux/qml_tray_app`:
```bash
mkdir -p "$HOME/.local/share/applications"
install -m 0644 nearby-file-share.desktop "$HOME/.local/share/applications/nearby-file-share.desktop"
sed -i "s|^Exec=.*|Exec=$(pwd)/build/nearby_qml_file_tray_app|" "$HOME/.local/share/applications/nearby-file-share.desktop"
sed -i "s|^Icon=.*|Icon=$(pwd)/nearby-linux-desktop.png|" "$HOME/.local/share/applications/nearby-file-share.desktop"
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
```
After this, search for `Nearby File Share` in your desktop launcher.
## Documentation
Technical deep dives are being moved from README content to the wiki.
- Wiki: https://github.com/kidfromjupiter/nearby/wiki
This README stays focused on status, installation, and known limitations.
## Demo Assets (Planned)
- Video demo of end-to-end sharing flow.
- GIF showing the sharing process.
@@ -1,89 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/linux/nearby_connections_adapter.h"
#include <memory>
#include <utility>
namespace nearby::sharing::linux {
CoreNearbyConnectionsAdapter::CoreNearbyConnectionsAdapter()
: router_(std::make_unique<connections::ServiceControllerRouter>()),
core_(std::make_unique<connections::Core>(router_.get())) {}
CoreNearbyConnectionsAdapter::~CoreNearbyConnectionsAdapter() = default;
void CoreNearbyConnectionsAdapter::StartAdvertising(
absl::string_view service_id,
connections::AdvertisingOptions advertising_options,
connections::ConnectionRequestInfo request_info,
std::function<void(connections::Status)> callback) {
core_->StartAdvertising(service_id, std::move(advertising_options),
std::move(request_info), std::move(callback));
}
void CoreNearbyConnectionsAdapter::StopAdvertising(
std::function<void(connections::Status)> callback) {
core_->StopAdvertising(std::move(callback));
}
void CoreNearbyConnectionsAdapter::StartDiscovery(
absl::string_view service_id,
connections::DiscoveryOptions discovery_options,
connections::DiscoveryListener discovery_listener,
std::function<void(connections::Status)> callback) {
core_->StartDiscovery(service_id, std::move(discovery_options),
std::move(discovery_listener), std::move(callback));
}
void CoreNearbyConnectionsAdapter::StopDiscovery(
std::function<void(connections::Status)> callback) {
core_->StopDiscovery(std::move(callback));
}
void CoreNearbyConnectionsAdapter::RequestConnection(
absl::string_view endpoint_id,
connections::ConnectionRequestInfo request_info,
connections::ConnectionOptions connection_options,
std::function<void(connections::Status)> callback) {
core_->RequestConnection(endpoint_id, std::move(request_info),
std::move(connection_options), std::move(callback));
}
void CoreNearbyConnectionsAdapter::AcceptConnection(
absl::string_view endpoint_id, connections::PayloadListener listener,
std::function<void(connections::Status)> callback) {
core_->AcceptConnection(endpoint_id, std::move(listener), std::move(callback));
}
void CoreNearbyConnectionsAdapter::RejectConnection(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) {
core_->RejectConnection(endpoint_id, std::move(callback));
}
void CoreNearbyConnectionsAdapter::SendPayload(
absl::Span<const std::string> endpoint_ids, connections::Payload payload,
std::function<void(connections::Status)> callback) {
core_->SendPayload(endpoint_ids, std::move(payload), std::move(callback));
}
void CoreNearbyConnectionsAdapter::DisconnectFromEndpoint(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) {
core_->DisconnectFromEndpoint(endpoint_id, std::move(callback));
}
} // namespace nearby::sharing::linux
-116
View File
@@ -1,116 +0,0 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_
#include <functional>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "connections/advertising_options.h"
#include "connections/connection_options.h"
#include "connections/core.h"
#include "connections/discovery_options.h"
#include "connections/implementation/service_controller_router.h"
#include "connections/listeners.h"
#include "connections/payload.h"
#include "connections/status.h"
namespace nearby::sharing::linux {
class NearbyConnectionsAdapter {
public:
virtual ~NearbyConnectionsAdapter() = default;
virtual void StartAdvertising(
absl::string_view service_id,
connections::AdvertisingOptions advertising_options,
connections::ConnectionRequestInfo request_info,
std::function<void(connections::Status)> callback) = 0;
virtual void StopAdvertising(
std::function<void(connections::Status)> callback) = 0;
virtual void StartDiscovery(
absl::string_view service_id,
connections::DiscoveryOptions discovery_options,
connections::DiscoveryListener discovery_listener,
std::function<void(connections::Status)> callback) = 0;
virtual void StopDiscovery(
std::function<void(connections::Status)> callback) = 0;
virtual void RequestConnection(
absl::string_view endpoint_id,
connections::ConnectionRequestInfo request_info,
connections::ConnectionOptions connection_options,
std::function<void(connections::Status)> callback) = 0;
virtual void AcceptConnection(
absl::string_view endpoint_id, connections::PayloadListener listener,
std::function<void(connections::Status)> callback) = 0;
virtual void RejectConnection(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) = 0;
virtual void SendPayload(
absl::Span<const std::string> endpoint_ids, connections::Payload payload,
std::function<void(connections::Status)> callback) = 0;
virtual void DisconnectFromEndpoint(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) = 0;
};
class CoreNearbyConnectionsAdapter : public NearbyConnectionsAdapter {
public:
CoreNearbyConnectionsAdapter();
~CoreNearbyConnectionsAdapter() override;
void StartAdvertising(
absl::string_view service_id,
connections::AdvertisingOptions advertising_options,
connections::ConnectionRequestInfo request_info,
std::function<void(connections::Status)> callback) override;
void StopAdvertising(
std::function<void(connections::Status)> callback) override;
void StartDiscovery(
absl::string_view service_id,
connections::DiscoveryOptions discovery_options,
connections::DiscoveryListener discovery_listener,
std::function<void(connections::Status)> callback) override;
void StopDiscovery(
std::function<void(connections::Status)> callback) override;
void RequestConnection(
absl::string_view endpoint_id,
connections::ConnectionRequestInfo request_info,
connections::ConnectionOptions connection_options,
std::function<void(connections::Status)> callback) override;
void AcceptConnection(
absl::string_view endpoint_id, connections::PayloadListener listener,
std::function<void(connections::Status)> callback) override;
void RejectConnection(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) override;
void SendPayload(
absl::Span<const std::string> endpoint_ids, connections::Payload payload,
std::function<void(connections::Status)> callback) override;
void DisconnectFromEndpoint(
absl::string_view endpoint_id,
std::function<void(connections::Status)> callback) override;
private:
std::unique_ptr<connections::ServiceControllerRouter> router_;
std::unique_ptr<connections::Core> core_;
};
} // namespace nearby::sharing::linux
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_NEARBY_CONNECTIONS_ADAPTER_H_
-659
View File
@@ -1,659 +0,0 @@
#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::kStream:
facade_payload.type = NearbyConnectionsApi::PayloadType::kStream;
facade_payload.stream_bytes = payload.content.stream_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:
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::kStream: {
nearby::sharing::StreamPayload stream_payload;
stream_payload.bytes = std::move(payload.stream_bytes);
stream_payload.input_stream = std::move(payload.stream_input);
return std::make_unique<nearby::sharing::Payload>(payload.id,
std::move(stream_payload));
}
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
-290
View File
@@ -1,290 +0,0 @@
// 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>
#include "internal/platform/input_stream.h"
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,
kStream = 3,
};
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::vector<uint8_t> stream_bytes;
std::shared_ptr<nearby::InputStream> stream_input;
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;
}
static Payload FromStream(int64_t id, std::vector<uint8_t> stream_bytes) {
Payload payload;
payload.id = id;
payload.type = PayloadType::kStream;
payload.stream_bytes = std::move(stream_bytes);
return payload;
}
static Payload FromInputStream(int64_t id,
std::shared_ptr<nearby::InputStream> stream_input) {
Payload payload;
payload.id = id;
payload.type = PayloadType::kStream;
payload.stream_input = std::move(stream_input);
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_
@@ -1,75 +0,0 @@
#include "sharing/linux/nearby_connections_api.h"
#include <memory>
#include <vector>
#include "gtest/gtest.h"
#include "internal/platform/pipe.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.stream_bytes.empty());
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());
EXPECT_TRUE(payload.stream_bytes.empty());
}
TEST(NearbyConnectionsApiTest, PayloadFromStreamSetsFields) {
NearbyConnectionsApi::Payload payload =
NearbyConnectionsApi::Payload::FromStream(9, {4, 5, 6});
EXPECT_EQ(payload.id, 9);
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream);
EXPECT_EQ(payload.stream_bytes, (std::vector<uint8_t>{4, 5, 6}));
EXPECT_TRUE(payload.bytes.empty());
EXPECT_TRUE(payload.file_path.empty());
}
TEST(NearbyConnectionsApiTest, PayloadFromInputStreamSetsFields) {
auto [input, output] = CreatePipe();
(void)output;
NearbyConnectionsApi::Payload payload =
NearbyConnectionsApi::Payload::FromInputStream(
10, std::shared_ptr<nearby::InputStream>(std::move(input)));
EXPECT_EQ(payload.id, 10);
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream);
EXPECT_TRUE(payload.stream_bytes.empty());
EXPECT_NE(payload.stream_input, nullptr);
EXPECT_TRUE(payload.bytes.empty());
EXPECT_TRUE(payload.file_path.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
-680
View File
@@ -1,680 +0,0 @@
#include "sharing/linux/nearby_sharing_api.h"
#include <limits>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include "absl/strings/escaping.h"
#include "absl/time/time.h"
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
#include "internal/crypto_cros/ec_private_key.h"
#include "internal/flags/nearby_flags.h"
#include "internal/platform/implementation/linux/linux_flags.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/attachment_container.h"
#include "sharing/file_attachment.h"
#include "sharing/flags/generated/nearby_sharing_feature_flags.h"
#include "sharing/linux/platform/linux_sharing_platform.h"
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
#include "sharing/nearby_sharing_service_factory.h"
#include "sharing/proto/enums.pb.h"
#include "sharing/share_target_discovered_callback.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
namespace nearby::sharing {
namespace {
using NativeService = nearby::sharing::NearbySharingService;
void EnableBleL2capDefaults() {
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
nearby::connections::config_package_nearby::nearby_connections_feature::
kEnableBleL2cap,
true);
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
nearby::sharing::config_package_nearby::nearby_sharing_feature::
kEnableBleForTransfer,
true);
//nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
// nearby::connections::config_package_nearby::nearby_connections_feature::
// kRefactorBleL2cap,
// false);
}
NearbySharingApi::StatusCode ToFacadeStatus(
nearby::sharing::NearbySharingService::StatusCodes status) {
switch (status) {
case nearby::sharing::NearbySharingService::StatusCodes::kOk:
return NearbySharingApi::StatusCode::kOk;
case nearby::sharing::NearbySharingService::StatusCodes::kError:
return NearbySharingApi::StatusCode::kError;
case nearby::sharing::NearbySharingService::StatusCodes::kOutOfOrderApiCall:
return NearbySharingApi::StatusCode::kOutOfOrderApiCall;
case nearby::sharing::NearbySharingService::StatusCodes::kStatusAlreadyStopped:
return NearbySharingApi::StatusCode::kStatusAlreadyStopped;
case nearby::sharing::NearbySharingService::StatusCodes::kTransferAlreadyInProgress:
return NearbySharingApi::StatusCode::kTransferAlreadyInProgress;
case nearby::sharing::NearbySharingService::StatusCodes::kNoAvailableConnectionMedium:
return NearbySharingApi::StatusCode::kNoAvailableConnectionMedium;
case nearby::sharing::NearbySharingService::StatusCodes::kIrrecoverableHardwareError:
return NearbySharingApi::StatusCode::kIrrecoverableHardwareError;
case nearby::sharing::NearbySharingService::StatusCodes::kInvalidArgument:
return NearbySharingApi::StatusCode::kInvalidArgument;
}
return NearbySharingApi::StatusCode::kError;
}
NearbySharingApi::TransferStatus ToFacadeTransferStatus(
nearby::sharing::TransferMetadata::Status status) {
using NativeStatus = nearby::sharing::TransferMetadata::Status;
using FacadeStatus = NearbySharingApi::TransferStatus;
switch (status) {
case NativeStatus::kUnknown:
return FacadeStatus::kUnknown;
case NativeStatus::kConnecting:
return FacadeStatus::kConnecting;
case NativeStatus::kAwaitingLocalConfirmation:
return FacadeStatus::kAwaitingLocalConfirmation;
case NativeStatus::kAwaitingRemoteAcceptance:
return FacadeStatus::kAwaitingRemoteAcceptance;
case NativeStatus::kInProgress:
return FacadeStatus::kInProgress;
case NativeStatus::kComplete:
return FacadeStatus::kComplete;
case NativeStatus::kFailed:
return FacadeStatus::kFailed;
case NativeStatus::kRejected:
return FacadeStatus::kRejected;
case NativeStatus::kCancelled:
return FacadeStatus::kCancelled;
case NativeStatus::kTimedOut:
return FacadeStatus::kTimedOut;
case NativeStatus::kMediaUnavailable:
return FacadeStatus::kMediaUnavailable;
case NativeStatus::kNotEnoughSpace:
return FacadeStatus::kNotEnoughSpace;
case NativeStatus::kUnsupportedAttachmentType:
return FacadeStatus::kUnsupportedAttachmentType;
case NativeStatus::kDeviceAuthenticationFailed:
return FacadeStatus::kDeviceAuthenticationFailed;
case NativeStatus::kIncompletePayloads:
return FacadeStatus::kIncompletePayloads;
}
return FacadeStatus::kUnknown;
}
NearbySharingApi::TextAttachmentType ToFacadeTextAttachmentType(
nearby::sharing::TextAttachment::Type type) {
using FacadeType = NearbySharingApi::TextAttachmentType;
switch (type) {
case nearby::sharing::service::proto::TextMetadata::TEXT:
return FacadeType::kText;
case nearby::sharing::service::proto::TextMetadata::URL:
return FacadeType::kUrl;
case nearby::sharing::service::proto::TextMetadata::PHONE_NUMBER:
return FacadeType::kPhoneNumber;
case nearby::sharing::service::proto::TextMetadata::ADDRESS:
return FacadeType::kAddress;
case nearby::sharing::service::proto::TextMetadata::UNKNOWN:
return FacadeType::kUnknown;
}
return FacadeType::kUnknown;
}
float NormalizeFacadeProgress(float progress) {
if (progress <= 0.0f) {
return 0.0f;
}
if (progress >= 100.0f) {
return 1.0f;
}
return progress / 100.0f;
}
std::string GenerateQrCodeUrl() {
auto ec_key = nearby::crypto::ECPrivateKey::Create();
if (!ec_key) {
return {};
}
const EC_KEY* raw_ec_key = EVP_PKEY_get0_EC_KEY(ec_key->key());
if (!raw_ec_key) {
return {};
}
const EC_GROUP* group = EC_KEY_get0_group(raw_ec_key);
const EC_POINT* public_key = EC_KEY_get0_public_key(raw_ec_key);
if (!group || !public_key) {
return {};
}
BIGNUM* x = BN_new();
BIGNUM* y = BN_new();
if (!x || !y) {
BN_free(x);
BN_free(y);
return {};
}
if (!EC_POINT_get_affine_coordinates_GFp(group, public_key, x, y, nullptr)) {
BN_free(x);
BN_free(y);
return {};
}
std::vector<uint8_t> x_bytes(32, 0);
const int x_len = BN_num_bytes(x);
if (x_len > static_cast<int>(x_bytes.size())) {
BN_free(x);
BN_free(y);
return {};
}
BN_bn2bin(x, x_bytes.data() + (x_bytes.size() - x_len));
const uint8_t prefix = BN_is_odd(y) ? 0x03 : 0x02;
BN_free(x);
BN_free(y);
std::vector<uint8_t> key_data;
key_data.reserve(35);
key_data.push_back(0x00);
key_data.push_back(0x00);
key_data.push_back(prefix);
key_data.insert(key_data.end(), x_bytes.begin(), x_bytes.end());
std::string encoded;
absl::WebSafeBase64Escape(
std::string(reinterpret_cast<const char*>(key_data.data()),
key_data.size()),
&encoded);
return "https://quickshare.google/qrcode#key=" + encoded;
}
} // namespace
class NearbySharingApi::Impl : public nearby::sharing::ShareTargetDiscoveredCallback,
public nearby::sharing::TransferUpdateCallback {
public:
Impl()
: analytics_recorder(0, nullptr),
platform(),
service(NearbySharingServiceFactory::GetInstance()->CreateSharingService(
platform, &analytics_recorder, /*event_logger=*/nullptr,
/*supports_file_sync=*/false)) {}
explicit Impl(std::string device_name_override)
: analytics_recorder(0, nullptr),
device_name_override(device_name_override),
platform(device_name_override),
service(NearbySharingServiceFactory::GetInstance()->CreateSharingService(
platform, &analytics_recorder, /*event_logger=*/nullptr,
/*supports_file_sync=*/false)) {
if (service != nullptr && !device_name_override.empty() &&
service->GetLocalDeviceDataManager() != nullptr) {
service->GetLocalDeviceDataManager()->SetDeviceName(device_name_override);
}
}
void OnShareTargetDiscovered(const nearby::sharing::ShareTarget& share_target)
override {
Listener listener_copy;
{
std::scoped_lock lock(listener_mutex);
listener_copy = listener;
}
if (!listener_copy.target_discovered_cb) {
return;
}
listener_copy.target_discovered_cb(ToShareTargetInfo(share_target));
}
void OnShareTargetLost(const nearby::sharing::ShareTarget& share_target)
override {
Listener listener_copy;
{
std::scoped_lock lock(listener_mutex);
listener_copy = listener;
}
if (!listener_copy.target_lost_cb) {
return;
}
listener_copy.target_lost_cb(share_target.id);
}
void OnShareTargetUpdated(const nearby::sharing::ShareTarget& share_target)
override {
Listener listener_copy;
{
std::scoped_lock lock(listener_mutex);
listener_copy = listener;
}
if (!listener_copy.target_updated_cb) {
return;
}
listener_copy.target_updated_cb(ToShareTargetInfo(share_target));
}
void OnTransferUpdate(
const nearby::sharing::ShareTarget& share_target,
const nearby::sharing::AttachmentContainer& attachment_container,
const nearby::sharing::TransferMetadata& transfer_metadata) override {
Listener listener_copy;
{
std::scoped_lock lock(listener_mutex);
listener_copy = listener;
}
if (!listener_copy.transfer_update_cb) {
return;
}
NearbySharingApi::TransferUpdateInfo info;
info.share_target_id = share_target.id;
info.device_name = share_target.device_name;
info.is_incoming = share_target.is_incoming;
info.status = ToFacadeTransferStatus(transfer_metadata.status());
info.progress = NormalizeFacadeProgress(transfer_metadata.progress());
info.transferred_bytes = transfer_metadata.transferred_bytes();
info.total_attachments = transfer_metadata.total_attachments_count();
info.transferred_attachments = transfer_metadata.transferred_attachments_count();
if (!attachment_container.GetFileAttachments().empty()) {
const nearby::sharing::FileAttachment& file =
attachment_container.GetFileAttachments().front();
info.first_file_name = std::string(file.file_name());
if (file.file_path().has_value()) {
info.first_file_path = file.file_path()->ToString();
}
}
info.text_attachments.reserve(
attachment_container.GetTextAttachments().size());
for (const nearby::sharing::TextAttachment& text :
attachment_container.GetTextAttachments()) {
NearbySharingApi::TextAttachmentInfo text_info;
text_info.type = ToFacadeTextAttachmentType(text.type());
text_info.text_title = std::string(text.text_title());
text_info.text_body = std::string(text.text_body());
info.text_attachments.push_back(std::move(text_info));
}
listener_copy.transfer_update_cb(info);
}
NearbySharingApi::ShareTargetInfo ToShareTargetInfo(
const nearby::sharing::ShareTarget& share_target) {
NearbySharingApi::ShareTargetInfo info;
info.id = share_target.id;
info.device_name = share_target.device_name;
info.is_incoming = share_target.is_incoming;
info.device_type = static_cast<int>(share_target.type);
return info;
}
nearby::sharing::analytics::AnalyticsRecorder analytics_recorder;
std::string device_name_override;
LinuxSharingPlatform platform;
NativeService* service = nullptr;
bool send_mode_started = false;
bool receive_mode_started = false;
std::string qr_code_url;
std::mutex listener_mutex;
NearbySharingApi::Listener listener;
};
NearbySharingApi::NearbySharingApi() {
EnableBleL2capDefaults();
impl_ = std::make_unique<Impl>();
}
NearbySharingApi::NearbySharingApi(std::string device_name_override)
: impl_(nullptr) {
EnableBleL2capDefaults();
impl_ = std::make_unique<Impl>(std::move(device_name_override));
}
NearbySharingApi::~NearbySharingApi() = default;
NearbySharingApi::NearbySharingApi(NearbySharingApi&&) noexcept = default;
NearbySharingApi& NearbySharingApi::operator=(NearbySharingApi&&) noexcept = default;
void NearbySharingApi::SetListener(Listener listener) {
std::scoped_lock lock(impl_->listener_mutex);
impl_->listener = std::move(listener);
}
void NearbySharingApi::StartSendMode(std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
if (impl_->send_mode_started) {
if (callback) {
callback(StatusCode::kOk);
}
return;
}
impl_->service->RegisterSendSurface(
impl_.get(), impl_.get(),
nearby::sharing::NearbySharingService::SendSurfaceState::kForeground,
nearby::sharing::Advertisement::BlockedVendorId::kNone,
/*disable_wifi_hotspot=*/false,
[this, cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) {
impl_->send_mode_started = true;
}
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::StopSendMode(std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
if (!impl_->send_mode_started) {
if (callback) {
callback(StatusCode::kStatusAlreadyStopped);
}
return;
}
impl_->service->UnregisterSendSurface(
impl_.get(),
[this, cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) {
impl_->send_mode_started = false;
}
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::StartReceiveMode(std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
if (impl_->receive_mode_started) {
if (callback) {
callback(StatusCode::kOk);
}
return;
}
impl_->service->SetVisibility(
nearby::sharing::proto::DeviceVisibility::DEVICE_VISIBILITY_EVERYONE,
absl::Minutes(10),
[this, cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (status != nearby::sharing::NearbySharingService::StatusCodes::kOk) {
if (cb) {
cb(ToFacadeStatus(status));
}
return;
}
impl_->service->RegisterReceiveSurface(
impl_.get(),
nearby::sharing::NearbySharingService::ReceiveSurfaceState::
kForeground,
nearby::sharing::Advertisement::BlockedVendorId::kNone,
[this, cb = std::move(cb)](
nearby::sharing::NearbySharingService::StatusCodes status)
mutable {
if (status ==
nearby::sharing::NearbySharingService::StatusCodes::kOk) {
impl_->receive_mode_started = true;
}
if (cb) {
cb(ToFacadeStatus(status));
}
});
});
}
void NearbySharingApi::StopReceiveMode(std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
if (!impl_->receive_mode_started) {
if (callback) {
callback(StatusCode::kStatusAlreadyStopped);
}
return;
}
impl_->service->UnregisterReceiveSurface(
impl_.get(),
[this, cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) {
impl_->receive_mode_started = false;
}
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::SendFile(int64_t share_target_id,
const std::string& file_path,
std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
if (file_path.empty()) {
if (callback) {
callback(StatusCode::kInvalidArgument);
}
return;
}
FilePath path(file_path);
std::optional<uintmax_t> file_size = nearby::Files::GetFileSize(path);
if (!file_size.has_value() || *file_size == 0 ||
*file_size >
static_cast<uintmax_t>(std::numeric_limits<int64_t>::max())) {
if (callback) {
callback(StatusCode::kInvalidArgument);
}
return;
}
nearby::sharing::AttachmentContainer::Builder builder;
nearby::sharing::FileAttachment attachment(path);
attachment.set_size(static_cast<int64_t>(*file_size));
builder.AddFileAttachment(std::move(attachment));
std::unique_ptr<nearby::sharing::AttachmentContainer> attachments =
builder.Build();
if (!attachments || !attachments->HasAttachments()) {
if (callback) {
callback(StatusCode::kInvalidArgument);
}
return;
}
impl_->service->SendAttachments(
share_target_id, std::move(attachments),
[cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::Accept(int64_t share_target_id,
std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
impl_->service->Accept(
share_target_id,
[cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::Reject(int64_t share_target_id,
std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
impl_->service->Reject(
share_target_id,
[cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::Cancel(int64_t share_target_id,
std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
impl_->service->Cancel(
share_target_id,
[cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
void NearbySharingApi::Set5GhzHotspotEnabled(bool enabled) {
nearby::linux::Set5GhzHotspotEnabled(enabled);
}
void NearbySharingApi::SetDeviceName(const std::string& device_name) {
if (impl_->service == nullptr || device_name.empty()) {
return;
}
if (impl_->service->GetLocalDeviceDataManager() == nullptr) {
return;
}
impl_->service->GetLocalDeviceDataManager()->SetDeviceName(device_name);
}
void NearbySharingApi::Shutdown(std::function<void(StatusCode)> callback) {
if (impl_->service == nullptr) {
if (callback) {
callback(StatusCode::kError);
}
return;
}
impl_->service->Shutdown(
[this, cb = std::move(callback)](
nearby::sharing::NearbySharingService::StatusCodes status) mutable {
if (status == nearby::sharing::NearbySharingService::StatusCodes::kOk) {
impl_->send_mode_started = false;
impl_->receive_mode_started = false;
}
if (cb) {
cb(ToFacadeStatus(status));
}
});
}
std::string NearbySharingApi::GetQrCodeUrl() const {
if (impl_->qr_code_url.empty()) {
impl_->qr_code_url = GenerateQrCodeUrl();
}
return impl_->qr_code_url;
}
std::string NearbySharingApi::StatusCodeToString(StatusCode status) {
switch (status) {
case StatusCode::kOk:
return "Ok";
case StatusCode::kError:
return "Error";
case StatusCode::kOutOfOrderApiCall:
return "OutOfOrderApiCall";
case StatusCode::kStatusAlreadyStopped:
return "StatusAlreadyStopped";
case StatusCode::kTransferAlreadyInProgress:
return "TransferAlreadyInProgress";
case StatusCode::kNoAvailableConnectionMedium:
return "NoAvailableConnectionMedium";
case StatusCode::kIrrecoverableHardwareError:
return "IrrecoverableHardwareError";
case StatusCode::kInvalidArgument:
return "InvalidArgument";
}
return "Error";
}
std::string NearbySharingApi::TransferStatusToString(TransferStatus status) {
switch (status) {
case TransferStatus::kUnknown:
return "Unknown";
case TransferStatus::kConnecting:
return "Connecting";
case TransferStatus::kAwaitingLocalConfirmation:
return "AwaitingLocalConfirmation";
case TransferStatus::kAwaitingRemoteAcceptance:
return "AwaitingRemoteAcceptance";
case TransferStatus::kInProgress:
return "InProgress";
case TransferStatus::kComplete:
return "Complete";
case TransferStatus::kFailed:
return "Failed";
case TransferStatus::kRejected:
return "Rejected";
case TransferStatus::kCancelled:
return "Cancelled";
case TransferStatus::kTimedOut:
return "TimedOut";
case TransferStatus::kMediaUnavailable:
return "MediaUnavailable";
case TransferStatus::kNotEnoughSpace:
return "NotEnoughSpace";
case TransferStatus::kUnsupportedAttachmentType:
return "UnsupportedAttachmentType";
case TransferStatus::kDeviceAuthenticationFailed:
return "DeviceAuthenticationFailed";
case TransferStatus::kIncompletePayloads:
return "IncompletePayloads";
}
return "Unknown";
}
} // namespace nearby::sharing
-131
View File
@@ -1,131 +0,0 @@
// Copyright 2026
//
// Thin app-facing API for NearbySharingServiceLinux that avoids exposing
// internal Nearby headers to external consumers.
#ifndef SHARING_LINUX_NEARBY_SHARING_API_H_
#define SHARING_LINUX_NEARBY_SHARING_API_H_
#include <stdint.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
namespace nearby {
namespace sharing {
class __attribute__((visibility("default"))) NearbySharingApi {
public:
enum class StatusCode {
kOk = 0,
kError = 1,
kOutOfOrderApiCall = 2,
kStatusAlreadyStopped = 3,
kTransferAlreadyInProgress = 4,
kNoAvailableConnectionMedium = 5,
kIrrecoverableHardwareError = 6,
kInvalidArgument = 7,
};
enum class TransferStatus {
kUnknown = 0,
kConnecting = 1,
kAwaitingLocalConfirmation = 2,
kAwaitingRemoteAcceptance = 3,
kInProgress = 4,
kComplete = 5,
kFailed = 6,
kRejected = 7,
kCancelled = 8,
kTimedOut = 9,
kMediaUnavailable = 10,
kNotEnoughSpace = 11,
kUnsupportedAttachmentType = 12,
kDeviceAuthenticationFailed = 13,
kIncompletePayloads = 14,
};
enum class TextAttachmentType {
kUnknown = 0,
kText = 1,
kUrl = 2,
kPhoneNumber = 3,
kAddress = 4,
};
struct ShareTargetInfo {
int64_t id = 0;
std::string device_name;
bool is_incoming = false;
int device_type = 0;
};
struct TextAttachmentInfo {
TextAttachmentType type = TextAttachmentType::kUnknown;
std::string text_title;
std::string text_body;
};
struct TransferUpdateInfo {
int64_t share_target_id = 0;
std::string device_name;
bool is_incoming = false;
TransferStatus status = TransferStatus::kUnknown;
float progress = 0.0f;
uint64_t transferred_bytes = 0;
int total_attachments = 0;
int transferred_attachments = 0;
std::string first_file_name;
std::string first_file_path;
std::vector<TextAttachmentInfo> text_attachments;
};
struct Listener {
std::function<void(const ShareTargetInfo&)> target_discovered_cb;
std::function<void(const ShareTargetInfo&)> target_updated_cb;
std::function<void(int64_t)> target_lost_cb;
std::function<void(const TransferUpdateInfo&)> transfer_update_cb;
};
NearbySharingApi();
explicit NearbySharingApi(std::string device_name_override);
~NearbySharingApi();
NearbySharingApi(const NearbySharingApi&) = delete;
NearbySharingApi& operator=(const NearbySharingApi&) = delete;
NearbySharingApi(NearbySharingApi&&) noexcept;
NearbySharingApi& operator=(NearbySharingApi&&) noexcept;
void SetListener(Listener listener);
void StartSendMode(std::function<void(StatusCode)> callback);
void StopSendMode(std::function<void(StatusCode)> callback);
void StartReceiveMode(std::function<void(StatusCode)> callback);
void StopReceiveMode(std::function<void(StatusCode)> callback);
void SendFile(int64_t share_target_id, const std::string& file_path,
std::function<void(StatusCode)> callback);
void Accept(int64_t share_target_id, std::function<void(StatusCode)> callback);
void Reject(int64_t share_target_id, std::function<void(StatusCode)> callback);
void Cancel(int64_t share_target_id, std::function<void(StatusCode)> callback);
void Set5GhzHotspotEnabled(bool enabled);
void SetDeviceName(const std::string& device_name);
void Shutdown(std::function<void(StatusCode)> callback);
std::string GetQrCodeUrl() const;
static std::string StatusCodeToString(StatusCode status);
static std::string TransferStatusToString(TransferStatus status);
private:
class Impl;
std::unique_ptr<Impl> impl_;
};
} // namespace sharing
} // namespace nearby
#endif // SHARING_LINUX_NEARBY_SHARING_API_H_
@@ -0,0 +1,102 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/linux/platform/linux_account_manager.h"
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
namespace nearby::sharing::linux::internal {
namespace {
class FailedSigninAttempt final : public SigninAttempt {
public:
std::string Start(
absl::AnyInvocable<void(AuthStatus, absl::string_view, absl::string_view,
const AccountInfo&)>
callback) override {
if (callback) {
std::move(callback)(UNSUPPORTED, "", "", AccountInfo{});
}
return {};
}
void Close() override {}
};
class LinuxAccountManager final : public AccountManager {
public:
std::optional<Account> GetCurrentAccount() override { return std::nullopt; }
std::unique_ptr<SigninAttempt> Login(absl::string_view client_id,
absl::string_view client_secret)
override {
last_client_id_ = std::string(client_id);
last_client_secret_ = std::string(client_secret);
return std::make_unique<FailedSigninAttempt>();
}
void Logout(absl::AnyInvocable<void(absl::Status)> logout_callback) override {
if (logout_callback) {
std::move(logout_callback)(absl::OkStatus());
}
}
bool GetAccessToken(
absl::AnyInvocable<void(absl::StatusOr<std::string>)> callback)
override {
if (!callback) {
return false;
}
std::move(callback)(
absl::UnavailableError("Linux account integration is not available"));
return true;
}
std::pair<std::string, std::string> GetOAuthClientCredential() override {
return {last_client_id_, last_client_secret_};
}
void AddObserver(Observer* observer) override { observers_.insert(observer); }
void RemoveObserver(Observer* observer) override {
observers_.erase(observer);
}
void SaveAccountPrefs(absl::string_view user_id, absl::string_view client_id,
absl::string_view client_secret) override {
static_cast<void>(user_id);
last_client_id_ = std::string(client_id);
last_client_secret_ = std::string(client_secret);
}
private:
absl::flat_hash_set<Observer*> observers_;
std::string last_client_id_;
std::string last_client_secret_;
};
} // namespace
std::unique_ptr<AccountManager> CreateLinuxAccountManager() {
return std::make_unique<LinuxAccountManager>();
}
} // namespace nearby::sharing::linux::internal
@@ -0,0 +1,28 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_
#define SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_
#include <memory>
#include "location/nearby/sharing/lib/account/account_manager.h"
namespace nearby::sharing::linux::internal {
std::unique_ptr<AccountManager> CreateLinuxAccountManager();
} // namespace nearby::sharing::linux::internal
#endif // SHARING_LINUX_PLATFORM_LINUX_ACCOUNT_MANAGER_H_
@@ -0,0 +1,381 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/linux/platform/linux_platform_components.h"
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <cstdlib>
#include <functional>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/base/file_path.h"
#include "internal/platform/mac_address.h"
#include "sharing/internal/api/private_certificate_data.h"
#include "sharing/linux/platform/platform_util.h"
#include "sharing/proto/rpc_resources.pb.h"
namespace nearby::sharing::linux::internal {
namespace {
using ::nearby::sharing::api::PreferenceManager;
using ::nearby::sharing::api::PublicCertificateDatabase;
using ::nearby::sharing::proto::PublicCertificate;
constexpr absl::string_view kAppFirstRunPref = "nearby_sharing.app.first_run";
constexpr absl::string_view kAppActivePref = "nearby_sharing.app.active";
class LinuxNetworkMonitor final : public nearby::api::NetworkMonitor {
public:
LinuxNetworkMonitor(std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback)
: nearby::api::NetworkMonitor(std::move(lan_connected_callback),
std::move(internet_connected_callback)) {
if (lan_connected_callback_) {
lan_connected_callback_(IsLanConnected());
}
if (internet_connected_callback_) {
internet_connected_callback_(IsInternetConnected());
}
}
bool IsLanConnected() override { return HasNonLoopbackInterface(); }
bool IsInternetConnected() override { return HasNonLoopbackInterface(); }
};
class LinuxSystemInfo final : public nearby::api::SystemInfo {
public:
std::string GetComputerManufacturer() override { return "Unknown"; }
std::string GetComputerModel() override { return "Unknown"; }
int64_t GetComputerPhysicalMemory() override {
struct sysinfo info;
if (sysinfo(&info) != 0) {
return 0;
}
return static_cast<int64_t>(info.totalram) * info.mem_unit;
}
int GetComputerProcessorCount() override {
long processors = sysconf(_SC_NPROCESSORS_CONF);
return processors > 0 ? static_cast<int>(processors) : 1;
}
int GetComputerLogicProcessorCount() override {
long processors = sysconf(_SC_NPROCESSORS_ONLN);
return processors > 0 ? static_cast<int>(processors) : 1;
}
int GetProcessorMemoryInfo() override { return 0; }
BatteryChargeStatus QueryBatteryInfo(int& seconds, int& percent,
bool& battery_saver) override {
seconds = 0;
percent = 0;
battery_saver = false;
return BatteryChargeStatus::UNKNOWN;
}
std::string GetOsManufacturer() override { return "Linux"; }
std::string GetOsName() override { return "Linux"; }
std::string GetOsVersion() override {
struct utsname info;
return uname(&info) == 0 ? std::string(info.release) : std::string();
}
std::string GetOsArchitecture() override {
struct utsname info;
return uname(&info) == 0 ? std::string(info.machine) : std::string();
}
std::string GetOsLanguage() override {
return GetLanguageCode().value_or("en");
}
std::string GetProcessorManufacturer() override { return "Unknown"; }
std::string GetProcessorName() override { return "Unknown"; }
std::list<DriverInfo> GetBluetoothDriverInfos() override { return {}; }
std::list<DriverInfo> GetNetworkDriverInfos() override { return {}; }
void GetBatteryUsageReport(const FilePath& save_path) override {
static_cast<void>(save_path);
}
};
class LinuxAppInfo final : public nearby::api::AppInfo {
public:
explicit LinuxAppInfo(PreferenceManager& preference_manager)
: preference_manager_(preference_manager) {}
std::optional<std::string> GetAppVersion() override {
return std::string("linux");
}
std::optional<std::string> GetAppLanguage() override {
return GetLanguageCode();
}
std::optional<std::string> GetUpdateTrack() override { return std::nullopt; }
std::optional<std::string> GetAppInstallSource() override {
return std::string("manual");
}
bool GetFirstRunDone() override {
return preference_manager_.GetBoolean(kAppFirstRunPref, false);
}
bool SetFirstRunDone(bool value) override {
preference_manager_.SetBoolean(kAppFirstRunPref, value);
return true;
}
bool SetActiveFlag() override {
preference_manager_.SetBoolean(kAppActivePref, true);
return true;
}
private:
PreferenceManager& preference_manager_;
};
class LinuxDeviceInfo final : public nearby::api::DeviceInfo {
public:
std::optional<std::string> GetOsDeviceName() const override {
char hostname[256] = {};
if (gethostname(hostname, sizeof(hostname)) == 0 && hostname[0] != '\0') {
return std::string(hostname);
}
return std::string("Linux");
}
DeviceType GetDeviceType() const override { return DeviceType::kLaptop; }
OsType GetOsType() const override { return OsType::kUnknown; }
FilePath GetDownloadPath() const override {
const char* xdg_download_dir = std::getenv("XDG_DOWNLOAD_DIR");
if (xdg_download_dir != nullptr && *xdg_download_dir != '\0') {
return FilePath(std::string(xdg_download_dir));
}
return BuildPathFromBase(GetHomeDirectory(), {"Downloads"});
}
FilePath GetLocalAppDataPath(FilePath sub_path) const override {
std::string config_home = GetEnvOrDefault(
"XDG_CONFIG_HOME",
BuildPathFromBase(GetHomeDirectory(), {".config"}).ToString());
FilePath path = BuildPathFromBase(config_home, {"Google Nearby"});
if (!sub_path.IsEmpty()) {
path.append(sub_path);
}
return path;
}
FilePath GetTemporaryPath() const override {
const char* runtime_dir = std::getenv("XDG_RUNTIME_DIR");
if (runtime_dir != nullptr && *runtime_dir != '\0') {
return BuildPathFromBase(runtime_dir, {"Google Nearby"});
}
return BuildPathFromBase("/tmp", {"Google Nearby"});
}
FilePath GetLogPath() const override {
return GetLocalAppDataPath(FilePath("logs"));
}
bool IsScreenLocked() const override { return false; }
void RegisterScreenLockedListener(
absl::string_view listener_name,
std::function<void(ScreenStatus)> callback) override {
screen_locked_listeners_[std::string(listener_name)] = std::move(callback);
}
void UnregisterScreenLockedListener(
absl::string_view listener_name) override {
screen_locked_listeners_.erase(std::string(listener_name));
}
bool PreventSleep() override { return true; }
bool AllowSleep() override { return true; }
private:
mutable absl::flat_hash_map<std::string, std::function<void(ScreenStatus)>>
screen_locked_listeners_;
};
class LinuxBluetoothAdapter final : public api::BluetoothAdapter {
public:
explicit LinuxBluetoothAdapter(
std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter)
: adapter_(std::move(adapter)) {}
bool IsPresent() const override { return GetAddress().IsSet(); }
bool IsPowered() const override {
return adapter_ != nullptr && adapter_->IsEnabled();
}
bool IsLowEnergySupported() const override { return adapter_ != nullptr; }
bool IsScanOffloadSupported() const override { return false; }
bool IsAdvertisementOffloadSupported() const override { return false; }
bool IsExtendedAdvertisingSupported() const override { return false; }
bool IsPeripheralRoleSupported() const override { return adapter_ != nullptr; }
PermissionStatus GetOsPermissionStatus() const override {
return adapter_ != nullptr ? PermissionStatus::kAllowed
: PermissionStatus::kSystemDenied;
}
void SetPowered(bool powered, std::function<void()> success_callback,
std::function<void()> error_callback) override {
if (adapter_ == nullptr) {
if (error_callback) {
error_callback();
}
return;
}
bool success = adapter_->SetStatus(
powered ? nearby::api::BluetoothAdapter::Status::kEnabled
: nearby::api::BluetoothAdapter::Status::kDisabled);
if (success) {
if (success_callback) {
success_callback();
}
return;
}
if (error_callback) {
error_callback();
}
}
std::optional<std::string> GetAdapterId() const override {
if (adapter_ == nullptr) {
return std::nullopt;
}
std::string name = adapter_->GetName();
return name.empty() ? std::nullopt : std::make_optional(name);
}
MacAddress GetAddress() const override {
return adapter_ != nullptr ? adapter_->GetMacAddress() : MacAddress();
}
void AddObserver(Observer* observer) override { observers_.insert(observer); }
void RemoveObserver(Observer* observer) override {
observers_.erase(observer);
}
bool HasObserver(Observer* observer) override {
return observers_.contains(observer);
}
private:
std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter_;
absl::flat_hash_set<Observer*> observers_;
};
class LinuxPublicCertificateDatabase final : public PublicCertificateDatabase {
public:
void Initialize(absl::AnyInvocable<void(InitStatus) &&> callback) override {
if (callback) {
std::move(callback)(InitStatus::kOk);
}
}
void LoadEntries(
absl::AnyInvocable<void(bool, std::unique_ptr<std::vector<
PublicCertificate>>) &&>
callback) override {
auto certificates = std::make_unique<std::vector<PublicCertificate>>();
{
absl::MutexLock lock(mutex_);
for (const auto& [id, certificate] : entries_) {
static_cast<void>(id);
certificates->push_back(certificate);
}
}
if (callback) {
std::move(callback)(true, std::move(certificates));
}
}
void LoadCertificate(
absl::string_view id,
absl::AnyInvocable<void(bool, std::unique_ptr<PublicCertificate>) &&>
callback) override {
auto certificate = std::make_unique<PublicCertificate>();
bool found = false;
{
absl::MutexLock lock(mutex_);
auto it = entries_.find(std::string(id));
if (it != entries_.end()) {
*certificate = it->second;
found = true;
}
}
if (callback) {
std::move(callback)(found, found ? std::move(certificate) : nullptr);
}
}
void AddCertificates(absl::Span<const PublicCertificate> certificates,
absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(mutex_);
for (const PublicCertificate& certificate : certificates) {
entries_[certificate.secret_id()] = certificate;
}
}
if (callback) {
std::move(callback)(true);
}
}
void RemoveCertificatesById(
std::vector<std::string> ids_to_remove,
absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(mutex_);
for (const std::string& id : ids_to_remove) {
entries_.erase(id);
}
}
if (callback) {
std::move(callback)(true);
}
}
void Destroy(absl::AnyInvocable<void(bool) &&> callback) override {
{
absl::MutexLock lock(mutex_);
entries_.clear();
}
if (callback) {
std::move(callback)(true);
}
}
private:
absl::Mutex mutex_;
std::map<std::string, PublicCertificate> entries_ ABSL_GUARDED_BY(mutex_);
};
} // namespace
std::unique_ptr<nearby::api::NetworkMonitor> CreateLinuxNetworkMonitor(
std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback) {
return std::make_unique<LinuxNetworkMonitor>(
std::move(lan_connected_callback), std::move(internet_connected_callback));
}
std::unique_ptr<nearby::api::SystemInfo> CreateLinuxSystemInfo() {
return std::make_unique<LinuxSystemInfo>();
}
std::unique_ptr<nearby::api::AppInfo> CreateLinuxAppInfo(
api::PreferenceManager& preference_manager) {
return std::make_unique<LinuxAppInfo>(preference_manager);
}
std::unique_ptr<nearby::api::DeviceInfo> CreateLinuxDeviceInfo() {
return std::make_unique<LinuxDeviceInfo>();
}
std::unique_ptr<api::BluetoothAdapter> CreateLinuxBluetoothAdapter(
std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter) {
return std::make_unique<LinuxBluetoothAdapter>(std::move(adapter));
}
std::unique_ptr<api::PublicCertificateDatabase>
CreateLinuxPublicCertificateDatabase() {
return std::make_unique<LinuxPublicCertificateDatabase>();
}
} // namespace nearby::sharing::linux::internal
@@ -0,0 +1,46 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_
#define SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_
#include <functional>
#include <memory>
#include "internal/platform/implementation/device_info.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "sharing/internal/api/app_info.h"
#include "sharing/internal/api/bluetooth_adapter.h"
#include "sharing/internal/api/network_monitor.h"
#include "sharing/internal/api/preference_manager.h"
#include "sharing/internal/api/public_certificate_database.h"
#include "sharing/internal/api/system_info.h"
namespace nearby::sharing::linux::internal {
std::unique_ptr<nearby::api::NetworkMonitor> CreateLinuxNetworkMonitor(
std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback);
std::unique_ptr<nearby::api::SystemInfo> CreateLinuxSystemInfo();
std::unique_ptr<nearby::api::AppInfo> CreateLinuxAppInfo(
api::PreferenceManager& preference_manager);
std::unique_ptr<nearby::api::DeviceInfo> CreateLinuxDeviceInfo();
std::unique_ptr<api::BluetoothAdapter> CreateLinuxBluetoothAdapter(
std::shared_ptr<::nearby::linux::BluetoothAdapter> adapter);
std::unique_ptr<api::PublicCertificateDatabase>
CreateLinuxPublicCertificateDatabase();
} // namespace nearby::sharing::linux::internal
#endif // SHARING_LINUX_PLATFORM_LINUX_PLATFORM_COMPONENTS_H_
@@ -0,0 +1,410 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/linux/platform/linux_preference_manager.h"
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"
#include "internal/platform/implementation/platform.h"
#include "location/nearby/sharing/lib/sync/sync_binding_prefs.pb.h"
#include "nlohmann/json.hpp"
#include "sharing/internal/api/private_certificate_data.h"
#include "sharing/internal/public/pref_names.h"
namespace nearby::sharing::linux::internal {
namespace {
using Json = nlohmann::json;
using ::nearby::sharing::api::PreferenceManager;
using ::nearby::sharing::api::PrivateCertificateData;
constexpr absl::string_view kPreferencesFile = "nearby_sharing_linux.json";
constexpr absl::string_view kFileSyncBindingName = "FileSync";
Json ToJson(const PrivateCertificateData& certificate) {
return Json{
{PrivateCertificateData::kVisibility, certificate.visibility},
{PrivateCertificateData::kNotBefore, certificate.not_before},
{PrivateCertificateData::kNotAfter, certificate.not_after},
{PrivateCertificateData::kKeyPair, certificate.key_pair},
{PrivateCertificateData::kSecretKey, certificate.secret_key},
{PrivateCertificateData::kMetadataEncryptionKey,
certificate.metadata_encryption_key},
{PrivateCertificateData::kId, certificate.id},
{PrivateCertificateData::kUnencryptedMetadata,
certificate.unencrypted_metadata_proto},
{PrivateCertificateData::kConsumedSalts, certificate.consumed_salts},
};
}
std::optional<PrivateCertificateData> FromJson(const Json& value) {
if (!value.is_object()) {
return std::nullopt;
}
PrivateCertificateData certificate;
try {
certificate.visibility = value.at(PrivateCertificateData::kVisibility);
certificate.not_before = value.at(PrivateCertificateData::kNotBefore);
certificate.not_after = value.at(PrivateCertificateData::kNotAfter);
certificate.key_pair = value.at(PrivateCertificateData::kKeyPair);
certificate.secret_key = value.at(PrivateCertificateData::kSecretKey);
certificate.metadata_encryption_key =
value.at(PrivateCertificateData::kMetadataEncryptionKey);
certificate.id = value.at(PrivateCertificateData::kId);
certificate.unencrypted_metadata_proto =
value.at(PrivateCertificateData::kUnencryptedMetadata);
certificate.consumed_salts =
value.at(PrivateCertificateData::kConsumedSalts);
return certificate;
} catch (const Json::exception&) {
return std::nullopt;
}
}
class LinuxPreferenceManager final : public PreferenceManager {
public:
LinuxPreferenceManager()
: storage_(nearby::api::ImplementationPlatform::CreatePreferencesManager(
kPreferencesFile)) {}
void SetBoolean(absl::string_view key, bool value) override {
if (storage_ != nullptr && storage_->SetBoolean(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInteger(absl::string_view key, int value) override {
if (storage_ != nullptr && storage_->SetInteger(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInt64(absl::string_view key, int64_t value) override {
if (storage_ != nullptr && storage_->SetInt64(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetString(absl::string_view key, absl::string_view value) override {
if (storage_ != nullptr && storage_->SetString(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetTime(absl::string_view key, absl::Time value) override {
if (storage_ != nullptr && storage_->SetTime(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetBooleanArray(absl::string_view key,
absl::Span<const bool> value) override {
if (storage_ != nullptr && storage_->SetBooleanArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetIntegerArray(absl::string_view key,
absl::Span<const int> value) override {
if (storage_ != nullptr && storage_->SetIntegerArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetInt64Array(absl::string_view key,
absl::Span<const int64_t> value) override {
if (storage_ != nullptr && storage_->SetInt64Array(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetStringArray(absl::string_view key,
absl::Span<const std::string> value) override {
if (storage_ != nullptr && storage_->SetStringArray(key, value)) {
NotifyPreferenceChanged(key);
}
}
void SetPrivateCertificateArray(
absl::string_view key, absl::Span<const PrivateCertificateData> value)
override {
Json certificates = Json::array();
for (const PrivateCertificateData& certificate : value) {
certificates.push_back(ToJson(certificate));
}
if (storage_ != nullptr && storage_->Set(key, certificates)) {
NotifyPreferenceChanged(key);
}
}
void SetCertificateExpirationArray(
absl::string_view key,
absl::Span<const std::pair<std::string, int64_t>> value) override {
Json expirations = Json::array();
for (const auto& [id, expiration] : value) {
expirations.push_back(Json{{"id", id}, {"expiration", expiration}});
}
if (storage_ != nullptr && storage_->Set(key, expirations)) {
NotifyPreferenceChanged(key);
}
}
void SetDictionaryBooleanValue(absl::string_view key,
absl::string_view dictionary_item,
bool value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryIntegerValue(absl::string_view key,
absl::string_view dictionary_item,
int value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryInt64Value(absl::string_view key,
absl::string_view dictionary_item,
int64_t value) override {
SetDictionaryValue(key, dictionary_item, value);
}
void SetDictionaryStringValue(absl::string_view key,
absl::string_view dictionary_item,
std::string value) override {
SetDictionaryValue(key, dictionary_item, std::move(value));
}
void RemoveDictionaryItem(absl::string_view key,
absl::string_view dictionary_item) override {
if (storage_ == nullptr) {
return;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object()) {
return;
}
dictionary.erase(std::string(dictionary_item));
if (storage_->Set(key, dictionary)) {
NotifyPreferenceChanged(key);
}
}
void SetSyncBindingValue(
const nearby::sharing::sync::SyncBindingPrefs& value) override {
std::string serialized;
if (value.SerializeToString(&serialized)) {
SetString(absl::StrCat(PrefNames::kBindingConfigPrefix,
kFileSyncBindingName),
serialized);
}
}
bool GetBoolean(absl::string_view key, bool default_value) const override {
return storage_ != nullptr ? storage_->GetBoolean(key, default_value)
: default_value;
}
int GetInteger(absl::string_view key, int default_value) const override {
return storage_ != nullptr ? storage_->GetInteger(key, default_value)
: default_value;
}
int64_t GetInt64(absl::string_view key,
int64_t default_value) const override {
return storage_ != nullptr ? storage_->GetInt64(key, default_value)
: default_value;
}
std::string GetString(absl::string_view key,
const std::string& default_value) const override {
return storage_ != nullptr ? storage_->GetString(key, default_value)
: default_value;
}
absl::Time GetTime(absl::string_view key,
absl::Time default_value) const override {
return storage_ != nullptr ? storage_->GetTime(key, default_value)
: default_value;
}
std::vector<bool> GetBooleanArray(
absl::string_view key,
absl::Span<const bool> default_value) const override {
return storage_ != nullptr ? storage_->GetBooleanArray(key, default_value)
: std::vector<bool>(default_value.begin(),
default_value.end());
}
std::vector<int> GetIntegerArray(
absl::string_view key,
absl::Span<const int> default_value) const override {
return storage_ != nullptr ? storage_->GetIntegerArray(key, default_value)
: std::vector<int>(default_value.begin(),
default_value.end());
}
std::vector<int64_t> GetInt64Array(
absl::string_view key,
absl::Span<const int64_t> default_value) const override {
return storage_ != nullptr ? storage_->GetInt64Array(key, default_value)
: std::vector<int64_t>(default_value.begin(),
default_value.end());
}
std::vector<std::string> GetStringArray(
absl::string_view key,
absl::Span<const std::string> default_value) const override {
return storage_ != nullptr ? storage_->GetStringArray(key, default_value)
: std::vector<std::string>(default_value.begin(),
default_value.end());
}
std::vector<PrivateCertificateData> GetPrivateCertificateArray(
absl::string_view key) const override {
std::vector<PrivateCertificateData> result;
if (storage_ == nullptr) {
return result;
}
Json certificates = storage_->Get(key, Json::array());
if (!certificates.is_array()) {
return result;
}
for (const Json& certificate_json : certificates) {
std::optional<PrivateCertificateData> certificate =
FromJson(certificate_json);
if (certificate.has_value()) {
result.push_back(*certificate);
}
}
return result;
}
std::vector<std::pair<std::string, int64_t>>
GetCertificateExpirationArray(absl::string_view key) const override {
std::vector<std::pair<std::string, int64_t>> result;
if (storage_ == nullptr) {
return result;
}
Json expirations = storage_->Get(key, Json::array());
if (!expirations.is_array()) {
return result;
}
for (const Json& item : expirations) {
if (!item.is_object()) {
continue;
}
try {
result.emplace_back(item.at("id").get<std::string>(),
item.at("expiration").get<int64_t>());
} catch (const Json::exception&) {
}
}
return result;
}
std::optional<bool> GetDictionaryBooleanValue(
absl::string_view key, absl::string_view dictionary_item) const override {
return GetDictionaryValue<bool>(key, dictionary_item);
}
std::optional<int> GetDictionaryIntegerValue(
absl::string_view key, absl::string_view dictionary_item) const override {
return GetDictionaryValue<int>(key, dictionary_item);
}
std::optional<int64_t> GetDictionaryInt64Value(
absl::string_view key, absl::string_view dictionary_item) const override {
return GetDictionaryValue<int64_t>(key, dictionary_item);
}
std::optional<std::string> GetDictionaryStringValue(
absl::string_view key, absl::string_view dictionary_item) const override {
return GetDictionaryValue<std::string>(key, dictionary_item);
}
std::optional<nearby::sharing::sync::SyncBindingPrefs> GetSyncBindingValue()
const override {
std::string serialized =
GetString(absl::StrCat(PrefNames::kBindingConfigPrefix,
kFileSyncBindingName),
"");
if (serialized.empty()) {
return std::nullopt;
}
nearby::sharing::sync::SyncBindingPrefs value;
if (!value.ParseFromString(serialized)) {
return std::nullopt;
}
return value;
}
void Remove(absl::string_view key) override {
if (storage_ != nullptr) {
storage_->Remove(key);
NotifyPreferenceChanged(key);
}
}
void RemoveAllBindingConfigs() override {
if (storage_ != nullptr &&
storage_->RemoveKeyPrefix(PrefNames::kBindingConfigPrefix)) {
NotifyPreferenceChanged(PrefNames::kBindingConfigPrefix);
}
}
void AddObserver(
absl::string_view name,
std::function<void(absl::string_view pref_name)> observer) override {
observers_[std::string(name)] = std::move(observer);
}
void RemoveObserver(absl::string_view name) override {
observers_.erase(std::string(name));
}
private:
template <typename T>
void SetDictionaryValue(absl::string_view key,
absl::string_view dictionary_item, T value) {
if (storage_ == nullptr) {
return;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object()) {
dictionary = Json::object();
}
dictionary[std::string(dictionary_item)] = std::move(value);
if (storage_->Set(key, dictionary)) {
NotifyPreferenceChanged(key);
}
}
template <typename T>
std::optional<T> GetDictionaryValue(
absl::string_view key, absl::string_view dictionary_item) const {
if (storage_ == nullptr) {
return std::nullopt;
}
Json dictionary = storage_->Get(key, Json::object());
if (!dictionary.is_object()) {
return std::nullopt;
}
auto it = dictionary.find(std::string(dictionary_item));
if (it == dictionary.end()) {
return std::nullopt;
}
try {
return it->get<T>();
} catch (const Json::exception&) {
return std::nullopt;
}
}
void NotifyPreferenceChanged(absl::string_view key) {
for (const auto& [name, observer] : observers_) {
static_cast<void>(name);
if (observer) {
observer(key);
}
}
}
std::unique_ptr<nearby::api::PreferencesManager> storage_;
absl::flat_hash_map<std::string,
std::function<void(absl::string_view pref_name)>>
observers_;
};
} // namespace
std::unique_ptr<api::PreferenceManager> CreateLinuxPreferenceManager() {
return std::make_unique<LinuxPreferenceManager>();
}
} // namespace nearby::sharing::linux::internal
@@ -12,20 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_HIGHWAY_FINGERPRINT_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_HIGHWAY_FINGERPRINT_H_
#ifndef SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_
#define SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_
#include <cstdint>
#include <memory>
#include "absl/hash/hash.h"
#include "absl/strings/string_view.h"
#include "sharing/internal/api/preference_manager.h"
namespace util_hash {
namespace nearby::sharing::linux::internal {
inline uint64_t HighwayFingerprint64(absl::string_view input) {
return absl::Hash<absl::string_view>{}(input);
}
std::unique_ptr<api::PreferenceManager> CreateLinuxPreferenceManager();
} // namespace util_hash
} // namespace nearby::sharing::linux::internal
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_HIGHWAY_FINGERPRINT_H_
#endif // SHARING_LINUX_PLATFORM_LINUX_PREFERENCE_MANAGER_H_
File diff suppressed because it is too large Load Diff
+31 -23
View File
@@ -1,23 +1,30 @@
// Copyright 2026
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
#ifndef SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
#define SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "internal/base/file_path.h"
#include "internal/platform/device_info.h"
#include "internal/platform/implementation/account_manager.h"
#include "internal/platform/task_runner.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "location/nearby/sharing/lib/account/account_manager.h"
#include "sharing/internal/api/sharing_platform.h"
namespace nearby::sharing {
namespace nearby::sharing::linux {
class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform {
class LinuxSharingPlatform final : public api::SharingPlatform {
public:
LinuxSharingPlatform();
explicit LinuxSharingPlatform(std::string device_name_override);
@@ -33,32 +40,33 @@ class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform
std::function<void(bool)> lan_connected_callback,
std::function<void(bool)> internet_connected_callback) override;
nearby::sharing::api::BluetoothAdapter& GetBluetoothAdapter() override;
api::BluetoothAdapter& GetBluetoothAdapter() override;
nearby::api::FastInitBleBeacon& GetFastInitBleBeacon() override;
nearby::api::FastInitiationManager& GetFastInitiationManager() override;
std::unique_ptr<nearby::api::SystemInfo> CreateSystemInfo() override;
std::unique_ptr<nearby::api::AppInfo> CreateAppInfo() override;
nearby::sharing::api::PreferenceManager& GetPreferenceManager() override;
api::PreferenceManager& GetPreferenceManager() override;
AccountManager& GetAccountManager() override;
TaskRunner& GetDefaultTaskRunner() override;
nearby::DeviceInfo& GetDeviceInfo() override;
std::unique_ptr<nearby::sharing::api::PublicCertificateDatabase>
nearby::api::DeviceInfo& GetDeviceInfo() override;
std::unique_ptr<api::PublicCertificateDatabase>
CreatePublicCertificateDatabase(const FilePath& database_path) override;
bool UpdateFileOriginMetadata(std::vector<FilePath>& file_paths) override;
private:
void Initialize(std::string device_name_override);
std::unique_ptr<nearby::sharing::api::PreferenceManager> preference_manager_;
std::shared_ptr<::nearby::linux::BluetoothAdapter> fast_init_adapter_;
std::unique_ptr<api::PreferenceManager> preference_manager_;
std::unique_ptr<AccountManager> account_manager_;
std::unique_ptr<nearby::sharing::api::BluetoothAdapter> bluetooth_adapter_;
std::unique_ptr<api::BluetoothAdapter> bluetooth_adapter_;
std::unique_ptr<nearby::api::FastInitBleBeacon> fast_init_ble_beacon_;
std::unique_ptr<nearby::api::FastInitiationManager> fast_initiation_manager_;
std::unique_ptr<TaskRunner> default_task_runner_;
std::unique_ptr<nearby::DeviceInfo> device_info_;
std::unique_ptr<nearby::api::FastInitiationManager>
fast_initiation_manager_;
std::unique_ptr<nearby::api::DeviceInfo> device_info_;
absl::string_view (*product_id_getter_)() = nullptr;
};
} // namespace nearby::sharing::linux
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
#endif // SHARING_LINUX_PLATFORM_LINUX_SHARING_PLATFORM_H_
+120
View File
@@ -0,0 +1,120 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sharing/linux/platform/platform_util.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <cstdlib>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <sdbus-c++/Error.h>
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/logging.h"
namespace nearby::sharing::linux::internal {
std::string GetEnvOrDefault(const char* key, std::string fallback) {
const char* value = std::getenv(key);
if (value == nullptr || *value == '\0') {
return fallback;
}
return value;
}
std::string GetHomeDirectory() { return GetEnvOrDefault("HOME", "/tmp"); }
FilePath BuildPathFromBase(const std::string& base,
std::initializer_list<std::string> components) {
std::filesystem::path path(base);
for (const std::string& component : components) {
path /= component;
}
return FilePath(path.string());
}
std::optional<std::string> GetLanguageCode() {
const char* lang = std::getenv("LANG");
if (lang == nullptr || *lang == '\0') {
return std::string("en");
}
std::string value(lang);
size_t dot = value.find('.');
if (dot != std::string::npos) {
value.resize(dot);
}
size_t underscore = value.find('_');
if (underscore != std::string::npos) {
value.resize(underscore);
}
if (value.empty()) {
return std::string("en");
}
return value;
}
bool HasNonLoopbackInterface() {
struct ifaddrs* interfaces = nullptr;
if (getifaddrs(&interfaces) != 0) {
return false;
}
bool connected = false;
for (struct ifaddrs* current = interfaces; current != nullptr;
current = current->ifa_next) {
if (current->ifa_name == nullptr || current->ifa_flags == 0) {
continue;
}
if ((current->ifa_flags & IFF_UP) == 0 ||
(current->ifa_flags & IFF_LOOPBACK) != 0) {
continue;
}
connected = true;
break;
}
freeifaddrs(interfaces);
return connected;
}
std::shared_ptr<::nearby::linux::BluetoothAdapter>
CreateFastInitBluetoothAdapter() {
auto system_bus = ::nearby::linux::getSystemBusConnection();
auto manager = ::nearby::linux::bluez::BluezObjectManager(*system_bus);
try {
auto interfaces = manager.GetManagedObjects();
for (auto& [object, properties] : interfaces) {
if (properties.count(sdbus::InterfaceName(
org::bluez::Adapter1_proxy::INTERFACE_NAME)) == 1) {
LOG(INFO) << __func__ << ": found bluetooth adapter " << object;
return std::make_shared<::nearby::linux::BluetoothAdapter>(system_bus,
object);
}
}
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e);
}
LOG(WARNING) << __func__
<< ": couldn't find a bluetooth adapter on this system";
return nullptr;
}
} // namespace nearby::sharing::linux::internal
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_
#define SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_
#include <initializer_list>
#include <memory>
#include <optional>
#include <string>
#include "internal/base/file_path.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
namespace nearby::sharing::linux::internal {
std::string GetEnvOrDefault(const char* key, std::string fallback);
std::string GetHomeDirectory();
FilePath BuildPathFromBase(const std::string& base,
std::initializer_list<std::string> components);
std::optional<std::string> GetLanguageCode();
bool HasNonLoopbackInterface();
std::shared_ptr<::nearby::linux::BluetoothAdapter>
CreateFastInitBluetoothAdapter();
} // namespace nearby::sharing::linux::internal
#endif // SHARING_LINUX_PLATFORM_PLATFORM_UTIL_H_
@@ -1,26 +0,0 @@
// Copyright 2026
#include "internal/platform/system_clock.h"
#include <chrono>
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace nearby {
void SystemClock::Init() {}
absl::Time SystemClock::ElapsedRealtime() {
return absl::FromUnixNanos(
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch())
.count());
}
Exception SystemClock::Sleep(absl::Duration duration) {
absl::SleepFor(duration);
return {Exception::kSuccess};
}
} // namespace nearby
-41
View File
@@ -1,41 +0,0 @@
load("@rules_cc//cc:cc_library.bzl", "cc_library")
licenses(["notice"])
cc_library(
name = "rpc",
hdrs = [
"grpc_async_client_factory.h",
"identity_rpc_types.h",
"sharing_rpc_client.h",
],
visibility = ["//visibility:public"],
deps = [
"//internal/platform/implementation:account_manager",
"//sharing/analytics",
"//sharing/proto:share_cc_proto",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_protobuf//:protobuf",
],
)
cc_library(
name = "sync",
hdrs = ["sync_manager.h"],
visibility = ["//visibility:public"],
deps = [
"//sharing/internal/api:platform",
"@com_google_absl//absl/strings:string_view",
],
)
cc_library(
name = "highway_fingerprint",
hdrs = ["highway_fingerprint.h"],
visibility = ["//visibility:public"],
deps = [
"@com_google_absl//absl/hash",
"@com_google_absl//absl/strings:string_view",
],
)
@@ -1,86 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_
#include <memory>
#include <utility>
#include "internal/platform/clock.h"
#include "internal/platform/implementation/account_manager.h"
#include "sharing/analytics/analytics_recorder.h"
#include "sharing/linux/stubs/sharing_rpc_client.h"
namespace nearby::sharing::platform::common {
namespace internal {
class NoOpSharingRpcClient : public nearby::sharing::api::SharingRpcClient {
public:
void ListContactPeople(
nearby::sharing::proto::ListContactPeopleRequest request,
ListContactPeopleCallback callback) override {
static_cast<void>(request);
callback(nearby::sharing::proto::ListContactPeopleResponse());
}
};
class NoOpIdentityRpcClient : public nearby::sharing::api::IdentityRpcClient {
public:
void QuerySharedCredentials(
google::nearby::identity::v1::QuerySharedCredentialsRequest request,
QuerySharedCredentialsCallback callback) override {
static_cast<void>(request);
callback(google::nearby::identity::v1::QuerySharedCredentialsResponse());
}
void PublishDevice(
google::nearby::identity::v1::PublishDeviceRequest request,
PublishDeviceCallback callback) override {
static_cast<void>(request);
callback(google::nearby::identity::v1::PublishDeviceResponse());
}
void GetAccountInfo(
google::nearby::identity::v1::GetAccountInfoRequest request,
GetAccountInfoCallback callback) override {
static_cast<void>(request);
callback(google::nearby::identity::v1::GetAccountInfoResponse());
}
};
} // namespace internal
class GrpcAsyncClientFactory {
public:
GrpcAsyncClientFactory(AccountManager* account_manager, Clock* clock,
analytics::AnalyticsRecorder* analytics_recorder) {
static_cast<void>(account_manager);
static_cast<void>(clock);
static_cast<void>(analytics_recorder);
}
std::unique_ptr<nearby::sharing::api::SharingRpcClient> CreateInstance() {
return std::make_unique<internal::NoOpSharingRpcClient>();
}
std::unique_ptr<nearby::sharing::api::IdentityRpcClient>
CreateIdentityInstance() {
return std::make_unique<internal::NoOpIdentityRpcClient>();
}
};
} // namespace nearby::sharing::platform::common
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_GRPC_ASYNC_CLIENT_FACTORY_H_
-208
View File
@@ -1,208 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/timestamp.pb.h"
namespace google::nearby::identity::v1 {
class SharedCredential {
public:
enum DataType {
DATA_TYPE_UNKNOWN = 0,
DATA_TYPE_PUBLIC_CERTIFICATE = 1,
};
void set_id(uint64_t id) { id_ = id; }
uint64_t id() const { return id_; }
void set_data(std::string data) { data_ = std::move(data); }
const std::string& data() const { return data_; }
void set_data_type(DataType data_type) { data_type_ = data_type; }
DataType data_type() const { return data_type_; }
google::protobuf::Timestamp* mutable_expiration_time() {
return &expiration_time_;
}
const google::protobuf::Timestamp& expiration_time() const {
return expiration_time_;
}
private:
uint64_t id_ = 0;
std::string data_;
DataType data_type_ = DATA_TYPE_UNKNOWN;
google::protobuf::Timestamp expiration_time_;
};
class PerVisibilitySharedCredentials {
public:
enum Visibility {
VISIBILITY_UNKNOWN = 0,
VISIBILITY_SELF = 1,
VISIBILITY_CONTACTS = 2,
};
void set_visibility(Visibility visibility) { visibility_ = visibility; }
Visibility visibility() const { return visibility_; }
SharedCredential* add_shared_credentials() {
shared_credentials_.emplace_back();
return &shared_credentials_.back();
}
const std::vector<SharedCredential>& shared_credentials() const {
return shared_credentials_;
}
private:
Visibility visibility_ = VISIBILITY_UNKNOWN;
std::vector<SharedCredential> shared_credentials_;
};
class Device {
public:
enum Contact {
CONTACT_UNKNOWN = 0,
CONTACT_GOOGLE_CONTACT = 1,
CONTACT_GOOGLE_CONTACT_LATEST = 2,
};
void set_name(std::string name) { name_ = std::move(name); }
const std::string& name() const { return name_; }
void set_display_name(std::string display_name) {
display_name_ = std::move(display_name);
}
const std::string& display_name() const { return display_name_; }
void set_contact(Contact contact) { contact_ = contact; }
Contact contact() const { return contact_; }
PerVisibilitySharedCredentials* add_per_visibility_shared_credentials() {
per_visibility_shared_credentials_.emplace_back();
return &per_visibility_shared_credentials_.back();
}
const std::vector<PerVisibilitySharedCredentials>&
per_visibility_shared_credentials() const {
return per_visibility_shared_credentials_;
}
private:
std::string name_;
std::string display_name_;
Contact contact_ = CONTACT_UNKNOWN;
std::vector<PerVisibilitySharedCredentials> per_visibility_shared_credentials_;
};
class QuerySharedCredentialsRequest {
public:
void set_name(std::string name) { name_ = std::move(name); }
const std::string& name() const { return name_; }
void set_page_token(std::string page_token) {
page_token_ = std::move(page_token);
}
const std::string& page_token() const { return page_token_; }
private:
std::string name_;
std::string page_token_;
};
class QuerySharedCredentialsResponse {
public:
SharedCredential* add_shared_credentials() {
shared_credentials_.emplace_back();
return &shared_credentials_.back();
}
const std::vector<SharedCredential>& shared_credentials() const {
return shared_credentials_;
}
void set_next_page_token(std::string next_page_token) {
next_page_token_ = std::move(next_page_token);
}
const std::string& next_page_token() const { return next_page_token_; }
private:
std::vector<SharedCredential> shared_credentials_;
std::string next_page_token_;
};
class PublishDeviceRequest {
public:
Device* mutable_device() { return &device_; }
const Device& device() const { return device_; }
private:
Device device_;
};
class PublishDeviceResponse {
public:
enum ContactUpdate {
CONTACT_UPDATE_UNKNOWN = 0,
CONTACT_UPDATE_REMOVED = 1,
};
void add_contact_updates(ContactUpdate contact_update) {
contact_updates_.push_back(contact_update);
}
const std::vector<ContactUpdate>& contact_updates() const {
return contact_updates_;
}
private:
std::vector<ContactUpdate> contact_updates_;
};
class GetAccountInfoRequest {};
class AccountInfo {
public:
enum Capability {
CAPABILITY_UNKNOWN = 0,
CAPABILITY_TITANIUM = 1,
};
void add_capabilities(Capability capability) {
capabilities_.push_back(capability);
}
const std::vector<Capability>& capabilities() const { return capabilities_; }
private:
std::vector<Capability> capabilities_;
};
class GetAccountInfoResponse {
public:
AccountInfo* mutable_account_info() { return &account_info_; }
const AccountInfo& account_info() const { return account_info_; }
private:
AccountInfo account_info_;
};
} // namespace google::nearby::identity::v1
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_IDENTITY_RPC_TYPES_H_
-66
View File
@@ -1,66 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SHARING_RPC_CLIENT_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SHARING_RPC_CLIENT_H_
#include <functional>
#include "absl/status/statusor.h"
#include "sharing/linux/stubs/identity_rpc_types.h"
#include "sharing/proto/contact_rpc.pb.h"
namespace nearby::sharing::api {
class SharingRpcClient {
public:
using ListContactPeopleCallback = std::function<void(
const absl::StatusOr<nearby::sharing::proto::ListContactPeopleResponse>&)>;
virtual ~SharingRpcClient() = default;
virtual void ListContactPeople(
nearby::sharing::proto::ListContactPeopleRequest request,
ListContactPeopleCallback callback) = 0;
};
class IdentityRpcClient {
public:
using QuerySharedCredentialsCallback = std::function<void(
const absl::StatusOr<
google::nearby::identity::v1::QuerySharedCredentialsResponse>&)>;
using PublishDeviceCallback = std::function<void(
const absl::StatusOr<google::nearby::identity::v1::PublishDeviceResponse>&)>;
using GetAccountInfoCallback = std::function<void(
const absl::StatusOr<
google::nearby::identity::v1::GetAccountInfoResponse>&)>;
virtual ~IdentityRpcClient() = default;
virtual void QuerySharedCredentials(
google::nearby::identity::v1::QuerySharedCredentialsRequest request,
QuerySharedCredentialsCallback callback) = 0;
virtual void PublishDevice(
google::nearby::identity::v1::PublishDeviceRequest request,
PublishDeviceCallback callback) = 0;
virtual void GetAccountInfo(
google::nearby::identity::v1::GetAccountInfoRequest request,
GetAccountInfoCallback callback) = 0;
};
} // namespace nearby::sharing::api
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SHARING_RPC_CLIENT_H_
-47
View File
@@ -1,47 +0,0 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_
#define THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_
#include <optional>
#include "absl/strings/string_view.h"
#include "sharing/internal/api/preference_manager.h"
namespace nearby::sharing {
class SyncManager {
public:
explicit SyncManager(api::PreferenceManager* preference_manager)
: preference_manager_(preference_manager) {}
bool IsFileSyncBinding(absl::string_view binding_id) const {
static_cast<void>(binding_id);
return false;
}
std::optional<nearby::sharing::sync::SyncConfigPrefs> GetSyncConfig(
absl::string_view binding_id) const {
static_cast<void>(binding_id);
return std::nullopt;
}
private:
api::PreferenceManager* preference_manager_;
};
} // namespace nearby::sharing
#endif // THIRD_PARTY_NEARBY_SHARING_LINUX_STUBS_SYNC_MANAGER_H_