diff --git a/.github/workflows/release-nearby-qml-tray.yaml b/.github/workflows/release-nearby-qml-tray.yaml
index 3e8c291c..a5c13d5d 100644
--- a/.github/workflows/release-nearby-qml-tray.yaml
+++ b/.github/workflows/release-nearby-qml-tray.yaml
@@ -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 \
diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml
index 668bcc19..f1e4d0c7 100644
--- a/.github/workflows/validate.yaml
+++ b/.github/workflows/validate.yaml
@@ -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
diff --git a/README.md b/README.md
index 047bcb14..78844d3c 100644
--- a/README.md
+++ b/README.md
@@ -1,150 +1,88 @@
# Google Nearby Sharing & Connections for Linux (Unofficial)
-> P2P file sharing, discovery, and connectivity on Linux using Google Nearby protocols.
+
+
-
## 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*
diff --git a/internal/platform/implementation/linux/BUILD b/internal/platform/implementation/linux/BUILD
index d7c4db88..a3779ab1 100644
--- a/internal/platform/implementation/linux/BUILD
+++ b/internal/platform/implementation/linux/BUILD
@@ -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",
diff --git a/internal/platform/implementation/linux/ble_gatt_client.cc b/internal/platform/implementation/linux/ble_gatt_client.cc
index 74047ab2..e88a1be8 100644
--- a/internal/platform/implementation/linux/ble_gatt_client.cc
+++ b/internal/platform/implementation/linux/ble_gatt_client.cc
@@ -208,7 +208,6 @@ bool BluezGattDiscovery::InitializeKnownServices() {
return false;
}
- absl::flat_hash_map 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(
- system_bus_, device_object_path, std::move(on_characteristic_changed_cb));
+ system_bus_, path_it->second, std::move(on_characteristic_changed_cb));
}
std::optional>
@@ -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(
diff --git a/internal/platform/implementation/linux/ble_gatt_client.h b/internal/platform/implementation/linux/ble_gatt_client.h
index 108b4769..20718422 100644
--- a/internal/platform/implementation/linux/ble_gatt_client.h
+++ b/internal/platform/implementation/linux/ble_gatt_client.h
@@ -93,7 +93,7 @@ class BluezGattDiscovery final : public bluez::BluezObjectManager {
absl::Mutex mutex_;
absl::flat_hash_map>
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,
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 {
diff --git a/internal/platform/implementation/linux/ble_gatt_server.cc b/internal/platform/implementation/linux/ble_gatt_server.cc
index cc761c22..ee0a0f40 100644
--- a/internal/platform/implementation/linux/ble_gatt_server.cc
+++ b/internal/platform/implementation/linux/ble_gatt_server.cc
@@ -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
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 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 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
diff --git a/internal/platform/implementation/linux/ble_gatt_server.h b/internal/platform/implementation/linux/ble_gatt_server.h
index 1343a4be..2d41b4c5 100644
--- a/internal/platform/implementation/linux/ble_gatt_server.h
+++ b/internal/platform/implementation/linux/ble_gatt_server.h
@@ -16,6 +16,7 @@
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
#include
+#include
#include
#include
@@ -69,7 +70,16 @@ class GattServer : public api::ble::GattServer {
sdbus::ObjectPath("/com/google/nearby/medium/ble/gatt"))),
gatt_manager_(std::make_unique(system_bus_, adapter_.GetObjectPath())),
server_cb_(std::make_shared(
- std::move(server_cb))) {}
+ std::move(server_cb))) {
+ gatt_manager_->SetRegisterApplicationReplyCallback(
+ [this](std::optional error) {
+ OnRegisterApplicationReply(std::move(error));
+ });
+ gatt_manager_->SetUnregisterApplicationReplyCallback(
+ [this](std::optional error) {
+ OnUnregisterApplicationReply(std::move(error));
+ });
+ }
~GattServer() override = default;
absl::optional CreateCharacteristic(
@@ -85,6 +95,9 @@ class GattServer : public api::ble::GattServer {
void Stop() override;
private:
+ void OnRegisterApplicationReply(std::optional error);
+ void OnUnregisterApplicationReply(std::optional error);
+
sdbus::IConnection& system_bus_;
std::shared_ptr devices_;
BluetoothAdapter adapter_;
@@ -93,9 +106,20 @@ class GattServer : public api::ble::GattServer {
std::unique_ptr gatt_service_root_object_manager;
absl::Mutex profiles_mutex_;
absl::flat_hash_map> gatt_profiles_;
- ABSL_GUARDED_BY(profiles_mutex_)
+ ABSL_GUARDED_BY(profiles_mutex_);
std::unique_ptr gatt_manager_;
std::shared_ptr 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 register_application_call_
+ ABSL_GUARDED_BY(registration_mutex_);
+ std::optional unregister_application_call_
+ ABSL_GUARDED_BY(registration_mutex_);
absl::Mutex services_mutex_;
absl::flat_hash_map> services_
ABSL_GUARDED_BY(services_mutex_);
diff --git a/internal/platform/implementation/linux/ble_v2_medium.cc b/internal/platform/implementation/linux/ble_v2_medium.cc
index 50d0eb2c..6e0892bb 100644
--- a/internal/platform/implementation/linux/ble_v2_medium.cc
+++ b/internal/platform/implementation/linux/ble_v2_medium.cc
@@ -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(system_bus_)),
observers_(std::make_shared>()),
devices_(std::make_unique(
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 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 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 BleV2Medium::StartGattServer(
api::ble::ServerGattConnectionCallback callback) {
- // (void)callback;
- // return nullptr;
-
- return std::make_unique(
- *system_bus_, adapter_, devices_,std::move(callback)
- );
+ LOG(INFO) << __func__ << ": Starting Linux GATT server.";
+ return std::make_unique(*system_bus_, adapter_, devices_,
+ std::move(callback));
}
std::unique_ptr BleV2Medium::ConnectToGattServer(
@@ -452,8 +482,7 @@ std::unique_ptr 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;
}
diff --git a/internal/platform/implementation/linux/ble_v2_medium.h b/internal/platform/implementation/linux/ble_v2_medium.h
index e9c3baea..686013ee 100644
--- a/internal/platform/implementation/linux/ble_v2_medium.h
+++ b/internal/platform/implementation/linux/ble_v2_medium.h
@@ -17,8 +17,11 @@
#include
#include
+#include
#include
+#include "absl/base/thread_annotations.h"
+#include "absl/synchronization/notification.h"
#include
#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 error);
bool MonitorManagerSupportsOr() {
std::vector supported_types;
@@ -148,6 +153,14 @@ class BleV2Medium final : public api::ble::BleMedium {
std::unique_ptr root_object_manager_;
std::unique_ptr 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,
diff --git a/internal/platform/implementation/linux/bluetooth_classic_medium.cc b/internal/platform/implementation/linux/bluetooth_classic_medium.cc
index 0d3d23f7..208fb28e 100644
--- a/internal/platform/implementation/linux/bluetooth_classic_medium.cc
+++ b/internal/platform/implementation/linux/bluetooth_classic_medium.cc
@@ -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(*system_bus_)),
- profile_manager_(nullptr) {
- auto shared =
- GetSharedBluetoothDevices(system_bus_, adapter_.GetObjectPath());
- observers_ = shared->observers;
- devices_ = shared->devices;
- profile_manager_ =
- std::make_unique(*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(*system_bus_)),
+ profile_manager_(nullptr) {
+ auto shared =
+ GetSharedBluetoothDevices(system_bus_, adapter_.GetObjectPath());
+ observers_ = shared->observers;
+ devices_ = shared->devices;
+ profile_manager_ =
+ std::make_unique(*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 BluetoothClassicMedium::ConnectToService(
new BluetoothSocket(device, fd.value()));
}
-std::unique_ptr
-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
+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;
diff --git a/internal/platform/implementation/linux/bluetooth_classic_medium.h b/internal/platform/implementation/linux/bluetooth_classic_medium.h
index 42edf168..0320d765 100644
--- a/internal/platform/implementation/linux/bluetooth_classic_medium.h
+++ b/internal/platform/implementation/linux/bluetooth_classic_medium.h
@@ -26,7 +26,7 @@
#include
#include
-// #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 devices_;
std::unique_ptr device_watcher_;
- // std::unique_ptr agent_manager_;
+ std::unique_ptr agent_manager_;
std::unique_ptr profile_manager_;
};
diff --git a/internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h b/internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h
index 5c285fbb..3db1e3c0 100644
--- a/internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h
+++ b/internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h
@@ -15,9 +15,13 @@
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
+#include
+
#include
#include
+#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)>;
+
private:
friend std::unique_ptr
std::make_unique(
@@ -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
DiscoverAdvertisementMonitorManager(
sdbus::IConnection &system_bus,
@@ -82,6 +99,38 @@ class AdvertisementMonitorManager final
return std::make_unique(system_bus, adapter);
}
+
+ protected:
+ void onRegisterMonitorReply(std::optional error) override {
+ std::optional 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 error) override {
+ std::optional 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 register_monitor_reply_callback_
+ ABSL_GUARDED_BY(callbacks_mutex_);
+ std::optional unregister_monitor_reply_callback_
+ ABSL_GUARDED_BY(callbacks_mutex_);
};
} // namespace bluez
} // namespace linux
diff --git a/internal/platform/implementation/linux/bluez_agent.cc b/internal/platform/implementation/linux/bluez_agent.cc
new file mode 100644
index 00000000..ba1cc14b
--- /dev/null
+++ b/internal/platform/implementation/linux/bluez_agent.cc
@@ -0,0 +1,99 @@
+#include "internal/platform/implementation/linux/bluez_agent.h"
+
+#include
+
+#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 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(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
diff --git a/internal/platform/implementation/linux/bluez_agent.h b/internal/platform/implementation/linux/bluez_agent.h
new file mode 100644
index 00000000..c40f79f7
--- /dev/null
+++ b/internal/platform/implementation/linux/bluez_agent.h
@@ -0,0 +1,85 @@
+#pragma once
+
+#include
+#include