mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
fixed size 0 introduction frame error when sending file to android
This commit is contained in:
+62
-330
@@ -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.
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user