mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Merge remote-tracking branch 'origin/main'
# Conflicts: # .github/workflows/release-nearby-qml-tray.yaml
This commit is contained in:
@@ -15,8 +15,14 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
build-and-release-linux:
|
build-and-release-linux:
|
||||||
name: Build and Release Linux Bundle
|
name: Build and Release Linux Bundle
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install git
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y git ca-certificates
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
@@ -42,13 +48,14 @@ jobs:
|
|||||||
|
|
||||||
- name: Install Linux build dependencies
|
- name: Install Linux build dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
apt-get update
|
||||||
sudo apt-get install -y \
|
apt-get install -y \
|
||||||
build-essential \
|
build-essential \
|
||||||
clang \
|
clang \
|
||||||
cmake \
|
cmake \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
|
libsdbus-c++-dev \
|
||||||
libssl-dev \
|
libssl-dev \
|
||||||
libsystemd-dev \
|
libsystemd-dev \
|
||||||
libcurlpp-dev \
|
libcurlpp-dev \
|
||||||
|
|||||||
@@ -23,7 +23,13 @@ jobs:
|
|||||||
build-linux:
|
build-linux:
|
||||||
name: Build Linux
|
name: Build Linux
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ubuntu:25.04
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install git
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y git ca-certificates
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v6
|
||||||
- name: Build Connections
|
- name: Build Connections
|
||||||
run: CC=clang-18 CXX=clang-18++ BAZEL_CXXOPTS="-std=c++20" bazel build --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true --copt='-DGITHUB_BUILD' //connections:core
|
run: CC=clang-18 CXX=clang-18++ BAZEL_CXXOPTS="-std=c++20" bazel build --@com_google_protobuf//bazel/toolchains:prefer_prebuilt_protoc=true --copt='-DGITHUB_BUILD' //connections:core
|
||||||
|
|||||||
@@ -1,150 +1,88 @@
|
|||||||
# Google Nearby Sharing & Connections for Linux (Unofficial)
|
# Google Nearby Sharing & Connections for Linux (Unofficial)
|
||||||
|
|
||||||
> P2P file sharing, discovery, and connectivity on Linux using Google Nearby protocols.
|
<img width="982" height="760" alt="image" src="https://github.com/user-attachments/assets/9533ea09-81d9-4162-b90f-e0bd8b714d1d" />
|
||||||
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
|
||||||
https://github.com/user-attachments/assets/048afa1e-40a4-4351-a859-c81b642fc6e3
|
https://github.com/user-attachments/assets/048afa1e-40a4-4351-a859-c81b642fc6e3
|
||||||
|
|
||||||
|
## What
|
||||||
|
This repo consists of the entirety of google's open source nearby library. Currently, it is seperated into 3 sections.
|
||||||
|
- Sharing
|
||||||
|
- Connections
|
||||||
|
- Presence
|
||||||
|
|
||||||
|
|
||||||
|
Linux specific implementation and compatibility shims are provided for building **Sharing** and **Connections**.
|
||||||
|
Nearby presence may or may not build. I haven't tested it.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
This repo could've been a PR on the official repo. All I've done is implement the platform abstraction layer google has provided for a linux specific environment.
|
||||||
|
It's not like I haven't made a PR towards the official repo. I got tired of begging the official nearby maintainers for a PR review. So here we are.
|
||||||
|
|
||||||
|
|
||||||
|
Moreover, all I've wanted was seamless file sharing between my android devices and my linux workstation + laptop. With this repo and the example application provided here,
|
||||||
|
it accomplishes that goal perfectly. This repo wasn't created out of any altruistic goals or out of the goodness of my heart. I had a problem. I solved it. Simple as that.
|
||||||
|
|
||||||
This is an unofficial Linux-focused fork of Nearby.
|
|
||||||
|
|
||||||
## Purpose
|
## How
|
||||||
|
### How to use
|
||||||
|
Proper documentation is coming I swear. University's getting pretty hectic so docs is on the backburner. The wiki has some more information but it's nowhere near a proper documentation. It has some good brief
|
||||||
|
overviews and where generally everything is located if you're thinking about contributing.
|
||||||
|
|
||||||
The primary goal of this repository is to provide reusable Linux libraries/APIs for Nearby.
|
If you want any clarification on anything, feel free to open an issue. I'll get back to you ASAP.
|
||||||
|
|
||||||
Nearby Share sample applications are included and will continue to be supported, but they are examples first and library integration is the main long-term target.
|
As a consolation prize, I've indexed this project using [Deepwiki](https://deepwiki.com/kidfromjupiter/nearby). You might have strong feeling about AI use. But I feel like documenting very large codebases is a perfect usecase for such models. (They are called Large Language Models for a reason )
|
||||||
|
|
||||||
## Documentation
|
### How to install
|
||||||
|
This repo provides prebuilt binaries of the Quick Share application. The only officially supported distro is Fedora 43 for now. The newest ubuntu images *should* work fine
|
||||||
|
although that needs to be tested. I want to support more distros so if you encounter issues installing on your distro, please let me know.
|
||||||
|
|
||||||
Technical deep dives are being moved to the project wiki so this README can stay focused on setup and status.
|
|
||||||
|
|
||||||
- Wiki: https://github.com/kidfromjupiter/nearby/wiki
|
#### Prerequisites
|
||||||
|
|
||||||
## Requirements
|
- `sdbus-cpp >= 2.0`
|
||||||
|
- `bluez >= 5.85`
|
||||||
|
|
||||||
To run this project on Linux, you need:
|
**To install the prerequisites, run this command**
|
||||||
- BlueZ `5.86` or higher
|
|
||||||
- `NetworkManager`
|
|
||||||
- `Avahi`
|
|
||||||
- Qt (required for the tray UI app)
|
|
||||||
|
|
||||||
## Installation (End Users)
|
|
||||||
|
|
||||||
1. Download the latest release archive from the Releases page.
|
|
||||||
- https://github.com/kidfromjupiter/nearby/releases
|
|
||||||
|
|
||||||
2. Extract it:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
tar -xzf nearby-file-share-linux-*.tar.gz
|
sudo dnf install -y \
|
||||||
|
bluez bluez-libs bluez-libs-devel \
|
||||||
|
sdbus-cpp sdbus-cpp-devel
|
||||||
```
|
```
|
||||||
|
---
|
||||||
|
|
||||||
3. Install the application, shared library, and launcher:
|
**To install the Quick Share application,**
|
||||||
(run this from the extracted bundle directory)
|
|
||||||
|
|
||||||
```bash
|
1. Go to [releases](https://github.com/kidfromjupiter/nearby/releases)
|
||||||
./install_nearby_file_share.sh
|
2. Download the latest `nearby-file-share-linux-*.tar.gz`
|
||||||
```
|
3. `mkdir -p nearby && tar -xf nearby-file-share-linux-*.tar.gz -C nearby`
|
||||||
|
4. `cd nearby`
|
||||||
|
5. `chmod +x install_nearby_file_share.sh`
|
||||||
|
6. `./install_nearby_file_share.sh`
|
||||||
|
|
||||||
4. Apply a temporary BlueZ service override (required for now).
|
**To install the actual library and headers,**
|
||||||
Create/edit the `bluetooth.service` override:
|
|
||||||
|
|
||||||
```bash
|
Currently there are no prebuilt shared library or headers. You'll have to build them yourself
|
||||||
sudo systemctl edit bluetooth
|
|
||||||
```
|
|
||||||
|
|
||||||
Add:
|
### How to build
|
||||||
|
Check the [wiki](https://github.com/kidfromjupiter/nearby/wiki/Development-Environment-and-Building)
|
||||||
|
### How to contribute
|
||||||
|
Check the [wiki](https://github.com/kidfromjupiter/nearby/wiki/Development-Environment-and-Building)
|
||||||
|
|
||||||
```ini
|
## Apologies
|
||||||
[Service]
|
I may have done things in *incredibly* stupid and overcomplicated ways. It doesn't certainly help that this was the way I decided to learn C++. Blessed be my naive soul. I also do not have much experience working with such
|
||||||
ExecStart=
|
enormous codebases.
|
||||||
ExecStart=/usr/local/libexec/bluetooth/bluetoothd -E --debug=src/plugin.c --noplugin=bap,bass,mcp,vcp,micp,ccp,csip,tmap,asha,midi
|
|
||||||
```
|
|
||||||
|
|
||||||
Then reload and restart:
|
If you see any such stupidities, feel free to berate me in the most shameless of manners in an issue. I look forward to learning how to do it the proper way and to improve my atrocious code quality.
|
||||||
|
|
||||||
```bash
|
## Special thanks
|
||||||
sudo systemctl daemon-reload
|
|
||||||
sudo systemctl restart bluetooth
|
|
||||||
```
|
|
||||||
|
|
||||||
These plugins are currently disabled due to an ongoing likely upstream BlueZ bug:
|
https://github.com/proatgram and https://github.com/vibhavp
|
||||||
`bap,bass,mcp,vcp,micp,ccp,csip,tmap,asha,midi`.
|
|
||||||
This workaround will be removed once the issue is fixed.
|
|
||||||
|
|
||||||
This installs to `~/.local` by default.
|
*they were the original authors of the linux platform support [PR](https://github.com/google/nearby/pull/2098) that I have based much of this codebase upon. I am standing on the shoulders
|
||||||
|
of giants*
|
||||||
For system-wide installation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./install_nearby_file_share.sh --system
|
|
||||||
```
|
|
||||||
|
|
||||||
The installer also installs:
|
|
||||||
- `nearby_qml_file_tray_app`
|
|
||||||
- `libnearby_sharing_api_shared.so`
|
|
||||||
- `.desktop` launcher entry
|
|
||||||
- launcher icon
|
|
||||||
|
|
||||||
## Build From Source
|
|
||||||
|
|
||||||
Build and install the Linux Nearby Sharing library:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./sharing/linux/install_nearby_sharing_service.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Build tray sample app:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd sharing/linux/qml_tray_app
|
|
||||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DNEARBY_PREFIX=/usr/local
|
|
||||||
cmake --build build -j
|
|
||||||
./build/nearby_qml_file_tray_app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Current Status and Compatibility
|
|
||||||
|
|
||||||
The Linux implementation currently works with the reverse-engineered certificate manager used in this project.
|
|
||||||
|
|
||||||
Compatibility can still break if Google changes certificate manager behavior or protocol details. That component is closed source, so upstream regressions may be hard to diagnose immediately.
|
|
||||||
|
|
||||||
## Test Scope and Known Limits
|
|
||||||
|
|
||||||
- Single-file sharing has been tested.
|
|
||||||
- Multi-transfer behavior in one app lifetime has not been fully validated.
|
|
||||||
- Current practical testing often restarts the app before a new transfer.
|
|
||||||
|
|
||||||
In this project, a "session" means one application lifetime (start to exit).
|
|
||||||
|
|
||||||
After one transfer, some endpoints/internal state can reset or change, and this is still being investigated.
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
- Linux hotspot startup can be slow.
|
|
||||||
- Connecting to another device's hotspot can be slow on Linux.
|
|
||||||
- Android-initiated connection formation can be very slow.
|
|
||||||
- 5 GHz hotspot discovery and connection support is currently removed due to Intel Wi-Fi driver instability related to LAR behavior on Linux. There is no known fix yet; this may be re-enabled in a later release.
|
|
||||||
|
|
||||||
A likely area to investigate is connection negotiation paths that Linux may not handle optimally yet.
|
|
||||||
|
|
||||||
## Wi-Fi Direct
|
|
||||||
|
|
||||||
Wi-Fi Direct is theoretically possible, but not implemented yet.
|
|
||||||
|
|
||||||
Reason: NetworkManager does not natively support the Wi-Fi Direct Group Owner flow required here.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
- General: `CONTRIBUTING.md`
|
|
||||||
- Linux-specific: `LINUX_CONTRIBUTING.md`
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Released under `LICENSE`.
|
|
||||||
|
|||||||
@@ -92,8 +92,8 @@ cc_library(
|
|||||||
"bluetooth_devices.h",
|
"bluetooth_devices.h",
|
||||||
"bluetooth_pairing.h",
|
"bluetooth_pairing.h",
|
||||||
"bluez.h",
|
"bluez.h",
|
||||||
|
"bluez_agent.h",
|
||||||
"bluez_device.h",
|
"bluez_device.h",
|
||||||
# "bluez_agent.h",
|
|
||||||
"bluez_advertisement_monitor.h",
|
"bluez_advertisement_monitor.h",
|
||||||
"bluez_advertisement_monitor_manager.h",
|
"bluez_advertisement_monitor_manager.h",
|
||||||
"bluez_gatt_characteristic_client.h",
|
"bluez_gatt_characteristic_client.h",
|
||||||
@@ -163,6 +163,22 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "system_clock",
|
||||||
|
srcs = [
|
||||||
|
"system_clock.cc",
|
||||||
|
],
|
||||||
|
alwayslink = True,
|
||||||
|
visibility = [
|
||||||
|
"//sharing/linux:__pkg__",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//internal/platform:base",
|
||||||
|
"//internal/platform/implementation:types",
|
||||||
|
"@com_google_absl//absl/time",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "linux",
|
name = "linux",
|
||||||
srcs = [
|
srcs = [
|
||||||
@@ -184,7 +200,7 @@ cc_library(
|
|||||||
"bluetooth_devices.cc",
|
"bluetooth_devices.cc",
|
||||||
"bluetooth_pairing.cc",
|
"bluetooth_pairing.cc",
|
||||||
"bluez.cc",
|
"bluez.cc",
|
||||||
#"bluez_agent.cc",
|
"bluez_agent.cc",
|
||||||
"bluez_advertisement_monitor.cc",
|
"bluez_advertisement_monitor.cc",
|
||||||
"bluez_gatt_characteristic_client.cc",
|
"bluez_gatt_characteristic_client.cc",
|
||||||
"bluez_gatt_characteristic_server.cc",
|
"bluez_gatt_characteristic_server.cc",
|
||||||
@@ -200,7 +216,6 @@ cc_library(
|
|||||||
"scheduled_executor.cc",
|
"scheduled_executor.cc",
|
||||||
"stream.cc",
|
"stream.cc",
|
||||||
"submittable_executor.cc",
|
"submittable_executor.cc",
|
||||||
"system_clock.cc",
|
|
||||||
"thread_pool.cc",
|
"thread_pool.cc",
|
||||||
"utils.cc",
|
"utils.cc",
|
||||||
# "wifi_direct.cc",
|
# "wifi_direct.cc",
|
||||||
@@ -225,6 +240,7 @@ cc_library(
|
|||||||
deps = [
|
deps = [
|
||||||
":comm",
|
":comm",
|
||||||
":crypto", # build_cleaner: keep
|
":crypto", # build_cleaner: keep
|
||||||
|
":system_clock",
|
||||||
":types",
|
":types",
|
||||||
"//internal/flags:nearby_flags",
|
"//internal/flags:nearby_flags",
|
||||||
"//internal/platform:base",
|
"//internal/platform:base",
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ bool BluezGattDiscovery::InitializeKnownServices() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::flat_hash_map<sdbus::ObjectPath, GattServiceClient> cached_services;
|
|
||||||
absl::MutexLock lock(&mutex_);
|
absl::MutexLock lock(&mutex_);
|
||||||
auto chr_it = std::find_if(
|
auto chr_it = std::find_if(
|
||||||
objects.cbegin(), objects.cend(),
|
objects.cbegin(), objects.cend(),
|
||||||
@@ -219,29 +218,27 @@ bool BluezGattDiscovery::InitializeKnownServices() {
|
|||||||
sdbus::InterfaceName(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME)) == 1;
|
sdbus::InterfaceName(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME)) == 1;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (; chr_it != objects.cend(); ++chr_it) {
|
for (; chr_it != objects.cend(); ++chr_it) {
|
||||||
const auto& [path, ifaces] = *chr_it;
|
const auto& [path, ifaces] = *chr_it;
|
||||||
|
|
||||||
auto iface_it = ifaces.find(sdbus::InterfaceName(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME));
|
auto iface_it = ifaces.find(sdbus::InterfaceName(
|
||||||
if (iface_it == ifaces.end()) {
|
org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME));
|
||||||
// Not a GattCharacteristic1 object (or interfaces map incomplete) -> skip
|
if (iface_it == ifaces.end()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& properties = iface_it->second;
|
||||||
|
auto maybe_props = characteristicProperties(path, properties);
|
||||||
|
if (!maybe_props.has_value()) continue;
|
||||||
|
|
||||||
|
auto [service_uuid, chr_uuid, device_path] = *maybe_props;
|
||||||
|
|
||||||
|
discovered_characteristics_.emplace(
|
||||||
|
std::make_tuple(service_uuid, chr_uuid, device_path), path);
|
||||||
|
characteristics_properties_.emplace(
|
||||||
|
path, std::make_tuple(service_uuid, chr_uuid, device_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& properties = iface_it->second;
|
|
||||||
|
|
||||||
auto maybe_props = characteristicProperties(path, properties);
|
|
||||||
if (!maybe_props.has_value()) continue;
|
|
||||||
|
|
||||||
auto [chr_uuid, service_uuid, device_path] = *maybe_props;
|
|
||||||
|
|
||||||
discovered_characteristics_.emplace(
|
|
||||||
std::make_tuple(chr_uuid, service_uuid, device_path), path);
|
|
||||||
|
|
||||||
characteristics_properties_.emplace(
|
|
||||||
path, std::make_tuple(chr_uuid, service_uuid, device_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,7 +334,7 @@ BluezGattDiscovery::GetSubscribedCharacteristic(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return std::make_unique<bluez::SubscribedGattCharacteristicClient>(
|
return std::make_unique<bluez::SubscribedGattCharacteristicClient>(
|
||||||
system_bus_, device_object_path, std::move(on_characteristic_changed_cb));
|
system_bus_, path_it->second, std::move(on_characteristic_changed_cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
|
std::optional<std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
|
||||||
@@ -396,7 +393,7 @@ try {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(*chr_uuid, service_uuid, device_path);
|
return std::make_tuple(service_uuid, *chr_uuid, device_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluezGattDiscovery::onInterfacesAdded(
|
void BluezGattDiscovery::onInterfacesAdded(
|
||||||
@@ -414,12 +411,12 @@ void BluezGattDiscovery::onInterfacesAdded(
|
|||||||
absl::MutexLock lock(&mutex_);
|
absl::MutexLock lock(&mutex_);
|
||||||
auto maybe_props = characteristicProperties(objectPath, properties);
|
auto maybe_props = characteristicProperties(objectPath, properties);
|
||||||
if (!maybe_props.has_value()) return;
|
if (!maybe_props.has_value()) return;
|
||||||
auto [chr_uuid, service_uuid, device_path] = *maybe_props;
|
auto [service_uuid, chr_uuid, device_path] = *maybe_props;
|
||||||
|
|
||||||
discovered_characteristics_.emplace(
|
discovered_characteristics_.emplace(
|
||||||
std::make_tuple(chr_uuid, service_uuid, device_path), objectPath);
|
std::make_tuple(service_uuid, chr_uuid, device_path), objectPath);
|
||||||
characteristics_properties_.emplace(
|
characteristics_properties_.emplace(
|
||||||
objectPath, std::make_tuple(chr_uuid, service_uuid, device_path));
|
objectPath, std::make_tuple(service_uuid, chr_uuid, device_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BluezGattDiscovery::onInterfacesRemoved(
|
void BluezGattDiscovery::onInterfacesRemoved(
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class BluezGattDiscovery final : public bluez::BluezObjectManager {
|
|||||||
absl::Mutex mutex_;
|
absl::Mutex mutex_;
|
||||||
absl::flat_hash_map<sdbus::ObjectPath, std::unique_ptr<GattServiceClient>>
|
absl::flat_hash_map<sdbus::ObjectPath, std::unique_ptr<GattServiceClient>>
|
||||||
cached_services_ ABSL_GUARDED_BY(mutex_);
|
cached_services_ ABSL_GUARDED_BY(mutex_);
|
||||||
// Tuple order: service uuid, characteristic uuid, device object path
|
// Tuple order: service uuid, characteristic uuid, device object path.
|
||||||
absl::flat_hash_map<std::tuple<Uuid, Uuid, sdbus::ObjectPath>,
|
absl::flat_hash_map<std::tuple<Uuid, Uuid, sdbus::ObjectPath>,
|
||||||
sdbus::ObjectPath>
|
sdbus::ObjectPath>
|
||||||
discovered_characteristics_ ABSL_GUARDED_BY(mutex_);
|
discovered_characteristics_ ABSL_GUARDED_BY(mutex_);
|
||||||
@@ -122,7 +122,7 @@ class GattClient : public api::ble::GattClient {
|
|||||||
peripheral_object_path_(peripheral_object_path),
|
peripheral_object_path_(peripheral_object_path),
|
||||||
gatt_discovery_(std::move(gatt_discovery)),
|
gatt_discovery_(std::move(gatt_discovery)),
|
||||||
discovery_cancel_(false) {
|
discovery_cancel_(false) {
|
||||||
disconnected_callback_it_ = gatt_discovery->AddPeripheralConnection(
|
disconnected_callback_it_ = gatt_discovery_->AddPeripheralConnection(
|
||||||
peripheral_object_path_, std::move(disconnected_callback));
|
peripheral_object_path_, std::move(disconnected_callback));
|
||||||
}
|
}
|
||||||
~GattClient() override {
|
~GattClient() override {
|
||||||
|
|||||||
@@ -24,6 +24,19 @@
|
|||||||
|
|
||||||
namespace nearby {
|
namespace nearby {
|
||||||
namespace linux {
|
namespace linux {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void LogAsyncGattManagerError(const bluez::GattManager& manager,
|
||||||
|
const char* method_name,
|
||||||
|
const sdbus::Error& error) {
|
||||||
|
LOG(ERROR) << method_name << ": Got error '" << error.getName()
|
||||||
|
<< "' with message '" << error.getMessage()
|
||||||
|
<< "' while calling " << method_name << " on object "
|
||||||
|
<< manager.getProxy().getObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
absl::optional<api::ble::GattCharacteristic>
|
absl::optional<api::ble::GattCharacteristic>
|
||||||
GattServer::CreateCharacteristic(
|
GattServer::CreateCharacteristic(
|
||||||
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
||||||
@@ -60,23 +73,53 @@ GattServer::CreateCharacteristic(
|
|||||||
|
|
||||||
if (service->AddCharacteristic(service_uuid, characteristic_uuid, permission,
|
if (service->AddCharacteristic(service_uuid, characteristic_uuid, permission,
|
||||||
property)) {
|
property)) {
|
||||||
try {
|
|
||||||
LOG(INFO)<< __func__ << ": Registering service on gattmanager with characteristic_uuid: "
|
|
||||||
<< std::string(characteristic_uuid) << " and service_uuid: " << std::string(service_uuid);
|
|
||||||
|
|
||||||
gatt_manager_ -> RegisterApplication(gatt_service_root_object_manager -> getObject().getObjectPath(), {});
|
|
||||||
} catch (const sdbus::Error& e) {
|
|
||||||
LOG(ERROR)
|
|
||||||
<< __func__
|
|
||||||
<< ": error calling RegisterAplication for GattManager with object path "
|
|
||||||
<< gatt_manager_->getProxy().getObjectPath() << " with name '" << e.getName()
|
|
||||||
<< "' and message '" << e.getMessage() << "'";
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
services_.insert({service_uuid, std::move(service)});
|
services_.insert({service_uuid, std::move(service)});
|
||||||
|
{
|
||||||
|
absl::MutexLock profile_lock(&profiles_mutex_);
|
||||||
|
gatt_profiles_.insert({service_uuid, std::move(profile)});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool should_register_application = false;
|
||||||
|
{
|
||||||
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
|
should_register_application =
|
||||||
|
!gatt_application_registered_ && !gatt_application_register_pending_;
|
||||||
|
if (should_register_application) {
|
||||||
|
gatt_application_register_pending_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_register_application) {
|
||||||
|
try {
|
||||||
|
LOG(INFO) << __func__
|
||||||
|
<< ": Registering GATT application root "
|
||||||
|
<< gatt_service_root_object_manager->getObject().getObjectPath()
|
||||||
|
<< " with characteristic_uuid: "
|
||||||
|
<< std::string(characteristic_uuid)
|
||||||
|
<< " and service_uuid: " << std::string(service_uuid);
|
||||||
|
auto call = gatt_manager_->RegisterApplication(
|
||||||
|
gatt_service_root_object_manager->getObject().getObjectPath(), {});
|
||||||
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
|
register_application_call_ = std::move(call);
|
||||||
|
} catch (const sdbus::Error& e) {
|
||||||
|
{
|
||||||
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
|
gatt_application_register_pending_ = false;
|
||||||
|
register_application_call_.reset();
|
||||||
|
}
|
||||||
|
services_.erase(service_uuid);
|
||||||
|
{
|
||||||
|
absl::MutexLock profile_lock(&profiles_mutex_);
|
||||||
|
gatt_profiles_.erase(service_uuid);
|
||||||
|
}
|
||||||
|
LOG(ERROR)
|
||||||
|
<< __func__
|
||||||
|
<< ": error calling RegisterApplication for GattManager with object path "
|
||||||
|
<< gatt_manager_->getProxy().getObjectPath() << " with name '"
|
||||||
|
<< e.getName() << "' and message '" << e.getMessage() << "'";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
api::ble::GattCharacteristic characteristic{
|
api::ble::GattCharacteristic characteristic{
|
||||||
characteristic_uuid, service_uuid, permission, property};
|
characteristic_uuid, service_uuid, permission, property};
|
||||||
@@ -137,19 +180,86 @@ absl::Status GattServer::NotifyCharacteristicChanged(
|
|||||||
return chr->NotifyChanged(confirm, new_value);
|
return chr->NotifyChanged(confirm, new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GattServer::OnRegisterApplicationReply(std::optional<sdbus::Error> error) {
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(®istration_mutex_);
|
||||||
|
gatt_application_register_pending_ = false;
|
||||||
|
register_application_call_.reset();
|
||||||
|
gatt_application_registered_ = !error.has_value() || !error->isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.has_value() && error->isValid()) {
|
||||||
|
LogAsyncGattManagerError(*gatt_manager_, "RegisterApplication", *error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GattServer::OnUnregisterApplicationReply(
|
||||||
|
std::optional<sdbus::Error> error) {
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(®istration_mutex_);
|
||||||
|
gatt_application_unregister_pending_ = false;
|
||||||
|
unregister_application_call_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.has_value() && error->isValid()) {
|
||||||
|
LogAsyncGattManagerError(*gatt_manager_, "UnregisterApplication", *error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GattServer::Stop() {
|
void GattServer::Stop() {
|
||||||
bluez::GattManager manager(system_bus_, adapter_.GetObjectPath());
|
sdbus::ObjectPath app_path =
|
||||||
absl::MutexLock lock(&services_mutex_);
|
gatt_service_root_object_manager->getObject().getObjectPath();
|
||||||
for (auto& [uuid, service] : services_) {
|
bool should_unregister = false;
|
||||||
LOG(INFO) << __func__ << ": Unregistering service "
|
|
||||||
<< service->getObject().getObjectPath();
|
{
|
||||||
try {
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
manager.UnregisterApplication(sdbus::ObjectPath("/"));
|
if (register_application_call_.has_value() &&
|
||||||
} catch (const sdbus::Error& e) {
|
register_application_call_->isPending()) {
|
||||||
DBUS_LOG_METHOD_CALL_ERROR(&manager, "UnregisterApplication", e);
|
register_application_call_->cancel();
|
||||||
|
}
|
||||||
|
register_application_call_.reset();
|
||||||
|
gatt_application_register_pending_ = false;
|
||||||
|
|
||||||
|
should_unregister = gatt_application_registered_ &&
|
||||||
|
!gatt_application_unregister_pending_;
|
||||||
|
if (should_unregister) {
|
||||||
|
gatt_application_registered_ = false;
|
||||||
|
gatt_application_unregister_pending_ = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// services_.clear();
|
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(&services_mutex_);
|
||||||
|
for (auto& [uuid, service] : services_) {
|
||||||
|
LOG(INFO) << __func__ << ": Unregistering service "
|
||||||
|
<< service->getObject().getObjectPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_unregister) {
|
||||||
|
try {
|
||||||
|
auto call = gatt_manager_->UnregisterApplication(app_path);
|
||||||
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
|
unregister_application_call_ = std::move(call);
|
||||||
|
} catch (const sdbus::Error& e) {
|
||||||
|
{
|
||||||
|
absl::MutexLock registration_lock(®istration_mutex_);
|
||||||
|
gatt_application_unregister_pending_ = false;
|
||||||
|
unregister_application_call_.reset();
|
||||||
|
}
|
||||||
|
DBUS_LOG_METHOD_CALL_ERROR(gatt_manager_.get(), "UnregisterApplication",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(&services_mutex_);
|
||||||
|
services_.clear();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
absl::MutexLock profile_lock(&profiles_mutex_);
|
||||||
|
gatt_profiles_.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace linux
|
} // namespace linux
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
|
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
|
|
||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
@@ -69,7 +70,16 @@ class GattServer : public api::ble::GattServer {
|
|||||||
sdbus::ObjectPath("/com/google/nearby/medium/ble/gatt"))),
|
sdbus::ObjectPath("/com/google/nearby/medium/ble/gatt"))),
|
||||||
gatt_manager_(std::make_unique<bluez::GattManager>(system_bus_, adapter_.GetObjectPath())),
|
gatt_manager_(std::make_unique<bluez::GattManager>(system_bus_, adapter_.GetObjectPath())),
|
||||||
server_cb_(std::make_shared<api::ble::ServerGattConnectionCallback>(
|
server_cb_(std::make_shared<api::ble::ServerGattConnectionCallback>(
|
||||||
std::move(server_cb))) {}
|
std::move(server_cb))) {
|
||||||
|
gatt_manager_->SetRegisterApplicationReplyCallback(
|
||||||
|
[this](std::optional<sdbus::Error> error) {
|
||||||
|
OnRegisterApplicationReply(std::move(error));
|
||||||
|
});
|
||||||
|
gatt_manager_->SetUnregisterApplicationReplyCallback(
|
||||||
|
[this](std::optional<sdbus::Error> error) {
|
||||||
|
OnUnregisterApplicationReply(std::move(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
~GattServer() override = default;
|
~GattServer() override = default;
|
||||||
|
|
||||||
absl::optional<api::ble::GattCharacteristic> CreateCharacteristic(
|
absl::optional<api::ble::GattCharacteristic> CreateCharacteristic(
|
||||||
@@ -85,6 +95,9 @@ class GattServer : public api::ble::GattServer {
|
|||||||
void Stop() override;
|
void Stop() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void OnRegisterApplicationReply(std::optional<sdbus::Error> error);
|
||||||
|
void OnUnregisterApplicationReply(std::optional<sdbus::Error> error);
|
||||||
|
|
||||||
sdbus::IConnection& system_bus_;
|
sdbus::IConnection& system_bus_;
|
||||||
std::shared_ptr<BluetoothDevices> devices_;
|
std::shared_ptr<BluetoothDevices> devices_;
|
||||||
BluetoothAdapter adapter_;
|
BluetoothAdapter adapter_;
|
||||||
@@ -93,9 +106,20 @@ class GattServer : public api::ble::GattServer {
|
|||||||
std::unique_ptr<RootObjectManager> gatt_service_root_object_manager;
|
std::unique_ptr<RootObjectManager> gatt_service_root_object_manager;
|
||||||
absl::Mutex profiles_mutex_;
|
absl::Mutex profiles_mutex_;
|
||||||
absl::flat_hash_map<Uuid, std::unique_ptr<bluez::GattProfile>> gatt_profiles_;
|
absl::flat_hash_map<Uuid, std::unique_ptr<bluez::GattProfile>> gatt_profiles_;
|
||||||
ABSL_GUARDED_BY(profiles_mutex_)
|
ABSL_GUARDED_BY(profiles_mutex_);
|
||||||
std::unique_ptr<bluez::GattManager> gatt_manager_;
|
std::unique_ptr<bluez::GattManager> gatt_manager_;
|
||||||
std::shared_ptr<api::ble::ServerGattConnectionCallback> server_cb_;
|
std::shared_ptr<api::ble::ServerGattConnectionCallback> server_cb_;
|
||||||
|
absl::Mutex registration_mutex_;
|
||||||
|
bool gatt_application_registered_ ABSL_GUARDED_BY(registration_mutex_) =
|
||||||
|
false;
|
||||||
|
bool gatt_application_register_pending_ ABSL_GUARDED_BY(registration_mutex_) =
|
||||||
|
false;
|
||||||
|
bool gatt_application_unregister_pending_
|
||||||
|
ABSL_GUARDED_BY(registration_mutex_) = false;
|
||||||
|
std::optional<sdbus::PendingAsyncCall> register_application_call_
|
||||||
|
ABSL_GUARDED_BY(registration_mutex_);
|
||||||
|
std::optional<sdbus::PendingAsyncCall> unregister_application_call_
|
||||||
|
ABSL_GUARDED_BY(registration_mutex_);
|
||||||
absl::Mutex services_mutex_;
|
absl::Mutex services_mutex_;
|
||||||
absl::flat_hash_map<Uuid, std::unique_ptr<bluez::GattServiceServer>> services_
|
absl::flat_hash_map<Uuid, std::unique_ptr<bluez::GattServiceServer>> services_
|
||||||
ABSL_GUARDED_BY(services_mutex_);
|
ABSL_GUARDED_BY(services_mutex_);
|
||||||
|
|||||||
@@ -27,17 +27,14 @@
|
|||||||
|
|
||||||
#include "absl/synchronization/mutex.h"
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "internal/platform/implementation/ble.h"
|
#include "internal/platform/implementation/ble.h"
|
||||||
// #include "internal/platform/implementation/linux/ble_gatt_client.h"
|
|
||||||
// #include "internal/platform/implementation/linux/ble_gatt_server.h"
|
|
||||||
#include "internal/platform/implementation/linux/ble_v2_medium.h"
|
#include "internal/platform/implementation/linux/ble_v2_medium.h"
|
||||||
|
|
||||||
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||||
#include "absl/types/span.h"
|
#include "absl/types/span.h"
|
||||||
#include "absl/time/time.h"
|
#include "absl/time/time.h"
|
||||||
#include "ble_gatt_client.h"
|
|
||||||
#include "ble_gatt_server.h"
|
|
||||||
#include "ble_l2cap_server_socket.h"
|
#include "ble_l2cap_server_socket.h"
|
||||||
#include "ble_l2cap_socket.h"
|
#include "ble_l2cap_socket.h"
|
||||||
|
#include "ble_gatt_server.h"
|
||||||
#include "internal/base/observer_list.h"
|
#include "internal/base/observer_list.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_devices.h"
|
#include "internal/platform/implementation/linux/bluetooth_devices.h"
|
||||||
@@ -68,7 +65,6 @@ BleL2capSocket::ProtocolMode GetL2capProtocolMode() {
|
|||||||
BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
|
BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
|
||||||
: system_bus_(adapter.GetConnection()),
|
: system_bus_(adapter.GetConnection()),
|
||||||
adapter_(adapter),
|
adapter_(adapter),
|
||||||
gatt_discovery_(std::make_shared<BluezGattDiscovery>(system_bus_)),
|
|
||||||
observers_(std::make_shared<ObserverList<api::BluetoothClassicMedium::Observer>>()),
|
observers_(std::make_shared<ObserverList<api::BluetoothClassicMedium::Observer>>()),
|
||||||
devices_(std::make_unique<BluetoothDevices>(
|
devices_(std::make_unique<BluetoothDevices>(
|
||||||
system_bus_, adapter_.GetObjectPath(), *observers_)),
|
system_bus_, adapter_.GetObjectPath(), *observers_)),
|
||||||
@@ -89,16 +85,55 @@ BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
|
|||||||
<< __func__
|
<< __func__
|
||||||
<< ": Registering path /com/google/nearby/medium/ble/advertisement/monitor with AdvertisementMonitorManager at "
|
<< ": Registering path /com/google/nearby/medium/ble/advertisement/monitor with AdvertisementMonitorManager at "
|
||||||
<< adv_monitor_manager_->getProxy().getObjectPath();
|
<< adv_monitor_manager_->getProxy().getObjectPath();
|
||||||
try {
|
adv_monitor_manager_->SetRegisterMonitorReplyCallback(
|
||||||
adv_monitor_manager_->RegisterMonitor(root_object_manager_->getObject().getObjectPath());
|
[this](std::optional<sdbus::Error> error) {
|
||||||
} catch (const sdbus::Error &e) {
|
OnRegisterMonitorReply(std::move(error));
|
||||||
DBUS_LOG_METHOD_CALL_ERROR(adv_monitor_manager_, "RegisterMonitor", e);
|
});
|
||||||
|
adv_monitor_manager_->RegisterMonitor(
|
||||||
|
root_object_manager_->getObject().getObjectPath());
|
||||||
|
} else {
|
||||||
|
adv_monitor_manager_ready_notification_.Notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BleV2Medium::OnRegisterMonitorReply(std::optional<sdbus::Error> error) {
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(&adv_monitor_manager_ready_mutex_);
|
||||||
|
adv_monitor_manager_ready_ = !error.has_value() || !error->isValid();
|
||||||
|
if (error.has_value() && error->isValid()) {
|
||||||
|
adv_monitor_manager_error_name_ = error->getName();
|
||||||
|
adv_monitor_manager_error_message_ = error->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if (gatt_discovery_->InitializeKnownServices()) {
|
|
||||||
// LOG(ERROR) << __func__
|
if (error.has_value() && error->isValid()) {
|
||||||
// << ": Could not initialize known GATT services";
|
LOG(ERROR) << __func__ << ": Got error '" << error->getName()
|
||||||
// }
|
<< "' with message '" << error->getMessage()
|
||||||
|
<< "' while calling RegisterMonitor on object "
|
||||||
|
<< adv_monitor_manager_->getProxy().getObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
adv_monitor_manager_ready_notification_.Notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BleV2Medium::WaitForAdvertisementMonitorManager() {
|
||||||
|
if (adv_monitor_manager_ == nullptr) {
|
||||||
|
LOG(WARNING) << __func__ << ": Advertising monitor not supported by BlueZ";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
adv_monitor_manager_ready_notification_.WaitForNotification();
|
||||||
|
|
||||||
|
absl::MutexLock lock(&adv_monitor_manager_ready_mutex_);
|
||||||
|
if (adv_monitor_manager_ready_) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(WARNING) << __func__
|
||||||
|
<< ": AdvertisementMonitorManager registration failed with name '"
|
||||||
|
<< adv_monitor_manager_error_name_ << "' and message '"
|
||||||
|
<< adv_monitor_manager_error_message_ << "'";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sync api
|
// sync api
|
||||||
@@ -240,9 +275,7 @@ bool BleV2Medium::StartScanning(const Uuid &service_uuid,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adv_monitor_manager_ == nullptr) {
|
if (!WaitForAdvertisementMonitorManager()) {
|
||||||
LOG(WARNING) << __func__
|
|
||||||
<< ": Advertising monitor not supported by BlueZ";
|
|
||||||
// TODO: Implement manual monitoring.
|
// TODO: Implement manual monitoring.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -314,7 +347,7 @@ bool BleV2Medium::StopScanning() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adv_monitor_manager_ == nullptr) {
|
if (!WaitForAdvertisementMonitorManager()) {
|
||||||
// TODO: Implement manual monitoring.
|
// TODO: Implement manual monitoring.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -349,7 +382,7 @@ bool BleV2Medium::StopScanning() {
|
|||||||
BleV2Medium::StartScanning(const Uuid &service_uuid,
|
BleV2Medium::StartScanning(const Uuid &service_uuid,
|
||||||
api::ble::TxPowerLevel tx_power_level,
|
api::ble::TxPowerLevel tx_power_level,
|
||||||
ScanningCallback callback) {
|
ScanningCallback callback) {
|
||||||
if (adv_monitor_manager_ == nullptr) {
|
if (!WaitForAdvertisementMonitorManager()) {
|
||||||
// TODO: Implement manual monitoring.
|
// TODO: Implement manual monitoring.
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -437,12 +470,9 @@ bool BleV2Medium::StopScanning() {
|
|||||||
|
|
||||||
std::unique_ptr<api::ble::GattServer> BleV2Medium::StartGattServer(
|
std::unique_ptr<api::ble::GattServer> BleV2Medium::StartGattServer(
|
||||||
api::ble::ServerGattConnectionCallback callback) {
|
api::ble::ServerGattConnectionCallback callback) {
|
||||||
// (void)callback;
|
LOG(INFO) << __func__ << ": Starting Linux GATT server.";
|
||||||
// return nullptr;
|
return std::make_unique<GattServer>(*system_bus_, adapter_, devices_,
|
||||||
|
std::move(callback));
|
||||||
return std::make_unique<GattServer>(
|
|
||||||
*system_bus_, adapter_, devices_,std::move(callback)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<api::ble::GattClient> BleV2Medium::ConnectToGattServer(
|
std::unique_ptr<api::ble::GattClient> BleV2Medium::ConnectToGattServer(
|
||||||
@@ -452,8 +482,7 @@ std::unique_ptr<api::ble::GattClient> BleV2Medium::ConnectToGattServer(
|
|||||||
(void)peripheral_id;
|
(void)peripheral_id;
|
||||||
(void)tx_power_level;
|
(void)tx_power_level;
|
||||||
(void)callback;
|
(void)callback;
|
||||||
LOG(WARNING) << __func__
|
LOG(INFO) << __func__ << ": GATT is disabled on linux.";
|
||||||
<< ": GATT client connection is not supported on Linux yet.";
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,11 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/base/thread_annotations.h"
|
||||||
|
#include "absl/synchronization/notification.h"
|
||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
|
|
||||||
#include "absl/base/attributes.h"
|
#include "absl/base/attributes.h"
|
||||||
@@ -118,6 +121,8 @@ class BleV2Medium final : public api::ble::BleMedium {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool StartLEDiscovery();
|
bool StartLEDiscovery();
|
||||||
|
bool WaitForAdvertisementMonitorManager();
|
||||||
|
void OnRegisterMonitorReply(std::optional<sdbus::Error> error);
|
||||||
|
|
||||||
bool MonitorManagerSupportsOr() {
|
bool MonitorManagerSupportsOr() {
|
||||||
std::vector<std::string> supported_types;
|
std::vector<std::string> supported_types;
|
||||||
@@ -148,6 +153,14 @@ class BleV2Medium final : public api::ble::BleMedium {
|
|||||||
|
|
||||||
std::unique_ptr<RootObjectManager> root_object_manager_;
|
std::unique_ptr<RootObjectManager> root_object_manager_;
|
||||||
std::unique_ptr<bluez::AdvertisementMonitorManager> adv_monitor_manager_;
|
std::unique_ptr<bluez::AdvertisementMonitorManager> adv_monitor_manager_;
|
||||||
|
absl::Notification adv_monitor_manager_ready_notification_;
|
||||||
|
absl::Mutex adv_monitor_manager_ready_mutex_;
|
||||||
|
bool adv_monitor_manager_ready_ ABSL_GUARDED_BY(adv_monitor_manager_ready_mutex_) =
|
||||||
|
false;
|
||||||
|
std::string adv_monitor_manager_error_name_
|
||||||
|
ABSL_GUARDED_BY(adv_monitor_manager_ready_mutex_);
|
||||||
|
std::string adv_monitor_manager_error_message_
|
||||||
|
ABSL_GUARDED_BY(adv_monitor_manager_ready_mutex_);
|
||||||
absl::Mutex active_adv_monitors_mutex_;
|
absl::Mutex active_adv_monitors_mutex_;
|
||||||
absl::flat_hash_map<
|
absl::flat_hash_map<
|
||||||
Uuid,
|
Uuid,
|
||||||
|
|||||||
@@ -21,34 +21,48 @@
|
|||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "internal/base/observer_list.h"
|
#include "internal/base/observer_list.h"
|
||||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.h"
|
#include "internal/platform/implementation/linux/bluez_agent.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_classic_medium.h"
|
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||||
|
#include "internal/platform/implementation/linux/bluetooth_classic_medium.h"
|
||||||
// #include "bluez_agent.h"
|
|
||||||
#include "internal/platform/implementation/linux/bluetooth_classic_server_socket.h"
|
#include "internal/platform/implementation/linux/bluetooth_classic_server_socket.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_classic_socket.h"
|
#include "internal/platform/implementation/linux/bluetooth_classic_socket.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_pairing.h"
|
#include "internal/platform/implementation/linux/bluetooth_pairing.h"
|
||||||
#include "internal/platform/logging.h"
|
#include "internal/platform/logging.h"
|
||||||
|
|
||||||
namespace nearby {
|
namespace nearby {
|
||||||
namespace linux {
|
namespace linux {
|
||||||
BluetoothClassicMedium::BluetoothClassicMedium(BluetoothAdapter &adapter)
|
namespace {
|
||||||
: system_bus_(adapter.GetConnection()),
|
|
||||||
adapter_(adapter),
|
constexpr char kBluezAgentPath[] = "/com/google/nearby/bluetooth/agent";
|
||||||
observers_(nullptr),
|
|
||||||
devices_(nullptr),
|
} // namespace
|
||||||
device_watcher_(nullptr),
|
|
||||||
// agent_manager_(std::make_unique<AgentManager>(*system_bus_)),
|
BluetoothClassicMedium::BluetoothClassicMedium(BluetoothAdapter &adapter)
|
||||||
profile_manager_(nullptr) {
|
: system_bus_(adapter.GetConnection()),
|
||||||
auto shared =
|
adapter_(adapter),
|
||||||
GetSharedBluetoothDevices(system_bus_, adapter_.GetObjectPath());
|
observers_(nullptr),
|
||||||
observers_ = shared->observers;
|
devices_(nullptr),
|
||||||
devices_ = shared->devices;
|
device_watcher_(nullptr),
|
||||||
profile_manager_ =
|
agent_manager_(std::make_unique<AgentManager>(*system_bus_)),
|
||||||
std::make_unique<ProfileManager>(*system_bus_, *devices_);
|
profile_manager_(nullptr) {
|
||||||
}
|
auto shared =
|
||||||
|
GetSharedBluetoothDevices(system_bus_, adapter_.GetObjectPath());
|
||||||
|
observers_ = shared->observers;
|
||||||
|
devices_ = shared->devices;
|
||||||
|
profile_manager_ =
|
||||||
|
std::make_unique<ProfileManager>(*system_bus_, *devices_);
|
||||||
|
|
||||||
|
if (!agent_manager_->Register(
|
||||||
|
/*capability=*/absl::string_view("NoInputNoOutput"),
|
||||||
|
sdbus::ObjectPath(kBluezAgentPath))) {
|
||||||
|
LOG(WARNING) << __func__
|
||||||
|
<< ": Failed to register default BlueZ agent at "
|
||||||
|
<< kBluezAgentPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool BluetoothClassicMedium::StartDiscovery(
|
bool BluetoothClassicMedium::StartDiscovery(
|
||||||
DiscoveryCallback discovery_callback) {
|
DiscoveryCallback discovery_callback) {
|
||||||
@@ -145,14 +159,12 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
|
|||||||
new BluetoothSocket(device, fd.value()));
|
new BluetoothSocket(device, fd.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<api::BluetoothServerSocket>
|
std::unique_ptr<api::BluetoothServerSocket>
|
||||||
BluetoothClassicMedium::ListenForService(const std::string &service_name,
|
BluetoothClassicMedium::ListenForService(const std::string &service_name,
|
||||||
const std::string &service_uuid) {
|
const std::string &service_uuid) {
|
||||||
LOG(INFO) << __func__ << ": Creating bluez agent on path: " << "/com/example/bluez_agent" ;
|
if (!profile_manager_->ProfileRegistered(service_uuid)) {
|
||||||
|
if (!profile_manager_->Register(service_name, service_uuid)) {
|
||||||
if (!profile_manager_->ProfileRegistered(service_uuid)) {
|
LOG(ERROR) << __func__ << ": Could not register profile "
|
||||||
if (!profile_manager_->Register(service_name, service_uuid)) {
|
|
||||||
LOG(ERROR) << __func__ << ": Could not register profile "
|
|
||||||
<< service_name << " " << service_uuid
|
<< service_name << " " << service_uuid
|
||||||
<< " with Bluez";
|
<< " with Bluez";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
#include <sdbus-c++/StandardInterfaces.h>
|
#include <sdbus-c++/StandardInterfaces.h>
|
||||||
#include <sdbus-c++/Types.h>
|
#include <sdbus-c++/Types.h>
|
||||||
|
|
||||||
// #include "bluez_agent.h"
|
#include "internal/platform/implementation/linux/bluez_agent.h"
|
||||||
#include "internal/base/observer_list.h"
|
#include "internal/base/observer_list.h"
|
||||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||||
@@ -107,7 +107,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
|
|||||||
std::shared_ptr<BluetoothDevices> devices_;
|
std::shared_ptr<BluetoothDevices> devices_;
|
||||||
std::unique_ptr<DeviceWatcher> device_watcher_;
|
std::unique_ptr<DeviceWatcher> device_watcher_;
|
||||||
|
|
||||||
// std::unique_ptr<AgentManager> agent_manager_;
|
std::unique_ptr<AgentManager> agent_manager_;
|
||||||
std::unique_ptr<ProfileManager> profile_manager_;
|
std::unique_ptr<ProfileManager> profile_manager_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,13 @@
|
|||||||
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
|
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
|
||||||
#define PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
|
#define PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
#include <sdbus-c++/ProxyInterfaces.h>
|
#include <sdbus-c++/ProxyInterfaces.h>
|
||||||
|
|
||||||
|
#include "absl/functional/any_invocable.h"
|
||||||
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||||
#include "internal/platform/implementation/linux/bluez.h"
|
#include "internal/platform/implementation/linux/bluez.h"
|
||||||
#include "internal/platform/implementation/linux/dbus.h"
|
#include "internal/platform/implementation/linux/dbus.h"
|
||||||
@@ -29,6 +33,9 @@ namespace bluez {
|
|||||||
class AdvertisementMonitorManager final
|
class AdvertisementMonitorManager final
|
||||||
: public sdbus::ProxyInterfaces<
|
: public sdbus::ProxyInterfaces<
|
||||||
org::bluez::AdvertisementMonitorManager1_proxy> {
|
org::bluez::AdvertisementMonitorManager1_proxy> {
|
||||||
|
public:
|
||||||
|
using ReplyCallback = absl::AnyInvocable<void(std::optional<sdbus::Error>)>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend std::unique_ptr<AdvertisementMonitorManager>
|
friend std::unique_ptr<AdvertisementMonitorManager>
|
||||||
std::make_unique<AdvertisementMonitorManager>(
|
std::make_unique<AdvertisementMonitorManager>(
|
||||||
@@ -50,6 +57,16 @@ class AdvertisementMonitorManager final
|
|||||||
delete;
|
delete;
|
||||||
~AdvertisementMonitorManager() { unregisterProxy(); }
|
~AdvertisementMonitorManager() { unregisterProxy(); }
|
||||||
|
|
||||||
|
void SetRegisterMonitorReplyCallback(ReplyCallback callback) {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
register_monitor_reply_callback_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUnregisterMonitorReplyCallback(ReplyCallback callback) {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
unregister_monitor_reply_callback_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
static std::unique_ptr<AdvertisementMonitorManager>
|
static std::unique_ptr<AdvertisementMonitorManager>
|
||||||
DiscoverAdvertisementMonitorManager(
|
DiscoverAdvertisementMonitorManager(
|
||||||
sdbus::IConnection &system_bus,
|
sdbus::IConnection &system_bus,
|
||||||
@@ -82,6 +99,38 @@ class AdvertisementMonitorManager final
|
|||||||
|
|
||||||
return std::make_unique<AdvertisementMonitorManager>(system_bus, adapter);
|
return std::make_unique<AdvertisementMonitorManager>(system_bus, adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void onRegisterMonitorReply(std::optional<sdbus::Error> error) override {
|
||||||
|
std::optional<ReplyCallback> callback;
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
callback = std::move(register_monitor_reply_callback_);
|
||||||
|
register_monitor_reply_callback_.reset();
|
||||||
|
}
|
||||||
|
if (callback.has_value()) {
|
||||||
|
(*callback)(std::move(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUnregisterMonitorReply(std::optional<sdbus::Error> error) override {
|
||||||
|
std::optional<ReplyCallback> callback;
|
||||||
|
{
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
callback = std::move(unregister_monitor_reply_callback_);
|
||||||
|
unregister_monitor_reply_callback_.reset();
|
||||||
|
}
|
||||||
|
if (callback.has_value()) {
|
||||||
|
(*callback)(std::move(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
absl::Mutex callbacks_mutex_;
|
||||||
|
std::optional<ReplyCallback> register_monitor_reply_callback_
|
||||||
|
ABSL_GUARDED_BY(callbacks_mutex_);
|
||||||
|
std::optional<ReplyCallback> unregister_monitor_reply_callback_
|
||||||
|
ABSL_GUARDED_BY(callbacks_mutex_);
|
||||||
};
|
};
|
||||||
} // namespace bluez
|
} // namespace bluez
|
||||||
} // namespace linux
|
} // namespace linux
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
#include "internal/platform/implementation/linux/bluez_agent.h"
|
||||||
|
|
||||||
|
#include <sdbus-c++/AdaptorInterfaces.h>
|
||||||
|
|
||||||
|
#include "internal/platform/implementation/linux/generated/dbus/bluez/agent_server.h"
|
||||||
|
#include "internal/platform/implementation/linux/generated/dbus/bluez/agentmanager_client.h"
|
||||||
|
#include "internal/platform/implementation/linux/bluez.h"
|
||||||
|
#include "internal/platform/logging.h"
|
||||||
|
|
||||||
|
namespace nearby {
|
||||||
|
namespace linux {
|
||||||
|
|
||||||
|
void Agent::Release() { LOG(INFO) << "[agent] Release()"; }
|
||||||
|
|
||||||
|
std::string Agent::RequestPinCode(const sdbus::ObjectPath& device) {
|
||||||
|
LOG(INFO) << "[agent] RequestPinCode(" << device << ")";
|
||||||
|
return "0000";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::DisplayPinCode(const sdbus::ObjectPath& device,
|
||||||
|
const std::string& pincode) {
|
||||||
|
LOG(INFO) << "[agent] DisplayPinCode(" << device << ", " << pincode << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Agent::RequestPasskey(const sdbus::ObjectPath& device) {
|
||||||
|
LOG(INFO) << "[agent] RequestPasskey(" << device << ")";
|
||||||
|
return 123456;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::DisplayPasskey(const sdbus::ObjectPath& device,
|
||||||
|
const uint32_t& passkey,
|
||||||
|
const uint16_t& entered) {
|
||||||
|
LOG(INFO) << "[agent] DisplayPasskey(" << device << ", " << passkey
|
||||||
|
<< ", entered=" << entered << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::RequestConfirmation(const sdbus::ObjectPath& device,
|
||||||
|
const uint32_t& passkey) {
|
||||||
|
LOG(INFO) << "[agent] RequestConfirmation(" << device << ", " << passkey
|
||||||
|
<< ") -> ACCEPT";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::RequestAuthorization(const sdbus::ObjectPath& device) {
|
||||||
|
LOG(INFO) << "[agent] RequestAuthorization(" << device << ") -> ACCEPT";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::AuthorizeService(const sdbus::ObjectPath& device,
|
||||||
|
const std::string& uuid) {
|
||||||
|
LOG(INFO) << "[agent] AuthorizeService(" << device << ", " << uuid
|
||||||
|
<< ") -> ACCEPT";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Agent::Cancel() { LOG(INFO) << "[agent] Cancel()"; }
|
||||||
|
|
||||||
|
bool AgentManager::AgentRegistered(absl::string_view agent_object_path) {
|
||||||
|
registered_agents_mutex_.ReaderLock();
|
||||||
|
bool registered = registered_agents_.count(std::string(agent_object_path)) == 1;
|
||||||
|
registered_agents_mutex_.ReaderUnlock();
|
||||||
|
return registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AgentManager::Register(std::optional<absl::string_view> capability,
|
||||||
|
const sdbus::ObjectPath& agent_object_path) {
|
||||||
|
absl::MutexLock l(®istered_agents_mutex_);
|
||||||
|
|
||||||
|
const std::string agent_path_str = std::string(agent_object_path);
|
||||||
|
|
||||||
|
if (registered_agents_.count(agent_path_str) == 1) {
|
||||||
|
LOG(WARNING) << __func__ << ": Trying to register agent " << agent_path_str
|
||||||
|
<< " which was already registered.";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto agent = std::make_shared<Agent>(getProxy().getConnection(),
|
||||||
|
sdbus::ObjectPath(agent_object_path));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const std::string cap =
|
||||||
|
capability.has_value() ? std::string(*capability) : "NoInputNoOutput";
|
||||||
|
RegisterAgent(agent->getObject().getObjectPath(), cap);
|
||||||
|
RequestDefaultAgent(agent->getObject().getObjectPath());
|
||||||
|
} catch (const sdbus::Error& e) {
|
||||||
|
LOG(ERROR) << __func__ << ": Got error '" << e.getName()
|
||||||
|
<< "' with message '" << e.getMessage()
|
||||||
|
<< "' while calling RegisterAgent/RequestDefaultAgent on object "
|
||||||
|
<< getProxy().getObjectPath();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
registered_agents_.emplace(agent_path_str, agent);
|
||||||
|
|
||||||
|
LOG(INFO) << __func__ << ": Registered agent instance at path "
|
||||||
|
<< agent_path_str;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace linux
|
||||||
|
} // namespace nearby
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
|
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
|
#include "absl/synchronization/mutex.h"
|
||||||
|
#include "internal/platform/implementation/linux/generated/dbus/bluez/agent_server.h"
|
||||||
|
#include "internal/platform/implementation/linux/generated/dbus/bluez/agentmanager_client.h"
|
||||||
|
#include "internal/platform/implementation/linux/bluez.h"
|
||||||
|
#include "internal/platform/logging.h"
|
||||||
|
|
||||||
|
namespace nearby::linux {
|
||||||
|
|
||||||
|
class Agent final
|
||||||
|
: public sdbus::AdaptorInterfaces<org::bluez::Agent1_adaptor> {
|
||||||
|
public:
|
||||||
|
Agent(const Agent&) = delete;
|
||||||
|
Agent(Agent&&) = delete;
|
||||||
|
Agent& operator=(const Agent&) = delete;
|
||||||
|
Agent& operator=(Agent&&) = delete;
|
||||||
|
|
||||||
|
Agent(sdbus::IConnection& system_bus, sdbus::ObjectPath path)
|
||||||
|
: AdaptorInterfaces(system_bus, std::move(path)) {
|
||||||
|
registerAdaptor();
|
||||||
|
LOG(INFO) << "Created new Agent at path: " << getObject().getObjectPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Agent() { unregisterAdaptor(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Release() override;
|
||||||
|
|
||||||
|
std::string RequestPinCode(const sdbus::ObjectPath& device) override;
|
||||||
|
void DisplayPinCode(const sdbus::ObjectPath& device,
|
||||||
|
const std::string& pincode) override;
|
||||||
|
|
||||||
|
uint32_t RequestPasskey(const sdbus::ObjectPath& device) override;
|
||||||
|
void DisplayPasskey(const sdbus::ObjectPath& device, const uint32_t& passkey,
|
||||||
|
const uint16_t& entered) override;
|
||||||
|
|
||||||
|
void RequestConfirmation(const sdbus::ObjectPath& device,
|
||||||
|
const uint32_t& passkey) override;
|
||||||
|
|
||||||
|
void RequestAuthorization(const sdbus::ObjectPath& device) override;
|
||||||
|
|
||||||
|
void AuthorizeService(const sdbus::ObjectPath& device,
|
||||||
|
const std::string& uuid) override;
|
||||||
|
|
||||||
|
void Cancel() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AgentManager final
|
||||||
|
: public sdbus::ProxyInterfaces<org::bluez::AgentManager1_proxy> {
|
||||||
|
public:
|
||||||
|
AgentManager(const AgentManager&) = delete;
|
||||||
|
AgentManager(AgentManager&&) = delete;
|
||||||
|
AgentManager& operator=(const AgentManager&) = delete;
|
||||||
|
AgentManager& operator=(AgentManager&&) = delete;
|
||||||
|
|
||||||
|
explicit AgentManager(sdbus::IConnection& system_bus)
|
||||||
|
: ProxyInterfaces(system_bus, sdbus::ServiceName(bluez::SERVICE_DEST),
|
||||||
|
sdbus::ObjectPath("/org/bluez")) {
|
||||||
|
registerProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
~AgentManager() { unregisterProxy(); }
|
||||||
|
|
||||||
|
bool Register(std::optional<absl::string_view> capability,
|
||||||
|
const sdbus::ObjectPath& agent_object_path);
|
||||||
|
|
||||||
|
bool AgentRegistered(absl::string_view agent_object_path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
absl::Mutex registered_agents_mutex_;
|
||||||
|
std::map<std::string, std::shared_ptr<Agent>> registered_agents_
|
||||||
|
ABSL_GUARDED_BY(registered_agents_mutex_);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace nearby::linux
|
||||||
@@ -191,7 +191,10 @@ void GattCharacteristicServer::StopNotify() {
|
|||||||
if ((characteristic_.property |
|
if ((characteristic_.property |
|
||||||
api::ble::GattCharacteristic::Property::kNotify) ==
|
api::ble::GattCharacteristic::Property::kNotify) ==
|
||||||
api::ble::GattCharacteristic::Property::kNotify) {
|
api::ble::GattCharacteristic::Property::kNotify) {
|
||||||
if (notify_sessions_.fetch_sub(0) == 1) {
|
if (notify_sessions_.load() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (notify_sessions_.fetch_sub(1) == 1) {
|
||||||
if (server_cb_->characteristic_unsubscription_cb != nullptr) {
|
if (server_cb_->characteristic_unsubscription_cb != nullptr) {
|
||||||
server_cb_->characteristic_unsubscription_cb(characteristic_);
|
server_cb_->characteristic_unsubscription_cb(characteristic_);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,10 +14,15 @@
|
|||||||
|
|
||||||
#ifndef PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
|
#ifndef PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
|
||||||
#define PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
|
#define PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <sdbus-c++/IConnection.h>
|
#include <sdbus-c++/IConnection.h>
|
||||||
#include <sdbus-c++/ProxyInterfaces.h>
|
#include <sdbus-c++/ProxyInterfaces.h>
|
||||||
#include <sdbus-c++/Types.h>
|
#include <sdbus-c++/Types.h>
|
||||||
|
|
||||||
|
#include "absl/functional/any_invocable.h"
|
||||||
|
#include "absl/synchronization/mutex.h"
|
||||||
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_manager_client.h"
|
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_manager_client.h"
|
||||||
namespace nearby {
|
namespace nearby {
|
||||||
namespace linux {
|
namespace linux {
|
||||||
@@ -25,6 +30,8 @@ namespace bluez {
|
|||||||
class GattManager
|
class GattManager
|
||||||
: public sdbus::ProxyInterfaces<org::bluez::GattManager1_proxy> {
|
: public sdbus::ProxyInterfaces<org::bluez::GattManager1_proxy> {
|
||||||
public:
|
public:
|
||||||
|
using ReplyCallback = absl::AnyInvocable<void(std::optional<sdbus::Error>)>;
|
||||||
|
|
||||||
GattManager(const GattManager &) = delete;
|
GattManager(const GattManager &) = delete;
|
||||||
GattManager(GattManager &&) = delete;
|
GattManager(GattManager &&) = delete;
|
||||||
GattManager &operator=(const GattManager &) = delete;
|
GattManager &operator=(const GattManager &) = delete;
|
||||||
@@ -37,6 +44,38 @@ class GattManager
|
|||||||
registerProxy();
|
registerProxy();
|
||||||
}
|
}
|
||||||
~GattManager() { unregisterProxy(); }
|
~GattManager() { unregisterProxy(); }
|
||||||
|
|
||||||
|
void SetRegisterApplicationReplyCallback(ReplyCallback callback) {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
register_application_reply_callback_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUnregisterApplicationReplyCallback(ReplyCallback callback) {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
unregister_application_reply_callback_ = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void onRegisterApplicationReply(std::optional<sdbus::Error> error) override {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
if (register_application_reply_callback_.has_value()) {
|
||||||
|
(*register_application_reply_callback_)(std::move(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onUnregisterApplicationReply(std::optional<sdbus::Error> error) override {
|
||||||
|
absl::MutexLock lock(&callbacks_mutex_);
|
||||||
|
if (unregister_application_reply_callback_.has_value()) {
|
||||||
|
(*unregister_application_reply_callback_)(std::move(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
absl::Mutex callbacks_mutex_;
|
||||||
|
std::optional<ReplyCallback> register_application_reply_callback_
|
||||||
|
ABSL_GUARDED_BY(callbacks_mutex_);
|
||||||
|
std::optional<ReplyCallback> unregister_application_reply_callback_
|
||||||
|
ABSL_GUARDED_BY(callbacks_mutex_);
|
||||||
};
|
};
|
||||||
} // namespace bluez
|
} // namespace bluez
|
||||||
} // namespace linux
|
} // namespace linux
|
||||||
|
|||||||
+9
-6
@@ -3,8 +3,8 @@
|
|||||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_dbus_bluez_advertisement_monitor_manager_client_v2_h__proxy__H__
|
#ifndef __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_advertisement_monitor_manager_client_h__proxy__H__
|
||||||
#define __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_dbus_bluez_advertisement_monitor_manager_client_v2_h__proxy__H__
|
#define __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_advertisement_monitor_manager_client_h__proxy__H__
|
||||||
|
|
||||||
#include <sdbus-c++/sdbus-c++.h>
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -35,15 +35,18 @@ protected:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onRegisterMonitorReply(std::optional<sdbus::Error> error) = 0;
|
||||||
|
virtual void onUnregisterMonitorReply(std::optional<sdbus::Error> error) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void RegisterMonitor(const sdbus::ObjectPath& application)
|
sdbus::PendingAsyncCall RegisterMonitor(const sdbus::ObjectPath& application)
|
||||||
{
|
{
|
||||||
m_proxy.callMethod("RegisterMonitor").onInterface(INTERFACE_NAME).withArguments(application);
|
return m_proxy.callMethodAsync("RegisterMonitor").onInterface(INTERFACE_NAME).withArguments(application).uponReplyInvoke([this](std::optional<sdbus::Error> error){ this->onRegisterMonitorReply(std::move(error)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterMonitor(const sdbus::ObjectPath& application)
|
sdbus::PendingAsyncCall UnregisterMonitor(const sdbus::ObjectPath& application)
|
||||||
{
|
{
|
||||||
m_proxy.callMethod("UnregisterMonitor").onInterface(INTERFACE_NAME).withArguments(application);
|
return m_proxy.callMethodAsync("UnregisterMonitor").onInterface(INTERFACE_NAME).withArguments(application).uponReplyInvoke([this](std::optional<sdbus::Error> error){ this->onUnregisterMonitorReply(std::move(error)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_agent_server_h__adaptor__H__
|
||||||
|
#define __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_agent_server_h__adaptor__H__
|
||||||
|
|
||||||
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace org {
|
||||||
|
namespace bluez {
|
||||||
|
|
||||||
|
class Agent1_adaptor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr const char* INTERFACE_NAME = "org.bluez.Agent1";
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Agent1_adaptor(sdbus::IObject& object)
|
||||||
|
: m_object(object)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Agent1_adaptor(const Agent1_adaptor&) = delete;
|
||||||
|
Agent1_adaptor& operator=(const Agent1_adaptor&) = delete;
|
||||||
|
Agent1_adaptor(Agent1_adaptor&&) = delete;
|
||||||
|
Agent1_adaptor& operator=(Agent1_adaptor&&) = delete;
|
||||||
|
|
||||||
|
~Agent1_adaptor() = default;
|
||||||
|
|
||||||
|
void registerAdaptor()
|
||||||
|
{
|
||||||
|
m_object.addVTable( sdbus::registerMethod("Release").implementedAs([this](){ return this->Release(); })
|
||||||
|
, sdbus::registerMethod("RequestPinCode").withInputParamNames("device").withOutputParamNames("pincode").implementedAs([this](const sdbus::ObjectPath& device){ return this->RequestPinCode(device); })
|
||||||
|
, sdbus::registerMethod("DisplayPinCode").withInputParamNames("device", "pincode").implementedAs([this](const sdbus::ObjectPath& device, const std::string& pincode){ return this->DisplayPinCode(device, pincode); })
|
||||||
|
, sdbus::registerMethod("RequestPasskey").withInputParamNames("device").withOutputParamNames("passkey").implementedAs([this](const sdbus::ObjectPath& device){ return this->RequestPasskey(device); })
|
||||||
|
, sdbus::registerMethod("DisplayPasskey").withInputParamNames("device", "passkey", "entered").implementedAs([this](const sdbus::ObjectPath& device, const uint32_t& passkey, const uint16_t& entered){ return this->DisplayPasskey(device, passkey, entered); })
|
||||||
|
, sdbus::registerMethod("RequestConfirmation").withInputParamNames("device", "passkey").implementedAs([this](const sdbus::ObjectPath& device, const uint32_t& passkey){ return this->RequestConfirmation(device, passkey); })
|
||||||
|
, sdbus::registerMethod("RequestAuthorization").withInputParamNames("device").implementedAs([this](const sdbus::ObjectPath& device){ return this->RequestAuthorization(device); })
|
||||||
|
, sdbus::registerMethod("AuthorizeService").withInputParamNames("device", "uuid").implementedAs([this](const sdbus::ObjectPath& device, const std::string& uuid){ return this->AuthorizeService(device, uuid); })
|
||||||
|
, sdbus::registerMethod("Cancel").implementedAs([this](){ return this->Cancel(); })
|
||||||
|
).forInterface(INTERFACE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual void Release() = 0;
|
||||||
|
virtual std::string RequestPinCode(const sdbus::ObjectPath& device) = 0;
|
||||||
|
virtual void DisplayPinCode(const sdbus::ObjectPath& device, const std::string& pincode) = 0;
|
||||||
|
virtual uint32_t RequestPasskey(const sdbus::ObjectPath& device) = 0;
|
||||||
|
virtual void DisplayPasskey(const sdbus::ObjectPath& device, const uint32_t& passkey, const uint16_t& entered) = 0;
|
||||||
|
virtual void RequestConfirmation(const sdbus::ObjectPath& device, const uint32_t& passkey) = 0;
|
||||||
|
virtual void RequestAuthorization(const sdbus::ObjectPath& device) = 0;
|
||||||
|
virtual void AuthorizeService(const sdbus::ObjectPath& device, const std::string& uuid) = 0;
|
||||||
|
virtual void Cancel() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
sdbus::IObject& m_object;
|
||||||
|
};
|
||||||
|
|
||||||
|
}} // namespaces
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_agentmanager_client_h__proxy__H__
|
||||||
|
#define __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_agentmanager_client_h__proxy__H__
|
||||||
|
|
||||||
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace org {
|
||||||
|
namespace bluez {
|
||||||
|
|
||||||
|
class AgentManager1_proxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr const char* INTERFACE_NAME = "org.bluez.AgentManager1";
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AgentManager1_proxy(sdbus::IProxy& proxy)
|
||||||
|
: m_proxy(proxy)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentManager1_proxy(const AgentManager1_proxy&) = delete;
|
||||||
|
AgentManager1_proxy& operator=(const AgentManager1_proxy&) = delete;
|
||||||
|
AgentManager1_proxy(AgentManager1_proxy&&) = delete;
|
||||||
|
AgentManager1_proxy& operator=(AgentManager1_proxy&&) = delete;
|
||||||
|
|
||||||
|
~AgentManager1_proxy() = default;
|
||||||
|
|
||||||
|
void registerProxy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void RegisterAgent(const sdbus::ObjectPath& agent, const std::string& capability)
|
||||||
|
{
|
||||||
|
m_proxy.callMethod("RegisterAgent").onInterface(INTERFACE_NAME).withArguments(agent, capability);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnregisterAgent(const sdbus::ObjectPath& agent)
|
||||||
|
{
|
||||||
|
m_proxy.callMethod("UnregisterAgent").onInterface(INTERFACE_NAME).withArguments(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestDefaultAgent(const sdbus::ObjectPath& agent)
|
||||||
|
{
|
||||||
|
m_proxy.callMethod("RequestDefaultAgent").onInterface(INTERFACE_NAME).withArguments(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
sdbus::IProxy& m_proxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
}} // namespaces
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -3,8 +3,8 @@
|
|||||||
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_v2_h__proxy__H__
|
#ifndef __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_h__proxy__H__
|
||||||
#define __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_v2_h__proxy__H__
|
#define __sdbuscpp__internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_h__proxy__H__
|
||||||
|
|
||||||
#include <sdbus-c++/sdbus-c++.h>
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -35,15 +35,18 @@ protected:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onRegisterApplicationReply(std::optional<sdbus::Error> error) = 0;
|
||||||
|
virtual void onUnregisterApplicationReply(std::optional<sdbus::Error> error) = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void RegisterApplication(const sdbus::ObjectPath& application, const std::map<std::string, sdbus::Variant>& options)
|
sdbus::PendingAsyncCall RegisterApplication(const sdbus::ObjectPath& application, const std::map<std::string, sdbus::Variant>& options)
|
||||||
{
|
{
|
||||||
m_proxy.callMethod("RegisterApplication").onInterface(INTERFACE_NAME).withArguments(application, options);
|
return m_proxy.callMethodAsync("RegisterApplication").onInterface(INTERFACE_NAME).withArguments(application, options).uponReplyInvoke([this](std::optional<sdbus::Error> error){ this->onRegisterApplicationReply(std::move(error)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterApplication(const sdbus::ObjectPath& application)
|
sdbus::PendingAsyncCall UnregisterApplication(const sdbus::ObjectPath& application)
|
||||||
{
|
{
|
||||||
m_proxy.callMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withArguments(application);
|
return m_proxy.callMethodAsync("UnregisterApplication").onInterface(INTERFACE_NAME).withArguments(application).uponReplyInvoke([this](std::optional<sdbus::Error> error){ this->onUnregisterApplicationReply(std::move(error)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+2
@@ -3,9 +3,11 @@
|
|||||||
<node>
|
<node>
|
||||||
<interface name="org.bluez.AdvertisementMonitorManager1">
|
<interface name="org.bluez.AdvertisementMonitorManager1">
|
||||||
<method name="RegisterMonitor">
|
<method name="RegisterMonitor">
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||||
<arg name="application" type="o" direction="in" />
|
<arg name="application" type="o" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
<method name="UnregisterMonitor">
|
<method name="UnregisterMonitor">
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||||
<arg name="application" type="o" direction="in" />
|
<arg name="application" type="o" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
<property name="SupportedMonitorTypes" type="as" access="read" />
|
<property name="SupportedMonitorTypes" type="as" access="read" />
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="org.bluez.Agent1">
|
||||||
|
<method name="Release"/>
|
||||||
|
|
||||||
|
<method name="RequestPinCode">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="pincode" type="s" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="DisplayPinCode">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="pincode" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="RequestPasskey">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="passkey" type="u" direction="out"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="DisplayPasskey">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="passkey" type="u" direction="in"/>
|
||||||
|
<arg name="entered" type="q" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="RequestConfirmation">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="passkey" type="u" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="RequestAuthorization">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="AuthorizeService">
|
||||||
|
<arg name="device" type="o" direction="in"/>
|
||||||
|
<arg name="uuid" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="Cancel"/>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</node>
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
|
||||||
|
<interface name="org.bluez.AgentManager1">
|
||||||
|
<method name="RegisterAgent">
|
||||||
|
<arg name="agent" type="o" direction="in"/>
|
||||||
|
<arg name="capability" type="s" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="UnregisterAgent">
|
||||||
|
<arg name="agent" type="o" direction="in"/>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="RequestDefaultAgent">
|
||||||
|
<arg name="agent" type="o" direction="in"/>
|
||||||
|
</method>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
</node>
|
||||||
@@ -3,10 +3,12 @@
|
|||||||
<node>
|
<node>
|
||||||
<interface name="org.bluez.GattManager1">
|
<interface name="org.bluez.GattManager1">
|
||||||
<method name="RegisterApplication">
|
<method name="RegisterApplication">
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||||
<arg name="application" type="o" direction="in" />
|
<arg name="application" type="o" direction="in" />
|
||||||
<arg name="options" type="a{sv}" direction="in" />
|
<arg name="options" type="a{sv}" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
<method name="UnregisterApplication">
|
<method name="UnregisterApplication">
|
||||||
|
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||||
<arg name="application" type="o" direction="in" />
|
<arg name="application" type="o" direction="in" />
|
||||||
</method>
|
</method>
|
||||||
</interface>
|
</interface>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ cc_library(
|
|||||||
"//internal/base:file_path",
|
"//internal/base:file_path",
|
||||||
"//internal/base:files",
|
"//internal/base:files",
|
||||||
"//internal/crypto_cros", # buildcleaner: keep
|
"//internal/crypto_cros", # buildcleaner: keep
|
||||||
|
"//internal/platform:base",
|
||||||
"//internal/interop:authentication_status",
|
"//internal/interop:authentication_status",
|
||||||
"@com_google_absl//absl/random",
|
"@com_google_absl//absl/random",
|
||||||
"@com_google_absl//absl/strings:string_view",
|
"@com_google_absl//absl/strings:string_view",
|
||||||
|
|||||||
+55
-14
@@ -13,7 +13,8 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
licenses(["notice"])
|
licenses(["notice"])
|
||||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
|
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
|
||||||
|
load("@rules_cc//cc/private/rules_impl:cc_static_library.bzl", "cc_static_library")
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "linux_sharing_platform",
|
name = "linux_sharing_platform",
|
||||||
@@ -46,17 +47,6 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "system_clock_shim",
|
|
||||||
srcs = ["platform/system_clock_shim.cc"],
|
|
||||||
alwayslink = True,
|
|
||||||
visibility = ["//visibility:private"],
|
|
||||||
deps = [
|
|
||||||
"//internal/platform:types",
|
|
||||||
"@com_google_absl//absl/time",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
cc_library(
|
||||||
name = "nearby_sharing_api",
|
name = "nearby_sharing_api",
|
||||||
srcs = ["nearby_sharing_api.cc"],
|
srcs = ["nearby_sharing_api.cc"],
|
||||||
@@ -65,8 +55,8 @@ cc_library(
|
|||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
deps = [
|
deps = [
|
||||||
":linux_sharing_platform",
|
":linux_sharing_platform",
|
||||||
":system_clock_shim",
|
|
||||||
"//internal/base:file_path",
|
"//internal/base:file_path",
|
||||||
|
"//internal/platform/implementation/linux:system_clock",
|
||||||
"//sharing:attachments",
|
"//sharing:attachments",
|
||||||
"//sharing:nearby_sharing_service",
|
"//sharing:nearby_sharing_service",
|
||||||
"//sharing/analytics",
|
"//sharing/analytics",
|
||||||
@@ -74,6 +64,26 @@ cc_library(
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "nearby_connections_api",
|
||||||
|
srcs = ["nearby_connections_api.cc"],
|
||||||
|
hdrs = ["nearby_connections_api.h"],
|
||||||
|
alwayslink = True,
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
deps = [
|
||||||
|
"//internal/platform:base",
|
||||||
|
":linux_sharing_platform",
|
||||||
|
"//internal/base:file_path",
|
||||||
|
"//sharing/internal/public:nearby_context",
|
||||||
|
"//sharing:nearby_sharing_service",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_static_library(
|
||||||
|
name = "nearby_sharing_api_static",
|
||||||
|
deps = [":nearby_sharing_api"],
|
||||||
|
)
|
||||||
|
|
||||||
cc_binary(
|
cc_binary(
|
||||||
name = "nearby_sharing_api_shared",
|
name = "nearby_sharing_api_shared",
|
||||||
linkshared = True,
|
linkshared = True,
|
||||||
@@ -86,14 +96,45 @@ cc_binary(
|
|||||||
"-Wl,--exclude-libs,ALL",
|
"-Wl,--exclude-libs,ALL",
|
||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//internal/platform:base",
|
||||||
":linux_sharing_platform",
|
":linux_sharing_platform",
|
||||||
":system_clock_shim",
|
|
||||||
"//internal/base:file_path",
|
"//internal/base:file_path",
|
||||||
"//internal/platform/implementation:platform",
|
"//internal/platform/implementation:platform",
|
||||||
"//internal/platform/implementation/linux",
|
"//internal/platform/implementation/linux",
|
||||||
|
"//internal/platform/implementation/linux:system_clock",
|
||||||
"//sharing:attachments",
|
"//sharing:attachments",
|
||||||
"//sharing:nearby_sharing_service",
|
"//sharing:nearby_sharing_service",
|
||||||
"//sharing/analytics",
|
"//sharing/analytics",
|
||||||
"//sharing/local_device_data",
|
"//sharing/local_device_data",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cc_binary(
|
||||||
|
name = "nearby_connections_api_shared",
|
||||||
|
linkshared = True,
|
||||||
|
srcs = [
|
||||||
|
"nearby_connections_api.cc",
|
||||||
|
"nearby_connections_api.h",
|
||||||
|
],
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
linkopts = [
|
||||||
|
"-Wl,--exclude-libs,ALL",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
":linux_sharing_platform",
|
||||||
|
"//internal/base:file_path",
|
||||||
|
"//internal/platform/implementation:platform",
|
||||||
|
"//internal/platform/implementation/linux",
|
||||||
|
"//sharing/internal/public:nearby_context",
|
||||||
|
"//sharing:nearby_sharing_service",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
cc_test(
|
||||||
|
name = "nearby_connections_api_test",
|
||||||
|
srcs = ["nearby_connections_api_test.cc"],
|
||||||
|
deps = [
|
||||||
|
":nearby_connections_api",
|
||||||
|
"@com_google_googletest//:gtest_main",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,659 @@
|
|||||||
|
#include "sharing/linux/nearby_connections_api.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/time/time.h"
|
||||||
|
#include "internal/base/file_path.h"
|
||||||
|
#include "internal/interop/authentication_status.h"
|
||||||
|
#include "sharing/internal/public/context_impl.h"
|
||||||
|
#include "sharing/linux/platform/linux_sharing_platform.h"
|
||||||
|
#include "sharing/nearby_connections_service.h"
|
||||||
|
#include "sharing/nearby_connections_service_impl.h"
|
||||||
|
|
||||||
|
namespace nearby::sharing {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using NativeService = nearby::sharing::NearbyConnectionsService;
|
||||||
|
|
||||||
|
NearbyConnectionsApi::StatusCode ToFacadeStatus(Status status) {
|
||||||
|
switch (status) {
|
||||||
|
case Status::kSuccess:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kSuccess;
|
||||||
|
case Status::kError:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kError;
|
||||||
|
case Status::kOutOfOrderApiCall:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kOutOfOrderApiCall;
|
||||||
|
case Status::kAlreadyHaveActiveStrategy:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kAlreadyHaveActiveStrategy;
|
||||||
|
case Status::kAlreadyAdvertising:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kAlreadyAdvertising;
|
||||||
|
case Status::kAlreadyDiscovering:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kAlreadyDiscovering;
|
||||||
|
case Status::kAlreadyListening:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kAlreadyListening;
|
||||||
|
case Status::kEndpointIOError:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kEndpointIOError;
|
||||||
|
case Status::kEndpointUnknown:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kEndpointUnknown;
|
||||||
|
case Status::kConnectionRejected:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kConnectionRejected;
|
||||||
|
case Status::kAlreadyConnectedToEndpoint:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kAlreadyConnectedToEndpoint;
|
||||||
|
case Status::kNotConnectedToEndpoint:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kNotConnectedToEndpoint;
|
||||||
|
case Status::kBluetoothError:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kBluetoothError;
|
||||||
|
case Status::kBleError:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kBleError;
|
||||||
|
case Status::kWifiLanError:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kWifiLanError;
|
||||||
|
case Status::kPayloadUnknown:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kPayloadUnknown;
|
||||||
|
case Status::kReset:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kReset;
|
||||||
|
case Status::kTimeout:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kTimeout;
|
||||||
|
case Status::kUnknown:
|
||||||
|
return NearbyConnectionsApi::StatusCode::kUnknown;
|
||||||
|
case Status::kNextValue:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return NearbyConnectionsApi::StatusCode::kUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::AuthenticationStatus ToFacadeAuthenticationStatus(
|
||||||
|
::nearby::AuthenticationStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case ::nearby::AuthenticationStatus::kUnknown:
|
||||||
|
return NearbyConnectionsApi::AuthenticationStatus::kUnknown;
|
||||||
|
case ::nearby::AuthenticationStatus::kSuccess:
|
||||||
|
return NearbyConnectionsApi::AuthenticationStatus::kSuccess;
|
||||||
|
case ::nearby::AuthenticationStatus::kFailure:
|
||||||
|
return NearbyConnectionsApi::AuthenticationStatus::kFailure;
|
||||||
|
}
|
||||||
|
return NearbyConnectionsApi::AuthenticationStatus::kUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::DistanceInfo ToFacadeDistanceInfo(
|
||||||
|
nearby::sharing::DistanceInfo distance_info) {
|
||||||
|
switch (distance_info) {
|
||||||
|
case nearby::sharing::DistanceInfo::kUnknown:
|
||||||
|
return NearbyConnectionsApi::DistanceInfo::kUnknown;
|
||||||
|
case nearby::sharing::DistanceInfo::kVeryClose:
|
||||||
|
return NearbyConnectionsApi::DistanceInfo::kVeryClose;
|
||||||
|
case nearby::sharing::DistanceInfo::kClose:
|
||||||
|
return NearbyConnectionsApi::DistanceInfo::kClose;
|
||||||
|
case nearby::sharing::DistanceInfo::kFar:
|
||||||
|
return NearbyConnectionsApi::DistanceInfo::kFar;
|
||||||
|
}
|
||||||
|
return NearbyConnectionsApi::DistanceInfo::kUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::Medium ToFacadeMedium(nearby::sharing::Medium medium) {
|
||||||
|
switch (medium) {
|
||||||
|
case nearby::sharing::Medium::kUnknown:
|
||||||
|
return NearbyConnectionsApi::Medium::kUnknown;
|
||||||
|
case nearby::sharing::Medium::kMdns:
|
||||||
|
return NearbyConnectionsApi::Medium::kMdns;
|
||||||
|
case nearby::sharing::Medium::kBluetooth:
|
||||||
|
return NearbyConnectionsApi::Medium::kBluetooth;
|
||||||
|
case nearby::sharing::Medium::kWifiHotspot:
|
||||||
|
return NearbyConnectionsApi::Medium::kWifiHotspot;
|
||||||
|
case nearby::sharing::Medium::kBle:
|
||||||
|
return NearbyConnectionsApi::Medium::kBle;
|
||||||
|
case nearby::sharing::Medium::kWifiLan:
|
||||||
|
return NearbyConnectionsApi::Medium::kWifiLan;
|
||||||
|
case nearby::sharing::Medium::kWifiAware:
|
||||||
|
return NearbyConnectionsApi::Medium::kWifiAware;
|
||||||
|
case nearby::sharing::Medium::kNfc:
|
||||||
|
return NearbyConnectionsApi::Medium::kNfc;
|
||||||
|
case nearby::sharing::Medium::kWifiDirect:
|
||||||
|
return NearbyConnectionsApi::Medium::kWifiDirect;
|
||||||
|
case nearby::sharing::Medium::kWebRtc:
|
||||||
|
return NearbyConnectionsApi::Medium::kWebRtc;
|
||||||
|
case nearby::sharing::Medium::kBleL2Cap:
|
||||||
|
return NearbyConnectionsApi::Medium::kBleL2Cap;
|
||||||
|
}
|
||||||
|
return NearbyConnectionsApi::Medium::kUnknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearby::sharing::Strategy ToNativeStrategy(
|
||||||
|
NearbyConnectionsApi::Strategy strategy) {
|
||||||
|
switch (strategy) {
|
||||||
|
case NearbyConnectionsApi::Strategy::kP2pCluster:
|
||||||
|
return nearby::sharing::Strategy::kP2pCluster;
|
||||||
|
case NearbyConnectionsApi::Strategy::kP2pStar:
|
||||||
|
return nearby::sharing::Strategy::kP2pStar;
|
||||||
|
case NearbyConnectionsApi::Strategy::kP2pPointToPoint:
|
||||||
|
return nearby::sharing::Strategy::kP2pPointToPoint;
|
||||||
|
}
|
||||||
|
return nearby::sharing::Strategy::kP2pCluster;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearby::sharing::MediumSelection ToNativeMediumSelection(
|
||||||
|
const NearbyConnectionsApi::MediumSelection& selection) {
|
||||||
|
nearby::sharing::MediumSelection native_selection;
|
||||||
|
native_selection.bluetooth = selection.bluetooth;
|
||||||
|
native_selection.ble = selection.ble;
|
||||||
|
native_selection.web_rtc = selection.web_rtc;
|
||||||
|
native_selection.wifi_lan = selection.wifi_lan;
|
||||||
|
native_selection.wifi_hotspot = selection.wifi_hotspot;
|
||||||
|
return native_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearby::sharing::AdvertisingOptions ToNativeAdvertisingOptions(
|
||||||
|
const NearbyConnectionsApi::AdvertisingOptions& options) {
|
||||||
|
nearby::sharing::AdvertisingOptions native_options;
|
||||||
|
native_options.strategy = ToNativeStrategy(options.strategy);
|
||||||
|
native_options.allowed_mediums =
|
||||||
|
ToNativeMediumSelection(options.allowed_mediums);
|
||||||
|
native_options.auto_upgrade_bandwidth = options.auto_upgrade_bandwidth;
|
||||||
|
native_options.enforce_topology_constraints =
|
||||||
|
options.enforce_topology_constraints;
|
||||||
|
native_options.enable_bluetooth_listening =
|
||||||
|
options.enable_bluetooth_listening;
|
||||||
|
native_options.enable_webrtc_listening = options.enable_webrtc_listening;
|
||||||
|
native_options.use_stable_endpoint_id = options.use_stable_endpoint_id;
|
||||||
|
native_options.force_new_endpoint_id = options.force_new_endpoint_id;
|
||||||
|
native_options.fast_advertisement_service_uuid =
|
||||||
|
nearby::sharing::Uuid(options.fast_advertisement_service_uuid);
|
||||||
|
return native_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearby::sharing::DiscoveryOptions ToNativeDiscoveryOptions(
|
||||||
|
const NearbyConnectionsApi::DiscoveryOptions& options) {
|
||||||
|
nearby::sharing::DiscoveryOptions native_options;
|
||||||
|
native_options.strategy = ToNativeStrategy(options.strategy);
|
||||||
|
native_options.allowed_mediums =
|
||||||
|
ToNativeMediumSelection(options.allowed_mediums);
|
||||||
|
if (options.has_fast_advertisement_service_uuid) {
|
||||||
|
native_options.fast_advertisement_service_uuid =
|
||||||
|
nearby::sharing::Uuid(options.fast_advertisement_service_uuid.uuid);
|
||||||
|
}
|
||||||
|
native_options.is_out_of_band_connection =
|
||||||
|
options.is_out_of_band_connection;
|
||||||
|
if (options.has_alternate_service_uuid) {
|
||||||
|
native_options.alternate_service_uuid = options.alternate_service_uuid;
|
||||||
|
}
|
||||||
|
return native_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
nearby::sharing::ConnectionOptions ToNativeConnectionOptions(
|
||||||
|
const NearbyConnectionsApi::ConnectionOptions& options) {
|
||||||
|
nearby::sharing::ConnectionOptions native_options;
|
||||||
|
native_options.allowed_mediums =
|
||||||
|
ToNativeMediumSelection(options.allowed_mediums);
|
||||||
|
if (!options.remote_bluetooth_mac_address.empty()) {
|
||||||
|
native_options.remote_bluetooth_mac_address =
|
||||||
|
options.remote_bluetooth_mac_address;
|
||||||
|
}
|
||||||
|
if (options.has_keep_alive_interval_millis &&
|
||||||
|
options.keep_alive_interval_millis >= 0) {
|
||||||
|
native_options.keep_alive_interval =
|
||||||
|
absl::Milliseconds(options.keep_alive_interval_millis);
|
||||||
|
}
|
||||||
|
if (options.has_keep_alive_timeout_millis &&
|
||||||
|
options.keep_alive_timeout_millis >= 0) {
|
||||||
|
native_options.keep_alive_timeout =
|
||||||
|
absl::Milliseconds(options.keep_alive_timeout_millis);
|
||||||
|
}
|
||||||
|
native_options.non_disruptive_hotspot_mode =
|
||||||
|
options.non_disruptive_hotspot_mode;
|
||||||
|
return native_options;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::ConnectionInfo ToFacadeConnectionInfo(
|
||||||
|
const nearby::sharing::ConnectionInfo& info) {
|
||||||
|
NearbyConnectionsApi::ConnectionInfo facade_info;
|
||||||
|
facade_info.authentication_token = info.authentication_token;
|
||||||
|
facade_info.raw_authentication_token = info.raw_authentication_token;
|
||||||
|
facade_info.endpoint_info = info.endpoint_info;
|
||||||
|
facade_info.is_incoming_connection = info.is_incoming_connection;
|
||||||
|
facade_info.connection_layer_status =
|
||||||
|
ToFacadeStatus(info.connection_layer_status);
|
||||||
|
facade_info.authentication_status =
|
||||||
|
ToFacadeAuthenticationStatus(info.authentication_status);
|
||||||
|
return facade_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::DiscoveredEndpointInfo ToFacadeDiscoveredEndpointInfo(
|
||||||
|
const nearby::sharing::DiscoveredEndpointInfo& info) {
|
||||||
|
NearbyConnectionsApi::DiscoveredEndpointInfo facade_info;
|
||||||
|
facade_info.endpoint_info = info.endpoint_info;
|
||||||
|
facade_info.service_id = info.service_id;
|
||||||
|
return facade_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::PayloadStatus ToFacadePayloadStatus(
|
||||||
|
nearby::sharing::PayloadStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case nearby::sharing::PayloadStatus::kSuccess:
|
||||||
|
return NearbyConnectionsApi::PayloadStatus::kSuccess;
|
||||||
|
case nearby::sharing::PayloadStatus::kFailure:
|
||||||
|
return NearbyConnectionsApi::PayloadStatus::kFailure;
|
||||||
|
case nearby::sharing::PayloadStatus::kInProgress:
|
||||||
|
return NearbyConnectionsApi::PayloadStatus::kInProgress;
|
||||||
|
case nearby::sharing::PayloadStatus::kCanceled:
|
||||||
|
return NearbyConnectionsApi::PayloadStatus::kCanceled;
|
||||||
|
}
|
||||||
|
return NearbyConnectionsApi::PayloadStatus::kFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::Payload ToFacadePayload(
|
||||||
|
const nearby::sharing::Payload& payload) {
|
||||||
|
NearbyConnectionsApi::Payload facade_payload;
|
||||||
|
facade_payload.id = payload.id;
|
||||||
|
switch (payload.content.type) {
|
||||||
|
case nearby::sharing::PayloadContent::Type::kBytes:
|
||||||
|
facade_payload.type = NearbyConnectionsApi::PayloadType::kBytes;
|
||||||
|
facade_payload.bytes = payload.content.bytes_payload.bytes;
|
||||||
|
break;
|
||||||
|
case nearby::sharing::PayloadContent::Type::kStream:
|
||||||
|
facade_payload.type = NearbyConnectionsApi::PayloadType::kStream;
|
||||||
|
facade_payload.stream_bytes = payload.content.stream_payload.bytes;
|
||||||
|
break;
|
||||||
|
case nearby::sharing::PayloadContent::Type::kFile:
|
||||||
|
facade_payload.type = NearbyConnectionsApi::PayloadType::kFile;
|
||||||
|
facade_payload.file_path =
|
||||||
|
payload.content.file_payload.file_path.ToString();
|
||||||
|
facade_payload.parent_folder = payload.content.file_payload.parent_folder;
|
||||||
|
break;
|
||||||
|
case nearby::sharing::PayloadContent::Type::kUnknown:
|
||||||
|
facade_payload.type = NearbyConnectionsApi::PayloadType::kUnknown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return facade_payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<nearby::sharing::Payload> ToNativePayload(
|
||||||
|
NearbyConnectionsApi::Payload payload) {
|
||||||
|
switch (payload.type) {
|
||||||
|
case NearbyConnectionsApi::PayloadType::kBytes:
|
||||||
|
return std::make_unique<nearby::sharing::Payload>(
|
||||||
|
payload.id, std::move(payload.bytes));
|
||||||
|
case NearbyConnectionsApi::PayloadType::kFile:
|
||||||
|
return std::make_unique<nearby::sharing::Payload>(
|
||||||
|
payload.id, FilePath(payload.file_path), payload.parent_folder);
|
||||||
|
case NearbyConnectionsApi::PayloadType::kStream: {
|
||||||
|
nearby::sharing::StreamPayload stream_payload;
|
||||||
|
stream_payload.bytes = std::move(payload.stream_bytes);
|
||||||
|
stream_payload.input_stream = std::move(payload.stream_input);
|
||||||
|
return std::make_unique<nearby::sharing::Payload>(payload.id,
|
||||||
|
std::move(stream_payload));
|
||||||
|
}
|
||||||
|
case NearbyConnectionsApi::PayloadType::kUnknown:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::PayloadTransferUpdate ToFacadePayloadTransferUpdate(
|
||||||
|
const nearby::sharing::PayloadTransferUpdate& update) {
|
||||||
|
NearbyConnectionsApi::PayloadTransferUpdate facade_update;
|
||||||
|
facade_update.payload_id = update.payload_id;
|
||||||
|
facade_update.status = ToFacadePayloadStatus(update.status);
|
||||||
|
facade_update.total_bytes = update.total_bytes;
|
||||||
|
facade_update.bytes_transferred = update.bytes_transferred;
|
||||||
|
return facade_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class NearbyConnectionsApi::Impl {
|
||||||
|
public:
|
||||||
|
struct ListenerState {
|
||||||
|
std::mutex mutex;
|
||||||
|
NearbyConnectionsApi::Listener listener;
|
||||||
|
};
|
||||||
|
|
||||||
|
Impl()
|
||||||
|
: platform(),
|
||||||
|
context(platform),
|
||||||
|
service(std::make_unique<NearbyConnectionsServiceImpl>(
|
||||||
|
context.GetConnectivityManager(), /*event_logger=*/nullptr)),
|
||||||
|
listener_state(std::make_shared<ListenerState>()) {}
|
||||||
|
|
||||||
|
static NearbyConnectionsApi::Listener CopyListener(
|
||||||
|
const std::shared_ptr<ListenerState>& listener_state) {
|
||||||
|
std::scoped_lock lock(listener_state->mutex);
|
||||||
|
return listener_state->listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NativeService::ConnectionListener BuildConnectionListener(
|
||||||
|
const std::shared_ptr<ListenerState>& listener_state) {
|
||||||
|
NativeService::ConnectionListener listener;
|
||||||
|
listener.initiated_cb =
|
||||||
|
[listener_state](const std::string& endpoint_id,
|
||||||
|
const nearby::sharing::ConnectionInfo& info) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (!listener_copy.connection_initiated_cb) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listener_copy.connection_initiated_cb(endpoint_id,
|
||||||
|
ToFacadeConnectionInfo(info));
|
||||||
|
};
|
||||||
|
listener.accepted_cb = [listener_state](const std::string& endpoint_id) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.connection_accepted_cb) {
|
||||||
|
listener_copy.connection_accepted_cb(endpoint_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.rejected_cb = [listener_state](const std::string& endpoint_id,
|
||||||
|
Status status) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.connection_rejected_cb) {
|
||||||
|
listener_copy.connection_rejected_cb(endpoint_id,
|
||||||
|
ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.disconnected_cb = [listener_state](const std::string& endpoint_id) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.disconnected_cb) {
|
||||||
|
listener_copy.disconnected_cb(endpoint_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.bandwidth_changed_cb =
|
||||||
|
[listener_state](const std::string& endpoint_id,
|
||||||
|
nearby::sharing::Medium medium) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.bandwidth_changed_cb) {
|
||||||
|
listener_copy.bandwidth_changed_cb(endpoint_id,
|
||||||
|
ToFacadeMedium(medium));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NativeService::DiscoveryListener BuildDiscoveryListener(
|
||||||
|
const std::shared_ptr<ListenerState>& listener_state) {
|
||||||
|
NativeService::DiscoveryListener listener;
|
||||||
|
listener.endpoint_found_cb =
|
||||||
|
[listener_state](const std::string& endpoint_id,
|
||||||
|
const nearby::sharing::DiscoveredEndpointInfo& info) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.endpoint_found_cb) {
|
||||||
|
listener_copy.endpoint_found_cb(
|
||||||
|
endpoint_id, ToFacadeDiscoveredEndpointInfo(info));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.endpoint_lost_cb = [listener_state](const std::string& endpoint_id) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.endpoint_lost_cb) {
|
||||||
|
listener_copy.endpoint_lost_cb(endpoint_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.endpoint_distance_changed_cb =
|
||||||
|
[listener_state](const std::string& endpoint_id,
|
||||||
|
nearby::sharing::DistanceInfo distance_info) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.endpoint_distance_changed_cb) {
|
||||||
|
listener_copy.endpoint_distance_changed_cb(
|
||||||
|
endpoint_id, ToFacadeDistanceInfo(distance_info));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
static NativeService::PayloadListener BuildPayloadListener(
|
||||||
|
const std::shared_ptr<ListenerState>& listener_state) {
|
||||||
|
NativeService::PayloadListener listener;
|
||||||
|
listener.payload_cb = [listener_state](absl::string_view endpoint_id,
|
||||||
|
nearby::sharing::Payload payload) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.payload_received_cb) {
|
||||||
|
listener_copy.payload_received_cb(std::string(endpoint_id),
|
||||||
|
ToFacadePayload(payload));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
listener.payload_progress_cb =
|
||||||
|
[listener_state](
|
||||||
|
absl::string_view endpoint_id,
|
||||||
|
const nearby::sharing::PayloadTransferUpdate& update) {
|
||||||
|
NearbyConnectionsApi::Listener listener_copy =
|
||||||
|
CopyListener(listener_state);
|
||||||
|
if (listener_copy.payload_transfer_update_cb) {
|
||||||
|
listener_copy.payload_transfer_update_cb(
|
||||||
|
std::string(endpoint_id), ToFacadePayloadTransferUpdate(update));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxSharingPlatform platform;
|
||||||
|
::nearby::ContextImpl context;
|
||||||
|
std::unique_ptr<NativeService> service;
|
||||||
|
std::shared_ptr<ListenerState> listener_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
NearbyConnectionsApi::NearbyConnectionsApi() : impl_(std::make_unique<Impl>()) {}
|
||||||
|
|
||||||
|
NearbyConnectionsApi::~NearbyConnectionsApi() = default;
|
||||||
|
|
||||||
|
NearbyConnectionsApi::NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept =
|
||||||
|
default;
|
||||||
|
|
||||||
|
NearbyConnectionsApi& NearbyConnectionsApi::operator=(
|
||||||
|
NearbyConnectionsApi&&) noexcept = default;
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::SetListener(Listener listener) {
|
||||||
|
std::scoped_lock lock(impl_->listener_state->mutex);
|
||||||
|
impl_->listener_state->listener = std::move(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::StartAdvertising(
|
||||||
|
const std::string& service_id, const std::vector<uint8_t>& endpoint_info,
|
||||||
|
const AdvertisingOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->StartAdvertising(
|
||||||
|
service_id, endpoint_info, ToNativeAdvertisingOptions(options),
|
||||||
|
Impl::BuildConnectionListener(impl_->listener_state),
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::StopAdvertising(
|
||||||
|
const std::string& service_id, std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->StopAdvertising(
|
||||||
|
service_id, [callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::StartDiscovery(
|
||||||
|
const std::string& service_id, const DiscoveryOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->StartDiscovery(
|
||||||
|
service_id, ToNativeDiscoveryOptions(options),
|
||||||
|
Impl::BuildDiscoveryListener(impl_->listener_state),
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::StopDiscovery(
|
||||||
|
const std::string& service_id, std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->StopDiscovery(
|
||||||
|
service_id, [callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::RequestConnection(
|
||||||
|
const std::string& service_id, const std::vector<uint8_t>& endpoint_info,
|
||||||
|
const std::string& endpoint_id, const ConnectionOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->RequestConnection(
|
||||||
|
service_id, endpoint_info, endpoint_id, ToNativeConnectionOptions(options),
|
||||||
|
Impl::BuildConnectionListener(impl_->listener_state),
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::DisconnectFromEndpoint(
|
||||||
|
const std::string& service_id, const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->DisconnectFromEndpoint(
|
||||||
|
service_id, endpoint_id,
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::SendPayload(
|
||||||
|
const std::string& service_id, const std::vector<std::string>& endpoint_ids,
|
||||||
|
Payload payload, std::function<void(StatusCode)> callback) {
|
||||||
|
std::unique_ptr<nearby::sharing::Payload> native_payload =
|
||||||
|
ToNativePayload(std::move(payload));
|
||||||
|
if (native_payload == nullptr) {
|
||||||
|
if (callback) {
|
||||||
|
callback(StatusCode::kError);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_->service->SendPayload(
|
||||||
|
service_id, endpoint_ids, std::move(native_payload),
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::CancelPayload(
|
||||||
|
const std::string& service_id, int64_t payload_id,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->CancelPayload(
|
||||||
|
service_id, payload_id,
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::InitiateBandwidthUpgrade(
|
||||||
|
const std::string& service_id, const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->InitiateBandwidthUpgrade(
|
||||||
|
service_id, endpoint_id,
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::AcceptConnection(
|
||||||
|
const std::string& service_id, const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->AcceptConnection(
|
||||||
|
service_id, endpoint_id, Impl::BuildPayloadListener(impl_->listener_state),
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::StopAllEndpoints(
|
||||||
|
std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->StopAllEndpoints(
|
||||||
|
[callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::SetCustomSavePath(
|
||||||
|
const std::string& path, std::function<void(StatusCode)> callback) {
|
||||||
|
impl_->service->SetCustomSavePath(
|
||||||
|
path, [callback = std::move(callback)](Status status) mutable {
|
||||||
|
if (callback) {
|
||||||
|
callback(ToFacadeStatus(status));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void NearbyConnectionsApi::OverrideSavePath(const std::string& endpoint_id,
|
||||||
|
const std::string& path) {
|
||||||
|
impl_->service->OverrideSavePath(endpoint_id, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string NearbyConnectionsApi::Dump() const { return impl_->service->Dump(); }
|
||||||
|
|
||||||
|
std::string NearbyConnectionsApi::StatusCodeToString(StatusCode status) {
|
||||||
|
switch (status) {
|
||||||
|
case StatusCode::kSuccess:
|
||||||
|
return "Success";
|
||||||
|
case StatusCode::kError:
|
||||||
|
return "Error";
|
||||||
|
case StatusCode::kOutOfOrderApiCall:
|
||||||
|
return "OutOfOrderApiCall";
|
||||||
|
case StatusCode::kAlreadyHaveActiveStrategy:
|
||||||
|
return "AlreadyHaveActiveStrategy";
|
||||||
|
case StatusCode::kAlreadyAdvertising:
|
||||||
|
return "AlreadyAdvertising";
|
||||||
|
case StatusCode::kAlreadyDiscovering:
|
||||||
|
return "AlreadyDiscovering";
|
||||||
|
case StatusCode::kAlreadyListening:
|
||||||
|
return "AlreadyListening";
|
||||||
|
case StatusCode::kEndpointIOError:
|
||||||
|
return "EndpointIOError";
|
||||||
|
case StatusCode::kEndpointUnknown:
|
||||||
|
return "EndpointUnknown";
|
||||||
|
case StatusCode::kConnectionRejected:
|
||||||
|
return "ConnectionRejected";
|
||||||
|
case StatusCode::kAlreadyConnectedToEndpoint:
|
||||||
|
return "AlreadyConnectedToEndpoint";
|
||||||
|
case StatusCode::kNotConnectedToEndpoint:
|
||||||
|
return "NotConnectedToEndpoint";
|
||||||
|
case StatusCode::kBluetoothError:
|
||||||
|
return "BluetoothError";
|
||||||
|
case StatusCode::kBleError:
|
||||||
|
return "BleError";
|
||||||
|
case StatusCode::kWifiLanError:
|
||||||
|
return "WifiLanError";
|
||||||
|
case StatusCode::kPayloadUnknown:
|
||||||
|
return "PayloadUnknown";
|
||||||
|
case StatusCode::kReset:
|
||||||
|
return "Reset";
|
||||||
|
case StatusCode::kTimeout:
|
||||||
|
return "Timeout";
|
||||||
|
case StatusCode::kUnknown:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace nearby::sharing
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
// Copyright 2026
|
||||||
|
//
|
||||||
|
// Thin app-facing API for NearbyConnectionsService on Linux that avoids
|
||||||
|
// exposing internal Nearby headers to external consumers.
|
||||||
|
|
||||||
|
#ifndef SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
|
||||||
|
#define SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "internal/platform/input_stream.h"
|
||||||
|
|
||||||
|
namespace nearby {
|
||||||
|
namespace sharing {
|
||||||
|
|
||||||
|
class __attribute__((visibility("default"))) NearbyConnectionsApi {
|
||||||
|
public:
|
||||||
|
enum class StatusCode {
|
||||||
|
kSuccess = 0,
|
||||||
|
kError = 1,
|
||||||
|
kOutOfOrderApiCall = 2,
|
||||||
|
kAlreadyHaveActiveStrategy = 3,
|
||||||
|
kAlreadyAdvertising = 4,
|
||||||
|
kAlreadyDiscovering = 5,
|
||||||
|
kAlreadyListening = 6,
|
||||||
|
kEndpointIOError = 7,
|
||||||
|
kEndpointUnknown = 8,
|
||||||
|
kConnectionRejected = 9,
|
||||||
|
kAlreadyConnectedToEndpoint = 10,
|
||||||
|
kNotConnectedToEndpoint = 11,
|
||||||
|
kBluetoothError = 12,
|
||||||
|
kBleError = 13,
|
||||||
|
kWifiLanError = 14,
|
||||||
|
kPayloadUnknown = 15,
|
||||||
|
kReset = 16,
|
||||||
|
kTimeout = 17,
|
||||||
|
kUnknown = 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Strategy {
|
||||||
|
kP2pCluster = 0,
|
||||||
|
kP2pStar = 1,
|
||||||
|
kP2pPointToPoint = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Medium {
|
||||||
|
kUnknown = 0,
|
||||||
|
kMdns = 1,
|
||||||
|
kBluetooth = 2,
|
||||||
|
kWifiHotspot = 3,
|
||||||
|
kBle = 4,
|
||||||
|
kWifiLan = 5,
|
||||||
|
kWifiAware = 6,
|
||||||
|
kNfc = 7,
|
||||||
|
kWifiDirect = 8,
|
||||||
|
kWebRtc = 9,
|
||||||
|
kBleL2Cap = 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DistanceInfo {
|
||||||
|
kUnknown = 1,
|
||||||
|
kVeryClose = 2,
|
||||||
|
kClose = 3,
|
||||||
|
kFar = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AuthenticationStatus {
|
||||||
|
kUnknown = 0,
|
||||||
|
kSuccess = 1,
|
||||||
|
kFailure = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PayloadType {
|
||||||
|
kUnknown = 0,
|
||||||
|
kBytes = 1,
|
||||||
|
kFile = 2,
|
||||||
|
kStream = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PayloadStatus {
|
||||||
|
kSuccess = 0,
|
||||||
|
kFailure = 1,
|
||||||
|
kInProgress = 2,
|
||||||
|
kCanceled = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uuid {
|
||||||
|
std::string uuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediumSelection {
|
||||||
|
bool bluetooth = true;
|
||||||
|
bool ble = true;
|
||||||
|
bool web_rtc = true;
|
||||||
|
bool wifi_lan = true;
|
||||||
|
bool wifi_hotspot = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AdvertisingOptions {
|
||||||
|
Strategy strategy = Strategy::kP2pCluster;
|
||||||
|
MediumSelection allowed_mediums;
|
||||||
|
bool auto_upgrade_bandwidth = true;
|
||||||
|
bool enforce_topology_constraints = true;
|
||||||
|
bool enable_bluetooth_listening = false;
|
||||||
|
bool enable_webrtc_listening = false;
|
||||||
|
bool use_stable_endpoint_id = false;
|
||||||
|
bool force_new_endpoint_id = false;
|
||||||
|
std::string fast_advertisement_service_uuid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DiscoveryOptions {
|
||||||
|
Strategy strategy = Strategy::kP2pCluster;
|
||||||
|
MediumSelection allowed_mediums;
|
||||||
|
bool has_fast_advertisement_service_uuid = false;
|
||||||
|
Uuid fast_advertisement_service_uuid;
|
||||||
|
bool is_out_of_band_connection = false;
|
||||||
|
bool has_alternate_service_uuid = false;
|
||||||
|
uint16_t alternate_service_uuid = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectionOptions {
|
||||||
|
MediumSelection allowed_mediums;
|
||||||
|
std::vector<uint8_t> remote_bluetooth_mac_address;
|
||||||
|
bool has_keep_alive_interval_millis = false;
|
||||||
|
int64_t keep_alive_interval_millis = 0;
|
||||||
|
bool has_keep_alive_timeout_millis = false;
|
||||||
|
int64_t keep_alive_timeout_millis = 0;
|
||||||
|
bool non_disruptive_hotspot_mode = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConnectionInfo {
|
||||||
|
std::string authentication_token;
|
||||||
|
std::vector<uint8_t> raw_authentication_token;
|
||||||
|
std::vector<uint8_t> endpoint_info;
|
||||||
|
bool is_incoming_connection = false;
|
||||||
|
StatusCode connection_layer_status = StatusCode::kUnknown;
|
||||||
|
AuthenticationStatus authentication_status =
|
||||||
|
AuthenticationStatus::kUnknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DiscoveredEndpointInfo {
|
||||||
|
std::vector<uint8_t> endpoint_info;
|
||||||
|
std::string service_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PayloadTransferUpdate {
|
||||||
|
int64_t payload_id = 0;
|
||||||
|
PayloadStatus status = PayloadStatus::kInProgress;
|
||||||
|
uint64_t total_bytes = 0;
|
||||||
|
uint64_t bytes_transferred = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Payload {
|
||||||
|
int64_t id = 0;
|
||||||
|
PayloadType type = PayloadType::kUnknown;
|
||||||
|
std::vector<uint8_t> bytes;
|
||||||
|
std::vector<uint8_t> stream_bytes;
|
||||||
|
std::shared_ptr<nearby::InputStream> stream_input;
|
||||||
|
std::string file_path;
|
||||||
|
std::string parent_folder;
|
||||||
|
|
||||||
|
static Payload FromBytes(int64_t id, std::vector<uint8_t> bytes) {
|
||||||
|
Payload payload;
|
||||||
|
payload.id = id;
|
||||||
|
payload.type = PayloadType::kBytes;
|
||||||
|
payload.bytes = std::move(bytes);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Payload FromFile(int64_t id, std::string file_path,
|
||||||
|
std::string parent_folder = {}) {
|
||||||
|
Payload payload;
|
||||||
|
payload.id = id;
|
||||||
|
payload.type = PayloadType::kFile;
|
||||||
|
payload.file_path = std::move(file_path);
|
||||||
|
payload.parent_folder = std::move(parent_folder);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Payload FromStream(int64_t id, std::vector<uint8_t> stream_bytes) {
|
||||||
|
Payload payload;
|
||||||
|
payload.id = id;
|
||||||
|
payload.type = PayloadType::kStream;
|
||||||
|
payload.stream_bytes = std::move(stream_bytes);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Payload FromInputStream(int64_t id,
|
||||||
|
std::shared_ptr<nearby::InputStream> stream_input) {
|
||||||
|
Payload payload;
|
||||||
|
payload.id = id;
|
||||||
|
payload.type = PayloadType::kStream;
|
||||||
|
payload.stream_input = std::move(stream_input);
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Listener {
|
||||||
|
std::function<void(const std::string&, const DiscoveredEndpointInfo&)>
|
||||||
|
endpoint_found_cb;
|
||||||
|
std::function<void(const std::string&)> endpoint_lost_cb;
|
||||||
|
std::function<void(const std::string&, DistanceInfo)>
|
||||||
|
endpoint_distance_changed_cb;
|
||||||
|
|
||||||
|
std::function<void(const std::string&, const ConnectionInfo&)>
|
||||||
|
connection_initiated_cb;
|
||||||
|
std::function<void(const std::string&)> connection_accepted_cb;
|
||||||
|
std::function<void(const std::string&, StatusCode)> connection_rejected_cb;
|
||||||
|
std::function<void(const std::string&)> disconnected_cb;
|
||||||
|
std::function<void(const std::string&, Medium)> bandwidth_changed_cb;
|
||||||
|
|
||||||
|
std::function<void(const std::string&, const Payload&)>
|
||||||
|
payload_received_cb;
|
||||||
|
std::function<void(const std::string&, const PayloadTransferUpdate&)>
|
||||||
|
payload_transfer_update_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
NearbyConnectionsApi();
|
||||||
|
~NearbyConnectionsApi();
|
||||||
|
|
||||||
|
NearbyConnectionsApi(const NearbyConnectionsApi&) = delete;
|
||||||
|
NearbyConnectionsApi& operator=(const NearbyConnectionsApi&) = delete;
|
||||||
|
NearbyConnectionsApi(NearbyConnectionsApi&&) noexcept;
|
||||||
|
NearbyConnectionsApi& operator=(NearbyConnectionsApi&&) noexcept;
|
||||||
|
|
||||||
|
void SetListener(Listener listener);
|
||||||
|
|
||||||
|
void StartAdvertising(const std::string& service_id,
|
||||||
|
const std::vector<uint8_t>& endpoint_info,
|
||||||
|
const AdvertisingOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
void StopAdvertising(const std::string& service_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void StartDiscovery(const std::string& service_id,
|
||||||
|
const DiscoveryOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
void StopDiscovery(const std::string& service_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void RequestConnection(const std::string& service_id,
|
||||||
|
const std::vector<uint8_t>& endpoint_info,
|
||||||
|
const std::string& endpoint_id,
|
||||||
|
const ConnectionOptions& options,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void DisconnectFromEndpoint(const std::string& service_id,
|
||||||
|
const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void SendPayload(const std::string& service_id,
|
||||||
|
const std::vector<std::string>& endpoint_ids,
|
||||||
|
Payload payload,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
void CancelPayload(const std::string& service_id, int64_t payload_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void InitiateBandwidthUpgrade(const std::string& service_id,
|
||||||
|
const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void AcceptConnection(const std::string& service_id,
|
||||||
|
const std::string& endpoint_id,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void StopAllEndpoints(std::function<void(StatusCode)> callback);
|
||||||
|
|
||||||
|
void SetCustomSavePath(const std::string& path,
|
||||||
|
std::function<void(StatusCode)> callback);
|
||||||
|
void OverrideSavePath(const std::string& endpoint_id,
|
||||||
|
const std::string& path);
|
||||||
|
|
||||||
|
std::string Dump() const;
|
||||||
|
|
||||||
|
static std::string StatusCodeToString(StatusCode status);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sharing
|
||||||
|
} // namespace nearby
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_NEARBY_CONNECTIONS_API_H_
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#include "sharing/linux/nearby_connections_api.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "internal/platform/pipe.h"
|
||||||
|
|
||||||
|
namespace nearby::sharing {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(NearbyConnectionsApiTest, PayloadFromBytesSetsFields) {
|
||||||
|
NearbyConnectionsApi::Payload payload =
|
||||||
|
NearbyConnectionsApi::Payload::FromBytes(42, {1, 2, 3});
|
||||||
|
|
||||||
|
EXPECT_EQ(payload.id, 42);
|
||||||
|
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kBytes);
|
||||||
|
EXPECT_EQ(payload.bytes, (std::vector<uint8_t>{1, 2, 3}));
|
||||||
|
EXPECT_TRUE(payload.stream_bytes.empty());
|
||||||
|
EXPECT_TRUE(payload.file_path.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NearbyConnectionsApiTest, PayloadFromFileSetsFields) {
|
||||||
|
NearbyConnectionsApi::Payload payload =
|
||||||
|
NearbyConnectionsApi::Payload::FromFile(7, "/tmp/test.txt", "tmp");
|
||||||
|
|
||||||
|
EXPECT_EQ(payload.id, 7);
|
||||||
|
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kFile);
|
||||||
|
EXPECT_EQ(payload.file_path, "/tmp/test.txt");
|
||||||
|
EXPECT_EQ(payload.parent_folder, "tmp");
|
||||||
|
EXPECT_TRUE(payload.bytes.empty());
|
||||||
|
EXPECT_TRUE(payload.stream_bytes.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NearbyConnectionsApiTest, PayloadFromStreamSetsFields) {
|
||||||
|
NearbyConnectionsApi::Payload payload =
|
||||||
|
NearbyConnectionsApi::Payload::FromStream(9, {4, 5, 6});
|
||||||
|
|
||||||
|
EXPECT_EQ(payload.id, 9);
|
||||||
|
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream);
|
||||||
|
EXPECT_EQ(payload.stream_bytes, (std::vector<uint8_t>{4, 5, 6}));
|
||||||
|
EXPECT_TRUE(payload.bytes.empty());
|
||||||
|
EXPECT_TRUE(payload.file_path.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NearbyConnectionsApiTest, PayloadFromInputStreamSetsFields) {
|
||||||
|
auto [input, output] = CreatePipe();
|
||||||
|
(void)output;
|
||||||
|
|
||||||
|
NearbyConnectionsApi::Payload payload =
|
||||||
|
NearbyConnectionsApi::Payload::FromInputStream(
|
||||||
|
10, std::shared_ptr<nearby::InputStream>(std::move(input)));
|
||||||
|
|
||||||
|
EXPECT_EQ(payload.id, 10);
|
||||||
|
EXPECT_EQ(payload.type, NearbyConnectionsApi::PayloadType::kStream);
|
||||||
|
EXPECT_TRUE(payload.stream_bytes.empty());
|
||||||
|
EXPECT_NE(payload.stream_input, nullptr);
|
||||||
|
EXPECT_TRUE(payload.bytes.empty());
|
||||||
|
EXPECT_TRUE(payload.file_path.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(NearbyConnectionsApiTest, StatusCodeToStringCoversRepresentativeValues) {
|
||||||
|
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
|
||||||
|
NearbyConnectionsApi::StatusCode::kSuccess),
|
||||||
|
"Success");
|
||||||
|
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
|
||||||
|
NearbyConnectionsApi::StatusCode::kConnectionRejected),
|
||||||
|
"ConnectionRejected");
|
||||||
|
EXPECT_EQ(NearbyConnectionsApi::StatusCodeToString(
|
||||||
|
NearbyConnectionsApi::StatusCode::kUnknown),
|
||||||
|
"Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace nearby::sharing
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "sharing/analytics/analytics_recorder.h"
|
#include "sharing/analytics/analytics_recorder.h"
|
||||||
#include "sharing/attachment_container.h"
|
#include "sharing/attachment_container.h"
|
||||||
#include "sharing/file_attachment.h"
|
#include "sharing/file_attachment.h"
|
||||||
|
#include "sharing/flags/generated/nearby_sharing_feature_flags.h"
|
||||||
#include "sharing/linux/platform/linux_sharing_platform.h"
|
#include "sharing/linux/platform/linux_sharing_platform.h"
|
||||||
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
|
#include "sharing/local_device_data/nearby_share_local_device_data_manager.h"
|
||||||
#include "sharing/nearby_sharing_service_factory.h"
|
#include "sharing/nearby_sharing_service_factory.h"
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
#include "sharing/transfer_metadata.h"
|
#include "sharing/transfer_metadata.h"
|
||||||
#include "sharing/transfer_update_callback.h"
|
#include "sharing/transfer_update_callback.h"
|
||||||
|
|
||||||
namespace nearby::sharing::linux {
|
namespace nearby::sharing {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -39,6 +40,10 @@ void EnableBleL2capDefaults() {
|
|||||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||||
kEnableBleL2cap,
|
kEnableBleL2cap,
|
||||||
true);
|
true);
|
||||||
|
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||||
|
nearby::sharing::config_package_nearby::nearby_sharing_feature::
|
||||||
|
kEnableBleForTransfer,
|
||||||
|
true);
|
||||||
//nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
//nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||||
// nearby::connections::config_package_nearby::nearby_connections_feature::
|
// nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||||
// kRefactorBleL2cap,
|
// kRefactorBleL2cap,
|
||||||
@@ -125,6 +130,16 @@ NearbySharingApi::TextAttachmentType ToFacadeTextAttachmentType(
|
|||||||
return FacadeType::kUnknown;
|
return FacadeType::kUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float NormalizeFacadeProgress(float progress) {
|
||||||
|
if (progress <= 0.0f) {
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
if (progress >= 100.0f) {
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
return progress / 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
std::string GenerateQrCodeUrl() {
|
std::string GenerateQrCodeUrl() {
|
||||||
auto ec_key = nearby::crypto::ECPrivateKey::Create();
|
auto ec_key = nearby::crypto::ECPrivateKey::Create();
|
||||||
if (!ec_key) {
|
if (!ec_key) {
|
||||||
@@ -266,7 +281,7 @@ class NearbySharingApi::Impl : public nearby::sharing::ShareTargetDiscoveredCall
|
|||||||
info.device_name = share_target.device_name;
|
info.device_name = share_target.device_name;
|
||||||
info.is_incoming = share_target.is_incoming;
|
info.is_incoming = share_target.is_incoming;
|
||||||
info.status = ToFacadeTransferStatus(transfer_metadata.status());
|
info.status = ToFacadeTransferStatus(transfer_metadata.status());
|
||||||
info.progress = transfer_metadata.progress();
|
info.progress = NormalizeFacadeProgress(transfer_metadata.progress());
|
||||||
info.transferred_bytes = transfer_metadata.transferred_bytes();
|
info.transferred_bytes = transfer_metadata.transferred_bytes();
|
||||||
info.total_attachments = transfer_metadata.total_attachments_count();
|
info.total_attachments = transfer_metadata.total_attachments_count();
|
||||||
info.transferred_attachments = transfer_metadata.transferred_attachments_count();
|
info.transferred_attachments = transfer_metadata.transferred_attachments_count();
|
||||||
@@ -657,4 +672,4 @@ std::string NearbySharingApi::TransferStatusToString(TransferStatus status) {
|
|||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nearby::sharing::linux
|
} // namespace nearby::sharing
|
||||||
|
|||||||
@@ -13,15 +13,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Some toolchains define `linux` as a macro (e.g. `#define linux 1`), which
|
|
||||||
// breaks namespace tokens like `nearby::sharing::linux`.
|
|
||||||
#ifdef linux
|
|
||||||
#undef linux
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace nearby {
|
namespace nearby {
|
||||||
namespace sharing {
|
namespace sharing {
|
||||||
namespace linux {
|
|
||||||
|
|
||||||
class __attribute__((visibility("default"))) NearbySharingApi {
|
class __attribute__((visibility("default"))) NearbySharingApi {
|
||||||
public:
|
public:
|
||||||
@@ -131,7 +124,6 @@ class __attribute__((visibility("default"))) NearbySharingApi {
|
|||||||
std::unique_ptr<Impl> impl_;
|
std::unique_ptr<Impl> impl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace linux
|
|
||||||
} // namespace sharing
|
} // namespace sharing
|
||||||
} // namespace nearby
|
} // namespace nearby
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
#include "sharing/internal/public/pref_names.h"
|
#include "sharing/internal/public/pref_names.h"
|
||||||
#include "sharing/proto/rpc_resources.pb.h"
|
#include "sharing/proto/rpc_resources.pb.h"
|
||||||
|
|
||||||
namespace nearby::sharing::linux {
|
namespace nearby::sharing {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
using Json = nlohmann::json;
|
using Json = nlohmann::json;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#include "internal/platform/task_runner.h"
|
#include "internal/platform/task_runner.h"
|
||||||
#include "sharing/internal/api/sharing_platform.h"
|
#include "sharing/internal/api/sharing_platform.h"
|
||||||
|
|
||||||
namespace nearby::sharing::linux {
|
namespace nearby::sharing {
|
||||||
|
|
||||||
class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform {
|
class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ qt_add_executable(nearby_qml_file_tray_app
|
|||||||
file_share_tray_main.cpp
|
file_share_tray_main.cpp
|
||||||
file_share_tray_controller.cc
|
file_share_tray_controller.cc
|
||||||
file_share_tray_controller.h
|
file_share_tray_controller.h
|
||||||
|
file_share_state.cc
|
||||||
|
file_share_state.h
|
||||||
|
share_target_model.h
|
||||||
|
transfer_model.h
|
||||||
|
string_utils.cc
|
||||||
|
string_utils.h
|
||||||
|
status_mapper.cc
|
||||||
|
status_mapper.h
|
||||||
|
qr_code_generator.cc
|
||||||
|
qr_code_generator.h
|
||||||
notification_manager.cpp
|
notification_manager.cpp
|
||||||
notification_manager.h
|
notification_manager.h
|
||||||
third_party/libqrencode/qrencode_compat.h
|
third_party/libqrencode/qrencode_compat.h
|
||||||
|
|||||||
@@ -51,18 +51,16 @@ ApplicationWindow {
|
|||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0
|
readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0
|
||||||
readonly property bool isIdle: fileShareController.discoveredTargets.length === 0
|
|
||||||
&& fileShareController.transfers.length === 0
|
|
||||||
|
|
||||||
// ── Idle: animated blob ───────────────────────────────────
|
// ── Idle: animated blob ───────────────────────────────────
|
||||||
AnimatedBlob { visible: mainContent.isIdle }
|
AnimatedBlob { visible: !mainContent.isSendMode }
|
||||||
|
|
||||||
// ── Non-idle: scrollable device + transfer cards ──────────
|
// ── Non-idle: scrollable device + transfer cards ──────────
|
||||||
Flickable {
|
Flickable {
|
||||||
id: mainFlickable
|
id: mainFlickable
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
clip: true
|
clip: true
|
||||||
visible: !mainContent.isIdle
|
visible: mainContent.isSendMode
|
||||||
contentWidth: width
|
contentWidth: width
|
||||||
contentHeight: mainCol.implicitHeight + 96
|
contentHeight: mainCol.implicitHeight + 96
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
@@ -77,7 +75,6 @@ ApplicationWindow {
|
|||||||
SendUrlPanel {
|
SendUrlPanel {
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
width: Math.max(240, Math.min(mainCol.width, 420))
|
width: Math.max(240, Math.min(mainCol.width, 420))
|
||||||
visible: mainContent.isSendMode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ if(EXISTS "${_output_so}")
|
|||||||
ERROR_QUIET
|
ERROR_QUIET
|
||||||
)
|
)
|
||||||
if(_nm_result EQUAL 0)
|
if(_nm_result EQUAL 0)
|
||||||
string(FIND "${_nm_output}" "nearby::sharing::linux::NearbySharingApi::NearbySharingApi()" _api_ctor_idx)
|
string(FIND "${_nm_output}" "nearby::sharing::NearbySharingApi::NearbySharingApi()" _api_ctor_idx)
|
||||||
string(FIND "${_nm_output}" " U nearby::api::ImplementationPlatform::CreateScheduledExecutor()" _undef_platform_idx)
|
string(FIND "${_nm_output}" " U nearby::api::ImplementationPlatform::CreateScheduledExecutor()" _undef_platform_idx)
|
||||||
string(FIND "${_nm_output}" " U nearby::SystemClock::ElapsedRealtime()" _undef_clock_idx)
|
string(FIND "${_nm_output}" " U nearby::SystemClock::ElapsedRealtime()" _undef_clock_idx)
|
||||||
string(FIND "${_nm_output}" " U nearby::Crypto::Sha256(" _undef_crypto_idx)
|
string(FIND "${_nm_output}" " U nearby::Crypto::Sha256(" _undef_crypto_idx)
|
||||||
|
|||||||
@@ -1,13 +1,116 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
readonly property color textPrimary: "#111827"
|
readonly property color textPrimary: "#111827"
|
||||||
readonly property color textMuted: "#6b7280"
|
readonly property color textMuted: "#6b7280"
|
||||||
|
readonly property color textSoft: "#4b5563"
|
||||||
|
readonly property color cardSurface: "#ffffff"
|
||||||
|
readonly property color cardBorder: "#d1fae5"
|
||||||
readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0
|
readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0
|
||||||
|
readonly property var incomingTransfer: findIncomingTransfer()
|
||||||
|
readonly property var incomingTarget: findTargetForTransfer(incomingTransfer)
|
||||||
|
readonly property bool hasIncomingTransfer: incomingTransfer !== null
|
||||||
|
&& incomingTarget !== null
|
||||||
|
readonly property bool isReceivingActive: hasIncomingTransfer
|
||||||
|
&& String(incomingTransfer.status || "") !== "Complete"
|
||||||
|
readonly property real receivingIntensity: {
|
||||||
|
if (!isReceivingActive)
|
||||||
|
return 0
|
||||||
|
var numeric = Number(incomingTransfer.progress)
|
||||||
|
if (!isFinite(numeric) || numeric < 0)
|
||||||
|
numeric = 0
|
||||||
|
return Math.max(0.35, Math.min(1.0, numeric))
|
||||||
|
}
|
||||||
|
property real blobChaos: receivingIntensity
|
||||||
|
property string dismissedTransferKey: ""
|
||||||
|
|
||||||
|
Behavior on blobChaos {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 220
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isIncomingTransferActive(status) {
|
||||||
|
return status === "InProgress"
|
||||||
|
|| status === "Queued"
|
||||||
|
|| status === "Connecting"
|
||||||
|
|| status === "AwaitingLocalConfirmation"
|
||||||
|
|| status === "AwaitingRemoteAcceptance"
|
||||||
|
|| status === "Complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferKey(transfer) {
|
||||||
|
if (!transfer)
|
||||||
|
return ""
|
||||||
|
return String(transfer.targetId || "")
|
||||||
|
+ "|" + String(transfer.status || "")
|
||||||
|
+ "|" + String(transfer.filePath || "")
|
||||||
|
+ "|" + String(transfer.fileName || "")
|
||||||
|
}
|
||||||
|
|
||||||
|
function findIncomingTransfer() {
|
||||||
|
var transfers = fileShareController.transfers
|
||||||
|
var latestCompleted = null
|
||||||
|
for (var i = transfers.length - 1; i >= 0; --i) {
|
||||||
|
var entry = transfers[i]
|
||||||
|
if (!entry)
|
||||||
|
continue
|
||||||
|
if (String(entry.direction || "") !== "incoming")
|
||||||
|
continue
|
||||||
|
var status = String(entry.status || "")
|
||||||
|
if (!isIncomingTransferActive(status))
|
||||||
|
continue
|
||||||
|
if (status === "Complete") {
|
||||||
|
if (latestCompleted === null)
|
||||||
|
latestCompleted = entry
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return latestCompleted
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTargetForTransfer(transfer) {
|
||||||
|
if (!transfer)
|
||||||
|
return null
|
||||||
|
var targets = fileShareController.discoveredTargets
|
||||||
|
for (var i = 0; i < targets.length; ++i) {
|
||||||
|
var entry = targets[i]
|
||||||
|
if (entry && entry.id === transfer.targetId)
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: transfer.targetId,
|
||||||
|
name: String(transfer.targetName || "Incoming device"),
|
||||||
|
isIncoming: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function incomingHeadline(status) {
|
||||||
|
if (status === "Complete")
|
||||||
|
return "Received"
|
||||||
|
if (status === "AwaitingLocalConfirmation")
|
||||||
|
return "Incoming transfer"
|
||||||
|
if (status === "AwaitingRemoteAcceptance")
|
||||||
|
return "Preparing transfer"
|
||||||
|
if (status === "Connecting")
|
||||||
|
return "Connecting"
|
||||||
|
return "Receiving"
|
||||||
|
}
|
||||||
|
|
||||||
|
// The card only becomes actionable once the file has landed on disk and we
|
||||||
|
// have a local path to open.
|
||||||
|
function incomingClickReady() {
|
||||||
|
return hasIncomingTransfer
|
||||||
|
&& String(incomingTransfer.status || "") === "Complete"
|
||||||
|
&& String(incomingTransfer.filePath || "").length > 0
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
x: 48; y: 48
|
x: 48; y: 48
|
||||||
@@ -19,6 +122,141 @@ Item {
|
|||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: textPrimary
|
color: textPrimary
|
||||||
}
|
}
|
||||||
|
Canvas {
|
||||||
|
id: blobCanvas3
|
||||||
|
width: 380; height: 380
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !isSendMode
|
||||||
|
|
||||||
|
property real t: 0
|
||||||
|
property double lastMs: Date.now()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 16
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
var now = Date.now()
|
||||||
|
var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas3.lastMs) * 0.001))
|
||||||
|
blobCanvas3.lastMs = now
|
||||||
|
blobCanvas3.t += dt * (Math.PI * 2 / Math.max(4.6, 8.0 - blobChaos * 2.6))
|
||||||
|
blobCanvas3.requestPaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
|
||||||
|
var chaos = blobChaos
|
||||||
|
var cx = width / 2 + Math.sin(t * 0.33) * chaos * 10
|
||||||
|
var cy = height / 2 + Math.cos(t * 0.27) * chaos * 8
|
||||||
|
var n = 10
|
||||||
|
var pts = []
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var a = (i / n) * Math.PI * 2 - Math.PI / 2
|
||||||
|
var r = 150 + chaos * 22
|
||||||
|
+ Math.sin(a * 2 + t) * (11 + chaos * 14)
|
||||||
|
+ Math.cos(a * 3 - t * 0.2) * (8 + chaos * 10)
|
||||||
|
+ Math.sin(a * 1.5 + t * 0.7) * (6 + chaos * 8)
|
||||||
|
+ Math.sin(a * 1.5 + t) * (4 + chaos * 6)
|
||||||
|
+ Math.cos(a * 5 - t * 1.2) * (chaos * 9)
|
||||||
|
+ Math.sin(a * 7 + t * 0.85) * (chaos * 6)
|
||||||
|
pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r })
|
||||||
|
}
|
||||||
|
|
||||||
|
var len = pts.length
|
||||||
|
ctx.beginPath()
|
||||||
|
for (var j = 0; j < len; j++) {
|
||||||
|
var p0 = pts[(j - 1 + len) % len]
|
||||||
|
var p1 = pts[j]
|
||||||
|
var p2 = pts[(j + 1) % len]
|
||||||
|
var p3 = pts[(j + 2) % len]
|
||||||
|
var cp1x = p1.x + (p2.x - p0.x) / 6
|
||||||
|
var cp1y = p1.y + (p2.y - p0.y) / 6
|
||||||
|
var cp2x = p2.x - (p3.x - p1.x) / 6
|
||||||
|
var cp2y = p2.y - (p3.y - p1.y) / 6
|
||||||
|
if (j === 0) ctx.moveTo(p1.x, p1.y)
|
||||||
|
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
|
||||||
|
}
|
||||||
|
ctx.closePath()
|
||||||
|
|
||||||
|
var grad = ctx.createRadialGradient(cx - 40, cy - 40, 0, cx, cy, 150)
|
||||||
|
grad.addColorStop(0, "#e7faed")
|
||||||
|
ctx.fillStyle = grad
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: blobCanvas2
|
||||||
|
width: 380; height: 380
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: !isSendMode
|
||||||
|
|
||||||
|
property real t: 0
|
||||||
|
property double lastMs: Date.now()
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 16
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
var now = Date.now()
|
||||||
|
var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas2.lastMs) * 0.001))
|
||||||
|
blobCanvas2.lastMs = now
|
||||||
|
blobCanvas2.t += dt * (Math.PI * 2 / Math.max(4.9, 8.0 - blobChaos * 2.2))
|
||||||
|
blobCanvas2.requestPaint()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
var ctx = getContext("2d")
|
||||||
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
|
||||||
|
var chaos = blobChaos
|
||||||
|
var cx = width / 2 + Math.cos(t * 0.29) * chaos * 8
|
||||||
|
var cy = height / 2 + Math.sin(t * 0.41) * chaos * 11
|
||||||
|
var n = 10
|
||||||
|
var pts = []
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var a = (i / n) * Math.PI * 2 - Math.PI / 2
|
||||||
|
var r = 140 + chaos * 18
|
||||||
|
+ Math.sin(a * 2 + t) * (11 + chaos * 12)
|
||||||
|
+ Math.cos(a * 3 - t * 0.8) * (8 + chaos * 9)
|
||||||
|
+ Math.sin(a * 1.5 + t * 0.23) * (6 + chaos * 7)
|
||||||
|
+ Math.sin(a * 1.5 + t * 0.85) * (4 + chaos * 6)
|
||||||
|
+ Math.cos(a * 4 + t * 1.1) * (chaos * 7)
|
||||||
|
+ Math.sin(a * 6 - t * 0.95) * (chaos * 5)
|
||||||
|
pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r })
|
||||||
|
}
|
||||||
|
|
||||||
|
var len = pts.length
|
||||||
|
ctx.beginPath()
|
||||||
|
for (var j = 0; j < len; j++) {
|
||||||
|
var p0 = pts[(j - 1 + len) % len]
|
||||||
|
var p1 = pts[j]
|
||||||
|
var p2 = pts[(j + 1) % len]
|
||||||
|
var p3 = pts[(j + 2) % len]
|
||||||
|
var cp1x = p1.x + (p2.x - p0.x) / 6
|
||||||
|
var cp1y = p1.y + (p2.y - p0.y) / 6
|
||||||
|
var cp2x = p2.x - (p3.x - p1.x) / 6
|
||||||
|
var cp2y = p2.y - (p3.y - p1.y) / 6
|
||||||
|
if (j === 0) ctx.moveTo(p1.x, p1.y)
|
||||||
|
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y)
|
||||||
|
}
|
||||||
|
ctx.closePath()
|
||||||
|
|
||||||
|
var grad = ctx.createRadialGradient(cx - 40, cy - 40, 0, cx, cy, 150)
|
||||||
|
grad.addColorStop(0, "#caeada")
|
||||||
|
ctx.fillStyle = grad
|
||||||
|
ctx.fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Canvas {
|
Canvas {
|
||||||
id: blobCanvas
|
id: blobCanvas
|
||||||
@@ -27,15 +265,17 @@ Item {
|
|||||||
visible: !isSendMode
|
visible: !isSendMode
|
||||||
|
|
||||||
property real t: 0
|
property real t: 0
|
||||||
property double startMs: Date.now()
|
property double lastMs: Date.now()
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
interval: 16
|
interval: 16
|
||||||
running: true
|
running: true
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
// seconds -> radians scale (t grows forever, no loop jump)
|
var now = Date.now()
|
||||||
blobCanvas.t = (Date.now() - blobCanvas.startMs) * 0.001 * (Math.PI * 2 / 8.0)
|
var dt = Math.min(0.05, Math.max(0.0, (now - blobCanvas.lastMs) * 0.001))
|
||||||
|
blobCanvas.lastMs = now
|
||||||
|
blobCanvas.t += dt * (Math.PI * 2 / Math.max(4.4, 8.0 - blobChaos * 3.0))
|
||||||
blobCanvas.requestPaint()
|
blobCanvas.requestPaint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,17 +284,21 @@ Item {
|
|||||||
var ctx = getContext("2d")
|
var ctx = getContext("2d")
|
||||||
ctx.clearRect(0, 0, width, height)
|
ctx.clearRect(0, 0, width, height)
|
||||||
|
|
||||||
var cx = width / 2
|
var chaos = blobChaos
|
||||||
var cy = height / 2
|
var cx = width / 2 + Math.sin(t * 0.55) * chaos * 12
|
||||||
|
var cy = height / 2 + Math.cos(t * 0.47) * chaos * 10
|
||||||
var n = 10
|
var n = 10
|
||||||
var pts = []
|
var pts = []
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
var a = (i / n) * Math.PI * 2 - Math.PI / 2
|
var a = (i / n) * Math.PI * 2 - Math.PI / 2
|
||||||
var r = 130
|
var r = 130 + chaos * 15
|
||||||
+ Math.sin(a * 2 + t) * 11
|
+ Math.sin(a * 2 + t) * (11 + chaos * 15)
|
||||||
+ Math.cos(a * 3 - t * 0.6) * 8
|
+ Math.cos(a * 3 - t * 0.6) * (8 + chaos * 12)
|
||||||
+ Math.sin(a * 1.5 + t * 0.35) * 6
|
+ Math.sin(a * 1.5 + t * 0.35) * (6 + chaos * 9)
|
||||||
|
+ Math.sin(a + t * 0.75) * (3 + chaos * 6)
|
||||||
|
+ Math.cos(a * 5 - t * 1.35) * (chaos * 10)
|
||||||
|
+ Math.sin(a * 8 + t * 0.92) * (chaos * 6)
|
||||||
pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r })
|
pts.push({ x: cx + Math.cos(a) * r, y: cy + Math.sin(a) * r })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,12 +325,85 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SendUrlPanel {
|
Rectangle {
|
||||||
|
id: incomingTransferCard
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - 96
|
visible: !isSendMode && hasIncomingTransfer
|
||||||
visible: isSendMode
|
&& transferKey(incomingTransfer) !== dismissedTransferKey
|
||||||
|
width: 200
|
||||||
|
height: 210
|
||||||
|
radius: 34
|
||||||
|
color: cardSurface
|
||||||
|
border.color: cardBorder
|
||||||
|
border.width: 1
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 10
|
||||||
|
radius: parent.radius - 10
|
||||||
|
color: "#ffffff"
|
||||||
|
opacity: 0.84
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 24
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Label {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: incomingHeadline(String(incomingTransfer.status || ""))
|
||||||
|
font.pixelSize: 12
|
||||||
|
font.weight: Font.DemiBold
|
||||||
|
color: "#059669"
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceCard {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
modelData: incomingTarget
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
width: parent.width
|
||||||
|
visible: String(incomingTransfer.fileName || "").length > 0
|
||||||
|
text: String(incomingTransfer.fileName || "")
|
||||||
|
font.pixelSize: 13
|
||||||
|
font.weight: Font.StyleItalic
|
||||||
|
color: textPrimary
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
elide: Text.ElideRight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
enabled: incomingClickReady()
|
||||||
|
cursorShape: enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
|
onClicked: {
|
||||||
|
dismissedTransferKey = transferKey(incomingTransfer)
|
||||||
|
fileShareController.openFileLocation(String(incomingTransfer.filePath || ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onIncomingTransferChanged: {
|
||||||
|
if (!incomingTransfer) {
|
||||||
|
dismissedTransferKey = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transferKey(incomingTransfer) !== dismissedTransferKey)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (String(incomingTransfer.status || "") !== "Complete")
|
||||||
|
dismissedTransferKey = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ Item {
|
|||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
text: targetName
|
text: targetName
|
||||||
font.pixelSize: 13
|
font.pixelSize: 13
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Bold
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
maximumLineCount: 2
|
maximumLineCount: 2
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#include "file_share_state.h"
|
||||||
|
#include "status_mapper.h"
|
||||||
|
|
||||||
|
FileShareState::FileShareState() = default;
|
||||||
|
|
||||||
|
void FileShareState::AddOrUpdateTarget(qlonglong id, const QString& name,
|
||||||
|
bool is_incoming) {
|
||||||
|
target_names_[id] = name;
|
||||||
|
|
||||||
|
if (discovered_row_by_target_.contains(id)) {
|
||||||
|
const int row_index = discovered_row_by_target_.value(id);
|
||||||
|
if (row_index >= 0 && row_index < discovered_targets_.size()) {
|
||||||
|
QVariantMap target;
|
||||||
|
target[QStringLiteral("id")] = id;
|
||||||
|
target[QStringLiteral("name")] = name;
|
||||||
|
target[QStringLiteral("isIncoming")] = is_incoming;
|
||||||
|
discovered_targets_[row_index] = target;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap target;
|
||||||
|
target[QStringLiteral("id")] = id;
|
||||||
|
target[QStringLiteral("name")] = name;
|
||||||
|
target[QStringLiteral("isIncoming")] = is_incoming;
|
||||||
|
discovered_row_by_target_[id] = discovered_targets_.size();
|
||||||
|
discovered_targets_.append(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::RemoveTarget(qlonglong id) {
|
||||||
|
if (HasActiveTransferForTarget(id)) {
|
||||||
|
AddPendingTargetRemoval(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovePendingTargetRemoval(id);
|
||||||
|
target_names_.remove(id);
|
||||||
|
|
||||||
|
if (!discovered_row_by_target_.contains(id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int removed_index = discovered_row_by_target_.take(id);
|
||||||
|
if (removed_index < 0 || removed_index >= discovered_targets_.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
discovered_targets_.removeAt(removed_index);
|
||||||
|
for (auto it = discovered_row_by_target_.begin();
|
||||||
|
it != discovered_row_by_target_.end(); ++it) {
|
||||||
|
if (it.value() > removed_index) {
|
||||||
|
it.value() = it.value() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FileShareState::GetTargetName(qlonglong id) const {
|
||||||
|
const QString name = target_names_.value(id).trimmed();
|
||||||
|
return name.isEmpty() ? QStringLiteral("Unknown device") : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileShareState::HasTarget(qlonglong id) const {
|
||||||
|
return discovered_row_by_target_.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::AddOrUpdateTransfer(
|
||||||
|
qlonglong target_id, const QString& target_name, const QString& status,
|
||||||
|
double progress, qulonglong transferred_bytes, const QString& direction,
|
||||||
|
const QString& file_name, const QString& file_path) {
|
||||||
|
QVariantMap transfer{
|
||||||
|
{QStringLiteral("targetId"), target_id},
|
||||||
|
{QStringLiteral("targetName"), target_name},
|
||||||
|
{QStringLiteral("status"), status},
|
||||||
|
{QStringLiteral("progress"), progress},
|
||||||
|
{QStringLiteral("transferredBytes"), transferred_bytes},
|
||||||
|
{QStringLiteral("direction"), direction},
|
||||||
|
{QStringLiteral("fileName"), file_name},
|
||||||
|
{QStringLiteral("filePath"), file_path},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (transfer_row_by_target_.contains(target_id)) {
|
||||||
|
const int row_index = transfer_row_by_target_.value(target_id);
|
||||||
|
if (row_index >= 0 && row_index < transfers_.size()) {
|
||||||
|
transfers_[row_index] = transfer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transfer_row_by_target_.insert(target_id, transfers_.size());
|
||||||
|
transfers_.append(transfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::RemoveTransfer(qlonglong target_id) {
|
||||||
|
if (!transfer_row_by_target_.contains(target_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int removed_index = transfer_row_by_target_.take(target_id);
|
||||||
|
if (removed_index < 0 || removed_index >= transfers_.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
transfers_.removeAt(removed_index);
|
||||||
|
for (auto it = transfer_row_by_target_.begin();
|
||||||
|
it != transfer_row_by_target_.end(); ++it) {
|
||||||
|
if (it.value() > removed_index) {
|
||||||
|
it.value() = it.value() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileShareState::HasActiveTransferForTarget(qlonglong target_id) const {
|
||||||
|
for (const QVariant& row_value : transfers_) {
|
||||||
|
const QVariantMap row = row_value.toMap();
|
||||||
|
if (row.value(QStringLiteral("targetId")).toLongLong() != target_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString status = row.value(QStringLiteral("status")).toString();
|
||||||
|
if (StatusMapper::IsActiveTransferStatus(status)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileShareState::HasActiveTransfers() const {
|
||||||
|
for (const QVariant& row_value : transfers_) {
|
||||||
|
const QVariantMap row = row_value.toMap();
|
||||||
|
const QString status = row.value(QStringLiteral("status")).toString();
|
||||||
|
if (StatusMapper::IsActiveTransferStatus(status)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::AddPendingTargetRemoval(qlonglong id) {
|
||||||
|
pending_target_removals_.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::RemovePendingTargetRemoval(qlonglong id) {
|
||||||
|
pending_target_removals_.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileShareState::IsPendingTargetRemoval(qlonglong id) const {
|
||||||
|
return pending_target_removals_.contains(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileShareState::ClearAll() {
|
||||||
|
discovered_targets_.clear();
|
||||||
|
discovered_row_by_target_.clear();
|
||||||
|
target_names_.clear();
|
||||||
|
transfers_.clear();
|
||||||
|
transfer_row_by_target_.clear();
|
||||||
|
pending_target_removals_.clear();
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QVariantList>
|
||||||
|
#include <QVariantMap>
|
||||||
|
#include <QSet>
|
||||||
|
#include "share_target_model.h"
|
||||||
|
#include "transfer_model.h"
|
||||||
|
|
||||||
|
class FileShareState {
|
||||||
|
public:
|
||||||
|
FileShareState();
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
QString mode() const { return mode_; }
|
||||||
|
QString deviceName() const { return device_name_; }
|
||||||
|
QString statusMessage() const { return status_message_; }
|
||||||
|
bool running() const { return running_; }
|
||||||
|
bool autoAcceptIncoming() const { return auto_accept_incoming_; }
|
||||||
|
|
||||||
|
QString pendingSendFileName() const { return pending_send_file_name_; }
|
||||||
|
QString pendingSendFilePath() const { return pending_send_file_path_; }
|
||||||
|
qlonglong pendingSendTargetId() const { return pending_send_target_id_; }
|
||||||
|
|
||||||
|
QVariantList discoveredTargets() const { return discovered_targets_; }
|
||||||
|
QVariantList transfers() const { return transfers_; }
|
||||||
|
|
||||||
|
QString qrCodeUrl() const { return qr_code_url_; }
|
||||||
|
QStringList qrCodeRows() const { return qr_code_rows_; }
|
||||||
|
int qrCodeSize() const { return qr_code_size_; }
|
||||||
|
|
||||||
|
QString logPath() const { return log_path_; }
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
void SetMode(const QString& mode) { mode_ = mode; }
|
||||||
|
void SetDeviceName(const QString& name) { device_name_ = name; }
|
||||||
|
void SetStatusMessage(const QString& message) { status_message_ = message; }
|
||||||
|
void SetRunning(bool running) { running_ = running; }
|
||||||
|
void SetAutoAcceptIncoming(bool enabled) { auto_accept_incoming_ = enabled; }
|
||||||
|
|
||||||
|
void SetPendingSendFile(const QString& file_path, const QString& file_name,
|
||||||
|
qlonglong target_id) {
|
||||||
|
pending_send_file_path_ = file_path;
|
||||||
|
pending_send_file_name_ = file_name;
|
||||||
|
pending_send_target_id_ = target_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearPendingSendFile() {
|
||||||
|
pending_send_file_path_.clear();
|
||||||
|
pending_send_file_name_.clear();
|
||||||
|
pending_send_target_id_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetQrCodeData(const QString& url, const QStringList& rows, int size) {
|
||||||
|
qr_code_url_ = url;
|
||||||
|
qr_code_rows_ = rows;
|
||||||
|
qr_code_size_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLogPath(const QString& path) { log_path_ = path; }
|
||||||
|
|
||||||
|
// Target management
|
||||||
|
void AddOrUpdateTarget(qlonglong id, const QString& name, bool is_incoming);
|
||||||
|
void RemoveTarget(qlonglong id);
|
||||||
|
QString GetTargetName(qlonglong id) const;
|
||||||
|
bool HasTarget(qlonglong id) const;
|
||||||
|
|
||||||
|
// Transfer management
|
||||||
|
void AddOrUpdateTransfer(qlonglong target_id, const QString& target_name,
|
||||||
|
const QString& status, double progress,
|
||||||
|
qulonglong transferred_bytes,
|
||||||
|
const QString& direction, const QString& file_name,
|
||||||
|
const QString& file_path);
|
||||||
|
void RemoveTransfer(qlonglong target_id);
|
||||||
|
bool HasActiveTransferForTarget(qlonglong target_id) const;
|
||||||
|
bool HasActiveTransfers() const;
|
||||||
|
|
||||||
|
// Pending target removal management
|
||||||
|
void AddPendingTargetRemoval(qlonglong id);
|
||||||
|
void RemovePendingTargetRemoval(qlonglong id);
|
||||||
|
bool IsPendingTargetRemoval(qlonglong id) const;
|
||||||
|
|
||||||
|
void ClearAll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString mode_ = QStringLiteral("Receive");
|
||||||
|
QString device_name_ = QStringLiteral("NearbyLinux");
|
||||||
|
QString status_message_ = QStringLiteral("Idle");
|
||||||
|
bool running_ = false;
|
||||||
|
bool auto_accept_incoming_ = true;
|
||||||
|
|
||||||
|
// QR Code
|
||||||
|
QString qr_code_url_;
|
||||||
|
QStringList qr_code_rows_;
|
||||||
|
int qr_code_size_ = 0;
|
||||||
|
QString log_path_ = QStringLiteral("/tmp/nearby_qml_file_tray.log");
|
||||||
|
|
||||||
|
// Pending send state
|
||||||
|
QString pending_send_file_path_;
|
||||||
|
QString pending_send_file_name_;
|
||||||
|
qlonglong pending_send_target_id_ = 0;
|
||||||
|
|
||||||
|
// Discovered targets
|
||||||
|
QVariantList discovered_targets_;
|
||||||
|
QHash<qlonglong, int> discovered_row_by_target_;
|
||||||
|
QHash<qlonglong, QString> target_names_;
|
||||||
|
|
||||||
|
// Transfers
|
||||||
|
QVariantList transfers_;
|
||||||
|
QHash<qlonglong, int> transfer_row_by_target_;
|
||||||
|
|
||||||
|
// Pending removals
|
||||||
|
QSet<qlonglong> pending_target_removals_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_STATE_H_
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -2,18 +2,12 @@
|
|||||||
#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
#define SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QString>
|
|
||||||
#include <QStringList>
|
|
||||||
#include <QVariantList>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "sharing/linux/nearby_sharing_api.h"
|
#include "file_share_state.h"
|
||||||
|
#include <sharing/linux/nearby_sharing_api.h>
|
||||||
|
|
||||||
using NearbySharingApi = nearby::sharing::linux::NearbySharingApi;
|
using NearbySharingApi = nearby::sharing::NearbySharingApi;
|
||||||
|
|
||||||
class FileShareTrayController : public QObject {
|
class FileShareTrayController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -35,28 +29,24 @@ class FileShareTrayController : public QObject {
|
|||||||
explicit FileShareTrayController(QObject* parent = nullptr);
|
explicit FileShareTrayController(QObject* parent = nullptr);
|
||||||
~FileShareTrayController() override;
|
~FileShareTrayController() override;
|
||||||
|
|
||||||
QString mode() const { return mode_; }
|
// Property accessors
|
||||||
|
QString mode() const { return state_.mode(); }
|
||||||
|
QString deviceName() const { return state_.deviceName(); }
|
||||||
|
QString statusMessage() const { return state_.statusMessage(); }
|
||||||
|
bool running() const { return state_.running(); }
|
||||||
|
QString pendingSendFileName() const { return state_.pendingSendFileName(); }
|
||||||
|
QString pendingSendFilePath() const { return state_.pendingSendFilePath(); }
|
||||||
|
QVariantList discoveredTargets() const { return state_.discoveredTargets(); }
|
||||||
|
QVariantList transfers() const { return state_.transfers(); }
|
||||||
|
bool autoAcceptIncoming() const { return state_.autoAcceptIncoming(); }
|
||||||
|
QString qrCodeUrl() const { return state_.qrCodeUrl(); }
|
||||||
|
QStringList qrCodeRows() const { return state_.qrCodeRows(); }
|
||||||
|
int qrCodeSize() const { return state_.qrCodeSize(); }
|
||||||
|
QString logPath() const { return state_.logPath(); }
|
||||||
|
|
||||||
QString deviceName() const { return device_name_; }
|
// Public methods
|
||||||
void setDeviceName(const QString& device_name);
|
void setDeviceName(const QString& device_name);
|
||||||
|
|
||||||
QString statusMessage() const { return status_message_; }
|
|
||||||
bool running() const { return running_; }
|
|
||||||
|
|
||||||
QString pendingSendFileName() const { return pending_send_file_name_; }
|
|
||||||
QString pendingSendFilePath() const { return pending_send_file_path_; }
|
|
||||||
|
|
||||||
QVariantList discoveredTargets() const { return discovered_targets_; }
|
|
||||||
QVariantList transfers() const { return transfers_; }
|
|
||||||
|
|
||||||
bool autoAcceptIncoming() const { return auto_accept_incoming_; }
|
|
||||||
void setAutoAcceptIncoming(bool enabled);
|
void setAutoAcceptIncoming(bool enabled);
|
||||||
|
|
||||||
QString qrCodeUrl() const { return qr_code_url_; }
|
|
||||||
QStringList qrCodeRows() const { return qr_code_rows_; }
|
|
||||||
int qrCodeSize() const { return qr_code_size_; }
|
|
||||||
|
|
||||||
QString logPath() const { return log_path_; }
|
|
||||||
void setLogPath(const QString& path);
|
void setLogPath(const QString& path);
|
||||||
|
|
||||||
Q_INVOKABLE void start();
|
Q_INVOKABLE void start();
|
||||||
@@ -65,6 +55,7 @@ class FileShareTrayController : public QObject {
|
|||||||
Q_INVOKABLE void switchToSendModeWithFile(const QString& file_path);
|
Q_INVOKABLE void switchToSendModeWithFile(const QString& file_path);
|
||||||
Q_INVOKABLE void sendPendingFileToTarget(qlonglong share_target_id);
|
Q_INVOKABLE void sendPendingFileToTarget(qlonglong share_target_id);
|
||||||
Q_INVOKABLE void copyTextToClipboard(const QString& text);
|
Q_INVOKABLE void copyTextToClipboard(const QString& text);
|
||||||
|
Q_INVOKABLE void openFileLocation(const QString& file_path);
|
||||||
Q_INVOKABLE void clearTransfers();
|
Q_INVOKABLE void clearTransfers();
|
||||||
Q_INVOKABLE void hideToTray();
|
Q_INVOKABLE void hideToTray();
|
||||||
|
|
||||||
@@ -84,65 +75,31 @@ class FileShareTrayController : public QObject {
|
|||||||
|
|
||||||
void requestTrayMessage(const QString& title, const QString& body);
|
void requestTrayMessage(const QString& title, const QString& body);
|
||||||
void requestCopyLinkTrayMessage(const QString& title, const QString& body,
|
void requestCopyLinkTrayMessage(const QString& title, const QString& body,
|
||||||
const QString& link);
|
const QString& link);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void CreateService();
|
void initializeService();
|
||||||
void AttachServiceListeners();
|
void attachServiceListeners();
|
||||||
|
void loadSettings();
|
||||||
|
void saveSettings() const;
|
||||||
|
void updateQrCodeData();
|
||||||
|
|
||||||
void startSendMode();
|
void startSendMode();
|
||||||
void startReceiveMode();
|
void startReceiveMode();
|
||||||
void LoadSettings();
|
|
||||||
void SaveSettings() const;
|
void updateTargetFromInfo(const NearbySharingApi::ShareTargetInfo& info);
|
||||||
void UpdateQrCodeData();
|
void handleTransferUpdate(const NearbySharingApi::TransferUpdateInfo& update);
|
||||||
|
void handleTransferComplete(const NearbySharingApi::TransferUpdateInfo& update);
|
||||||
void UpsertTarget(qlonglong share_target_id, const QString& name,
|
void handleIncomingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update,
|
||||||
bool is_incoming);
|
const QString& name, bool success);
|
||||||
void RemoveTarget(qlonglong share_target_id);
|
void handleOutgoingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update,
|
||||||
QString TargetName(qlonglong share_target_id) const;
|
const QString& name, bool success);
|
||||||
bool HasActiveTransferForTarget(qlonglong share_target_id) const;
|
|
||||||
|
void setStatus(const QString& status);
|
||||||
void UpsertTransfer(qlonglong share_target_id, const QString& target_name,
|
void notifyStateChange(const QString& property);
|
||||||
const QString& status, double progress,
|
|
||||||
qulonglong transferred_bytes, const QString& direction,
|
|
||||||
const QString& file_name);
|
|
||||||
|
|
||||||
void SetStatus(const QString& status);
|
|
||||||
bool HasActiveTransfers() const;
|
|
||||||
|
|
||||||
static bool IsActiveTransferStatus(const QString& status);
|
|
||||||
static QString StatusToString(NearbySharingApi::StatusCode status);
|
|
||||||
static QString TransferStatusToString(NearbySharingApi::TransferStatus status);
|
|
||||||
static bool IsFinalTransferStatus(NearbySharingApi::TransferStatus status);
|
|
||||||
|
|
||||||
std::unique_ptr<NearbySharingApi> service_;
|
std::unique_ptr<NearbySharingApi> service_;
|
||||||
|
FileShareState state_;
|
||||||
QString mode_ = QStringLiteral("Receive");
|
|
||||||
QString device_name_ = QStringLiteral("NearbyLinux");
|
|
||||||
QString status_message_ = QStringLiteral("Idle");
|
|
||||||
bool running_ = false;
|
|
||||||
|
|
||||||
bool auto_accept_incoming_ = true;
|
|
||||||
|
|
||||||
// qr stuff
|
|
||||||
QString qr_code_url_;
|
|
||||||
QStringList qr_code_rows_;
|
|
||||||
int qr_code_size_ = 0;
|
|
||||||
QString log_path_ = QStringLiteral("/tmp/nearby_qml_file_tray.log");
|
|
||||||
|
|
||||||
// pending send stuff
|
|
||||||
QString pending_send_file_path_;
|
|
||||||
QString pending_send_file_name_;
|
|
||||||
qlonglong pending_send_target_id_ = 0;
|
|
||||||
|
|
||||||
// send targets
|
|
||||||
QVariantList discovered_targets_;
|
|
||||||
QHash<qlonglong, int> discovered_row_by_target_;
|
|
||||||
QSet<qlonglong> pending_target_removals_;
|
|
||||||
QHash<qlonglong, QString> target_names_;
|
|
||||||
|
|
||||||
QVariantList transfers_;
|
|
||||||
QHash<qlonglong, int> transfer_row_by_target_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
#endif // SHARING_LINUX_QML_TRAY_APP_FILE_SHARE_TRAY_CONTROLLER_H_
|
||||||
|
|||||||
@@ -232,6 +232,8 @@ int main(int argc, char* argv[]) {
|
|||||||
tray.setContextMenu(&tray_menu);
|
tray.setContextMenu(&tray_menu);
|
||||||
tray.show();
|
tray.show();
|
||||||
|
|
||||||
|
controller.start();
|
||||||
|
//controller.
|
||||||
controller.switchToReceiveMode();
|
controller.switchToReceiveMode();
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#include "qr_code_generator.h"
|
||||||
|
#include <QByteArray>
|
||||||
|
#include "third_party/libqrencode/qrencode_compat.h"
|
||||||
|
|
||||||
|
namespace QrCodeGenerator {
|
||||||
|
|
||||||
|
QrCodeData GenerateQrCode(const QString& url) {
|
||||||
|
QrCodeData result;
|
||||||
|
result.size = 0;
|
||||||
|
|
||||||
|
const QByteArray encoded_url = url.trimmed().toUtf8();
|
||||||
|
if (encoded_url.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QRcode* qr_code = QRcode_encodeData(
|
||||||
|
encoded_url.size(),
|
||||||
|
reinterpret_cast<const unsigned char*>(encoded_url.constData()), 0,
|
||||||
|
QR_ECLEVEL_M);
|
||||||
|
|
||||||
|
if (qr_code == nullptr || qr_code->data == nullptr || qr_code->width <= 0) {
|
||||||
|
if (qr_code != nullptr) {
|
||||||
|
QRcode_free(qr_code);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.size = qr_code->width;
|
||||||
|
result.rows.reserve(result.size);
|
||||||
|
|
||||||
|
for (int row = 0; row < result.size; ++row) {
|
||||||
|
QString row_data;
|
||||||
|
row_data.reserve(result.size);
|
||||||
|
|
||||||
|
for (int col = 0; col < result.size; ++col) {
|
||||||
|
const unsigned char module =
|
||||||
|
qr_code->data[row * result.size + col] & 0x1;
|
||||||
|
row_data.append(module ? QLatin1Char('1') : QLatin1Char('0'));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.rows.append(row_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRcode_free(qr_code);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace QrCodeGenerator
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
namespace QrCodeGenerator {
|
||||||
|
|
||||||
|
struct QrCodeData {
|
||||||
|
QStringList rows;
|
||||||
|
int size;
|
||||||
|
};
|
||||||
|
|
||||||
|
QrCodeData GenerateQrCode(const QString& url);
|
||||||
|
|
||||||
|
} // namespace QrCodeGenerator
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_QR_CODE_GENERATOR_H_
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
class ShareTarget {
|
||||||
|
public:
|
||||||
|
explicit ShareTarget(qlonglong id, const QString& name, bool is_incoming)
|
||||||
|
: id_(id), name_(name), is_incoming_(is_incoming) {}
|
||||||
|
|
||||||
|
qlonglong id() const { return id_; }
|
||||||
|
QString name() const { return name_; }
|
||||||
|
bool isIncoming() const { return is_incoming_; }
|
||||||
|
|
||||||
|
QVariantMap toVariantMap() const {
|
||||||
|
QVariantMap map;
|
||||||
|
map[QStringLiteral("id")] = id_;
|
||||||
|
map[QStringLiteral("name")] = name_;
|
||||||
|
map[QStringLiteral("isIncoming")] = is_incoming_;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
qlonglong id_;
|
||||||
|
QString name_;
|
||||||
|
bool is_incoming_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_SHARE_TARGET_MODEL_H_
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include "status_mapper.h"
|
||||||
|
|
||||||
|
namespace StatusMapper {
|
||||||
|
|
||||||
|
QString TransferStatusToString(NearbySharingApi::TransferStatus status) {
|
||||||
|
return QString::fromStdString(NearbySharingApi::TransferStatusToString(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ApiStatusToString(NearbySharingApi::StatusCode status) {
|
||||||
|
return QString::fromStdString(NearbySharingApi::StatusCodeToString(status));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsActiveTransferStatus(const QString& status) {
|
||||||
|
return status == QStringLiteral("Queued") ||
|
||||||
|
status == QStringLiteral("Connecting") ||
|
||||||
|
status == QStringLiteral("AwaitingLocalConfirmation") ||
|
||||||
|
status == QStringLiteral("AwaitingRemoteAcceptance") ||
|
||||||
|
status == QStringLiteral("InProgress");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsFinalTransferStatus(NearbySharingApi::TransferStatus status) {
|
||||||
|
switch (status) {
|
||||||
|
case NearbySharingApi::TransferStatus::kComplete:
|
||||||
|
case NearbySharingApi::TransferStatus::kFailed:
|
||||||
|
case NearbySharingApi::TransferStatus::kRejected:
|
||||||
|
case NearbySharingApi::TransferStatus::kCancelled:
|
||||||
|
case NearbySharingApi::TransferStatus::kTimedOut:
|
||||||
|
case NearbySharingApi::TransferStatus::kMediaUnavailable:
|
||||||
|
case NearbySharingApi::TransferStatus::kNotEnoughSpace:
|
||||||
|
case NearbySharingApi::TransferStatus::kUnsupportedAttachmentType:
|
||||||
|
case NearbySharingApi::TransferStatus::kDeviceAuthenticationFailed:
|
||||||
|
case NearbySharingApi::TransferStatus::kIncompletePayloads:
|
||||||
|
return true;
|
||||||
|
case NearbySharingApi::TransferStatus::kUnknown:
|
||||||
|
case NearbySharingApi::TransferStatus::kConnecting:
|
||||||
|
case NearbySharingApi::TransferStatus::kAwaitingLocalConfirmation:
|
||||||
|
case NearbySharingApi::TransferStatus::kAwaitingRemoteAcceptance:
|
||||||
|
case NearbySharingApi::TransferStatus::kInProgress:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace StatusMapper
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include "sharing/linux/nearby_sharing_api.h"
|
||||||
|
|
||||||
|
using NearbySharingApi = nearby::sharing::NearbySharingApi;
|
||||||
|
|
||||||
|
namespace StatusMapper {
|
||||||
|
|
||||||
|
QString TransferStatusToString(NearbySharingApi::TransferStatus status);
|
||||||
|
|
||||||
|
QString ApiStatusToString(NearbySharingApi::StatusCode status);
|
||||||
|
|
||||||
|
bool IsActiveTransferStatus(const QString& status);
|
||||||
|
|
||||||
|
bool IsFinalTransferStatus(NearbySharingApi::TransferStatus status);
|
||||||
|
|
||||||
|
} // namespace StatusMapper
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_STATUS_MAPPER_H_
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#include "string_utils.h"
|
||||||
|
|
||||||
|
namespace StringUtils {
|
||||||
|
|
||||||
|
QString TrimmedOrFallback(const QString& value, const QString& fallback) {
|
||||||
|
const QString trimmed = value.trimmed();
|
||||||
|
return trimmed.isEmpty() ? fallback : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FromStdString(const std::string& value) {
|
||||||
|
return QString::fromStdString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TrimmedFromStdString(const std::string& value) {
|
||||||
|
return QString::fromStdString(value).trimmed();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace StringUtils
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace StringUtils {
|
||||||
|
|
||||||
|
QString TrimmedOrFallback(const QString& value, const QString& fallback);
|
||||||
|
|
||||||
|
QString FromStdString(const std::string& value);
|
||||||
|
|
||||||
|
QString TrimmedFromStdString(const std::string& value);
|
||||||
|
|
||||||
|
} // namespace StringUtils
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_STRING_UTILS_H_
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
#ifndef SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_
|
||||||
|
#define SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariantMap>
|
||||||
|
|
||||||
|
class Transfer {
|
||||||
|
public:
|
||||||
|
Transfer(qlonglong target_id, const QString& target_name,
|
||||||
|
const QString& status, double progress, qulonglong transferred_bytes,
|
||||||
|
const QString& direction, const QString& file_name)
|
||||||
|
: target_id_(target_id),
|
||||||
|
target_name_(target_name),
|
||||||
|
status_(status),
|
||||||
|
progress_(progress),
|
||||||
|
transferred_bytes_(transferred_bytes),
|
||||||
|
direction_(direction),
|
||||||
|
file_name_(file_name) {}
|
||||||
|
|
||||||
|
qlonglong targetId() const { return target_id_; }
|
||||||
|
QString targetName() const { return target_name_; }
|
||||||
|
QString status() const { return status_; }
|
||||||
|
double progress() const { return progress_; }
|
||||||
|
qulonglong transferredBytes() const { return transferred_bytes_; }
|
||||||
|
QString direction() const { return direction_; }
|
||||||
|
QString fileName() const { return file_name_; }
|
||||||
|
|
||||||
|
QVariantMap toVariantMap() const {
|
||||||
|
QVariantMap map;
|
||||||
|
map[QStringLiteral("targetId")] = target_id_;
|
||||||
|
map[QStringLiteral("targetName")] = target_name_;
|
||||||
|
map[QStringLiteral("status")] = status_;
|
||||||
|
map[QStringLiteral("progress")] = progress_;
|
||||||
|
map[QStringLiteral("transferredBytes")] = static_cast<qulonglong>(transferred_bytes_);
|
||||||
|
map[QStringLiteral("direction")] = direction_;
|
||||||
|
map[QStringLiteral("fileName")] = file_name_;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
qlonglong target_id_;
|
||||||
|
QString target_name_;
|
||||||
|
QString status_;
|
||||||
|
double progress_;
|
||||||
|
qulonglong transferred_bytes_;
|
||||||
|
QString direction_;
|
||||||
|
QString file_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SHARING_LINUX_QML_TRAY_APP_TRANSFER_MODEL_H_
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -42,6 +43,61 @@ using NcPayload = ::nearby::connections::Payload;
|
|||||||
using NcStatus = ::nearby::connections::Status;
|
using NcStatus = ::nearby::connections::Status;
|
||||||
using NcStrategy = ::nearby::connections::Strategy;
|
using NcStrategy = ::nearby::connections::Strategy;
|
||||||
|
|
||||||
|
class VectorInputStream final : public InputStream {
|
||||||
|
public:
|
||||||
|
explicit VectorInputStream(std::vector<uint8_t> bytes)
|
||||||
|
: bytes_(std::move(bytes)) {}
|
||||||
|
|
||||||
|
ExceptionOr<ByteArray> Read(std::int64_t size) override {
|
||||||
|
if (closed_) {
|
||||||
|
return ExceptionOr<ByteArray>(Exception::kIo);
|
||||||
|
}
|
||||||
|
if (offset_ >= bytes_.size() || size <= 0) {
|
||||||
|
return ExceptionOr<ByteArray>(ByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t remaining = bytes_.size() - offset_;
|
||||||
|
const size_t chunk_size = std::min<size_t>(remaining, static_cast<size_t>(size));
|
||||||
|
ByteArray chunk(reinterpret_cast<const char*>(bytes_.data() + offset_),
|
||||||
|
chunk_size);
|
||||||
|
offset_ += chunk_size;
|
||||||
|
return ExceptionOr<ByteArray>(std::move(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception Close() override {
|
||||||
|
closed_ = true;
|
||||||
|
return {Exception::kSuccess};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> bytes_;
|
||||||
|
size_t offset_ = 0;
|
||||||
|
bool closed_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SharedInputStream final : public InputStream {
|
||||||
|
public:
|
||||||
|
explicit SharedInputStream(std::shared_ptr<InputStream> stream)
|
||||||
|
: stream_(std::move(stream)) {}
|
||||||
|
|
||||||
|
ExceptionOr<ByteArray> Read(std::int64_t size) override {
|
||||||
|
if (stream_ == nullptr) {
|
||||||
|
return ExceptionOr<ByteArray>(Exception::kIo);
|
||||||
|
}
|
||||||
|
return stream_->Read(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception Close() override {
|
||||||
|
if (stream_ == nullptr) {
|
||||||
|
return {Exception::kIo};
|
||||||
|
}
|
||||||
|
return stream_->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<InputStream> stream_;
|
||||||
|
};
|
||||||
|
|
||||||
Status ConvertToStatus(NcStatus status) {
|
Status ConvertToStatus(NcStatus status) {
|
||||||
return static_cast<Status>(status.value);
|
return static_cast<Status>(status.value);
|
||||||
}
|
}
|
||||||
@@ -86,6 +142,17 @@ NcPayload ConvertToServicePayload(Payload payload) {
|
|||||||
return NcPayload(payload.id,
|
return NcPayload(payload.id,
|
||||||
ByteArray(std::string(bytes.begin(), bytes.end())));
|
ByteArray(std::string(bytes.begin(), bytes.end())));
|
||||||
}
|
}
|
||||||
|
case PayloadContent::Type::kStream: {
|
||||||
|
if (payload.content.stream_payload.input_stream != nullptr) {
|
||||||
|
return NcPayload(
|
||||||
|
payload.id,
|
||||||
|
std::make_unique<SharedInputStream>(
|
||||||
|
std::move(payload.content.stream_payload.input_stream)));
|
||||||
|
}
|
||||||
|
auto stream = std::make_unique<VectorInputStream>(
|
||||||
|
std::move(payload.content.stream_payload.bytes));
|
||||||
|
return NcPayload(payload.id, std::move(stream));
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return NcPayload();
|
return NcPayload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#include "absl/strings/string_view.h"
|
#include "absl/strings/string_view.h"
|
||||||
#include "absl/time/time.h"
|
#include "absl/time/time.h"
|
||||||
#include "internal/base/file_path.h"
|
#include "internal/base/file_path.h"
|
||||||
|
#include "internal/platform/input_stream.h"
|
||||||
#include "internal/interop/authentication_status.h"
|
#include "internal/interop/authentication_status.h"
|
||||||
|
|
||||||
namespace nearby {
|
namespace nearby {
|
||||||
@@ -362,6 +364,14 @@ struct BytesPayload {
|
|||||||
std::vector<uint8_t> bytes;
|
std::vector<uint8_t> bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A stream payload backed by an in-memory byte buffer on the sender side.
|
||||||
|
struct StreamPayload {
|
||||||
|
// The bytes to expose through the stream.
|
||||||
|
std::vector<uint8_t> bytes;
|
||||||
|
// Optional live stream source for long-lived outgoing stream payloads.
|
||||||
|
std::shared_ptr<nearby::InputStream> input_stream;
|
||||||
|
};
|
||||||
|
|
||||||
// A file payload representing a file.
|
// A file payload representing a file.
|
||||||
struct FilePayload {
|
struct FilePayload {
|
||||||
// The file to which this payload points to. When sending this payload, the
|
// The file to which this payload points to. When sending this payload, the
|
||||||
@@ -375,6 +385,8 @@ struct FilePayload {
|
|||||||
struct PayloadContent {
|
struct PayloadContent {
|
||||||
// A Payload consisting of a single byte array.
|
// A Payload consisting of a single byte array.
|
||||||
BytesPayload bytes_payload;
|
BytesPayload bytes_payload;
|
||||||
|
// A Payload exposed to Nearby as a finite stream.
|
||||||
|
StreamPayload stream_payload;
|
||||||
// A Payload representing a file on the device.
|
// A Payload representing a file on the device.
|
||||||
FilePayload file_payload;
|
FilePayload file_payload;
|
||||||
enum class Type { kUnknown = 0, kBytes = 1, kStream = 2, kFile = 3 };
|
enum class Type { kUnknown = 0, kBytes = 1, kStream = 2, kFile = 3 };
|
||||||
@@ -400,6 +412,9 @@ struct Payload {
|
|||||||
explicit Payload(std::vector<uint8_t> bytes)
|
explicit Payload(std::vector<uint8_t> bytes)
|
||||||
: Payload(GenerateId(), std::move(bytes)) {}
|
: Payload(GenerateId(), std::move(bytes)) {}
|
||||||
|
|
||||||
|
explicit Payload(StreamPayload stream_payload)
|
||||||
|
: Payload(GenerateId(), std::move(stream_payload)) {}
|
||||||
|
|
||||||
explicit Payload(FilePath file_path,
|
explicit Payload(FilePath file_path,
|
||||||
absl::string_view parent_folder = absl::string_view())
|
absl::string_view parent_folder = absl::string_view())
|
||||||
: Payload(std::hash<std::string>()(file_path.ToString()), file_path,
|
: Payload(std::hash<std::string>()(file_path.ToString()), file_path,
|
||||||
@@ -410,6 +425,12 @@ struct Payload {
|
|||||||
content.bytes_payload.bytes = std::move(bytes);
|
content.bytes_payload.bytes = std::move(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Payload(int64_t id, StreamPayload stream_payload) : id(id) {
|
||||||
|
content.type = PayloadContent::Type::kStream;
|
||||||
|
content.stream_payload.bytes = std::move(stream_payload.bytes);
|
||||||
|
content.stream_payload.input_stream = std::move(stream_payload.input_stream);
|
||||||
|
}
|
||||||
|
|
||||||
Payload(int64_t id, FilePath file_path,
|
Payload(int64_t id, FilePath file_path,
|
||||||
absl::string_view parent_folder = absl::string_view())
|
absl::string_view parent_folder = absl::string_view())
|
||||||
: id(id) {
|
: id(id) {
|
||||||
|
|||||||
Reference in New Issue
Block a user