mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
added nearby_sharing_app implementing nearby_sharing_service for linxu
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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"],
|
||||
)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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"],
|
||||
)
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user