fixed size 0 introduction frame error when sending file to android

This commit is contained in:
Lasan Mahaliyana
2026-03-07 18:40:40 +05:30
parent b5e3fd94b8
commit 7dba078c0a
3 changed files with 135 additions and 338 deletions
+62 -330
View File
@@ -1,373 +1,105 @@
# Nearby Sharing Linux Implementation
# Nearby Sharing for Linux
This directory contains the Linux-specific implementation of Nearby Sharing and a sample application demonstrating its usage.
This directory contains:
- A Linux-facing Nearby Sharing library (`NearbySharingApi`)
- A sample CLI app (`nearby_sharing_app`)
- A Qt/QML tray sample app (`qml_tray_app`)
## ⚠️ Important Compatibility Notice
## Scope and Intended Use
**This is a simplified implementation for Linux-to-Linux transfers only.**
This project is primarily intended to be used as a reusable library/API.
-**Works**: Linux ↔ Linux device transfers
-**Does NOT work**: Linux ↔ Android/ChromeOS (authentication failure)
The sample applications are still supported and will continue to be supported because they are used in real day-to-day device sharing workflows.
**Reason**: The full Nearby Sharing protocol requires certificate-based authentication, introduction frame exchange, and protocol frame handling which are not implemented in this simplified version.
## Current Status
**For Android/ChromeOS compatibility**, you need the full implementation in `sharing/nearby_sharing_service_impl.{h,cc}` which requires additional platform support. See [ANDROID_COMPATIBILITY.md](ANDROID_COMPATIBILITY.md) for details.
The current implementation works with the reverse-engineered certificate manager currently used in this project.
## Overview
Compatibility can still break if Google changes certificate manager behavior/protocol details. That component is closed source, so upstream changes can be difficult to inspect and adapt to quickly.
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.
## Test Coverage and Session Notes
## Components
- Verified: single-file sharing flow.
- Not fully verified: multiple transfers in one app lifetime.
- Current practical testing pattern: restart the application before each new transfer.
### NearbySharingServiceLinux
For this README, a "session" means one process lifetime (app start to app exit).
The main service class that provides nearby sharing functionality:
After one transfer, some endpoints may close and internal state can reset/change. Multi-transfer handling in one live session is still under investigation.
- **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)
## Known Issues
### NearbySharingApi
- Linux hotspot startup can be slow.
- Connecting to a hotspot started on another device can be slow on Linux.
- Android-initiated connection formation can be very slow.
For external/Linux app consumers (for example Qt/CMake apps), use
`nearby::sharing::linux::NearbySharingApi` from:
The Android/Linux connection latency issue still needs deeper investigation. One possible cause is connection/negotiation behavior that Linux does not currently handle well.
- `sharing/linux/nearby_sharing_api.h`
- `libnearby_sharing_api_shared.so`
## Wi-Fi Direct Status
Install artifacts with:
Wi-Fi Direct is theoretically possible but not implemented yet.
Reason: NetworkManager does not natively support creating Wi-Fi Direct Group Owners in the way this project needs.
## Installation
### 1. Install the shared library
From the repository root:
```bash
./sharing/linux/install_nearby_sharing_service.sh
```
### Key Concepts
This installs:
- `libnearby_sharing_api_shared.so`
- `sharing/linux/nearby_sharing_api.h`
#### 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. Build and run the CLI sample app (optional)
#### 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
From the repository root:
```bash
# Build the sample application
bazel build //sharing/linux:nearby_sharing_app
./bazel-bin/sharing/linux/nearby_sharing_app
# Optional custom device name
./bazel-bin/sharing/linux/nearby_sharing_app "MyDeviceName"
```
### Running
### 3. Build and run the tray sample app (optional)
From `sharing/linux/qml_tray_app`:
```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"
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_PREFIX=/usr/local
cmake --build build -j
./build/nearby_qml_file_tray_app
```
### Features
### 4. Install launcher entry (`.desktop`) for the tray app
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
From `sharing/linux/qml_tray_app`:
## 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");
```bash
mkdir -p "$HOME/.local/share/applications"
install -m 0644 nearby-file-share.desktop "$HOME/.local/share/applications/nearby-file-share.desktop"
sed -i "s|^Exec=.*|Exec=$(pwd)/build/nearby_qml_file_tray_app|" "$HOME/.local/share/applications/nearby-file-share.desktop"
sed -i "s|^Icon=.*|Icon=$(pwd)/tray_icon.png|" "$HOME/.local/share/applications/nearby-file-share.desktop"
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
```
**Device B (Receiver)**:
```cpp
NearbySharingApp app("Receiver-Device");
After this, search for `Nearby File Share` in your desktop launcher.
// Start advertising
app.StartAsReceiver();
## Documentation
// When connection is initiated (via callback), accept it
// This happens automatically when you see OnTransferUpdate with
// Status::kAwaitingLocalConfirmation
app.AcceptIncomingShare(target_id);
```
Technical deep dives are being moved from README content to the wiki.
### Example 2: Send Text
- Wiki: https://github.com/kidfromjupiter/nearby/wiki
```cpp
NearbySharingApp app("Text-Sender");
This README stays focused on status, installation, and known limitations.
// Start as sender
app.StartAsSender();
## Demo Assets (Planned)
// Wait for device discovery
std::this_thread::sleep_for(std::chrono::seconds(2));
- Video demo of end-to-end sharing flow.
- GIF showing the sharing process.
// 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.
+17 -2
View File
@@ -1,10 +1,13 @@
#include "sharing/linux/nearby_sharing_api.h"
#include <limits>
#include <mutex>
#include <optional>
#include <utility>
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
#include "internal/base/file_path.h"
#include "internal/base/files.h"
#include "internal/flags/nearby_flags.h"
#include "sharing/attachment_container.h"
#include "sharing/file_attachment.h"
@@ -307,9 +310,21 @@ void NearbySharingApi::SendFile(int64_t share_target_id,
return;
}
FilePath path(file_path);
std::optional<uintmax_t> file_size = nearby::Files::GetFileSize(path);
if (!file_size.has_value() || *file_size == 0 ||
*file_size >
static_cast<uintmax_t>(std::numeric_limits<int64_t>::max())) {
if (callback) {
callback(StatusCode::kInvalidArgument);
}
return;
}
nearby::sharing::AttachmentContainer::Builder builder;
builder.AddFileAttachment(
nearby::sharing::FileAttachment(FilePath(file_path)));
nearby::sharing::FileAttachment attachment(path);
attachment.set_size(static_cast<int64_t>(*file_size));
builder.AddFileAttachment(std::move(attachment));
std::unique_ptr<nearby::sharing::AttachmentContainer> attachments =
builder.Build();
if (!attachments || !attachments->HasAttachments()) {
+56 -6
View File
@@ -18,6 +18,7 @@
#include <algorithm>
#include <array>
#include <cstdint>
#include <limits>
#include <sstream>
#include <string>
#include <utility>
@@ -34,6 +35,7 @@
#include "connections/payload.h"
#include "connections/status.h"
#include "connections/strategy.h"
#include "internal/base/files.h"
#include "internal/crypto_cros/ec_private_key.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/file.h"
@@ -192,10 +194,28 @@ bool PrepareOutgoingIntroductionAndPayloads(auto& transfer_state,
transfer_state.expected_attachment_payload_count = 0;
introduction->set_start_transfer(true);
const AttachmentContainer& attachments = transfer_state.attachments;
for (const auto& file : attachments.GetFileAttachments()) {
AttachmentContainer& attachments = transfer_state.attachments;
for (int i = 0; i < attachments.GetFileAttachments().size(); ++i) {
FileAttachment& file = attachments.GetMutableFileAttachment(i);
if (!file.file_path().has_value()) {
continue;
LOG(WARNING) << "Outgoing file attachment missing path: "
<< std::string(file.file_name());
return false;
}
std::optional<uintmax_t> file_size = Files::GetFileSize(*file.file_path());
if (file_size.has_value()) {
if (*file_size >
static_cast<uintmax_t>(std::numeric_limits<int64_t>::max())) {
LOG(WARNING) << "Outgoing file attachment size overflow: "
<< std::string(file.file_name());
return false;
}
file.set_size(static_cast<int64_t>(*file_size));
}
if (file.size() <= 0) {
LOG(WARNING) << "Outgoing file attachment has invalid size: "
<< std::string(file.file_name()) << " size=" << file.size();
return false;
}
int64_t payload_id = connections::Payload::GenerateId();
auto* metadata = introduction->add_file_metadata();
@@ -1349,11 +1369,41 @@ connections::PayloadListener NearbySharingServiceLinux::MakePayloadListener(
introduction_frame.set_version(Frame::V1);
V1Frame* out_v1 = introduction_frame.mutable_v1();
out_v1->set_type(V1Frame::INTRODUCTION);
if (PrepareOutgoingIntroductionAndPayloads(
transfer_state, out_v1->mutable_introduction()) &&
SendFramePayload(core_.get(), std::string(endpoint_id),
if (!PrepareOutgoingIntroductionAndPayloads(
transfer_state, out_v1->mutable_introduction())) {
LOG(ERROR) << "Failed to prepare outgoing introduction frame.";
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kFailed)
.set_progress(0)
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
core_->DisconnectFromEndpoint(std::string(endpoint_id),
[](connections::Status) {});
active_transfers_.erase(transfer_it);
if (active_transfers_.empty()) {
is_transferring_ = false;
}
return;
}
if (SendFramePayload(core_.get(), std::string(endpoint_id),
transfer_state, introduction_frame)) {
transfer_state.introduction_sent = true;
} else {
LOG(ERROR) << "Failed to send outgoing introduction frame.";
TransferMetadata metadata =
TransferMetadataBuilder()
.set_status(TransferMetadata::Status::kFailed)
.set_progress(0)
.build();
NotifyTransferUpdate(*share_target, transfer_state, metadata);
core_->DisconnectFromEndpoint(std::string(endpoint_id),
[](connections::Status) {});
active_transfers_.erase(transfer_it);
if (active_transfers_.empty()) {
is_transferring_ = false;
}
return;
}
}