added nearby_sharing_app implementing nearby_sharing_service for linxu

This commit is contained in:
kidfromjupiter
2026-01-21 07:50:24 +05:30
parent 7db9672776
commit a209c46fa3
8 changed files with 2022 additions and 0 deletions
+5
View File
@@ -309,6 +309,11 @@ class FileShareApp {
: options_.mediums;
options.auto_upgrade_bandwidth = true;
auto device = CacheDiscoveredDevice(remote_device);
// disable advertising
core_ -> StopAdvertisingV3(
[](nearby::connections::Status status) {
LOG(INFO) << "StopAdvertising status: " << status.ToString();
});
core_->RequestConnectionV3(
local_device_, *device, options, MakeConnectionListener(),
[](nearby::connections::Status status) {
+104
View File
@@ -0,0 +1,104 @@
# 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.
licenses(["notice"])
cc_library(
name = "nearby_sharing_service_linux",
srcs = [
"nearby_sharing_service_linux.cc",
"//sharing:nearby_sharing_service.cc",
"//sharing:transfer_metadata.cc",
"//sharing:transfer_metadata_builder.cc",
'//sharing/certificates:nearby_share_certificate_manager.cc',
'//sharing/certificates:nearby_share_decrypted_public_certificate.cc',
'//sharing/certificates:nearby_share_encrypted_metadata_key.cc',
'//sharing/certificates:nearby_share_private_certificate.cc',
'//sharing/local_device_data:nearby_share_local_device_data_manager.cc',
'//sharing:nearby_sharing_settings.cc',
'//sharing/analytics:analytics_recorder.cc',
'//sharing:thread_timer.cc',
'//sharing/certificates:common.cc',
'//sharing/common:nearby_share_prefs.cc',
],
hdrs = [
"nearby_sharing_service_linux.h",
"//sharing:transfer_metadata.h",
"//sharing:transfer_metadata_builder.h",
'//sharing:nearby_sharing_service.h',
'//sharing/common:nearby_share_prefs.h',
'//sharing/certificates:nearby_share_certificate_manager.h',
'//sharing/certificates:nearby_share_decrypted_public_certificate.h',
'//sharing/certificates:nearby_share_encrypted_metadata_key.h',
'//sharing/certificates:nearby_share_private_certificate.h',
'//sharing/internal/api:private_certificate_data.h',
'//sharing/local_device_data:nearby_share_local_device_data_manager.h',
'//sharing:nearby_sharing_settings.h',
'//sharing/analytics:analytics_recorder.h',
'//sharing/analytics:analytics_device_settings.h',
'//sharing/analytics:analytics_information.h',
'//sharing/internal/api:preference_manager.h',
'//sharing/internal/public:context.h',
'//sharing/internal/public:pref_names.h',
'//sharing/internal/api:bluetooth_adapter.h',
'//sharing/internal/api:fast_initiation_manager.h',
'//sharing/internal/api:fast_init_ble_beacon.h',
'//sharing/internal/public:connectivity_manager.h',
'//sharing:thread_timer.h',
'//sharing:share_target_discovered_callback.h',
'//sharing:transfer_update_callback.h',
'//sharing/certificates:common.h',
'//sharing/certificates:constants.h',
],
visibility = ["//visibility:public"],
deps = [
"//connections:core",
"//connections:core_types",
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:types",
"//internal/platform/implementation:platform",
"//internal/platform/implementation/linux:linux",
"//sharing:attachments",
"//sharing:types",
"//sharing/proto:share_cc_proto",
"@com_google_absl//absl/strings",
],
)
cc_binary(
name = "nearby_sharing_app",
srcs = ["nearby_sharing_app.cc"],
deps = [
":nearby_sharing_service_linux",
"//sharing:attachments",
"//sharing:types",
"//internal/platform:base",
"@com_google_absl//absl/strings",
],
visibility = ["//visibility:public"],
)
cc_binary(
name = "simple_example",
srcs = ["simple_example.cc"],
deps = [
":nearby_sharing_service_linux",
"//sharing:attachments",
"//sharing:types",
"//internal/platform:base",
"@com_google_absl//absl/strings",
],
visibility = ["//visibility:public"],
)
+592
View File
@@ -0,0 +1,592 @@
# 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
@@ -0,0 +1,300 @@
# 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
+348
View File
@@ -0,0 +1,348 @@
# Nearby Sharing Linux Implementation
This directory contains the Linux-specific implementation of Nearby Sharing and a sample application demonstrating its usage.
## Overview
Nearby Sharing is a feature that allows users to share files, text, and other content between nearby devices using Bluetooth Low Energy (BLE) and Wi-Fi Direct. This implementation provides a simplified Linux interface built on top of the Nearby Connections API.
## Components
### NearbySharingServiceLinux
The main service class that provides nearby sharing functionality:
- **Discovery & Advertising**: Find nearby devices and advertise your device's availability
- **File Transfer**: Send and receive files
- **Text Transfer**: Send and receive text messages
- **Connection Management**: Handle connection lifecycle (accept, reject, cancel)
### Key Concepts
#### 1. Send Surface
Represents the sending side of a transfer:
- **Foreground**: Actively scans for nearby devices
- **Background**: Only listens for transfer updates without scanning
#### 2. Receive Surface
Represents the receiving side of a transfer:
- **Foreground**: Advertises to everyone, visible to all nearby devices
- **Background**: Advertises only to contacts (limited visibility)
#### 3. Callbacks
**TransferUpdateCallback**: Receives updates about ongoing transfers
- Status changes (connecting, in progress, complete, failed)
- Progress updates
- Transfer metadata (speed, bytes transferred, etc.)
**ShareTargetDiscoveredCallback**: Receives notifications about discovered devices
- Device discovered
- Device lost (out of range)
- Device updated
#### 4. Attachments
**FileAttachment**: Represents a file to be transferred
- Requires a file path
- Automatically determines MIME type and size
**TextAttachment**: Represents text content to be transferred
- Supports plain text, URLs, addresses, and phone numbers
- Includes optional title and MIME type
## Sample Application
The `nearby_sharing_app.cc` demonstrates how to use the service:
### Building
```bash
# Build the sample application
bazel build //sharing/linux:nearby_sharing_app
```
### Running
```bash
# Run with default device name
./bazel-bin/sharing/linux/nearby_sharing_app
# Run with custom device name
./bazel-bin/sharing/linux/nearby_sharing_app "MyCustomName"
```
### Features
1. **Start as Receiver**: Advertise your device to receive files
2. **Start as Sender**: Discover nearby devices to send files
3. **List Discovered Devices**: View all devices found during scanning
4. **Send File**: Transfer a file to a discovered device
5. **Send Text**: Send text content to a discovered device
6. **Accept/Reject**: Handle incoming transfer requests
7. **Cancel Transfer**: Cancel an ongoing transfer
8. **Status Info**: View Bluetooth and service status
## Usage Examples
### Example 1: Send a File
**Device A (Sender)**:
```cpp
NearbySharingApp app("Sender-Device");
// Start scanning for devices
app.StartAsSender();
// Wait for discovery...
std::this_thread::sleep_for(std::chrono::seconds(3));
// List discovered devices
app.ListDiscoveredDevices();
// Send file to target with ID 1
app.SendFile(1, "/path/to/file.txt");
```
**Device B (Receiver)**:
```cpp
NearbySharingApp app("Receiver-Device");
// Start advertising
app.StartAsReceiver();
// When connection is initiated (via callback), accept it
// This happens automatically when you see OnTransferUpdate with
// Status::kAwaitingLocalConfirmation
app.AcceptIncomingShare(target_id);
```
### Example 2: Send Text
```cpp
NearbySharingApp app("Text-Sender");
// Start as sender
app.StartAsSender();
// Wait for device discovery
std::this_thread::sleep_for(std::chrono::seconds(2));
// Send text to discovered device
app.SendText(1, "Hello from Nearby Sharing!");
```
### Example 3: Custom Callbacks
```cpp
class CustomTransferCallback : public TransferUpdateCallback {
public:
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
switch (transfer_metadata.status()) {
case TransferMetadata::Status::kAwaitingLocalConfirmation:
// Auto-accept incoming transfers
service_->Accept(share_target.id, [](auto status) {
std::cout << "Auto-accepted" << std::endl;
});
break;
case TransferMetadata::Status::kComplete:
std::cout << "Transfer completed!" << std::endl;
// Handle completed files from attachment_container
break;
case TransferMetadata::Status::kFailed:
std::cout << "Transfer failed!" << std::endl;
break;
default:
break;
}
}
};
```
## Architecture
### Service Initialization
```cpp
NearbySharingServiceLinux service("DeviceName");
```
The service initializes:
1. **Device Info**: Gets OS device name and type
2. **Bluetooth Adapter**: Checks BT availability
3. **Connections Core**: Sets up the Nearby Connections layer
4. **Service Controller Router**: Manages connection routing
### Discovery Flow
1. **Register Send Surface** (Foreground)
2. Service starts **scanning** for nearby devices
3. When device found: **OnShareTargetDiscovered** callback
4. Advertisement is **parsed** to extract device info
5. **ShareTarget** created with device details
6. User can **select target** and initiate transfer
### Advertising Flow
1. **Register Receive Surface** (Foreground/Background)
2. Service **builds advertisement** with device info
3. Service starts **advertising** via Bluetooth/Wi-Fi
4. When connection requested: **OnTransferUpdate** callback
5. User can **accept/reject** the incoming transfer
### Transfer Flow
#### Sending:
1. **SendAttachments()** with target ID and attachments
2. Service creates **connection request**
3. **Connection initiated** → Status: kConnecting
4. Connection **accepted** → Sends file/text payloads
5. **Payload transfer** → Status: kInProgress
6. **Transfer complete** → Status: kComplete
#### Receiving:
1. **Incoming connection** → Status: kAwaitingLocalConfirmation
2. **Accept()** called → Connection accepted
3. **Receive payloads** → Status: kInProgress
4. **Payloads saved** to local storage
5. **Transfer complete** → Status: kComplete
## Implementation Details
### Advertisement Format
The service uses a custom advertisement format:
- **Header byte**: Version, visibility, device type
- **Salt**: 2 random bytes
- **Metadata key**: 14 bytes (for encryption)
- **TLV fields**: Vendor ID, QR code, etc.
- **Device name** (optional): UTF-8 device name
### Connection Strategy
Uses `P2P_POINT_TO_POINT` strategy:
- Direct peer-to-peer connections
- Supports Bluetooth and Wi-Fi Direct
- Automatic medium selection based on availability
### Medium Selection
The service attempts to use available media:
1. **Bluetooth LE**: For discovery and initial connection
2. **Wi-Fi Direct**: For high-speed file transfer
3. **Wi-Fi LAN**: If devices on same network
### Payload Types
1. **BYTES**: For text content and metadata
2. **FILE**: For file transfers
3. **STREAM**: For real-time data
## API Reference
### Core Methods
#### RegisterSendSurface
```cpp
void RegisterSendSurface(
TransferUpdateCallback* transfer_callback,
ShareTargetDiscoveredCallback* discovery_callback,
SendSurfaceState state,
Advertisement::BlockedVendorId blocked_vendor_id,
bool disable_wifi_hotspot,
std::function<void(StatusCodes)> status_codes_callback);
```
#### RegisterReceiveSurface
```cpp
void RegisterReceiveSurface(
TransferUpdateCallback* transfer_callback,
ReceiveSurfaceState state,
Advertisement::BlockedVendorId vendor_id,
std::function<void(StatusCodes)> status_codes_callback);
```
#### SendAttachments
```cpp
void SendAttachments(
int64_t share_target_id,
std::unique_ptr<AttachmentContainer> attachment_container,
std::function<void(StatusCodes)> status_codes_callback);
```
#### Accept/Reject/Cancel
```cpp
void Accept(int64_t share_target_id,
std::function<void(StatusCodes)> status_codes_callback);
void Reject(int64_t share_target_id,
std::function<void(StatusCodes)> status_codes_callback);
void Cancel(int64_t share_target_id,
std::function<void(StatusCodes)> status_codes_callback);
```
### Status Codes
- **kOk**: Operation successful
- **kError**: General error
- **kOutOfOrderApiCall**: API called in wrong order
- **kTransferAlreadyInProgress**: Transfer already active
- **kNoAvailableConnectionMedium**: No BT/Wi-Fi available
- **kInvalidArgument**: Invalid parameter provided
## Limitations
Current implementation limitations:
- No settings persistence
- No contact management
- No certificate management
- No account integration
- Limited visibility control
- No Wi-Fi LAN detection
- No extended advertising support
## Future Enhancements
Potential improvements:
1. Add settings persistence (device name, visibility)
2. Implement contact management
3. Add certificate-based authentication
4. Support visibility time limits
5. Add Wi-Fi LAN connectivity detection
6. Implement file path updates during transfer
7. Add QR code generation for pairing
8. Support for extended advertising
## Troubleshooting
### Bluetooth Issues
```cpp
if (!service.IsBluetoothPresent()) {
std::cout << "Bluetooth adapter not found" << std::endl;
}
if (!service.IsBluetoothPowered()) {
std::cout << "Bluetooth is disabled" << std::endl;
}
```
### Discovery Not Working
- Ensure Bluetooth is enabled
- Check that sender is in foreground mode
- Verify receiver is advertising
- Check for Bluetooth permissions
### Transfer Failures
- Verify file paths are accessible
- Check available disk space
- Ensure stable Bluetooth connection
- Monitor transfer callbacks for errors
## License
Copyright 2025 Google LLC. Licensed under Apache 2.0.
+394
View File
@@ -0,0 +1,394 @@
// 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 <iostream>
#include <memory>
#include <string>
#include <thread>
#include <chrono>
#include <vector>
#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"
#include "sharing/transfer_update_callback.h"
#include "sharing/share_target_discovered_callback.h"
#include "internal/base/file_path.h"
using namespace nearby::sharing;
using namespace nearby::sharing::linux;
class MyTransferUpdateCallback : public TransferUpdateCallback {
public:
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
std::cout << "\n=== Transfer Update ===" << std::endl;
std::cout << "Device: " << share_target.device_name << std::endl;
std::cout << "Status: " << TransferMetadata::StatusToString(transfer_metadata.status()) << std::endl;
std::cout << "Progress: " << (transfer_metadata.progress() * 100) << "%" << std::endl;
std::cout << "Transferred: " << transfer_metadata.transferred_bytes() << " bytes" << std::endl;
std::cout << "Total attachments: " << transfer_metadata.total_attachments_count() << std::endl;
std::cout << "======================" << std::endl;
}
};
class MyShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback {
public:
void OnShareTargetDiscovered(const ShareTarget& share_target) override {
std::cout << "\n*** Device Discovered ***" << std::endl;
std::cout << "ID: " << share_target.id << std::endl;
std::cout << "Name: " << share_target.device_name << std::endl;
std::cout << "Vendor ID: " << static_cast<int>(share_target.vendor_id) << std::endl;
std::cout << "*************************" << std::endl;
discovered_targets_.push_back(share_target);
}
void OnShareTargetLost(const ShareTarget& share_target) override {
std::cout << "\n*** Device Lost ***" << std::endl;
std::cout << "Name: " << share_target.device_name << std::endl;
std::cout << "*******************" << std::endl;
for (auto it = discovered_targets_.begin(); it != discovered_targets_.end(); ++it) {
if (it->id == share_target.id) {
discovered_targets_.erase(it);
break;
}
}
}
void OnShareTargetUpdated(const ShareTarget& share_target) override {
std::cout << "\n*** Device Updated ***" << std::endl;
std::cout << "Name: " << share_target.device_name << std::endl;
std::cout << "**********************" << std::endl;
}
const std::vector<ShareTarget>& GetDiscoveredTargets() const {
return discovered_targets_;
}
private:
std::vector<ShareTarget> discovered_targets_;
};
class NearbySharingApp {
public:
NearbySharingApp(const std::string& device_name)
: service_(std::make_unique<NearbySharingServiceLinux>(device_name)),
transfer_callback_(std::make_unique<MyTransferUpdateCallback>()),
discovery_callback_(std::make_unique<MyShareTargetDiscoveredCallback>()) {
std::cout << "Nearby Sharing Application initialized with device name: "
<< device_name << std::endl;
}
~NearbySharingApp() {
Shutdown();
}
void StartAsReceiver() {
std::cout << "\n=== Starting as Receiver (Foreground) ===" << std::endl;
service_->RegisterReceiveSurface(
transfer_callback_.get(),
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Successfully registered as receiver!" << std::endl;
} else {
std::cout << "Failed to register as receiver: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
std::cout << "Advertising enabled. Waiting for incoming connections..." << std::endl;
}
void StartAsSender() {
std::cout << "\n=== Starting as Sender (Foreground) ===" << std::endl;
service_->RegisterSendSurface(
transfer_callback_.get(),
discovery_callback_.get(),
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
false, // disable_wifi_hotspot
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Successfully registered as sender!" << std::endl;
} else {
std::cout << "Failed to register as sender: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
std::cout << "Scanning for nearby devices..." << std::endl;
}
void SendFile(int64_t target_id, const std::string& file_path) {
std::cout << "\n=== Sending File ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
std::cout << "File: " << file_path << std::endl;
auto attachment_container = std::make_unique<AttachmentContainer>();
attachment_container->AddFileAttachment(FileAttachment(nearby::FilePath(file_path)));
service_->SendAttachments(
target_id,
std::move(attachment_container),
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "File send initiated successfully!" << std::endl;
} else {
std::cout << "Failed to send file: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void SendText(int64_t target_id, const std::string& text) {
std::cout << "\n=== Sending Text ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
std::cout << "Text: " << text << std::endl;
auto attachment_container = std::make_unique<AttachmentContainer>();
attachment_container->AddTextAttachment(TextAttachment(
nearby::sharing::service::proto::TextMetadata::TEXT,
text,
std::nullopt, // text_title
std::nullopt // mime_type
));
service_->SendAttachments(
target_id,
std::move(attachment_container),
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Text send initiated successfully!" << std::endl;
} else {
std::cout << "Failed to send text: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void AcceptIncomingShare(int64_t target_id) {
std::cout << "\n=== Accepting Incoming Share ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Accept(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Share accepted!" << std::endl;
} else {
std::cout << "Failed to accept share: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void RejectIncomingShare(int64_t target_id) {
std::cout << "\n=== Rejecting Incoming Share ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Reject(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Share rejected!" << std::endl;
} else {
std::cout << "Failed to reject share: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void CancelTransfer(int64_t target_id) {
std::cout << "\n=== Canceling Transfer ===" << std::endl;
std::cout << "Target ID: " << target_id << std::endl;
service_->Cancel(
target_id,
[](NearbySharingService::StatusCodes status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "Transfer cancelled!" << std::endl;
} else {
std::cout << "Failed to cancel transfer: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
}
void ListDiscoveredDevices() {
std::cout << "\n=== Discovered Devices ===" << std::endl;
const auto& targets = discovery_callback_->GetDiscoveredTargets();
if (targets.empty()) {
std::cout << "No devices found." << std::endl;
} else {
for (const auto& target : targets) {
std::cout << "ID: " << target.id
<< " | Name: " << target.device_name
<< " | Vendor: " << static_cast<int>(target.vendor_id) << std::endl;
}
}
std::cout << "==========================" << std::endl;
}
void PrintStatus() {
std::cout << "\n=== Service Status ===" << std::endl;
std::cout << "Bluetooth Present: " << (service_->IsBluetoothPresent() ? "Yes" : "No") << std::endl;
std::cout << "Bluetooth Powered: " << (service_->IsBluetoothPowered() ? "Yes" : "No") << std::endl;
std::cout << "Is Scanning: " << (service_->IsScanning() ? "Yes" : "No") << std::endl;
std::cout << "Is Transferring: " << (service_->IsTransferring() ? "Yes" : "No") << std::endl;
std::cout << "======================" << std::endl;
}
void Shutdown() {
std::cout << "\n=== Shutting Down ===" << std::endl;
service_->Shutdown([](NearbySharingService::StatusCodes status) {
std::cout << "Shutdown complete: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
});
}
private:
std::unique_ptr<NearbySharingServiceLinux> service_;
std::unique_ptr<MyTransferUpdateCallback> transfer_callback_;
std::unique_ptr<MyShareTargetDiscoveredCallback> discovery_callback_;
};
void PrintMenu() {
std::cout << "\n========== Nearby Sharing Menu ==========" << std::endl;
std::cout << "1. Start as Receiver (advertise)" << std::endl;
std::cout << "2. Start as Sender (discover)" << std::endl;
std::cout << "3. List discovered devices" << std::endl;
std::cout << "4. Send file to device" << std::endl;
std::cout << "5. Send text to device" << std::endl;
std::cout << "6. Accept incoming share" << std::endl;
std::cout << "7. Reject incoming share" << std::endl;
std::cout << "8. Cancel transfer" << std::endl;
std::cout << "9. Print status" << std::endl;
std::cout << "0. Exit" << std::endl;
std::cout << "=========================================" << std::endl;
std::cout << "Enter choice: ";
}
int main(int argc, char* argv[]) {
std::string device_name = "MyLinuxDevice";
if (argc > 1) {
device_name = argv[1];
}
std::cout << "========================================" << std::endl;
std::cout << " Nearby Sharing Linux Application" << std::endl;
std::cout << "========================================" << std::endl;
NearbySharingApp app(device_name);
bool running = true;
while (running) {
PrintMenu();
int choice;
std::cin >> choice;
switch (choice) {
case 1:
app.StartAsReceiver();
break;
case 2:
app.StartAsSender();
std::this_thread::sleep_for(std::chrono::seconds(2));
app.ListDiscoveredDevices();
break;
case 3:
app.ListDiscoveredDevices();
break;
case 4: {
int64_t target_id;
std::string file_path;
std::cout << "Enter target ID: ";
std::cin >> target_id;
std::cout << "Enter file path: ";
std::cin.ignore();
std::getline(std::cin, file_path);
app.SendFile(target_id, file_path);
break;
}
case 5: {
int64_t target_id;
std::string text;
std::cout << "Enter target ID: ";
std::cin >> target_id;
std::cout << "Enter text to send: ";
std::cin.ignore();
std::getline(std::cin, text);
app.SendText(target_id, text);
break;
}
case 6: {
int64_t target_id;
std::cout << "Enter target ID to accept: ";
std::cin >> target_id;
app.AcceptIncomingShare(target_id);
break;
}
case 7: {
int64_t target_id;
std::cout << "Enter target ID to reject: ";
std::cin >> target_id;
app.RejectIncomingShare(target_id);
break;
}
case 8: {
int64_t target_id;
std::cout << "Enter target ID to cancel: ";
std::cin >> target_id;
app.CancelTransfer(target_id);
break;
}
case 9:
app.PrintStatus();
break;
case 0:
running = false;
break;
default:
std::cout << "Invalid choice. Please try again." << std::endl;
break;
}
}
std::cout << "\nGoodbye!" << std::endl;
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
# 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.
licenses(["notice"])
cc_binary(
name = "nearby_sharing_app",
srcs = ["nearby_sharing_app.cc"],
deps = [
":nearby_sharing_service_linux",
"//sharing:attachments",
"//sharing:types",
"//internal/platform:base",
"@com_google_absl//absl/strings",
],
visibility = ["//visibility:public"],
)
+251
View File
@@ -0,0 +1,251 @@
// 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.
// Simple example: Send a text message to nearby device
//
// Usage:
// Terminal 1 (Receiver): ./simple_receiver
// Terminal 2 (Sender): ./simple_sender "Hello World"
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <chrono>
#include "sharing/linux/nearby_sharing_service_linux.h"
#include "sharing/attachment_container.h"
#include "sharing/text_attachment.h"
#include "sharing/share_target.h"
#include "sharing/transfer_metadata.h"
#include "sharing/transfer_update_callback.h"
#include "sharing/share_target_discovered_callback.h"
using namespace nearby::sharing;
using namespace nearby::sharing::linux;
// Simple callback that automatically accepts incoming transfers
class SimpleReceiverCallback : public TransferUpdateCallback {
public:
explicit SimpleReceiverCallback(NearbySharingServiceLinux* service)
: service_(service) {}
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
std::cout << "\n[Receiver] Transfer Update from: " << share_target.device_name << std::endl;
std::cout << "[Receiver] Status: "
<< TransferMetadata::StatusToString(transfer_metadata.status()) << std::endl;
// Auto-accept incoming transfers
if (transfer_metadata.status() == TransferMetadata::Status::kAwaitingLocalConfirmation) {
std::cout << "[Receiver] Auto-accepting transfer..." << std::endl;
service_->Accept(share_target.id, [](auto status) {
std::cout << "[Receiver] Accept status: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
});
}
// Show received content when complete
if (transfer_metadata.status() == TransferMetadata::Status::kComplete) {
std::cout << "\n[Receiver] ✓ Transfer Complete!" << std::endl;
// Display received text
for (const auto& text : attachment_container.GetTextAttachments()) {
std::cout << "[Receiver] Received text: \"" << text.text_body() << "\"" << std::endl;
}
// Display received files
for (const auto& file : attachment_container.GetFileAttachments()) {
std::cout << "[Receiver] Received file: " << file.file_name() << std::endl;
}
}
}
private:
NearbySharingServiceLinux* service_;
};
// Callback for discovering nearby devices
class SimpleSenderCallback : public ShareTargetDiscoveredCallback {
public:
void OnShareTargetDiscovered(const ShareTarget& share_target) override {
std::cout << "\n[Sender] Found device: " << share_target.device_name
<< " (ID: " << share_target.id << ")" << std::endl;
discovered_target_ = share_target;
has_target_ = true;
}
void OnShareTargetLost(const ShareTarget& share_target) override {
std::cout << "[Sender] Lost device: " << share_target.device_name << std::endl;
if (has_target_ && discovered_target_.id == share_target.id) {
has_target_ = false;
}
}
void OnShareTargetUpdated(const ShareTarget& share_target) override {}
bool HasTarget() const { return has_target_; }
const ShareTarget& GetTarget() const { return discovered_target_; }
private:
ShareTarget discovered_target_;
bool has_target_ = false;
};
// Transfer progress callback for sender
class SimpleSenderTransferCallback : public TransferUpdateCallback {
public:
void OnTransferUpdate(const ShareTarget& share_target,
const AttachmentContainer& attachment_container,
const TransferMetadata& transfer_metadata) override {
std::cout << "[Sender] Transfer to " << share_target.device_name
<< ": " << TransferMetadata::StatusToString(transfer_metadata.status())
<< " (" << (transfer_metadata.progress() * 100) << "%)" << std::endl;
if (transfer_metadata.status() == TransferMetadata::Status::kComplete) {
std::cout << "[Sender] ✓ Transfer Complete!" << std::endl;
transfer_complete_ = true;
} else if (transfer_metadata.status() == TransferMetadata::Status::kFailed) {
std::cout << "[Sender] ✗ Transfer Failed!" << std::endl;
transfer_complete_ = true;
}
}
bool IsTransferComplete() const { return transfer_complete_; }
private:
bool transfer_complete_ = false;
};
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cout << "Usage: " << argv[0] << " <receiver|sender> [message]" << std::endl;
std::cout << "Examples:" << std::endl;
std::cout << " " << argv[0] << " receiver # Start as receiver" << std::endl;
std::cout << " " << argv[0] << " sender \"Hello!\" # Send text message" << std::endl;
return 1;
}
std::string mode = argv[1];
if (mode == "receiver") {
// ============= RECEIVER MODE =============
std::cout << "=== Nearby Sharing Receiver ===" << std::endl;
NearbySharingServiceLinux service("Receiver-Device");
SimpleReceiverCallback callback(&service);
// Register as a receiver (advertise to nearby devices)
service.RegisterReceiveSurface(
&callback,
NearbySharingService::ReceiveSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
[](auto status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "[Receiver] ✓ Advertising started" << std::endl;
std::cout << "[Receiver] Waiting for incoming transfers..." << std::endl;
} else {
std::cout << "[Receiver] ✗ Failed to start: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
// Keep running to receive transfers
std::cout << "\nPress Ctrl+C to exit..." << std::endl;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
} else if (mode == "sender") {
// ============= SENDER MODE =============
std::string message = "Hello from Nearby Sharing!";
if (argc > 2) {
message = argv[2];
}
std::cout << "=== Nearby Sharing Sender ===" << std::endl;
std::cout << "[Sender] Message to send: \"" << message << "\"" << std::endl;
NearbySharingServiceLinux service("Sender-Device");
SimpleSenderCallback discovery_callback;
SimpleSenderTransferCallback transfer_callback;
// Register as sender (scan for nearby devices)
service.RegisterSendSurface(
&transfer_callback,
&discovery_callback,
NearbySharingService::SendSurfaceState::kForeground,
Advertisement::BlockedVendorId::kNone,
false,
[](auto status) {
if (status == NearbySharingService::StatusCodes::kOk) {
std::cout << "[Sender] ✓ Scanning started" << std::endl;
} else {
std::cout << "[Sender] ✗ Failed to start: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
}
});
// Wait for device discovery
std::cout << "[Sender] Scanning for nearby devices..." << std::endl;
int wait_time = 0;
while (!discovery_callback.HasTarget() && wait_time < 10) {
std::this_thread::sleep_for(std::chrono::seconds(1));
wait_time++;
std::cout << "." << std::flush;
}
std::cout << std::endl;
if (!discovery_callback.HasTarget()) {
std::cout << "[Sender] ✗ No devices found. Make sure receiver is running!" << std::endl;
return 1;
}
// Create text attachment
auto attachment_container = std::make_unique<AttachmentContainer>();
attachment_container->AddTextAttachment(TextAttachment(
nearby::sharing::service::proto::TextMetadata::TEXT,
message,
std::nullopt,
std::nullopt
));
// Send to discovered device
const auto& target = discovery_callback.GetTarget();
std::cout << "\n[Sender] Sending to: " << target.device_name << std::endl;
service.SendAttachments(
target.id,
std::move(attachment_container),
[](auto status) {
std::cout << "[Sender] Send initiated: "
<< NearbySharingService::StatusCodeToString(status) << std::endl;
});
// Wait for transfer to complete
std::cout << "[Sender] Transferring..." << std::endl;
while (!transfer_callback.IsTransferComplete()) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "\n[Sender] Done!" << std::endl;
} else {
std::cout << "Invalid mode. Use 'receiver' or 'sender'" << std::endl;
return 1;
}
return 0;
}