mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46: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:
|
||||
build-and-release-linux:
|
||||
name: Build and Release Linux Bundle
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Install git
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git ca-certificates
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
@@ -42,13 +48,14 @@ jobs:
|
||||
|
||||
- name: Install Linux build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
apt-get update
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
clang \
|
||||
cmake \
|
||||
ninja-build \
|
||||
pkg-config \
|
||||
libsdbus-c++-dev \
|
||||
libssl-dev \
|
||||
libsystemd-dev \
|
||||
libcurlpp-dev \
|
||||
|
||||
@@ -23,7 +23,13 @@ jobs:
|
||||
build-linux:
|
||||
name: Build Linux
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ubuntu:25.04
|
||||
steps:
|
||||
- name: Install git
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git ca-certificates
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
|
||||
@@ -1,150 +1,88 @@
|
||||
# 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
|
||||
|
||||
|
||||
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:
|
||||
- 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:
|
||||
**To install the prerequisites, run this command**
|
||||
|
||||
```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:
|
||||
(run this from the extracted bundle directory)
|
||||
**To install the Quick Share application,**
|
||||
|
||||
```bash
|
||||
./install_nearby_file_share.sh
|
||||
```
|
||||
1. Go to [releases](https://github.com/kidfromjupiter/nearby/releases)
|
||||
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).
|
||||
Create/edit the `bluetooth.service` override:
|
||||
**To install the actual library and headers,**
|
||||
|
||||
```bash
|
||||
sudo systemctl edit bluetooth
|
||||
```
|
||||
Currently there are no prebuilt shared library or headers. You'll have to build them yourself
|
||||
|
||||
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
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/local/libexec/bluetooth/bluetoothd -E --debug=src/plugin.c --noplugin=bap,bass,mcp,vcp,micp,ccp,csip,tmap,asha,midi
|
||||
```
|
||||
## Apologies
|
||||
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
|
||||
enormous codebases.
|
||||
|
||||
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
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart bluetooth
|
||||
```
|
||||
## Special thanks
|
||||
|
||||
These plugins are currently disabled due to an ongoing likely upstream BlueZ bug:
|
||||
`bap,bass,mcp,vcp,micp,ccp,csip,tmap,asha,midi`.
|
||||
This workaround will be removed once the issue is fixed.
|
||||
https://github.com/proatgram and https://github.com/vibhavp
|
||||
|
||||
This installs to `~/.local` by default.
|
||||
|
||||
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`.
|
||||
*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*
|
||||
|
||||
@@ -92,8 +92,8 @@ cc_library(
|
||||
"bluetooth_devices.h",
|
||||
"bluetooth_pairing.h",
|
||||
"bluez.h",
|
||||
"bluez_agent.h",
|
||||
"bluez_device.h",
|
||||
# "bluez_agent.h",
|
||||
"bluez_advertisement_monitor.h",
|
||||
"bluez_advertisement_monitor_manager.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(
|
||||
name = "linux",
|
||||
srcs = [
|
||||
@@ -184,7 +200,7 @@ cc_library(
|
||||
"bluetooth_devices.cc",
|
||||
"bluetooth_pairing.cc",
|
||||
"bluez.cc",
|
||||
#"bluez_agent.cc",
|
||||
"bluez_agent.cc",
|
||||
"bluez_advertisement_monitor.cc",
|
||||
"bluez_gatt_characteristic_client.cc",
|
||||
"bluez_gatt_characteristic_server.cc",
|
||||
@@ -200,7 +216,6 @@ cc_library(
|
||||
"scheduled_executor.cc",
|
||||
"stream.cc",
|
||||
"submittable_executor.cc",
|
||||
"system_clock.cc",
|
||||
"thread_pool.cc",
|
||||
"utils.cc",
|
||||
# "wifi_direct.cc",
|
||||
@@ -225,6 +240,7 @@ cc_library(
|
||||
deps = [
|
||||
":comm",
|
||||
":crypto", # build_cleaner: keep
|
||||
":system_clock",
|
||||
":types",
|
||||
"//internal/flags:nearby_flags",
|
||||
"//internal/platform:base",
|
||||
|
||||
@@ -208,7 +208,6 @@ bool BluezGattDiscovery::InitializeKnownServices() {
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::flat_hash_map<sdbus::ObjectPath, GattServiceClient> cached_services;
|
||||
absl::MutexLock lock(&mutex_);
|
||||
auto chr_it = std::find_if(
|
||||
objects.cbegin(), objects.cend(),
|
||||
@@ -219,29 +218,27 @@ bool BluezGattDiscovery::InitializeKnownServices() {
|
||||
sdbus::InterfaceName(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME)) == 1;
|
||||
});
|
||||
|
||||
for (; chr_it != objects.cend(); ++chr_it) {
|
||||
const auto& [path, ifaces] = *chr_it;
|
||||
for (; chr_it != objects.cend(); ++chr_it) {
|
||||
const auto& [path, ifaces] = *chr_it;
|
||||
|
||||
auto iface_it = ifaces.find(sdbus::InterfaceName(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME));
|
||||
if (iface_it == ifaces.end()) {
|
||||
// Not a GattCharacteristic1 object (or interfaces map incomplete) -> skip
|
||||
continue;
|
||||
auto iface_it = ifaces.find(sdbus::InterfaceName(
|
||||
org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME));
|
||||
if (iface_it == ifaces.end()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -337,7 +334,7 @@ BluezGattDiscovery::GetSubscribedCharacteristic(
|
||||
}
|
||||
|
||||
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>>
|
||||
@@ -396,7 +393,7 @@ try {
|
||||
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(
|
||||
@@ -414,12 +411,12 @@ void BluezGattDiscovery::onInterfacesAdded(
|
||||
absl::MutexLock lock(&mutex_);
|
||||
auto maybe_props = characteristicProperties(objectPath, properties);
|
||||
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(
|
||||
std::make_tuple(chr_uuid, service_uuid, device_path), objectPath);
|
||||
std::make_tuple(service_uuid, chr_uuid, device_path), objectPath);
|
||||
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(
|
||||
|
||||
@@ -93,7 +93,7 @@ class BluezGattDiscovery final : public bluez::BluezObjectManager {
|
||||
absl::Mutex mutex_;
|
||||
absl::flat_hash_map<sdbus::ObjectPath, std::unique_ptr<GattServiceClient>>
|
||||
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>,
|
||||
sdbus::ObjectPath>
|
||||
discovered_characteristics_ ABSL_GUARDED_BY(mutex_);
|
||||
@@ -122,7 +122,7 @@ class GattClient : public api::ble::GattClient {
|
||||
peripheral_object_path_(peripheral_object_path),
|
||||
gatt_discovery_(std::move(gatt_discovery)),
|
||||
discovery_cancel_(false) {
|
||||
disconnected_callback_it_ = gatt_discovery->AddPeripheralConnection(
|
||||
disconnected_callback_it_ = gatt_discovery_->AddPeripheralConnection(
|
||||
peripheral_object_path_, std::move(disconnected_callback));
|
||||
}
|
||||
~GattClient() override {
|
||||
|
||||
@@ -24,6 +24,19 @@
|
||||
|
||||
namespace nearby {
|
||||
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>
|
||||
GattServer::CreateCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
||||
@@ -60,23 +73,53 @@ GattServer::CreateCharacteristic(
|
||||
|
||||
if (service->AddCharacteristic(service_uuid, characteristic_uuid, permission,
|
||||
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)});
|
||||
{
|
||||
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{
|
||||
characteristic_uuid, service_uuid, permission, property};
|
||||
@@ -137,19 +180,86 @@ absl::Status GattServer::NotifyCharacteristicChanged(
|
||||
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() {
|
||||
bluez::GattManager manager(system_bus_, adapter_.GetObjectPath());
|
||||
absl::MutexLock lock(&services_mutex_);
|
||||
for (auto& [uuid, service] : services_) {
|
||||
LOG(INFO) << __func__ << ": Unregistering service "
|
||||
<< service->getObject().getObjectPath();
|
||||
try {
|
||||
manager.UnregisterApplication(sdbus::ObjectPath("/"));
|
||||
} catch (const sdbus::Error& e) {
|
||||
DBUS_LOG_METHOD_CALL_ERROR(&manager, "UnregisterApplication", e);
|
||||
sdbus::ObjectPath app_path =
|
||||
gatt_service_root_object_manager->getObject().getObjectPath();
|
||||
bool should_unregister = false;
|
||||
|
||||
{
|
||||
absl::MutexLock registration_lock(®istration_mutex_);
|
||||
if (register_application_call_.has_value() &&
|
||||
register_application_call_->isPending()) {
|
||||
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
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
@@ -69,7 +70,16 @@ class GattServer : public api::ble::GattServer {
|
||||
sdbus::ObjectPath("/com/google/nearby/medium/ble/gatt"))),
|
||||
gatt_manager_(std::make_unique<bluez::GattManager>(system_bus_, adapter_.GetObjectPath())),
|
||||
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;
|
||||
|
||||
absl::optional<api::ble::GattCharacteristic> CreateCharacteristic(
|
||||
@@ -85,6 +95,9 @@ class GattServer : public api::ble::GattServer {
|
||||
void Stop() override;
|
||||
|
||||
private:
|
||||
void OnRegisterApplicationReply(std::optional<sdbus::Error> error);
|
||||
void OnUnregisterApplicationReply(std::optional<sdbus::Error> error);
|
||||
|
||||
sdbus::IConnection& system_bus_;
|
||||
std::shared_ptr<BluetoothDevices> devices_;
|
||||
BluetoothAdapter adapter_;
|
||||
@@ -93,9 +106,20 @@ class GattServer : public api::ble::GattServer {
|
||||
std::unique_ptr<RootObjectManager> gatt_service_root_object_manager;
|
||||
absl::Mutex profiles_mutex_;
|
||||
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::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::flat_hash_map<Uuid, std::unique_ptr<bluez::GattServiceServer>> services_
|
||||
ABSL_GUARDED_BY(services_mutex_);
|
||||
|
||||
@@ -27,17 +27,14 @@
|
||||
|
||||
#include "absl/synchronization/mutex.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 "connections/implementation/flags/nearby_connections_feature_flags.h"
|
||||
#include "absl/types/span.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_socket.h"
|
||||
#include "ble_gatt_server.h"
|
||||
#include "internal/base/observer_list.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_devices.h"
|
||||
@@ -68,7 +65,6 @@ BleL2capSocket::ProtocolMode GetL2capProtocolMode() {
|
||||
BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
|
||||
: system_bus_(adapter.GetConnection()),
|
||||
adapter_(adapter),
|
||||
gatt_discovery_(std::make_shared<BluezGattDiscovery>(system_bus_)),
|
||||
observers_(std::make_shared<ObserverList<api::BluetoothClassicMedium::Observer>>()),
|
||||
devices_(std::make_unique<BluetoothDevices>(
|
||||
system_bus_, adapter_.GetObjectPath(), *observers_)),
|
||||
@@ -89,16 +85,55 @@ BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
|
||||
<< __func__
|
||||
<< ": Registering path /com/google/nearby/medium/ble/advertisement/monitor with AdvertisementMonitorManager at "
|
||||
<< adv_monitor_manager_->getProxy().getObjectPath();
|
||||
try {
|
||||
adv_monitor_manager_->RegisterMonitor(root_object_manager_->getObject().getObjectPath());
|
||||
} catch (const sdbus::Error &e) {
|
||||
DBUS_LOG_METHOD_CALL_ERROR(adv_monitor_manager_, "RegisterMonitor", e);
|
||||
adv_monitor_manager_->SetRegisterMonitorReplyCallback(
|
||||
[this](std::optional<sdbus::Error> error) {
|
||||
OnRegisterMonitorReply(std::move(error));
|
||||
});
|
||||
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__
|
||||
// << ": Could not initialize known GATT services";
|
||||
// }
|
||||
|
||||
if (error.has_value() && error->isValid()) {
|
||||
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
|
||||
@@ -240,9 +275,7 @@ bool BleV2Medium::StartScanning(const Uuid &service_uuid,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adv_monitor_manager_ == nullptr) {
|
||||
LOG(WARNING) << __func__
|
||||
<< ": Advertising monitor not supported by BlueZ";
|
||||
if (!WaitForAdvertisementMonitorManager()) {
|
||||
// TODO: Implement manual monitoring.
|
||||
return false;
|
||||
}
|
||||
@@ -314,7 +347,7 @@ bool BleV2Medium::StopScanning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (adv_monitor_manager_ == nullptr) {
|
||||
if (!WaitForAdvertisementMonitorManager()) {
|
||||
// TODO: Implement manual monitoring.
|
||||
return false;
|
||||
}
|
||||
@@ -349,7 +382,7 @@ bool BleV2Medium::StopScanning() {
|
||||
BleV2Medium::StartScanning(const Uuid &service_uuid,
|
||||
api::ble::TxPowerLevel tx_power_level,
|
||||
ScanningCallback callback) {
|
||||
if (adv_monitor_manager_ == nullptr) {
|
||||
if (!WaitForAdvertisementMonitorManager()) {
|
||||
// TODO: Implement manual monitoring.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -437,12 +470,9 @@ bool BleV2Medium::StopScanning() {
|
||||
|
||||
std::unique_ptr<api::ble::GattServer> BleV2Medium::StartGattServer(
|
||||
api::ble::ServerGattConnectionCallback callback) {
|
||||
// (void)callback;
|
||||
// return nullptr;
|
||||
|
||||
return std::make_unique<GattServer>(
|
||||
*system_bus_, adapter_, devices_,std::move(callback)
|
||||
);
|
||||
LOG(INFO) << __func__ << ": Starting Linux GATT server.";
|
||||
return std::make_unique<GattServer>(*system_bus_, adapter_, devices_,
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
std::unique_ptr<api::ble::GattClient> BleV2Medium::ConnectToGattServer(
|
||||
@@ -452,8 +482,7 @@ std::unique_ptr<api::ble::GattClient> BleV2Medium::ConnectToGattServer(
|
||||
(void)peripheral_id;
|
||||
(void)tx_power_level;
|
||||
(void)callback;
|
||||
LOG(WARNING) << __func__
|
||||
<< ": GATT client connection is not supported on Linux yet.";
|
||||
LOG(INFO) << __func__ << ": GATT is disabled on linux.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/synchronization/notification.h"
|
||||
#include <sdbus-c++/IConnection.h>
|
||||
|
||||
#include "absl/base/attributes.h"
|
||||
@@ -118,6 +121,8 @@ class BleV2Medium final : public api::ble::BleMedium {
|
||||
|
||||
private:
|
||||
bool StartLEDiscovery();
|
||||
bool WaitForAdvertisementMonitorManager();
|
||||
void OnRegisterMonitorReply(std::optional<sdbus::Error> error);
|
||||
|
||||
bool MonitorManagerSupportsOr() {
|
||||
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<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::flat_hash_map<
|
||||
Uuid,
|
||||
|
||||
@@ -21,34 +21,48 @@
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/base/observer_list.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.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_socket.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_pairing.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
BluetoothClassicMedium::BluetoothClassicMedium(BluetoothAdapter &adapter)
|
||||
: system_bus_(adapter.GetConnection()),
|
||||
adapter_(adapter),
|
||||
observers_(nullptr),
|
||||
devices_(nullptr),
|
||||
device_watcher_(nullptr),
|
||||
// agent_manager_(std::make_unique<AgentManager>(*system_bus_)),
|
||||
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_);
|
||||
}
|
||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/linux/bluez_agent.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_classic_medium.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_pairing.h"
|
||||
#include "internal/platform/logging.h"
|
||||
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
namespace {
|
||||
|
||||
constexpr char kBluezAgentPath[] = "/com/google/nearby/bluetooth/agent";
|
||||
|
||||
} // namespace
|
||||
|
||||
BluetoothClassicMedium::BluetoothClassicMedium(BluetoothAdapter &adapter)
|
||||
: system_bus_(adapter.GetConnection()),
|
||||
adapter_(adapter),
|
||||
observers_(nullptr),
|
||||
devices_(nullptr),
|
||||
device_watcher_(nullptr),
|
||||
agent_manager_(std::make_unique<AgentManager>(*system_bus_)),
|
||||
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(
|
||||
DiscoveryCallback discovery_callback) {
|
||||
@@ -145,14 +159,12 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
|
||||
new BluetoothSocket(device, fd.value()));
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BluetoothServerSocket>
|
||||
BluetoothClassicMedium::ListenForService(const std::string &service_name,
|
||||
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)) {
|
||||
LOG(ERROR) << __func__ << ": Could not register profile "
|
||||
std::unique_ptr<api::BluetoothServerSocket>
|
||||
BluetoothClassicMedium::ListenForService(const std::string &service_name,
|
||||
const std::string &service_uuid) {
|
||||
if (!profile_manager_->ProfileRegistered(service_uuid)) {
|
||||
if (!profile_manager_->Register(service_name, service_uuid)) {
|
||||
LOG(ERROR) << __func__ << ": Could not register profile "
|
||||
<< service_name << " " << service_uuid
|
||||
<< " with Bluez";
|
||||
return nullptr;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <sdbus-c++/StandardInterfaces.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/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
|
||||
@@ -107,7 +107,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
|
||||
std::shared_ptr<BluetoothDevices> devices_;
|
||||
std::unique_ptr<DeviceWatcher> device_watcher_;
|
||||
|
||||
// std::unique_ptr<AgentManager> agent_manager_;
|
||||
std::unique_ptr<AgentManager> agent_manager_;
|
||||
std::unique_ptr<ProfileManager> profile_manager_;
|
||||
};
|
||||
|
||||
|
||||
@@ -15,9 +15,13 @@
|
||||
#ifndef 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++/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/bluez.h"
|
||||
#include "internal/platform/implementation/linux/dbus.h"
|
||||
@@ -29,6 +33,9 @@ namespace bluez {
|
||||
class AdvertisementMonitorManager final
|
||||
: public sdbus::ProxyInterfaces<
|
||||
org::bluez::AdvertisementMonitorManager1_proxy> {
|
||||
public:
|
||||
using ReplyCallback = absl::AnyInvocable<void(std::optional<sdbus::Error>)>;
|
||||
|
||||
private:
|
||||
friend std::unique_ptr<AdvertisementMonitorManager>
|
||||
std::make_unique<AdvertisementMonitorManager>(
|
||||
@@ -50,6 +57,16 @@ class AdvertisementMonitorManager final
|
||||
delete;
|
||||
~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>
|
||||
DiscoverAdvertisementMonitorManager(
|
||||
sdbus::IConnection &system_bus,
|
||||
@@ -82,6 +99,38 @@ class AdvertisementMonitorManager final
|
||||
|
||||
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 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 |
|
||||
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) {
|
||||
server_cb_->characteristic_unsubscription_cb(characteristic_);
|
||||
}
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
|
||||
#ifndef 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++/ProxyInterfaces.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"
|
||||
namespace nearby {
|
||||
namespace linux {
|
||||
@@ -25,6 +30,8 @@ namespace bluez {
|
||||
class GattManager
|
||||
: public sdbus::ProxyInterfaces<org::bluez::GattManager1_proxy> {
|
||||
public:
|
||||
using ReplyCallback = absl::AnyInvocable<void(std::optional<sdbus::Error>)>;
|
||||
|
||||
GattManager(const GattManager &) = delete;
|
||||
GattManager(GattManager &&) = delete;
|
||||
GattManager &operator=(const GattManager &) = delete;
|
||||
@@ -37,6 +44,38 @@ class GattManager
|
||||
registerProxy();
|
||||
}
|
||||
~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 linux
|
||||
|
||||
+9
-6
@@ -3,8 +3,8 @@
|
||||
* 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__
|
||||
#define __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__internal_platform_implementation_linux_generated_dbus_bluez_advertisement_monitor_manager_client_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#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:
|
||||
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:
|
||||
|
||||
@@ -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!
|
||||
*/
|
||||
|
||||
#ifndef __sdbuscpp___home_lasan_Dev_nearby_latest_internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_v2_h__proxy__H__
|
||||
#define __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__internal_platform_implementation_linux_generated_dbus_bluez_gatt_manager_client_h__proxy__H__
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#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:
|
||||
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:
|
||||
|
||||
+2
@@ -3,9 +3,11 @@
|
||||
<node>
|
||||
<interface name="org.bluez.AdvertisementMonitorManager1">
|
||||
<method name="RegisterMonitor">
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||
<arg name="application" type="o" direction="in" />
|
||||
</method>
|
||||
<method name="UnregisterMonitor">
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||
<arg name="application" type="o" direction="in" />
|
||||
</method>
|
||||
<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>
|
||||
<interface name="org.bluez.GattManager1">
|
||||
<method name="RegisterApplication">
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||
<arg name="application" type="o" direction="in" />
|
||||
<arg name="options" type="a{sv}" direction="in" />
|
||||
</method>
|
||||
<method name="UnregisterApplication">
|
||||
<annotation name="org.freedesktop.DBus.Method.Async" value="client" />
|
||||
<arg name="application" type="o" direction="in" />
|
||||
</method>
|
||||
</interface>
|
||||
|
||||
@@ -24,6 +24,7 @@ cc_library(
|
||||
"//internal/base:file_path",
|
||||
"//internal/base:files",
|
||||
"//internal/crypto_cros", # buildcleaner: keep
|
||||
"//internal/platform:base",
|
||||
"//internal/interop:authentication_status",
|
||||
"@com_google_absl//absl/random",
|
||||
"@com_google_absl//absl/strings:string_view",
|
||||
|
||||
+55
-14
@@ -13,7 +13,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
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(
|
||||
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(
|
||||
name = "nearby_sharing_api",
|
||||
srcs = ["nearby_sharing_api.cc"],
|
||||
@@ -65,8 +55,8 @@ cc_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":linux_sharing_platform",
|
||||
":system_clock_shim",
|
||||
"//internal/base:file_path",
|
||||
"//internal/platform/implementation/linux:system_clock",
|
||||
"//sharing:attachments",
|
||||
"//sharing:nearby_sharing_service",
|
||||
"//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(
|
||||
name = "nearby_sharing_api_shared",
|
||||
linkshared = True,
|
||||
@@ -86,14 +96,45 @@ cc_binary(
|
||||
"-Wl,--exclude-libs,ALL",
|
||||
],
|
||||
deps = [
|
||||
"//internal/platform:base",
|
||||
":linux_sharing_platform",
|
||||
":system_clock_shim",
|
||||
"//internal/base:file_path",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation/linux",
|
||||
"//internal/platform/implementation/linux:system_clock",
|
||||
"//sharing:attachments",
|
||||
"//sharing:nearby_sharing_service",
|
||||
"//sharing/analytics",
|
||||
"//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/attachment_container.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/local_device_data/nearby_share_local_device_data_manager.h"
|
||||
#include "sharing/nearby_sharing_service_factory.h"
|
||||
@@ -28,7 +29,7 @@
|
||||
#include "sharing/transfer_metadata.h"
|
||||
#include "sharing/transfer_update_callback.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace nearby::sharing {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -39,6 +40,10 @@ void EnableBleL2capDefaults() {
|
||||
nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
kEnableBleL2cap,
|
||||
true);
|
||||
nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
nearby::sharing::config_package_nearby::nearby_sharing_feature::
|
||||
kEnableBleForTransfer,
|
||||
true);
|
||||
//nearby::NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
||||
// nearby::connections::config_package_nearby::nearby_connections_feature::
|
||||
// kRefactorBleL2cap,
|
||||
@@ -125,6 +130,16 @@ NearbySharingApi::TextAttachmentType ToFacadeTextAttachmentType(
|
||||
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() {
|
||||
auto ec_key = nearby::crypto::ECPrivateKey::Create();
|
||||
if (!ec_key) {
|
||||
@@ -266,7 +281,7 @@ class NearbySharingApi::Impl : public nearby::sharing::ShareTargetDiscoveredCall
|
||||
info.device_name = share_target.device_name;
|
||||
info.is_incoming = share_target.is_incoming;
|
||||
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.total_attachments = transfer_metadata.total_attachments_count();
|
||||
info.transferred_attachments = transfer_metadata.transferred_attachments_count();
|
||||
@@ -657,4 +672,4 @@ std::string NearbySharingApi::TransferStatusToString(TransferStatus status) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
} // namespace nearby::sharing::linux
|
||||
} // namespace nearby::sharing
|
||||
|
||||
@@ -13,15 +13,8 @@
|
||||
#include <string>
|
||||
#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 sharing {
|
||||
namespace linux {
|
||||
|
||||
class __attribute__((visibility("default"))) NearbySharingApi {
|
||||
public:
|
||||
@@ -131,7 +124,6 @@ class __attribute__((visibility("default"))) NearbySharingApi {
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace linux
|
||||
} // namespace sharing
|
||||
} // namespace nearby
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
#include "sharing/internal/public/pref_names.h"
|
||||
#include "sharing/proto/rpc_resources.pb.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace nearby::sharing {
|
||||
namespace {
|
||||
|
||||
using Json = nlohmann::json;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "internal/platform/task_runner.h"
|
||||
#include "sharing/internal/api/sharing_platform.h"
|
||||
|
||||
namespace nearby::sharing::linux {
|
||||
namespace nearby::sharing {
|
||||
|
||||
class LinuxSharingPlatform final : public nearby::sharing::api::SharingPlatform {
|
||||
public:
|
||||
|
||||
@@ -26,6 +26,16 @@ qt_add_executable(nearby_qml_file_tray_app
|
||||
file_share_tray_main.cpp
|
||||
file_share_tray_controller.cc
|
||||
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.h
|
||||
third_party/libqrencode/qrencode_compat.h
|
||||
|
||||
@@ -51,18 +51,16 @@ ApplicationWindow {
|
||||
clip: true
|
||||
|
||||
readonly property bool isSendMode: fileShareController.pendingSendFilePath.length > 0
|
||||
readonly property bool isIdle: fileShareController.discoveredTargets.length === 0
|
||||
&& fileShareController.transfers.length === 0
|
||||
|
||||
// ── Idle: animated blob ───────────────────────────────────
|
||||
AnimatedBlob { visible: mainContent.isIdle }
|
||||
AnimatedBlob { visible: !mainContent.isSendMode }
|
||||
|
||||
// ── Non-idle: scrollable device + transfer cards ──────────
|
||||
Flickable {
|
||||
id: mainFlickable
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
visible: !mainContent.isIdle
|
||||
visible: mainContent.isSendMode
|
||||
contentWidth: width
|
||||
contentHeight: mainCol.implicitHeight + 96
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
@@ -77,7 +75,6 @@ ApplicationWindow {
|
||||
SendUrlPanel {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
width: Math.max(240, Math.min(mainCol.width, 420))
|
||||
visible: mainContent.isSendMode
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ if(EXISTS "${_output_so}")
|
||||
ERROR_QUIET
|
||||
)
|
||||
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::SystemClock::ElapsedRealtime()" _undef_clock_idx)
|
||||
string(FIND "${_nm_output}" " U nearby::Crypto::Sha256(" _undef_crypto_idx)
|
||||
|
||||
@@ -1,13 +1,116 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property color textPrimary: "#111827"
|
||||
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 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 {
|
||||
x: 48; y: 48
|
||||
@@ -19,6 +122,141 @@ Item {
|
||||
font.weight: Font.Medium
|
||||
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 {
|
||||
id: blobCanvas
|
||||
@@ -27,15 +265,17 @@ Item {
|
||||
visible: !isSendMode
|
||||
|
||||
property real t: 0
|
||||
property double startMs: Date.now()
|
||||
property double lastMs: Date.now()
|
||||
|
||||
Timer {
|
||||
interval: 16
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
// seconds -> radians scale (t grows forever, no loop jump)
|
||||
blobCanvas.t = (Date.now() - blobCanvas.startMs) * 0.001 * (Math.PI * 2 / 8.0)
|
||||
var now = Date.now()
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -44,17 +284,21 @@ Item {
|
||||
var ctx = getContext("2d")
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
|
||||
var cx = width / 2
|
||||
var cy = height / 2
|
||||
var chaos = blobChaos
|
||||
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 pts = []
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
var a = (i / n) * Math.PI * 2 - Math.PI / 2
|
||||
var r = 130
|
||||
+ Math.sin(a * 2 + t) * 11
|
||||
+ Math.cos(a * 3 - t * 0.6) * 8
|
||||
+ Math.sin(a * 1.5 + t * 0.35) * 6
|
||||
var r = 130 + chaos * 15
|
||||
+ Math.sin(a * 2 + t) * (11 + chaos * 15)
|
||||
+ Math.cos(a * 3 - t * 0.6) * (8 + chaos * 12)
|
||||
+ 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 })
|
||||
}
|
||||
|
||||
@@ -81,12 +325,85 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
SendUrlPanel {
|
||||
Rectangle {
|
||||
id: incomingTransferCard
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - 96
|
||||
visible: isSendMode
|
||||
visible: !isSendMode && hasIncomingTransfer
|
||||
&& 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 {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
@@ -185,7 +185,7 @@ Item {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: targetName
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.Medium
|
||||
font.weight: Font.Bold
|
||||
elide: Text.ElideRight
|
||||
maximumLineCount: 2
|
||||
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_
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <QHash>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
|
||||
#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 {
|
||||
Q_OBJECT
|
||||
@@ -35,28 +29,24 @@ class FileShareTrayController : public QObject {
|
||||
explicit FileShareTrayController(QObject* parent = nullptr);
|
||||
~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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
Q_INVOKABLE void start();
|
||||
@@ -65,6 +55,7 @@ class FileShareTrayController : public QObject {
|
||||
Q_INVOKABLE void switchToSendModeWithFile(const QString& file_path);
|
||||
Q_INVOKABLE void sendPendingFileToTarget(qlonglong share_target_id);
|
||||
Q_INVOKABLE void copyTextToClipboard(const QString& text);
|
||||
Q_INVOKABLE void openFileLocation(const QString& file_path);
|
||||
Q_INVOKABLE void clearTransfers();
|
||||
Q_INVOKABLE void hideToTray();
|
||||
|
||||
@@ -84,65 +75,31 @@ class FileShareTrayController : public QObject {
|
||||
|
||||
void requestTrayMessage(const QString& title, const QString& body);
|
||||
void requestCopyLinkTrayMessage(const QString& title, const QString& body,
|
||||
const QString& link);
|
||||
const QString& link);
|
||||
|
||||
private:
|
||||
void CreateService();
|
||||
void AttachServiceListeners();
|
||||
void initializeService();
|
||||
void attachServiceListeners();
|
||||
void loadSettings();
|
||||
void saveSettings() const;
|
||||
void updateQrCodeData();
|
||||
|
||||
void startSendMode();
|
||||
void startReceiveMode();
|
||||
void LoadSettings();
|
||||
void SaveSettings() const;
|
||||
void UpdateQrCodeData();
|
||||
|
||||
void UpsertTarget(qlonglong share_target_id, const QString& name,
|
||||
bool is_incoming);
|
||||
void RemoveTarget(qlonglong share_target_id);
|
||||
QString TargetName(qlonglong share_target_id) const;
|
||||
bool HasActiveTransferForTarget(qlonglong share_target_id) const;
|
||||
|
||||
void UpsertTransfer(qlonglong share_target_id, const QString& target_name,
|
||||
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);
|
||||
|
||||
void updateTargetFromInfo(const NearbySharingApi::ShareTargetInfo& info);
|
||||
void handleTransferUpdate(const NearbySharingApi::TransferUpdateInfo& update);
|
||||
void handleTransferComplete(const NearbySharingApi::TransferUpdateInfo& update);
|
||||
void handleIncomingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update,
|
||||
const QString& name, bool success);
|
||||
void handleOutgoingTransferComplete(const NearbySharingApi::TransferUpdateInfo& update,
|
||||
const QString& name, bool success);
|
||||
|
||||
void setStatus(const QString& status);
|
||||
void notifyStateChange(const QString& property);
|
||||
|
||||
std::unique_ptr<NearbySharingApi> service_;
|
||||
|
||||
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_;
|
||||
FileShareState state_;
|
||||
};
|
||||
|
||||
#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.show();
|
||||
|
||||
controller.start();
|
||||
//controller.
|
||||
controller.switchToReceiveMode();
|
||||
|
||||
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 <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -42,6 +43,61 @@ using NcPayload = ::nearby::connections::Payload;
|
||||
using NcStatus = ::nearby::connections::Status;
|
||||
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) {
|
||||
return static_cast<Status>(status.value);
|
||||
}
|
||||
@@ -86,6 +142,17 @@ NcPayload ConvertToServicePayload(Payload payload) {
|
||||
return NcPayload(payload.id,
|
||||
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:
|
||||
return NcPayload();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -28,6 +29,7 @@
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/base/file_path.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/interop/authentication_status.h"
|
||||
|
||||
namespace nearby {
|
||||
@@ -362,6 +364,14 @@ struct BytesPayload {
|
||||
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.
|
||||
struct FilePayload {
|
||||
// The file to which this payload points to. When sending this payload, the
|
||||
@@ -375,6 +385,8 @@ struct FilePayload {
|
||||
struct PayloadContent {
|
||||
// A Payload consisting of a single byte array.
|
||||
BytesPayload bytes_payload;
|
||||
// A Payload exposed to Nearby as a finite stream.
|
||||
StreamPayload stream_payload;
|
||||
// A Payload representing a file on the device.
|
||||
FilePayload file_payload;
|
||||
enum class Type { kUnknown = 0, kBytes = 1, kStream = 2, kFile = 3 };
|
||||
@@ -400,6 +412,9 @@ struct Payload {
|
||||
explicit Payload(std::vector<uint8_t> bytes)
|
||||
: Payload(GenerateId(), std::move(bytes)) {}
|
||||
|
||||
explicit Payload(StreamPayload stream_payload)
|
||||
: Payload(GenerateId(), std::move(stream_payload)) {}
|
||||
|
||||
explicit Payload(FilePath file_path,
|
||||
absl::string_view parent_folder = absl::string_view())
|
||||
: Payload(std::hash<std::string>()(file_path.ToString()), file_path,
|
||||
@@ -410,6 +425,12 @@ struct Payload {
|
||||
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,
|
||||
absl::string_view parent_folder = absl::string_view())
|
||||
: id(id) {
|
||||
|
||||
Reference in New Issue
Block a user