merged abandoned linux port

This commit is contained in:
kidfromjupiter
2025-12-29 04:27:15 +00:00
186 changed files with 23004 additions and 0 deletions
@@ -0,0 +1,282 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"])
cc_library(
name = "types",
hdrs = [
"atomic_boolean.h",
"atomic_reference.h",
"atomic_uint32.h",
"bluetooth_adapter.h",
"condition_variable.h",
"device_info.h",
"executor.h",
"future.h",
"mutex.h",
"preferences_manager.h",
"preferences_repository.h",
"scheduled_executor.h",
"submittable_executor.h",
"timer.h",
"thread_pool.h",
"log_message.h",
"utils.h",
],
srcs = [
"device_info.cc",
"log_message.cc",
"timer.cc",
],
copts = ["-lrt"],
visibility = ["//third_party/nearby/sharing/internal/impl/linux:__pkg__"],
deps = [
":comm",
"//internal/platform/implementation:types",
"@com_google_absl//absl/strings",
"@libsystemd//:lib",
"@sdbus_cpp//:lib",
],
)
cc_library(
name = "comm",
hdrs = [
"avahi.h",
"ble_gatt_server.h",
"ble_gatt_client.h",
"ble_medium.h",
"ble_v2_medium.h",
"ble_v2_server_socket.h",
"bluetooth_adapter.h",
"bluetooth_bluez_profile.h",
"bluetooth_classic_device.h",
"bluetooth_classic_medium.h",
"bluetooth_classic_server_socket.h",
"bluetooth_classic_socket.h",
"bluetooth_devices.h",
"bluetooth_pairing.h",
"bluez.h",
"bluez_advertisement_monitor.h",
"bluez_advertisement_monitor_manager.h",
"bluez_gatt_characteristic_client.h",
"bluez_gatt_characteristic_server.h",
"bluez_gatt_manager.h",
"bluez_gatt_service_client.h",
"bluez_gatt_service_server.h",
"bluez_le_advertisement.h",
"dbus.h",
"network_manager.h",
"network_manager_active_connection.h",
"network_manager_access_point.h",
"stream.h",
"tcp_server_socket.h",
"wifi_direct.h",
"wifi_direct_server_socket.h",
"wifi_direct_socket.h",
"wifi_hotspot.h",
"wifi_hotspot_server_socket.h",
"wifi_hotspot_socket.h",
"wifi_lan.h",
"wifi_lan_server_socket.h",
"wifi_lan_socket.h",
"wifi_medium.h",
"wifi_socket.h",
],
deps = [
"//internal/platform:base",
"//internal/platform:comm",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:types",
"//internal/platform/implementation/linux/generated:types",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@libsystemd//:lib",
"@sdbus_cpp//:lib",
],
visibility = ["//visibility:private"],
)
cc_library(
name = "crypto",
srcs = [
"crypto.cc",
],
visibility = ["//visibility:private"],
deps = [
"//internal/platform:base",
"//internal/platform/implementation:types",
"@boringssl//:crypto",
"@com_google_absl//absl/strings",
])
cc_library(
name = "linux",
srcs = [
"avahi.cc",
"ble_gatt_client.cc",
"ble_gatt_server.cc",
"ble_v2_medium.cc",
"bluetooth_adapter.cc",
"bluetooth_bluez_profile.cc",
"bluetooth_classic_socket.cc",
"bluetooth_classic_device.cc",
"bluetooth_classic_medium.cc",
"bluetooth_classic_server_socket.cc",
"bluetooth_devices.cc",
"bluetooth_pairing.cc",
"bluez.cc",
"bluez_advertisement_monitor.cc",
"bluez_gatt_characteristic_client.cc",
"bluez_gatt_characteristic_server.cc",
"bluez_gatt_service_server.cc",
"bluez_le_advertisement.cc",
"dbus.cc",
"executor.cc",
"network_manager.cc",
"network_manager_active_connection.cc",
"platform.cc",
"preferences_manager.cc",
"preferences_repository.cc",
"scheduled_executor.cc",
"stream.cc",
"submittable_executor.cc",
"system_clock.cc",
"thread_pool.cc",
"utils.cc",
"wifi_direct.cc",
"wifi_direct_server_socket.cc",
"wifi_hotspot.cc",
"wifi_hotspot_server_socket.cc",
"wifi_lan.cc",
"wifi_lan_server_socket.cc",
"wifi_medium.cc",
],
visibility = [
"//connections:__subpackages__",
"//fastpair:__subpackages__",
"//location/nearby:__subpackages__",
"//presence:__subpackages__",
"//third_party/nearby/sharing:__subpackages__",
],
deps = [
":comm",
":crypto", # build_cleaner: keep
":types",
"//internal/flags:nearby_flags",
"//internal/platform:base",
"//internal/platform:cancellation_flag",
"//internal/platform:comm",
"//internal/platform:types",
"//internal/platform:uuid",
"//internal/platform/flags:platform_flags",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:platform",
"//internal/platform/implementation:types",
"//internal/platform/implementation/shared:count_down_latch",
"//internal/platform/implementation/shared:file",
"//internal/platform/implementation/linux/generated:types",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/functional:any_invocable",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
"@nlohmann_json//:json",
"@libsystemd//:lib",
"@sdbus_cpp//:lib",
"@libcurl//:lib"
],
)
cc_library(
name = "test_utils",
srcs = [
"test_utils.cc",
],
hdrs = [
"test_data.h",
"test_utils.h",
],
visibility = [
"//visibility:private", # Only private by automation, not intent. Owner may accept CLs adding visibility. See go/scheuklappen#explicit-private.
],
deps = [
"//internal/platform:base",
"@nlohmann_json//:json",
":types",
],
)
cc_test(
name = "impl_test",
size = "small",
srcs = [
"atomic_boolean_test.cc",
"atomic_reference_test.cc",
"mutex_test.cc",
"utils_test.cc",
# "bluetooth_adapter_test.cc",
# "crypto_test.cc",
# "device_info_test.cc",
# "executor_test.cc",
# "file_path_test.cc",
# "http_loader_test.cc",
# "preferences_manager_test.cc",
# "preferences_repository_test.cc",
# "scheduled_executor_test.cc",
# "submittable_executor_test.cc",
# "thread_pool_test.cc",
# "timer_test.cc",
],
tags = ["notap"],
deps = [
":comm",
":crypto",
":test_utils",
":types",
":linux",
"//internal/platform:base",
"//internal/platform/implementation:comm",
"//internal/platform/implementation:platform",
"//internal/platform/implementation:types",
"//internal/platform/implementation/shared:count_down_latch",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:span",
"@com_google_googletest//:gtest_main",
"@nlohmann_json//:json",
],
)
@@ -0,0 +1,41 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_
#define PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_
#include <atomic>
#include "internal/platform/implementation/atomic_boolean.h"
namespace nearby {
namespace linux {
// A boolean value that may be updated atomically.
class AtomicBoolean : public api::AtomicBoolean {
public:
AtomicBoolean(bool initial_value = false) : atomic_boolean_(initial_value) {}
~AtomicBoolean() override = default;
// Atomically read and return current value.
bool Get() const override { return atomic_boolean_; };
// Atomically exchange original value with a new one. Return previous value.
bool Set(bool value) override { return atomic_boolean_.exchange(value); };
private:
std::atomic_bool atomic_boolean_ = false;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_ATOMIC_BOOLEAN_H_
@@ -0,0 +1,32 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/atomic_boolean.h"
#include "gtest/gtest.h"
TEST(atomic_boolean, SuccessfulCreation) {
// Arrange
nearby::linux::AtomicBoolean atomicBoolean;
bool oldValue = true;
bool result = false;
// Act
oldValue = atomicBoolean.Set(true);
result = atomicBoolean.Get();
// Assert
EXPECT_TRUE(result);
EXPECT_FALSE(oldValue);
}
@@ -0,0 +1,43 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_
#define PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_
#include <atomic>
#include "internal/platform/implementation/atomic_reference.h"
namespace nearby {
namespace linux {
// Type that allows 32-bit atomic reads and writes.
class AtomicUint32 : public api::AtomicUint32 {
public:
~AtomicUint32() override = default;
// Atomically reads and returns stored value.
std::uint32_t Get() const override { return atomic_uint32_; };
// Atomically stores value.
void Set(std::uint32_t value) override { atomic_uint32_ = value; }
private:
std::atomic_int32_t atomic_uint32_ = 0;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_ATOMIC_REFERENCE_H_
@@ -0,0 +1,72 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/atomic_reference.h"
#include "gtest/gtest.h"
TEST(atomic_reference, SuccessfulCreation) {
// Arrange
nearby::linux::AtomicUint32 atomicUint32;
uint32_t result = UINT32_MAX;
const uint32_t expected = 0;
// Act
result = atomicUint32.Get();
// Assert
EXPECT_EQ(result, expected);
}
TEST(atomic_reference, SuccessfulMaxSet) {
// Arrange
nearby::linux::AtomicUint32 atomicUint32;
uint32_t result = 0;
const uint32_t expected = UINT32_MAX;
// Act
atomicUint32.Set(UINT32_MAX);
result = atomicUint32.Get();
// Assert
EXPECT_EQ(result, expected);
}
TEST(atomic_reference, SuccessfulMinSet) {
// Arrange
nearby::linux::AtomicUint32 atomicUint32;
uint32_t result = UINT32_MAX;
const uint32_t expected = 0;
// Act
atomicUint32.Set(0);
result = atomicUint32.Get();
// Assert
EXPECT_EQ(result, expected);
}
TEST(atomic_reference, SetNegativeOneReturnsMAXUINT) {
// Arrange
nearby::linux::AtomicUint32 atomicUint32;
uint32_t result = 0;
const uint32_t expected = UINT32_MAX;
// Act
atomicUint32.Set(-1); // Try Set -1, should actually store UINT32_MAX
result = atomicUint32.Get();
// Assert
EXPECT_EQ(result, expected);
}
@@ -0,0 +1,42 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_ATOMIC_UINT32_H_
#define PLATFORM_IMPL_LINUX_ATOMIC_UINT32_H_
#include <atomic>
#include <cstdint>
#include "internal/platform/implementation/atomic_reference.h"
namespace nearby {
namespace linux {
// A boolean value that may be updated atomically.
class AtomicUint32 : public api::AtomicUint32 {
public:
AtomicUint32(std::uint32_t initial_value) : atomic_uint_(initial_value) {}
~AtomicUint32() override = default;
// Atomically read and return current value.
std::uint32_t Get() const override { return atomic_uint_; };
// Atomically exchange original value with a new one. Return previous value.
void Set(std::uint32_t value) override { atomic_uint_ = value; };
private:
std::atomic_bool atomic_uint_ = false;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,128 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/avahi.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/logging.h"
#include "internal/platform/nsd_service_info.h"
namespace nearby {
namespace linux {
namespace avahi {
void ServiceBrowser::onItemNew(const int32_t &interface,
const int32_t &protocol, const std::string &name,
const std::string &type,
const std::string &domain,
const uint32_t &flags) {
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": Found new item through the ServiceBrowser: "
<< "interface: " << interface << ", protocol: "
<< protocol << ", name: '" << name << "', type: '"
<< type << "', domain: '" << domain
<< "', flags: " << flags;
if (flags & kAvahiLookupResultLocal) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Ignoring local service.";
return;
}
NsdServiceInfo info;
try {
auto [r_iface, r_protocol, r_name, r_type, r_domain, r_host, r_aprotocol,
r_address, r_port, r_txt, r_flags] =
server_->ResolveService(interface, protocol, name, type, domain,
0, // AVAHI_PROTO_INET
0);
info.SetServiceName(r_name);
info.SetIPAddress(r_address);
info.SetPort(r_port);
info.SetServiceType(r_type);
for (auto &attr : r_txt) {
auto attr_str = std::string(attr.begin(), attr.end());
size_t pos = attr_str.find('=');
if (pos == 0 || pos == std::string::npos || pos == attr_str.size() - 1) {
NEARBY_LOGS(WARNING) << " found invalid text attribute: " << attr_str;
continue;
}
info.SetTxtRecord(attr_str.substr(0, pos), attr_str.substr(pos + 1));
}
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(server_, "ResolveService", e);
}
discovery_cb_.service_discovered_cb(std::move(info));
}
void ServiceBrowser::onItemRemove(
const int32_t &interface, const int32_t &protocol, const std::string &name,
const std::string &type, const std::string &domain, const uint32_t &flags) {
// TODO: Can we even resolve removed items?
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": Item removed through the ServiceBrowser: "
<< "interface: " << interface << ", protocol: "
<< protocol << ", name: '" << name << "', type: '"
<< type << "', domain: '" << domain
<< "', flags: " << flags;
if (flags & kAvahiLookupResultLocal) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Ignoring local service.";
return;
}
NsdServiceInfo info;
try {
auto [r_iface, r_protocol, r_name, r_type, r_domain, r_host, r_aprotocol,
r_address, r_port, r_txt, r_flags] =
server_->ResolveService(interface, protocol, name, type, domain,
0, // AVAHI_PROTO_INET
flags);
info.SetServiceName(r_name);
info.SetIPAddress(r_address);
info.SetPort(r_port);
info.SetServiceType(r_type);
for (auto &attr : r_txt) {
auto attr_str = std::string(attr.begin(), attr.end());
size_t pos = attr_str.find('=');
if (pos == 0 || pos == std::string::npos || pos == attr_str.size() - 1) {
NEARBY_LOGS(WARNING) << " found invalid text attribute: " << attr_str;
continue;
}
info.SetTxtRecord(attr_str.substr(0, pos), attr_str.substr(pos + 1));
}
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(server_, "ResolveService", e);
}
discovery_cb_.service_lost_cb(std::move(info));
}
void ServiceBrowser::onFailure(const std::string &error) {
NEARBY_LOGS(ERROR) << __func__ << ": " << getObjectPath()
<< ": ServiceBrowser reported a failure: " << error;
}
void ServiceBrowser::onAllForNow() {
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": notified via ServiceBrowser that all records have "
"been added for now";
}
void ServiceBrowser::onCacheExhausted() {
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": notified via ServiceBrowser of cache exhaustion";
}
} // namespace avahi
} // namespace linux
} // namespace nearby
@@ -0,0 +1,127 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_AVAHI_H_
#define PLATFORM_IMPL_LINUX_AVAHI_H_
#include <memory>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/avahi/entrygroup_client.h"
#include "internal/platform/implementation/linux/generated/dbus/avahi/server2_client.h"
#include "internal/platform/implementation/linux/generated/dbus/avahi/servicebrowser_client.h"
#include "internal/platform/implementation/wifi_lan.h"
namespace nearby {
namespace linux {
namespace avahi {
class Server final
: public sdbus::ProxyInterfaces<org::freedesktop::Avahi::Server2_proxy> {
public:
Server(sdbus::IConnection &system_bus)
: ProxyInterfaces(system_bus, "org.freedesktop.Avahi", "/") {
registerProxy();
}
~Server() { unregisterProxy(); }
protected:
void onStateChanged(const int32_t &state, const std::string &error) override {
}
};
class EntryGroup final
: public sdbus::ProxyInterfaces<org::freedesktop::Avahi::EntryGroup_proxy> {
public:
EntryGroup(sdbus::IConnection &system_bus,
const sdbus::ObjectPath &entry_group_object_path)
: ProxyInterfaces(system_bus, "org.freedesktop.Avahi",
entry_group_object_path) {
registerProxy();
}
~EntryGroup() {
NEARBY_LOGS(VERBOSE) << __func__ << ": Freeing entry group "
<< getObjectPath();
try {
Free();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(this, "Free", e);
}
unregisterProxy();
}
protected:
void onStateChanged(const int32_t &state, const std::string &error) override {
}
};
class ServiceBrowser final
: public sdbus::ProxyInterfaces<
org::freedesktop::Avahi::ServiceBrowser_proxy> {
public:
ServiceBrowser(sdbus::IConnection &system_bus,
const sdbus::ObjectPath &service_browser_object_path,
api::WifiLanMedium::DiscoveredServiceCallback callback,
std::shared_ptr<Server> avahi_server)
: ProxyInterfaces(system_bus, "org.freedesktop.Avahi",
service_browser_object_path),
discovery_cb_(std::move(callback)),
server_(avahi_server) {
registerProxy();
}
~ServiceBrowser() {
NEARBY_LOGS(VERBOSE) << __func__ << ": Freeing service browser "
<< getObjectPath();
try {
Free();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(this, "Free", e);
}
unregisterProxy();
}
protected:
void onItemNew(const int32_t &interface, const int32_t &protocol,
const std::string &name, const std::string &type,
const std::string &domain, const uint32_t &flags) override;
void onItemRemove(const int32_t &interface, const int32_t &protocol,
const std::string &name, const std::string &type,
const std::string &domain, const uint32_t &flags) override;
void onFailure(const std::string &error) override;
void onAllForNow() override;
void onCacheExhausted() override;
private:
enum LookupResultFlags {
kAvahiLookupResultFlagCached = 1,
kAvahiLookupResultFlagWideArea = 2,
kAvahiLookupResultFlagMulticast = 4,
kAvahiLookupResultLocal = 8,
kAvahiLookupResultOurOwn = 16,
kAvahiLookupResultStatic = 32,
};
api::WifiLanMedium::DiscoveredServiceCallback discovery_cb_;
std::shared_ptr<Server> server_;
};
} // namespace avahi
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,428 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <map>
#include <string>
#include <variant>
#include <sdbus-c++/Types.h>
#include "absl/strings/substitute.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/ble_gatt_client.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_client.h"
#include "internal/platform/implementation/linux/bluez_gatt_service_client.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_client.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_service_client.h"
#include "internal/platform/implementation/linux/utils.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
bool GattClient::DiscoverServiceAndCharacteristics(
const Uuid &service_uuid, const std::vector<Uuid> &characteristic_uuids) {
return gatt_discovery_->DiscoverServiceAndCharacteristics(
peripheral_object_path_, service_uuid, characteristic_uuids,
discovery_cancel_);
}
absl::optional<api::ble_v2::GattCharacteristic> GattClient::GetCharacteristic(
const Uuid &service_uuid, const Uuid &characteristic_uuid) {
auto chr_proxy = gatt_discovery_->GetCharacteristic(
peripheral_object_path_, service_uuid, characteristic_uuid);
if (chr_proxy == nullptr) return std::nullopt;
api::ble_v2::GattCharacteristic chr;
chr.service_uuid = service_uuid;
chr.uuid = characteristic_uuid;
chr.property = api::ble_v2::GattCharacteristic::Property::kNone;
chr.permission = api::ble_v2::GattCharacteristic::Permission::kNone;
std::vector<std::string> flags;
try {
flags = chr_proxy->Flags();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(chr_proxy, "Flags", e);
return std::nullopt;
}
for (const auto &flag : flags) {
if (flag == "read") {
chr.property |= api::ble_v2::GattCharacteristic::Property::kRead;
chr.permission |= api::ble_v2::GattCharacteristic::Permission::kRead;
} else if (flag == "write") {
chr.property |= api::ble_v2::GattCharacteristic::Property::kWrite;
chr.permission |= api::ble_v2::GattCharacteristic::Permission::kWrite;
} else if (flag == "notify") {
chr.property |= api::ble_v2::GattCharacteristic::Property::kNotify;
} else if (flag == "indicate") {
chr.property |= api::ble_v2::GattCharacteristic::Property::kIndicate;
}
}
absl::MutexLock lock(&characteristics_mutex_);
characteristics_.emplace(chr, std::move(chr_proxy));
return chr;
}
absl::optional<std::string> GattClient::ReadCharacteristic(
const api::ble_v2::GattCharacteristic &characteristic) {
absl::ReaderMutexLock lock(&characteristics_mutex_);
if (characteristics_.count(characteristic) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown characteristic '"
<< absl::Substitute("$0", characteristic) << "'";
return std::nullopt;
}
return std::visit(
[](auto &&chr) {
try {
auto value_bytes = chr->ReadValue({});
return std::optional<std::string>{
std::string(value_bytes.begin(), value_bytes.end())};
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(chr, "ReadValue", e);
return std::optional<std::string>();
}
},
characteristics_[characteristic]);
}
bool GattClient::WriteCharacteristic(
const api::ble_v2::GattCharacteristic &characteristic,
absl::string_view value, WriteType type) {
absl::ReaderMutexLock lock(&characteristics_mutex_);
if (characteristics_.count(characteristic) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown characteristic '"
<< absl::Substitute("$0", characteristic) << "'";
return false;
}
return std::visit(
[value, type](auto &&chr) {
std::vector<uint8_t> value_bytes(value.begin(), value.end());
try {
chr->WriteValue(
value_bytes,
{{"type",
type == api::ble_v2::GattClient::WriteType::kWithResponse
? "request"
: "command"}});
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(chr, "WriteValue", e);
return false;
}
},
characteristics_[characteristic]);
}
bool GattClient::SetCharacteristicSubscription(
const api::ble_v2::GattCharacteristic &characteristic, bool enable,
absl::AnyInvocable<void(absl::string_view value)>
on_characteristic_changed_cb) {
absl::MutexLock lock(&characteristics_mutex_);
if (characteristics_.count(characteristic) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Unknown characteristic '"
<< absl::Substitute("$0", characteristic) << "'";
return false;
}
if (enable) {
auto subbed_chr = gatt_discovery_->GetSubscribedCharacteristic(
peripheral_object_path_, characteristic.service_uuid,
characteristic.uuid, std::move(on_characteristic_changed_cb));
if (subbed_chr == nullptr) return false;
try {
subbed_chr->StartNotify();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(subbed_chr, "StartNotify", e);
return false;
}
characteristics_[characteristic] = std::move(subbed_chr);
} else if (std::holds_alternative<
std::unique_ptr<bluez::SubscribedGattCharacteristicClient>>(
characteristics_[characteristic])) {
auto chr = gatt_discovery_->GetCharacteristic(peripheral_object_path_,
characteristic.service_uuid,
characteristic.uuid);
if (chr == nullptr) return false;
try {
chr->StopNotify();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(chr, "StopNotify", e);
return false;
}
characteristics_[characteristic] = std::move(chr);
}
return true;
}
void GattClient::Disconnect() {
absl::MutexLock lock(&disconnected_callback_mutex_);
if (!discovery_cancel_.Cancelled()) {
discovery_cancel_.Cancel();
if (*disconnected_callback_it_ != nullptr) (*disconnected_callback_it_)();
gatt_discovery_->RemovePeripheralConnection(peripheral_object_path_,
disconnected_callback_it_);
}
}
void BluezGattDiscovery::Shutdown() {
auto no_discovery = [&]() {
mutex_.AssertReaderHeld();
return pending_discovery_ == 0;
};
mutex_.Lock();
shutdown_ = true;
mutex_.Await(absl::Condition(&no_discovery));
mutex_.Unlock();
}
bool BluezGattDiscovery::InitializeKnownServices() {
std::map<sdbus::ObjectPath,
std::map<std::string, std::map<std::string, sdbus::Variant>>>
objects;
try {
objects = GetManagedObjects();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(this, "GetManagedObjects", e);
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(),
[](std::pair<sdbus::ObjectPath,
std::map<std::string, std::map<std::string, sdbus::Variant>>>
object) {
return object.second.count(
org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME) == 1;
});
for (; chr_it != objects.cend(); chr_it++) {
const auto &[path, ifaces] = *chr_it;
const auto &properties =
ifaces.at(org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME);
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;
}
BluezGattDiscovery::CallbackIter BluezGattDiscovery::AddPeripheralConnection(
const sdbus::ObjectPath &device_object_path,
absl::AnyInvocable<void()> disconnected_callback_) {
absl::MutexLock lock(&peripheral_disconnected_callbacks_mutex_);
if (peripheral_disconnected_callbacks_.count(device_object_path) == 0)
peripheral_disconnected_callbacks_.emplace(
device_object_path, std::list<absl::AnyInvocable<void()>>{});
auto &list = peripheral_disconnected_callbacks_[device_object_path];
list.push_back(std::move(disconnected_callback_));
return list.begin();
}
void BluezGattDiscovery::RemovePeripheralConnection(
const sdbus::ObjectPath &device_object_path,
BluezGattDiscovery::CallbackIter cb) {
absl::MutexLock lock(&peripheral_disconnected_callbacks_mutex_);
auto it = peripheral_disconnected_callbacks_.find(device_object_path);
if (it != peripheral_disconnected_callbacks_.end()) {
it->second.erase(cb);
if (it->second.empty())
peripheral_disconnected_callbacks_.erase(device_object_path);
}
}
bool BluezGattDiscovery::DiscoverServiceAndCharacteristics(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const std::vector<Uuid> &characteristic_uuids, CancellationFlag &cancel) {
CancellationFlagListener cancel_listen(&cancel, [&]() {
mutex_.Lock();
mutex_.Unlock();
});
auto discovered = [this, device_object_path, service_uuid,
characteristic_uuids, &cancel]() {
mutex_.AssertReaderHeld();
return cancel.Cancelled() ||
std::all_of(
characteristic_uuids.cbegin(), characteristic_uuids.cend(),
[this, service_uuid, device_object_path](auto &chr_uuid) {
mutex_.AssertReaderHeld();
return discovered_characteristics_.count(
{service_uuid, chr_uuid, device_object_path}) == 1;
});
};
absl::ReaderMutexLock lock(&mutex_, absl::Condition(&discovered));
return !cancel.Cancelled();
}
std::unique_ptr<bluez::GattCharacteristicClient>
BluezGattDiscovery::GetCharacteristic(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const Uuid &characteristic_uuid) {
auto key =
std::make_tuple(service_uuid, characteristic_uuid, device_object_path);
absl::ReaderMutexLock lock(&mutex_);
auto path_it = discovered_characteristics_.find(key);
if (path_it == discovered_characteristics_.end()) {
NEARBY_LOGS(ERROR) << __func__ << ": No characteristic known for device "
<< device_object_path << " with service "
<< std::string{service_uuid} << " and UUID "
<< std::string{characteristic_uuid};
return nullptr;
}
return std::make_unique<bluez::GattCharacteristicClient>(system_bus_,
path_it->second);
}
std::unique_ptr<bluez::GattCharacteristicClient>
BluezGattDiscovery::GetSubscribedCharacteristic(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const Uuid &characteristic_uuid,
absl::AnyInvocable<void(absl::string_view value)>
on_characteristic_changed_cb) {
auto key =
std::make_tuple(service_uuid, characteristic_uuid, device_object_path);
absl::ReaderMutexLock lock(&mutex_);
auto path_it = discovered_characteristics_.find(key);
if (path_it == discovered_characteristics_.end()) {
NEARBY_LOGS(ERROR) << __func__ << ": No characteristic known for device "
<< device_object_path << " with service "
<< std::string{service_uuid} << " and UUID "
<< std::string{characteristic_uuid};
return nullptr;
}
return std::make_unique<bluez::SubscribedGattCharacteristicClient>(
system_bus_, device_object_path, std::move(on_characteristic_changed_cb));
}
std::optional<std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
BluezGattDiscovery::characteristicProperties(
const sdbus::ObjectPath &path,
const std::map<std::string, sdbus::Variant> &properties) {
mutex_.AssertHeld();
const std::string &chr_uuid_str = properties.at("UUID");
auto chr_uuid = UuidFromString(chr_uuid_str);
if (!chr_uuid.has_value()) {
NEARBY_LOGS(ERROR) << ": Couldn't parse UUID '" << chr_uuid_str
<< "' in characteristic " << path;
return std::nullopt;
}
const sdbus::ObjectPath &service_path = properties.at("Service");
if (cached_services_.count(service_path) == 0) {
cached_services_.emplace(
path, std::make_unique<GattServiceClient>(system_bus_, path));
}
auto &service = cached_services_.at(service_path);
nearby::Uuid service_uuid;
try {
const std::string &service_uuid_str = service->UUID();
auto service_uuid_maybe = UuidFromString(service_uuid_str);
if (!service_uuid_maybe.has_value()) {
NEARBY_LOGS(ERROR) << ": Couldn't parse UUID '" << service_uuid_str
<< "' in service " << service_path;
return std::nullopt;
}
service_uuid = *service_uuid_maybe;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(service, "UUID", e);
return std::nullopt;
}
sdbus::ObjectPath device_path;
try {
device_path = service->Device();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(service, "Device", e);
return std::nullopt;
}
return std::make_tuple(*chr_uuid, service_uuid, device_path);
}
void BluezGattDiscovery::onInterfacesAdded(
const sdbus::ObjectPath &objectPath,
const std::map<std::string, std::map<std::string, sdbus::Variant>>
&interfacesAndProperties) {
if (interfacesAndProperties.count(
org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME) == 0)
return;
const auto &properties = interfacesAndProperties.at(
org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME);
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;
discovered_characteristics_.emplace(
std::make_tuple(chr_uuid, service_uuid, device_path), objectPath);
characteristics_properties_.emplace(
objectPath, std::make_tuple(chr_uuid, service_uuid, device_path));
}
void BluezGattDiscovery::onInterfacesRemoved(
const sdbus::ObjectPath &objectPath,
const std::vector<std::string> &interfaces) {
auto begin = interfaces.cbegin();
auto end = interfaces.cend();
auto service_it =
std::find(begin, end, org::bluez::GattService1_proxy::INTERFACE_NAME);
if (service_it != end) {
absl::MutexLock lock(&mutex_);
cached_services_.erase(objectPath);
return;
}
auto chr_it = std::find(
begin, end, org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME);
if (chr_it != end) {
absl::MutexLock lock(&mutex_);
{
auto &props = characteristics_properties_.at(objectPath);
discovered_characteristics_.erase(props);
}
characteristics_properties_.erase(objectPath);
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,210 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLE_GATT_CLIENT_H_
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_CLIENT_H_
#include <list>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/Types.h>
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_client.h"
#include "internal/platform/implementation/linux/bluez_gatt_service_client.h"
namespace nearby {
namespace linux {
class BluezGattDiscovery final : public bluez::BluezObjectManager {
public:
explicit BluezGattDiscovery(std::shared_ptr<sdbus::IConnection> system_bus)
: bluez::BluezObjectManager(*system_bus),
system_bus_(system_bus),
shutdown_(false),
pending_discovery_(0) {}
~BluezGattDiscovery() override { Shutdown(); }
bool InitializeKnownServices() ABSL_LOCKS_EXCLUDED(mutex_);
using CallbackIter = typename std::list<absl::AnyInvocable<void()>>::iterator;
CallbackIter AddPeripheralConnection(
const sdbus::ObjectPath &device_object_path,
absl::AnyInvocable<void()> disconnected_callback_)
ABSL_LOCKS_EXCLUDED(peripheral_disconnected_callbacks_mutex_);
void RemovePeripheralConnection(const sdbus::ObjectPath &device_object_path,
BluezGattDiscovery::CallbackIter cb)
ABSL_LOCKS_EXCLUDED(peripheral_disconnected_callbacks_mutex_);
bool DiscoverServiceAndCharacteristics(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const std::vector<Uuid> &characteristic_uuids, CancellationFlag &cancel)
ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<bluez::GattCharacteristicClient> GetCharacteristic(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const Uuid &characteristic_uuid) ABSL_LOCKS_EXCLUDED(mutex_);
std::unique_ptr<bluez::GattCharacteristicClient> GetSubscribedCharacteristic(
const sdbus::ObjectPath &device_object_path, const Uuid &service_uuid,
const Uuid &characteristic_uuid,
absl::AnyInvocable<void(absl::string_view value)>
on_characteristic_changed_cb) ABSL_LOCKS_EXCLUDED(mutex_);
protected:
void onInterfacesAdded(
const sdbus::ObjectPath &objectPath,
const std::map<std::string, std::map<std::string, sdbus::Variant>>
&interfacesAndProperties) override ABSL_LOCKS_EXCLUDED(mutex_);
void onInterfacesRemoved(const sdbus::ObjectPath &objectPath,
const std::vector<std::string> &interfaces) override
ABSL_LOCKS_EXCLUDED(mutex_);
private:
std::optional<std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
characteristicProperties(
const sdbus::ObjectPath &path,
const std::map<std::string, sdbus::Variant> &properties)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void Shutdown() ABSL_LOCKS_EXCLUDED(mutex_);
std::shared_ptr<sdbus::IConnection> system_bus_;
absl::Mutex peripheral_disconnected_callbacks_mutex_;
absl::flat_hash_map<sdbus::ObjectPath, std::list<absl::AnyInvocable<void()>>>
peripheral_disconnected_callbacks_
ABSL_GUARDED_BY(peripheral_disconnected_callbacks_mutex_);
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
absl::flat_hash_map<std::tuple<Uuid, Uuid, sdbus::ObjectPath>,
sdbus::ObjectPath>
discovered_characteristics_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<sdbus::ObjectPath,
std::tuple<Uuid, Uuid, sdbus::ObjectPath>>
characteristics_properties_ ABSL_GUARDED_BY(mutex_);
bool shutdown_ ABSL_GUARDED_BY(mutex_);
std::size_t pending_discovery_ ABSL_GUARDED_BY(mutex_);
};
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt
//
// Representation of a client GATT connection to a remote GATT server.
class GattClient : public api::ble_v2::GattClient {
public:
GattClient(const GattClient &) = delete;
GattClient(GattClient &&) = delete;
GattClient &operator=(const GattClient &) = delete;
GattClient &operator=(GattClient &&) = delete;
explicit GattClient(std::shared_ptr<sdbus::IConnection> system_bus,
const sdbus::ObjectPath &peripheral_object_path,
std::shared_ptr<BluezGattDiscovery> gatt_discovery,
absl::AnyInvocable<void()> disconnected_callback)
: system_bus_(std::move(system_bus)),
peripheral_object_path_(peripheral_object_path),
gatt_discovery_(std::move(gatt_discovery)),
discovery_cancel_(false) {
disconnected_callback_it_ = gatt_discovery->AddPeripheralConnection(
peripheral_object_path_, std::move(disconnected_callback));
}
~GattClient() override {
absl::MutexLock lock(&disconnected_callback_mutex_);
if (!discovery_cancel_.Cancelled()) {
discovery_cancel_.Cancel();
gatt_discovery_->RemovePeripheralConnection(peripheral_object_path_,
disconnected_callback_it_);
}
}
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices()
//
// Discovers available service and characteristics on this connection.
// Returns whether or not discovery finished successfully.
//
// This function should block until discovery has finished.
bool DiscoverServiceAndCharacteristics(
const Uuid &service_uuid,
const std::vector<Uuid> &characteristic_uuids) override;
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID)
// https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#getCharacteristic(java.util.UUID)
//
// Retrieves a GATT characteristic. On error, does not return a value.
//
// DiscoverServiceAndCharacteristics() should be called before this method to
// fetch all available services and characteristics first.
//
// It is okay for duplicate services to exist, as long as the specified
// characteristic UUID is unique among all services of the same UUID.
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<api::ble_v2::GattCharacteristic> GetCharacteristic(
const Uuid &service_uuid, const Uuid &characteristic_uuid) override
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue()
// NOLINTNEXTLINE(google3-legacy-absl-backports)
absl::optional<std::string> ReadCharacteristic(
const api::ble_v2::GattCharacteristic &characteristic) override
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
//
// Sends a remote characteristic write request to the server and returns
// whether or not it was successful.
bool WriteCharacteristic(
const api::ble_v2::GattCharacteristic &characteristic,
absl::string_view value, WriteType type) override
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic,%20boolean)
//
// Enable or disable notifications/indications for a given characteristic.
bool SetCharacteristicSubscription(
const api::ble_v2::GattCharacteristic &characteristic, bool enable,
absl::AnyInvocable<void(absl::string_view value)>
on_characteristic_changed_cb) override
ABSL_LOCKS_EXCLUDED(characteristics_mutex_);
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect()
void Disconnect() override;
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
sdbus::ObjectPath peripheral_object_path_;
std::shared_ptr<BluezGattDiscovery> gatt_discovery_;
absl::Mutex disconnected_callback_mutex_;
BluezGattDiscovery::CallbackIter disconnected_callback_it_
ABSL_GUARDED_BY(disconnected_callback_mutex_);
CancellationFlag discovery_cancel_;
using CharacteristicProxy =
std::variant<std::unique_ptr<bluez::GattCharacteristicClient>,
std::unique_ptr<bluez::SubscribedGattCharacteristicClient>>;
absl::Mutex characteristics_mutex_;
absl::flat_hash_map<api::ble_v2::GattCharacteristic, CharacteristicProxy>
characteristics_ ABSL_GUARDED_BY(characteristics_mutex_);
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,147 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/ble_gatt_server.h"
#include "absl/strings/substitute.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_server.h"
#include "internal/platform/implementation/linux/bluez_gatt_manager.h"
#include "internal/platform/implementation/linux/bluez_gatt_service_server.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_service_server.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
absl::optional<api::ble_v2::GattCharacteristic>
GattServer::CreateCharacteristic(
const Uuid& service_uuid, const Uuid& characteristic_uuid,
api::ble_v2::GattCharacteristic::Permission permission,
api::ble_v2::GattCharacteristic::Property property) {
absl::MutexLock lock(&services_mutex_);
if (services_.count(service_uuid) == 1) {
if (services_[service_uuid]->AddCharacteristic(
service_uuid, characteristic_uuid, permission, property)) {
api::ble_v2::GattCharacteristic characteristic{
characteristic_uuid, service_uuid, permission, property};
return characteristic;
}
return std::nullopt;
}
auto count = services_.size();
auto service = std::make_unique<bluez::GattServiceServer>(
system_bus_, count, service_uuid, server_cb_, devices_);
try {
service->emitInterfacesAddedSignal(
{org::bluez::GattService1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error& e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesAdded signal for object path "
<< service->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
return std::nullopt;
}
if (service->AddCharacteristic(service_uuid, characteristic_uuid, permission,
property)) {
bluez::GattManager manager(system_bus_, adapter_.GetObjectPath());
try {
NEARBY_LOGS(VERBOSE) << __func__ << ": registering service "
<< service->getObjectPath();
manager.RegisterApplication("/", {});
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "RegisterApplication", e);
return std::nullopt;
}
services_.insert({service_uuid, std::move(service)});
api::ble_v2::GattCharacteristic characteristic{
characteristic_uuid, service_uuid, permission, property};
return characteristic;
}
return std::nullopt;
}
bool GattServer::UpdateCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const nearby::ByteArray& value) {
std::shared_ptr<bluez::GattCharacteristicServer> chr = nullptr;
{
absl::ReaderMutexLock lock(&services_mutex_);
if (services_.count(characteristic.service_uuid) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": GATT Service "
<< std::string{characteristic.service_uuid}
<< " doesn't exist";
return false;
}
chr = services_[characteristic.service_uuid]->GetCharacteristic(
characteristic.uuid);
}
if (chr == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Characteristic "
<< std::string{characteristic.uuid}
<< " does not exist under service "
<< std::string{characteristic.service_uuid};
return false;
}
assert(chr != nullptr);
chr->Update(value);
return true;
}
absl::Status GattServer::NotifyCharacteristicChanged(
const api::ble_v2::GattCharacteristic& characteristic, bool confirm,
const ByteArray& new_value) {
std::shared_ptr<bluez::GattCharacteristicServer> chr = nullptr;
{
absl::ReaderMutexLock lock(&services_mutex_);
if (services_.count(characteristic.service_uuid) == 0) {
return absl::NotFoundError(
absl::Substitute("Service $0 doesn't exist",
std::string{characteristic.service_uuid}));
}
chr = services_[characteristic.service_uuid]->GetCharacteristic(
characteristic.uuid);
}
if (chr == nullptr) {
return absl::NotFoundError(
absl::Substitute("characteristic $0 doesn't exist under service $1",
std::string{characteristic.uuid},
std::string{characteristic.service_uuid}));
}
return chr->NotifyChanged(confirm, new_value);
}
void GattServer::Stop() {
bluez::GattManager manager(system_bus_, adapter_.GetObjectPath());
absl::MutexLock lock(&services_mutex_);
for (auto& [uuid, service] : services_) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Unregistering service "
<< service->getObjectPath();
try {
manager.UnregisterApplication("/");
} catch (const sdbus::Error& e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "UnregisterApplication", e);
}
}
// services_.clear();
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,96 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
#define PLATFORM_IMPL_LINUX_API_BLE_GATT_SERVER_H_
#include <memory>
#include <sdbus-c++/IConnection.h>
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/optional.h"
#include "internal/platform/bluetooth_utils.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez_gatt_service_server.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
class LocalBlePeripheral : public api::ble_v2::BlePeripheral {
public:
explicit LocalBlePeripheral(BluetoothAdapter& adapter) : adapter_(adapter) {
unique_id_ = BluetoothUtils::ToNumber(adapter_.GetMacAddress());
}
std::string GetAddress() const override { return adapter_.GetMacAddress(); }
UniqueId GetUniqueId() const override { return unique_id_; }
private:
BluetoothAdapter adapter_;
UniqueId unique_id_;
};
class GattServer : public api::ble_v2::GattServer {
public:
GattServer(const GattServer&) = delete;
GattServer(GattServer&&) = delete;
GattServer& operator=(const GattServer&) = delete;
GattServer& operator=(GattServer&&) = delete;
explicit GattServer(sdbus::IConnection& system_bus, BluetoothAdapter& adapter,
std::shared_ptr<BluetoothDevices> devices,
api::ble_v2::ServerGattConnectionCallback server_cb)
: system_bus_(system_bus),
devices_(std::move(devices)),
adapter_(adapter),
local_peripheral_(adapter_),
server_cb_(std::make_shared<api::ble_v2::ServerGattConnectionCallback>(
std::move(server_cb))) {}
~GattServer() override = default;
api::ble_v2::BlePeripheral& GetBlePeripheral() override {
return local_peripheral_;
}
absl::optional<api::ble_v2::GattCharacteristic> CreateCharacteristic(
const Uuid& service_uuid, const Uuid& characteristic_uuid,
api::ble_v2::GattCharacteristic::Permission permission,
api::ble_v2::GattCharacteristic::Property property) override;
bool UpdateCharacteristic(
const api::ble_v2::GattCharacteristic& characteristic,
const nearby::ByteArray& value) override;
absl::Status NotifyCharacteristicChanged(
const api::ble_v2::GattCharacteristic& characteristic, bool confirm,
const ByteArray& new_value) override;
void Stop() override;
private:
sdbus::IConnection& system_bus_;
std::shared_ptr<BluetoothDevices> devices_;
BluetoothAdapter adapter_;
LocalBlePeripheral local_peripheral_;
std::shared_ptr<api::ble_v2::ServerGattConnectionCallback> server_cb_;
absl::Mutex services_mutex_;
absl::flat_hash_map<Uuid, std::unique_ptr<bluez::GattServiceServer>> services_
ABSL_GUARDED_BY(services_mutex_);
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,73 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLE_MEDIUM_H_
#define PLATFORM_IMPL_LINUX_API_BLE_MEDIUM_H_
#include "internal/platform/implementation/ble.h"
namespace nearby {
namespace linux {
// Container of operations that can be performed over the BLE medium.
class BleMedium : public api::BleMedium {
public:
BleMedium() {}
~BleMedium() = default;
bool StartAdvertising(
const std::string &service_id, const ByteArray &advertisement_bytes,
const std::string &fast_advertisement_service_uuid) override {
return false;
}
bool StopAdvertising(const std::string &service_id) override { return false; }
// Returns true once the BLE scan has been initiated.
bool StartScanning(const std::string &service_id,
const std::string &fast_advertisement_service_uuid,
DiscoveredPeripheralCallback callback) override {
return false;
}
// Returns true once BLE scanning for service_id is well and truly stopped;
// after this returns, there must be no more invocations of the
// DiscoveredPeripheralCallback passed in to StartScanning() for service_id.
bool StopScanning(const std::string &service_id) override { return false; }
// Callback that is invoked when a new connection is accepted.
using AcceptedConnectionCallback = absl::AnyInvocable<void(
api::BleSocket &socket, const std::string &service_id)>;
// Returns true once BLE socket connection requests to service_id can be
// accepted.
bool StartAcceptingConnections(const std::string &service_id,
AcceptedConnectionCallback callback) override {
return false;
}
bool StopAcceptingConnections(const std::string &service_id) override {
return false;
}
// Connects to a BLE peripheral.
// On success, returns a new BleSocket.
// On error, returns nullptr.
std::unique_ptr<api::BleSocket> Connect(
api::BlePeripheral &peripheral, const std::string &service_id,
CancellationFlag *cancellation_flag) override {
return nullptr;
}
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,480 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <cassert>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/ble_gatt_client.h"
#include "internal/platform/implementation/linux/ble_gatt_server.h"
#include "internal/platform/implementation/linux/ble_v2_medium.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/bluez_advertisement_monitor.h"
#include "internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h"
#include "internal/platform/implementation/linux/bluez_le_advertisement.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/advertisement_monitor_server.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/le_advertisement_manager_client.h"
namespace nearby {
namespace linux {
BleV2Medium::BleV2Medium(BluetoothAdapter &adapter)
: system_bus_(adapter.GetConnection()),
adapter_(adapter),
devices_(std::make_unique<BluetoothDevices>(
system_bus_, adapter_.GetObjectPath(), observers_)),
gatt_discovery_(std::make_shared<BluezGattDiscovery>(system_bus_)),
root_object_manager_(std::make_unique<RootObjectManager>(*system_bus_)),
adv_monitor_manager_(
bluez::AdvertisementMonitorManager::
DiscoverAdvertisementMonitorManager(*system_bus_, adapter_)),
adv_manager_(std::make_unique<bluez::LEAdvertisementManager>(*system_bus_,
adapter)),
cur_adv_(nullptr) {
if (adv_monitor_manager_) {
NEARBY_LOGS(VERBOSE)
<< __func__
<< ": Registering path / with AdvertisementMonitorManager at "
<< adv_monitor_manager_->getObjectPath();
try {
adv_monitor_manager_->RegisterMonitor("/");
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(adv_monitor_manager_, "RegisterMonitor", e);
}
}
if (gatt_discovery_->InitializeKnownServices()) {
NEARBY_LOGS(ERROR) << __func__
<< ": Could not initialize known GATT services";
}
}
bool BleV2Medium::StartAdvertising(
const api::ble_v2::BleAdvertisementData &advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters) {
if (!adapter_.IsEnabled()) {
NEARBY_LOGS(WARNING) << "BLE cannot start advertising because the "
"bluetooth adapter is not enabled.";
return false;
}
if (advertising_data.service_data.empty()) {
NEARBY_LOGS(WARNING)
<< "BLE cannot start to advertise due to invalid service data.";
return false;
}
absl::MutexLock lock(&cur_adv_mutex_);
if (cur_adv_ != nullptr) {
NEARBY_LOGS(ERROR) << __func__
<< "Advertising is already enabled for this medium.";
return false;
}
cur_adv_ = bluez::LEAdvertisement::CreateLEAdvertisement(
*system_bus_, advertising_data, advertise_set_parameters);
NEARBY_LOGS(INFO) << __func__ << ": Registering advertisement "
<< cur_adv_->getObjectPath() << " on bluetooth adapter "
<< adapter_.GetObjectPath();
try {
adv_manager_->RegisterAdvertisement(cur_adv_->getObjectPath(), {});
} catch (const sdbus::Error &e) {
cur_adv_ = nullptr;
DBUS_LOG_METHOD_CALL_ERROR(adv_manager_, "RegisterAdvertisement", e);
return false;
}
return true;
}
bool BleV2Medium::StopAdvertising() {
absl::MutexLock lock(&cur_adv_mutex_);
if (cur_adv_ == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Advertising is not enabled.";
return false;
}
NEARBY_LOGS(VERBOSE) << __func__ << "Unregistering advertisement object "
<< cur_adv_->getObjectPath();
try {
adv_manager_->UnregisterAdvertisement(cur_adv_->getObjectPath());
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(adv_manager_, "UnregisterAdvertisement", e);
return false;
}
cur_adv_ = nullptr;
return true;
}
std::unique_ptr<api::ble_v2::BleMedium::AdvertisingSession>
BleV2Medium::StartAdvertising(
const api::ble_v2::BleAdvertisementData &advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters,
AdvertisingCallback callback) {
if (!adapter_.IsEnabled()) {
NEARBY_LOGS(WARNING) << ": BLE cannot start advertising because the "
"bluetooth adapter is not enabled.";
return nullptr;
}
if (advertising_data.service_data.empty()) {
NEARBY_LOGS(WARNING)
<< ": BLE cannot start to advertise due to invalid service data.";
return nullptr;
}
std::shared_ptr<sdbus::IProxy> proxy =
sdbus::createProxy(*system_bus_, "org.bluez", adapter_.GetObjectPath());
proxy->finishRegistration();
std::shared_ptr<AdvertisingCallback> shared_cb =
std::make_shared<AdvertisingCallback>(std::move(callback));
absl::MutexLock lock(&advs_mutex_);
advs_.push_front(bluez::LEAdvertisement::CreateLEAdvertisement(
*system_bus_, advertising_data, advertise_set_parameters));
auto adv_it = advs_.begin();
auto pending_call =
proxy->callMethodAsync("RegisterAdvertisement")
.onInterface(org::bluez::LEAdvertisingManager1_proxy::INTERFACE_NAME)
.withArguments((*adv_it)->getObjectPath(),
std::map<std::string, sdbus::Variant>{})
.uponReplyInvoke(
[this, proxy, shared_cb, adv_it](const sdbus::Error *error) {
if (error != nullptr && error->isValid()) {
{
absl::MutexLock lock(&advs_mutex_);
advs_.erase(adv_it);
}
DBUS_LOG_METHOD_CALL_ERROR(adv_manager_,
"RegisterAdvertisement", *error);
auto name = error->getName();
std::string msg = error->getMessage();
absl::Status status;
if (name == "org.bluez.Error.InvalidArguments" ||
name == "org.bluez.Error.InvalidLength") {
status = absl::InvalidArgumentError(msg);
} else if (name == "org.bluez.Error.AlreadyExists") {
status = absl::AlreadyExistsError(msg);
} else if (name == "org.bluez.Error.NotPermitted") {
status = absl::ResourceExhaustedError(msg);
} else {
status = absl::UnknownError(msg);
}
shared_cb->start_advertising_result(std::move(status));
} else {
shared_cb->start_advertising_result(absl::OkStatus());
}
});
absl::AnyInvocable<absl::Status()> stop_adv = [&, adv_it]() {
NEARBY_LOGS(VERBOSE) << __func__ << ": Unregistering advertisement object "
<< (*adv_it)->getObjectPath();
absl::MutexLock lock(&advs_mutex_);
try {
adv_manager_->UnregisterAdvertisement((*adv_it)->getObjectPath());
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(adv_manager_, "UnregisterAdvertisement", e);
return absl::UnknownError(e.getMessage());
}
advs_.erase(adv_it);
return absl::OkStatus();
};
return std::make_unique<api::ble_v2::BleMedium::AdvertisingSession>(
api::ble_v2::BleMedium::AdvertisingSession{std::move(stop_adv)});
}
std::unique_ptr<api::ble_v2::GattServer> BleV2Medium::StartGattServer(
api::ble_v2::ServerGattConnectionCallback callback) {
return std::make_unique<GattServer>(*system_bus_, adapter_, devices_,
std::move(callback));
}
std::unique_ptr<api::ble_v2::GattClient> BleV2Medium::ConnectToGattServer(
api::ble_v2::BlePeripheral &peripheral,
api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::ClientGattConnectionCallback callback) {
auto path = bluez::device_object_path(adapter_.GetObjectPath(),
peripheral.GetAddress());
return std::make_unique<GattClient>(system_bus_, path, gatt_discovery_,
std::move(callback.disconnected_cb));
}
bool BleV2Medium::IsExtendedAdvertisementsAvailable() {
try {
auto supported_channels = adv_manager_->SupportedSecondaryChannels();
return !supported_channels.empty();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(adv_manager_, "SupportedSecondaryChannels", e);
return false;
}
}
bool BleV2Medium::StartLEDiscovery() {
std::map<std::string, sdbus::Variant> filter;
filter["Transport"] = "auto";
auto &adapter = adapter_.GetBluezAdapterObject();
try {
adapter.SetDiscoveryFilter(filter);
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "SetDiscoveryFilter", e);
return false;
}
try {
NEARBY_LOGS(INFO) << __func__ << ": Starting LE discovery on "
<< adapter.getObjectPath();
adapter.StartDiscovery();
} catch (const sdbus::Error &e) {
if (e.getName() != "org.bluez.Error.InProgress") {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "StartDiscovery", e);
return false;
}
}
return true;
}
bool BleV2Medium::StartScanning(const Uuid &service_uuid,
api::ble_v2::TxPowerLevel tx_power_level,
ScanCallback callback) {
if (cur_monitored_service_uuid_.has_value()) {
NEARBY_LOGS(ERROR) << __func__
<< ": A sync scanning session is already active for "
<< std::string{*cur_monitored_service_uuid_};
return false;
}
if (adv_monitor_manager_ == nullptr) {
NEARBY_LOGS(WARNING) << __func__
<< ": Advertising monitor not supported by BlueZ";
// TODO: Implement manual monitoring.
return false;
}
if (!MonitorManagerSupportsOr()) {
NEARBY_LOGS(WARNING)
<< __func__
<< ": \"or_patterns\" not supported by AdvertisementMonitorManager";
// TODO: Implement manual monitoring.
return false;
}
absl::MutexLock lock(&active_adv_monitors_mutex_);
if (active_adv_monitors_.count(service_uuid) == 1) {
NEARBY_LOGS(ERROR) << __func__ << ": an advertising session for service "
<< std::string{service_uuid} << " already exists";
return false;
}
auto monitor = std::make_unique<bluez::AdvertisementMonitor>(
*system_bus_, service_uuid, tx_power_level, "or_patterns", devices_,
std::move(callback));
try {
monitor->emitInterfacesAddedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesAdded signal for object path "
<< monitor->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
return false;
}
auto device_watcher = std::make_unique<DeviceWatcher>(
*system_bus_, adapter_.GetObjectPath(), devices_);
if (!StartLEDiscovery()) {
NEARBY_LOGS(ERROR) << __func__
<< ": Could not start LE discovery on adapter "
<< adapter_.GetObjectPath();
device_watcher = nullptr;
try {
monitor->emitInterfacesRemovedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesRemoved signal for object path "
<< monitor->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
}
return false;
}
active_adv_monitors_[service_uuid] =
std::make_pair(std::move(monitor), std::move(device_watcher));
cur_monitored_service_uuid_ = service_uuid;
return true;
}
bool BleV2Medium::StopScanning() {
if (!cur_monitored_service_uuid_.has_value()) {
NEARBY_LOGS(ERROR) << __func__
<< ": No sync scanning session is currently active.";
return false;
}
if (adv_monitor_manager_ == nullptr) {
// TODO: Implement manual monitoring.
return false;
}
auto &adapter = adapter_.GetBluezAdapterObject();
NEARBY_LOGS(VERBOSE) << __func__ << ": Stopping discovery for adapter "
<< adapter.getObjectPath();
try {
adapter.StopDiscovery();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "StopDiscovery", e);
}
absl::MutexLock lock(&active_adv_monitors_mutex_);
auto monitor_it = active_adv_monitors_.find(*cur_monitored_service_uuid_);
assert(monitor_it != active_adv_monitors_.end());
{
auto &[_uuid, session] = *monitor_it;
auto &[adv_monitor, _watcher] = session;
NEARBY_LOGS(VERBOSE) << __func__ << ": Removing advertising monitor "
<< adv_monitor->getObjectPath();
adv_monitor->emitInterfacesRemovedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
}
active_adv_monitors_.erase(monitor_it);
cur_monitored_service_uuid_ = std::nullopt;
return true;
}
std::unique_ptr<api::ble_v2::BleMedium::ScanningSession>
BleV2Medium::StartScanning(const Uuid &service_uuid,
api::ble_v2::TxPowerLevel tx_power_level,
ScanningCallback callback) {
if (adv_monitor_manager_ == nullptr) {
// TODO: Implement manual monitoring.
return nullptr;
}
absl::MutexLock lock(&active_adv_monitors_mutex_);
if (active_adv_monitors_.count(service_uuid) == 1) {
NEARBY_LOGS(ERROR) << __func__ << ": Service " << std::string{service_uuid}
<< " is already being advertised";
return nullptr;
}
auto monitor = std::make_unique<bluez::AdvertisementMonitor>(
*system_bus_, service_uuid, tx_power_level, "or_patterns", devices_,
std::move(callback));
try {
monitor->emitInterfacesAddedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesAdded signal for object path "
<< monitor->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
return nullptr;
}
auto device_watcher = std::make_unique<DeviceWatcher>(
*system_bus_, adapter_.GetObjectPath(), devices_);
if (!StartLEDiscovery()) {
NEARBY_LOGS(ERROR) << __func__
<< ": Could not start LE discovery on adapter "
<< adapter_.GetObjectPath();
try {
monitor->emitInterfacesRemovedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesRemoved signal for object path "
<< monitor->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
}
return nullptr;
}
active_adv_monitors_[service_uuid] =
std::make_pair(std::move(monitor), std::move(device_watcher));
return std::make_unique<ScanningSession>(
ScanningSession{.stop_scanning = [this, service_uuid]() {
absl::MutexLock lock(&active_adv_monitors_mutex_);
if (active_adv_monitors_.count(service_uuid) == 0) {
NEARBY_LOGS(ERROR)
<< __func__ << ": Advertising monitor for service "
<< std::string{service_uuid} << " does not exist anymore";
return absl::NotFoundError(
"Advertising monitor for this service does not exist");
}
auto &[monitor, watcher] = active_adv_monitors_[service_uuid];
try {
monitor->emitInterfacesRemovedSignal(
{org::bluez::AdvertisementMonitor1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesRemoved signal for object path "
<< monitor->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
}
auto &adapter = adapter_.GetBluezAdapterObject();
absl::Status status;
try {
adapter.StopDiscovery();
status = absl::OkStatus();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "StopDiscovery", e);
status = absl::InternalError(e.getMessage());
}
active_adv_monitors_.erase(service_uuid);
return status;
}});
}
bool BleV2Medium::GetRemotePeripheral(const std::string &mac_address,
GetRemotePeripheralCallback callback) {
auto device = devices_->get_device_by_address(mac_address);
if (device == nullptr) return false;
callback(*device);
return true;
}
bool BleV2Medium::GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
GetRemotePeripheralCallback callback) {
auto device = devices_->get_device_by_unique_id(id);
if (device == nullptr) return false;
callback(*device);
return true;
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,144 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLE_V2_MEDIUM_H_
#define PLATFORM_IMPL_LINUX_API_BLE_V2_MEDIUM_H_
#include <memory>
#include <sdbus-c++/IConnection.h>
#include "absl/base/attributes.h"
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/ble_gatt_client.h"
#include "internal/platform/implementation/linux/ble_v2_server_socket.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez_advertisement_monitor.h"
#include "internal/platform/implementation/linux/bluez_advertisement_monitor_manager.h"
#include "internal/platform/implementation/linux/bluez_le_advertisement.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
class BleV2Medium final : public api::ble_v2::BleMedium {
public:
BleV2Medium(const BleV2Medium &) = delete;
BleV2Medium(BleV2Medium &&) = delete;
BleV2Medium &operator=(const BleV2Medium &) = delete;
BleV2Medium &operator=(BleV2Medium &&) = delete;
explicit BleV2Medium(BluetoothAdapter &adapter);
~BleV2Medium() override = default;
bool StartAdvertising(
const api::ble_v2::BleAdvertisementData &advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters) override
ABSL_LOCKS_EXCLUDED(cur_adv_mutex_);
std::unique_ptr<AdvertisingSession> StartAdvertising(
const api::ble_v2::BleAdvertisementData &advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters,
AdvertisingCallback callback) ABSL_LOCKS_EXCLUDED(advs_mutex_) override;
bool StopAdvertising() override ABSL_LOCKS_EXCLUDED(advs_mutex_);
bool StartScanning(const Uuid &service_uuid,
api::ble_v2::TxPowerLevel tx_power_level,
ScanCallback callback) override
ABSL_LOCKS_EXCLUDED(active_adv_monitors_mutex_);
bool StopScanning() override ABSL_LOCKS_EXCLUDED(active_adv_monitors_mutex_);
std::unique_ptr<ScanningSession> StartScanning(
const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level,
ScanningCallback callback) override;
std::unique_ptr<api::ble_v2::GattServer> StartGattServer(
api::ble_v2::ServerGattConnectionCallback callback) override;
std::unique_ptr<api::ble_v2::GattClient> ConnectToGattServer(
api::ble_v2::BlePeripheral &peripheral,
api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::ClientGattConnectionCallback callback) override;
std::unique_ptr<api::ble_v2::BleServerSocket> OpenServerSocket(
const std::string &service_id) override {
return std::make_unique<BleV2ServerSocket>();
}
std::unique_ptr<api::ble_v2::BleSocket> Connect(
const std::string &service_id, api::ble_v2::TxPowerLevel tx_power_level,
api::ble_v2::BlePeripheral &peripheral,
CancellationFlag *cancellation_flag) override {
return nullptr;
}
bool IsExtendedAdvertisementsAvailable() override;
bool GetRemotePeripheral(const std::string &mac_address,
GetRemotePeripheralCallback callback) override;
bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
GetRemotePeripheralCallback callback) override;
private:
bool StartLEDiscovery();
bool MonitorManagerSupportsOr() {
std::vector<std::string> supported_types;
try {
supported_types = adv_monitor_manager_->SupportedMonitorTypes();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(adv_monitor_manager_, "SupportedMonitorTypes",
e);
return false;
}
auto is_supported_type = [](std::string pattern) {
return pattern == "or_patterns";
};
auto end = supported_types.cend();
return std::find_if(supported_types.cbegin(), end, is_supported_type) !=
end;
}
std::shared_ptr<sdbus::IConnection> system_bus_;
BluetoothAdapter adapter_;
ObserverList<api::BluetoothClassicMedium::Observer> observers_ = {};
std::shared_ptr<BluetoothDevices> devices_;
std::shared_ptr<BluezGattDiscovery> gatt_discovery_;
std::unique_ptr<RootObjectManager> root_object_manager_;
std::unique_ptr<bluez::AdvertisementMonitorManager> adv_monitor_manager_;
absl::Mutex active_adv_monitors_mutex_;
absl::flat_hash_map<
Uuid,
std::pair<std::unique_ptr<bluez::AdvertisementMonitor>, std::unique_ptr<DeviceWatcher>>>
active_adv_monitors_ ABSL_GUARDED_BY(active_adv_monitors_mutex_);
// Used by the synchronous variant of StartScanning
std::optional<Uuid> cur_monitored_service_uuid_;
std::unique_ptr<bluez::LEAdvertisementManager> adv_manager_;
absl::Mutex cur_adv_mutex_;
std::unique_ptr<bluez::LEAdvertisement> cur_adv_
ABSL_GUARDED_BY(cur_adv_mutex_);
absl::Mutex advs_mutex_;
std::list<std::unique_ptr<bluez::LEAdvertisement>> advs_
ABSL_GUARDED_BY(advs_mutex_);
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,42 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLE_V2_SERVER_SOCKET_H_
#define PLATFORM_IMPL_LINUX_API_BLE_V2_SERVER_SOCKET_H_
#include "absl/synchronization/notification.h"
#include "internal/platform/implementation/ble_v2.h"
namespace nearby {
namespace linux {
class BleV2ServerSocket final : public api::ble_v2::BleServerSocket {
public:
std::unique_ptr<api::ble_v2::BleSocket> Accept() override {
stopped_.WaitForNotification();
return nullptr;
}
Exception Close() override {
if (stopped_.HasBeenNotified()) return {Exception::kIo};
stopped_.Notify();
return {Exception::kSuccess};
}
private:
absl::Notification stopped_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,121 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/Types.h>
#include "internal/platform/implementation/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/adapter_client.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
bool BluetoothAdapter::SetStatus(Status status) {
try {
bool val = status == api::BluetoothAdapter::Status::kEnabled;
bluez_adapter_->Powered(val);
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_SET_ERROR(bluez_adapter_, "Powered", e);
return false;
}
}
bool BluetoothAdapter::IsEnabled() const {
try {
return bluez_adapter_->Powered();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(bluez_adapter_, "Powered", e);
return false;
}
}
BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const {
bool powered = IsEnabled();
if (!powered) {
return ScanMode::kNone;
}
try {
bool discoverable = bluez_adapter_->Discoverable();
return discoverable ? ScanMode::kConnectableDiscoverable
: ScanMode::kConnectable;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(bluez_adapter_, "Discoverable", e);
return ScanMode::kUnknown;
}
}
bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) {
switch (scan_mode) {
case ScanMode::kConnectable:
return SetStatus(Status::kEnabled);
case ScanMode::kConnectableDiscoverable: {
if (!SetStatus(Status::kEnabled)) {
return false;
}
try {
bluez_adapter_->Discoverable(true);
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_SET_ERROR(bluez_adapter_, "Discoverable", e);
return false;
}
return true;
}
case ScanMode::kNone:
return SetStatus(Status::kDisabled);
default:
return false;
}
}
std::string BluetoothAdapter::GetName() const {
try {
return bluez_adapter_->Alias();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(bluez_adapter_, "Alias", e);
return {};
}
}
bool BluetoothAdapter::SetName(absl::string_view name, bool /*persist*/) {
return SetName(name);
}
bool BluetoothAdapter::SetName(absl::string_view name) {
try {
bluez_adapter_->Alias(std::string(name));
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_SET_ERROR(bluez_adapter_, "Alias", e);
return false;
}
}
std::string BluetoothAdapter::GetMacAddress() const {
try {
return bluez_adapter_->Address();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(bluez_adapter_, "Address", e);
return {};
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,84 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/adapter_client.h"
namespace nearby {
namespace linux {
class BluezAdapter : public sdbus::ProxyInterfaces<org::bluez::Adapter1_proxy> {
public:
BluezAdapter(sdbus::IConnection &system_bus,
const sdbus::ObjectPath &adapter_object_path)
: ProxyInterfaces(system_bus, bluez::SERVICE_DEST, adapter_object_path) {
registerProxy();
}
~BluezAdapter() { unregisterProxy(); }
};
class BluetoothAdapter : public api::BluetoothAdapter {
public:
BluetoothAdapter(std::shared_ptr<sdbus::IConnection> system_bus,
const sdbus::ObjectPath &adapter_object_path)
: system_bus_(std::move(system_bus)),
bluez_adapter_(std::make_shared<BluezAdapter>(*system_bus_,
adapter_object_path)) {}
~BluetoothAdapter() override = default;
bool SetStatus(Status status) override;
bool IsEnabled() const override;
ScanMode GetScanMode() const override;
bool SetScanMode(ScanMode scan_mode) override;
std::string GetName() const override;
bool SetName(absl::string_view name) override;
bool SetName(absl::string_view name, bool persist) override;
std::string GetMacAddress() const override;
bool RemoveDeviceByObjectPath(const sdbus::ObjectPath &device_object_path) {
try {
bluez_adapter_->RemoveDevice(device_object_path);
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(bluez_adapter_, "RemoveDevice", e);
return false;
}
}
sdbus::ObjectPath GetObjectPath() const {
return bluez_adapter_->getObjectPath();
}
BluezAdapter &GetBluezAdapterObject() { return *bluez_adapter_; }
std::shared_ptr<sdbus::IConnection> GetConnection() { return system_bus_; }
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
std::shared_ptr<BluezAdapter> bluez_adapter_;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_BLUETOOTH_ADAPTER_H_
@@ -0,0 +1,285 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#include "absl/synchronization/mutex.h"
#include "internal/platform/cancellation_flag_listener.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_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
bool ProfileManager::ProfileRegistered(absl::string_view service_uuid) {
registered_service_uuids_mutex_.ReaderLock();
bool registered = registered_services_.count(std::string(service_uuid)) == 1;
registered_service_uuids_mutex_.ReaderUnlock();
return registered;
}
void Profile::Release() {
released_ = true;
NEARBY_LOGS(VERBOSE) << __func__ << ": Profile object " << getObjectPath()
<< " has been released";
}
void Profile::NewConnection(
const sdbus::ObjectPath &device_object_path, const sdbus::UnixFd &fd,
const std::map<std::string, sdbus::Variant> &fd_props) {
if (released_) {
NEARBY_LOGS(ERROR) << __func__
<< ": NewConnection called on released object "
<< getObjectPath();
throw sdbus::Error("org.bluez.Error.Rejected",
"NewConnection called on released object");
}
auto device = devices_.get_device_by_path(device_object_path);
if (device == nullptr) {
device = devices_.add_new_device(device_object_path);
}
auto alias = device->GetName();
auto mac_addr = device->GetAddress();
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": Connected to " << mac_addr;
FDProperties props(fd_props);
absl::MutexLock l(&connections_lock_);
connections_[mac_addr].push_back(std::pair(fd, props));
}
void Profile::RequestDisconnection(
const sdbus::ObjectPath &device_object_path) {
auto device = devices_.get_device_by_path(device_object_path);
if (device == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": " << getObjectPath()
<< ": RequestDisconnection called with a device object "
"we don't know about: "
<< device_object_path;
throw sdbus::Error("org.bluez.Error.Rejected", "Unknown object");
}
auto mac_addr = device->GetMacAddress();
NEARBY_LOGS(VERBOSE) << __func__ << ": Disconnection requested for device "
<< device_object_path;
absl::MutexLock l(&connections_lock_);
if (connections_.count(mac_addr) == 0) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Disconnection requested, but we are not connected to this device";
return;
}
connections_.erase(mac_addr);
}
bool ProfileManager::Register(std::optional<absl::string_view> name,
absl::string_view service_uuid) {
absl::MutexLock l(&registered_service_uuids_mutex_);
if (registered_services_.count(std::string(service_uuid)) == 1) {
NEARBY_LOGS(WARNING) << __func__ << ": Trying to register profile "
<< service_uuid << " which was already registered.";
return true;
}
auto profile = std::make_shared<Profile>(
getProxy().getConnection(), bluez::profile_object_path(service_uuid),
devices_);
try {
std::map<std::string, sdbus::Variant> options;
if (name.has_value()) {
options["Name"] = std::string(*name);
}
options["RequireAuthorization"] = false;
options["RequireAuthentication"] = false;
options["Channel"] = static_cast<uint16_t>(0);
options["PSM"] = static_cast<uint16_t>(0);
RegisterProfile(profile->getObjectPath(), std::string(service_uuid),
options);
} catch (const sdbus::Error &e) {
BLUEZ_LOG_METHOD_CALL_ERROR(&getProxy(), "RegisterProfile", e);
return false;
}
registered_services_.emplace(service_uuid, profile);
NEARBY_LOGS(INFO) << __func__
<< ": Registered profile instance for service uuid "
<< service_uuid;
return true;
}
void ProfileManager::Unregister(absl::string_view service_uuid) {
absl::MutexLock l(&registered_service_uuids_mutex_);
if (registered_services_.count(std::string(service_uuid)) == 0) {
NEARBY_LOGS(WARNING)
<< __func__
<< ": attempted to unregister a profile that is not registered";
return;
}
auto profile_object_path = bluez::profile_object_path(service_uuid);
NEARBY_LOGS(VERBOSE) << __func__ << ": Unregistering profile "
<< profile_object_path;
try {
UnregisterProfile(profile_object_path);
} catch (const sdbus::Error &e) {
BLUEZ_LOG_METHOD_CALL_ERROR(&getProxy(), "UnregisterProfile", e);
}
registered_services_.erase(std::string(service_uuid));
}
// Get a service record FD for a connected profile (identified by service_uuid)
// to the given device.
std::optional<sdbus::UnixFd> ProfileManager::GetServiceRecordFD(
api::BluetoothDevice &remote_device, absl::string_view service_uuid,
CancellationFlag *cancellation_flag) {
std::shared_ptr<Profile> profile;
{
absl::ReaderMutexLock lock(&registered_service_uuids_mutex_);
if (registered_services_.count(std::string(service_uuid)) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Service " << service_uuid
<< " is not registered";
return std::nullopt;
}
profile = registered_services_[std::string(service_uuid)];
}
auto mac_addr = remote_device.GetMacAddress();
std::unique_ptr<CancellationFlagListener> cancel_listener;
if (cancellation_flag != nullptr)
cancel_listener = std::make_unique<CancellationFlagListener>(
cancellation_flag, [&profile]() {
profile->connections_lock_.Lock();
profile->connections_lock_.Unlock();
});
NEARBY_LOGS(VERBOSE) << __func__ << ": " << profile->getObjectPath()
<< ": Attempting to get a FD for service "
<< service_uuid << " on device " << mac_addr;
auto cond = [mac_addr, profile, cancellation_flag]() {
profile->connections_lock_.AssertReaderHeld();
return profile->connections_.count(mac_addr) != 0 ||
(cancellation_flag != nullptr && cancellation_flag->Cancelled());
};
absl::MutexLock connections_lock(&profile->connections_lock_,
absl::Condition(&cond));
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
NEARBY_LOGS(VERBOSE)
<< __func__ << ": " << profile->getObjectPath() << ": "
<< remote_device.GetMacAddress()
<< ": Cancelled waiting for a new connection on profile "
<< service_uuid;
return std::nullopt;
}
auto [fd, properties] = profile->connections_[mac_addr].back();
profile->connections_[mac_addr].pop_back();
if (profile->connections_[mac_addr].empty())
profile->connections_.erase(mac_addr);
return std::move(fd);
}
// Listen for a connected profile on any device, returning the connected device
// with its FD.
std::optional<std::pair<std::shared_ptr<BluetoothDevice>, sdbus::UnixFd>>
ProfileManager::GetServiceRecordFD(absl::string_view service_uuid,
CancellationFlag *cancellation_flag) {
std::shared_ptr<Profile> profile;
{
absl::ReaderMutexLock lock(&registered_service_uuids_mutex_);
if (registered_services_.count(std::string(service_uuid)) == 0) {
return std::nullopt;
}
profile = registered_services_[std::string(service_uuid)];
}
NEARBY_LOGS(VERBOSE) << __func__ << ": " << profile->getObjectPath()
<< ": Attempting to get a FD for service "
<< service_uuid;
std::unique_ptr<CancellationFlagListener> cancel_listener;
if (cancellation_flag != nullptr)
cancel_listener = std::make_unique<CancellationFlagListener>(
cancellation_flag, [&profile]() {
profile->connections_lock_.Lock();
profile->connections_lock_.Unlock();
});
profile->connections_lock_.Lock();
auto cond = [profile, &cancellation_flag]() {
profile->connections_lock_.AssertReaderHeld();
return !profile->connections_.empty() ||
(cancellation_flag != nullptr && cancellation_flag->Cancelled());
};
profile->connections_lock_.Await(absl::Condition(&cond));
if (cancellation_flag != nullptr && cancellation_flag->Cancelled()) {
NEARBY_LOGS(VERBOSE)
<< __func__ << ": Cancelled waiting for new connections on profile "
<< profile->getObjectPath();
profile->connections_lock_.Unlock();
return std::nullopt;
}
auto it = profile->connections_.begin();
auto mac_addr = it->first;
auto [fd, properties] = it->second.back();
it->second.pop_back();
if (it->second.empty()) profile->connections_.erase(it);
profile->connections_lock_.Unlock();
auto device = devices_.get_device_by_address(mac_addr);
if (device == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Device " << mac_addr
<< " is no longer available";
return std::nullopt;
}
return std::pair(device, std::move(fd));
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,147 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_BLUEZ_PROFILE_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_BLUEZ_PROFILE_H_
#include <atomic>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <tuple>
#include <utility>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/profile_manager_client.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/profile_server.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
class ProfileManager;
class Profile final
: public sdbus::AdaptorInterfaces<org::bluez::Profile1_adaptor,
sdbus::ManagedObject_adaptor> {
public:
Profile(const Profile &) = delete;
Profile(Profile &&) = delete;
Profile &operator=(const Profile &) = delete;
Profile &operator=(Profile &&) = delete;
Profile(sdbus::IConnection &system_bus, sdbus::ObjectPath profile_object_path,
BluetoothDevices &devices)
: AdaptorInterfaces(system_bus, std::move(profile_object_path)),
released_(false),
devices_(devices) {
registerAdaptor();
NEARBY_LOGS(VERBOSE) << __func__ << ": Created a new BlueZ profile at :"
<< getObjectPath();
}
~Profile() { unregisterAdaptor(); }
private:
friend class ProfileManager;
struct FDProperties {
explicit FDProperties(const std::map<std::string, sdbus::Variant> &fd_props)
: version(std::nullopt), features(std::nullopt) {
if (fd_props.count("Version") == 1) {
version = fd_props.at("Version");
}
if (fd_props.count("Features") == 1) {
features = fd_props.at("Features");
}
}
std::optional<uint16_t> version;
std::optional<uint16_t> features;
};
void Release() override;
void NewConnection(const sdbus::ObjectPath &, const sdbus::UnixFd &,
const std::map<std::string, sdbus::Variant> &) override
ABSL_LOCKS_EXCLUDED(connections_lock_);
void RequestDisconnection(const sdbus::ObjectPath &) override
ABSL_LOCKS_EXCLUDED(connections_lock_);
std::atomic_bool released_;
absl::Mutex connections_lock_;
std::map<std::string, std::vector<std::pair<sdbus::UnixFd, FDProperties>>>
connections_ ABSL_GUARDED_BY(connections_lock_);
BluetoothDevices &devices_;
};
class ProfileManager final
: private sdbus::ProxyInterfaces<org::bluez::ProfileManager1_proxy> {
public:
ProfileManager(const ProfileManager &) = delete;
ProfileManager(ProfileManager &&) = delete;
ProfileManager &operator=(const ProfileManager &) = delete;
ProfileManager &operator=(ProfileManager &&) = delete;
ProfileManager(sdbus::IConnection &system_bus, BluetoothDevices &devices)
: ProxyInterfaces(system_bus, bluez::SERVICE_DEST, "/org/bluez"),
devices_(devices) {
registerProxy();
}
~ProfileManager() { unregisterProxy(); }
bool ProfileRegistered(absl::string_view service_uuid)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_);
bool Register(std::optional<absl::string_view> service_name,
absl::string_view service_uuid)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_);
bool Register(absl::string_view service_uuid)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_) {
return Register(std::nullopt, service_uuid);
}
void Unregister(absl::string_view service_uuid)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_);
std::optional<sdbus::UnixFd> GetServiceRecordFD(
api::BluetoothDevice &remote_device, absl::string_view service_uuid,
CancellationFlag *cancellation_flag)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_);
std::optional<std::pair<std::shared_ptr<BluetoothDevice>, sdbus::UnixFd>>
GetServiceRecordFD(absl::string_view service_uuid,
CancellationFlag *cancellation_flag)
ABSL_LOCKS_EXCLUDED(registered_service_uuids_mutex_);
private:
BluetoothDevices &devices_;
// Maps service UUIDs to RegisteredService
absl::Mutex registered_service_uuids_mutex_;
std::map<std::string, std::shared_ptr<Profile>> registered_services_
ABSL_GUARDED_BY(registered_service_uuids_mutex_);
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,148 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include "absl/strings/string_view.h"
#include "internal/platform/bluetooth_utils.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/bluez_device.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
BluetoothDevice::BluetoothDevice(std::shared_ptr<bluez::Device> device)
: lost_(false), device_(device) {
try {
last_known_name_ = device->Alias();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(device, "Alias", e);
}
try {
last_known_address_ = device->Address();
unique_id_ = BluetoothUtils::ToNumber(last_known_address_);
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(device, "Address", e);
}
}
std::string BluetoothDevice::GetName() const {
auto device = device_.lock();
if (device == nullptr) {
absl::ReaderMutexLock l(&properties_mutex_);
return last_known_name_;
}
try {
std::string alias = device->Alias();
{
absl::MutexLock l(&properties_mutex_);
last_known_name_ = alias;
}
return alias;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(device, "Alias", e);
return {};
}
}
std::string BluetoothDevice::GetMacAddress() const {
auto device = device_.lock();
if (device == nullptr) {
absl::ReaderMutexLock l(&properties_mutex_);
return last_known_name_;
}
try {
std::string addr = device->Address();
{
absl::MutexLock l(&properties_mutex_);
last_known_address_ = addr;
}
return addr;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(device, "Address", e);
return std::string();
}
}
bool BluetoothDevice::ConnectToProfile(absl::string_view service_uuid) {
auto device = device_.lock();
if (device == nullptr) return false;
try {
device->ConnectProfile(std::string(service_uuid));
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(device, "ConnectProfile", e);
return false;
}
}
MonitoredBluetoothDevice::MonitoredBluetoothDevice(
std::shared_ptr<sdbus::IConnection> system_bus,
std::shared_ptr<bluez::Device> device,
ObserverList<api::BluetoothClassicMedium::Observer> &observers)
: BluetoothDevice(std::move(device)),
ProxyInterfaces<sdbus::Properties_proxy>(*system_bus, bluez::SERVICE_DEST,
device->getObjectPath()),
system_bus_(std::move(system_bus)),
observers_(observers) {
registerProxy();
}
void MonitoredBluetoothDevice::onPropertiesChanged(
const std::string &interfaceName,
const std::map<std::string, sdbus::Variant> &changedProperties,
const std::vector<std::string> &invalidatedProperties) {
if (interfaceName != bluez::DEVICE_INTERFACE) {
return;
}
for (auto it = changedProperties.begin(); it != changedProperties.end();
it++) {
if (it->first == bluez::DEVICE_PROP_ADDRESS) {
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< ": Notifying observers about address change";
std::string address = it->second;
for (const auto &observer : observers_.GetObservers()) {
observer->DeviceAddressChanged(*this, address);
}
} else if (it->first == bluez::DEVICE_PROP_PAIRED) {
NEARBY_LOGS(VERBOSE) << __func__ << ": " << getObjectPath()
<< "Notifying observers about paired status change.";
for (const auto &observer : observers_.GetObservers()) {
observer->DevicePairedChanged(*this, it->second);
}
} else if (it->first == bluez::DEVICE_PROP_CONNECTED) {
NEARBY_LOGS(VERBOSE)
<< __func__ << ": " << getObjectPath()
<< "Notifying observers about connected status change";
for (const auto &observer : observers_.GetObservers()) {
observer->DeviceConnectedStateChanged(*this, it->second);
}
} else if (it->first == bluez::DEVICE_NAME) {
auto callback = GetDiscoveryCallback();
if (callback != nullptr && callback->device_name_changed_cb != nullptr)
callback->device_name_changed_cb(*this);
}
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,182 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_DEVICE_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_DEVICE_H_
#include <atomic>
#include <memory>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/base/observer_list.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluez_device.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/device_client.h"
namespace nearby {
namespace linux {
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
class BluetoothDevice : public api::BluetoothDevice,
public api::ble_v2::BlePeripheral {
public:
using UniqueId = std::uint64_t;
BluetoothDevice(const BluetoothDevice &) = delete;
BluetoothDevice(BluetoothDevice &&) = delete;
BluetoothDevice &operator=(const BluetoothDevice &) = delete;
BluetoothDevice &operator=(BluetoothDevice &&) = delete;
explicit BluetoothDevice(std::shared_ptr<bluez::Device> device);
// BluetoothDevice methods
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName()
std::string GetName() const override;
// Returns BT MAC address assigned to this device.
std::string GetMacAddress() const override;
// BlePeripheral methods
std::string GetAddress() const override { return GetMacAddress(); }
UniqueId GetUniqueId() const override { return unique_id_; };
std::optional<std::map<std::string, sdbus::Variant>> ServiceData() {
auto device = device_.lock();
if (device == nullptr) return std::nullopt;
try {
return device->ServiceData();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(device, "ServiceData", e);
return std::nullopt;
}
}
bool Bonded() {
auto device = device_.lock();
if (device == nullptr) return false;
try {
return device->Bonded();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(device, "Bonded", e);
return false;
}
}
std::optional<sdbus::PendingAsyncCall> Pair() {
auto device = device_.lock();
if (device == nullptr) return std::nullopt;
try {
return device->Pair();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(device, "Pair", e);
return std::nullopt;
}
}
bool CancelPairing() {
auto device = device_.lock();
if (device == nullptr) return false;
try {
device->CancelPairing();
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(device, "CancelPairing", e);
return false;
}
}
void SetPairReplyCallback(absl::AnyInvocable<void(const sdbus::Error *)> cb) {
auto device = device_.lock();
if (device != nullptr) device->SetPairReplyCallback(std::move(cb));
}
bool ConnectToProfile(absl::string_view service_uuid);
void MarkLost() { lost_ = true; }
void UnmarkLost() { lost_ = false; }
bool Lost() const { return lost_; }
private:
UniqueId unique_id_;
std::atomic_bool lost_;
mutable absl::Mutex properties_mutex_;
mutable std::string last_known_name_ ABSL_GUARDED_BY(properties_mutex_);
mutable std::string last_known_address_ ABSL_GUARDED_BY(properties_mutex_);
mutable std::weak_ptr<bluez::Device> device_;
};
class MonitoredBluetoothDevice final
: public BluetoothDevice,
public sdbus::ProxyInterfaces<sdbus::Properties_proxy> {
public:
using sdbus::ProxyInterfaces<sdbus::Properties_proxy>::registerProxy;
using sdbus::ProxyInterfaces<sdbus::Properties_proxy>::unregisterProxy;
using sdbus::ProxyInterfaces<sdbus::Properties_proxy>::getObjectPath;
MonitoredBluetoothDevice(const MonitoredBluetoothDevice &) = delete;
MonitoredBluetoothDevice(MonitoredBluetoothDevice &&) = delete;
MonitoredBluetoothDevice &operator=(const MonitoredBluetoothDevice &) =
delete;
MonitoredBluetoothDevice &operator=(MonitoredBluetoothDevice &&) = delete;
MonitoredBluetoothDevice(
std::shared_ptr<sdbus::IConnection> system_bus,
std::shared_ptr<bluez::Device> device,
ObserverList<api::BluetoothClassicMedium::Observer> &observers);
~MonitoredBluetoothDevice() override { unregisterProxy(); }
void SetDiscoveryCallback(
std::shared_ptr<api::BluetoothClassicMedium::DiscoveryCallback> &callback)
ABSL_LOCKS_EXCLUDED(discovery_cb_mutex_) {
absl::MutexLock lock(&discovery_cb_mutex_);
discovery_cb_ = callback;
};
protected:
void onPropertiesChanged(
const std::string &interfaceName,
const std::map<std::string, sdbus::Variant> &changedProperties,
const std::vector<std::string> &invalidatedProperties) override;
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
std::shared_ptr<api::BluetoothClassicMedium::DiscoveryCallback>
GetDiscoveryCallback() ABSL_LOCKS_EXCLUDED(discovery_cb_mutex_) {
discovery_cb_mutex_.ReaderLock();
auto callback = discovery_cb_.lock();
discovery_cb_mutex_.ReaderUnlock();
return callback;
}
ObserverList<api::BluetoothClassicMedium::Observer> &observers_;
absl::Mutex discovery_cb_mutex_;
std::weak_ptr<api::BluetoothClassicMedium::DiscoveryCallback> discovery_cb_
ABSL_GUARDED_BY(discovery_cb_mutex_);
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,165 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstring>
#include <memory>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/Types.h>
#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 "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_(std::make_shared<ObserverList<Observer>>()),
devices_(std::make_shared<BluetoothDevices>(
system_bus_, adapter.GetObjectPath(), *observers_)),
device_watcher_(nullptr),
profile_manager_(
std::make_unique<ProfileManager>(*system_bus_, *devices_)) {}
bool BluetoothClassicMedium::StartDiscovery(
DiscoveryCallback discovery_callback) {
device_watcher_ = std::make_unique<DeviceWatcher>(
*system_bus_, adapter_.GetObjectPath(), devices_,
std::make_unique<DiscoveryCallback>(std::move(discovery_callback)),
observers_);
std::map<std::string, sdbus::Variant> filter;
filter["Transport"] = "auto";
auto &adapter = adapter_.GetBluezAdapterObject();
try {
adapter.SetDiscoveryFilter(filter);
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "SetDiscoveryFilter", e);
device_watcher_ = nullptr;
return false;
}
try {
NEARBY_LOGS(INFO) << __func__ << ": Starting BR/EDR discovery on "
<< adapter_.GetObjectPath();
adapter.StartDiscovery();
} catch (const sdbus::Error &e) {
if (e.getName() != "org.bluez.Error.InProgress") {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "StartDiscovery", e);
device_watcher_ = nullptr;
return false;
}
}
return true;
}
bool BluetoothClassicMedium::StopDiscovery() {
auto &adapter = adapter_.GetBluezAdapterObject();
NEARBY_LOGS(INFO) << __func__ << "Stopping discovery on "
<< adapter.getObjectPath();
auto ret = true;
try {
adapter.StopDiscovery();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&adapter, "StopDiscovery", e);
ret = false;
}
device_watcher_ = nullptr;
return ret;
}
std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
api::BluetoothDevice &remote_device, const std::string &service_uuid,
CancellationFlag *cancellation_flag) {
if (!profile_manager_->ProfileRegistered(service_uuid)) {
if (!profile_manager_->Register(std::nullopt, service_uuid)) {
NEARBY_LOGS(ERROR) << __func__ << ": Could not register profile "
<< service_uuid << " with Bluez";
return nullptr;
}
}
auto address = remote_device.GetMacAddress();
auto device = devices_->get_device_by_address(address);
if (device == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Device " << address
<< " is no longer known";
return nullptr;
}
if (!device->ConnectToProfile(service_uuid)) {
return nullptr;
}
auto fd = profile_manager_->GetServiceRecordFD(remote_device, service_uuid,
cancellation_flag);
if (!fd.has_value()) {
NEARBY_LOGS(WARNING) << __func__
<< ": Failed to get a new connection for profile "
<< service_uuid << " for device " << address;
return nullptr;
}
return std::unique_ptr<api::BluetoothSocket>(
new BluetoothSocket(device, fd.value()));
}
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)) {
NEARBY_LOGS(ERROR) << __func__ << ": Could not register profile "
<< service_name << " " << service_uuid
<< " with Bluez";
return nullptr;
}
}
return std::unique_ptr<api::BluetoothServerSocket>(
new BluetoothServerSocket(*profile_manager_, service_uuid));
}
api::BluetoothDevice *BluetoothClassicMedium::GetRemoteDevice(
const std::string &mac_address) {
auto device = devices_->get_device_by_address(mac_address);
if (device == nullptr) return nullptr;
return device.get();
}
std::unique_ptr<api::BluetoothPairing> BluetoothClassicMedium::CreatePairing(
api::BluetoothDevice &remote_device) {
auto device = devices_->get_device_by_address(remote_device.GetMacAddress());
if (device == nullptr) return nullptr;
return std::unique_ptr<api::BluetoothPairing>(
new BluetoothPairing(adapter_, device));
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,116 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_MEDIUM_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_CLASSIC_MEDIUM_H_
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.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_devices.h"
namespace nearby {
namespace linux {
// Container of operations that can be performed over the Bluetooth Classic
// medium.
class BluetoothClassicMedium : public api::BluetoothClassicMedium {
public:
explicit BluetoothClassicMedium(BluetoothAdapter &adapter);
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
//
// Returns true once the process of discovery has been initiated.
bool StartDiscovery(DiscoveryCallback discovery_callback) override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery()
//
// Returns true once discovery is well and truly stopped; after this returns,
// there must be no more invocations of the DiscoveryCallback passed in to
// StartDiscovery().
bool StopDiscovery() override;
// A combination of
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord
// followed by
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect().
//
// service_uuid is the canonical textual representation
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
// type 3 name-based
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
// UUID.
//
// On success, returns a new BluetoothSocket.
// On error, returns nullptr.
std::unique_ptr<api::BluetoothSocket> ConnectToService(
api::BluetoothDevice &remote_device, const std::string &service_uuid,
CancellationFlag *cancellation_flag) override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord
//
// service_uuid is the canonical textual representation
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
// type 3 name-based
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
// UUID.
//
// Returns nullptr error.
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
const std::string &service_name,
const std::string &service_uuid) override;
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createBond()
//
// Start the bonding (pairing) process with the remote device.
// Return a Bluetooth pairing instance to handle the pairing process with the
// remote device.
std::unique_ptr<api::BluetoothPairing> CreatePairing(
api::BluetoothDevice &remote_device) override;
api::BluetoothDevice *GetRemoteDevice(
const std::string &mac_address) override;
void AddObserver(Observer *observer) override {
observers_->AddObserver(observer);
};
void RemoveObserver(Observer *observer) override {
observers_->RemoveObserver(observer);
};
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
BluetoothAdapter adapter_;
std::shared_ptr<ObserverList<Observer>> observers_;
std::shared_ptr<BluetoothDevices> devices_;
std::unique_ptr<DeviceWatcher> device_watcher_;
std::unique_ptr<ProfileManager> profile_manager_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,55 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/bluetooth_classic_server_socket.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
#include "internal/platform/implementation/linux/bluetooth_classic_socket.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
if (stopped_.Cancelled()) {
NEARBY_LOGS(ERROR) << __func__ << ": server socket has been stopped";
return nullptr;
}
NEARBY_LOGS(VERBOSE) << __func__
<< ": accepting new connections for service uuid "
<< service_uuid_;
auto pair = profile_manager_.GetServiceRecordFD(service_uuid_, &stopped_);
if (!pair.has_value()) {
if (!stopped_.Cancelled())
NEARBY_LOGS(ERROR) << __func__
<< ": Failed to get a new connection for profile "
<< service_uuid_;
return nullptr;
}
auto [device, fd] = *pair;
return std::make_unique<BluetoothSocket>(device, std::move(fd));
}
Exception BluetoothServerSocket::Close() {
NEARBY_LOGS(ERROR) << __func__ << ": closing bluetooth server socket";
stopped_.Cancel();
profile_manager_.Unregister(service_uuid_);
return {Exception::kSuccess};
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,56 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_SERVER_SOCKET_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_SERVER_SOCKET_H_
#include "absl/strings/string_view.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_bluez_profile.h"
namespace nearby {
namespace linux {
class BluetoothServerSocket final : public api::BluetoothServerSocket {
public:
BluetoothServerSocket(ProfileManager &profile_manager,
absl::string_view service_uuid)
: profile_manager_(profile_manager), service_uuid_(service_uuid) {}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
//
// Blocks until either:
// - at least one incoming connection request is available, or
// - ServerSocket is closed.
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be
// closed.
std::unique_ptr<api::BluetoothSocket> Accept() override;
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
//
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override;
private:
CancellationFlag stopped_;
ProfileManager &profile_manager_;
std::string service_uuid_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,123 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sys/poll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <array>
#include <cerrno>
#include <cstdint>
#include <cstring>
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/linux/bluetooth_classic_socket.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
Exception Poller::Ready() {
while (true) {
auto ret = poll(fds_, 1, -1);
if (ret < 0) {
if (errno == EAGAIN) continue;
NEARBY_LOGS(ERROR) << __func__ << ": error polling socket for I/O: "
<< std::strerror(errno);
return {Exception::kIo};
}
if ((fds_[0].revents & poll_event_) != 0) {
return {Exception::kSuccess};
}
if ((fds_[0].revents & POLLHUP) != 0) {
NEARBY_LOGS(ERROR) << __func__ << ": socket disconnected";
return {Exception::kIo};
}
if ((fds_[0].revents & (POLLERR | POLLNVAL)) != 0) {
NEARBY_LOGS(ERROR) << __func__ << ": an error occured on the socket";
return {Exception::kIo};
}
}
}
ExceptionOr<ByteArray> BluetoothInputStream::Read(std::int64_t size) {
if (!fd_.isValid()) return Exception{Exception::kIo};
auto poller = Poller::CreateInputPoller(fd_);
std::string buffer;
buffer.resize(size);
char *data = buffer.data();
size_t total_read = 0;
while (total_read < size) {
auto result = poller.Ready();
if (result.Raised()) return result;
auto bytes_read = read(fd_.get(), &data[total_read], (size - total_read));
if (bytes_read < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
NEARBY_LOGS(ERROR) << __func__
<< ": error reading data on bluetooth socket: "
<< std::strerror(errno);
return {Exception::kIo};
}
total_read += bytes_read;
}
return ExceptionOr{ByteArray(std::move(buffer))};
}
Exception BluetoothInputStream::Close() {
if (!fd_.isValid()) return {Exception::kIo};
fd_.reset();
return {Exception::kSuccess};
}
Exception BluetoothOutputStream::Write(const ByteArray &data) {
if (!fd_.isValid()) return Exception{Exception::kIo};
auto poller = Poller::CreateOutputPoller(fd_);
size_t total_wrote = 0;
while (total_wrote < data.size()) {
auto result = poller.Ready();
if (result.Raised()) return result;
const char *buf = data.data();
auto wrote =
write(fd_.get(), &buf[total_wrote], (data.size() - total_wrote));
if (wrote < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) continue;
NEARBY_LOGS(ERROR) << __func__
<< ": error writing data on bluetooth socket: "
<< std::strerror(errno);
return {Exception::kIo};
}
total_wrote += wrote;
}
return {Exception::kSuccess};
}
Exception BluetoothOutputStream::Close() {
if (!fd_.isValid()) return {Exception::kIo};
fd_.reset();
return {Exception::kSuccess};
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,103 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_SOCKET_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_SOCKET_H_
#include <memory>
#include <optional>
#include <sdbus-c++/Types.h>
#include <sys/poll.h>
#include <systemd/sd-bus.h>
#include "internal/platform/exception.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/output_stream.h"
namespace nearby {
namespace linux {
// BlueZ's NewConnection gives us a non-blocking FD, so we need to poll
// it to be able to write/read bytes.
class Poller final {
public:
static Poller CreateInputPoller(const sdbus::UnixFd &fd) {
return Poller(fd, POLLIN);
}
static Poller CreateOutputPoller(const sdbus::UnixFd &fd) {
return Poller(fd, POLLOUT);
}
Exception Ready();
private:
Poller(const sdbus::UnixFd &fd, short event) : poll_event_(event) {
fds_[0].fd = fd.get();
fds_[0].events = event;
}
short poll_event_;
struct pollfd fds_[1];
};
class BluetoothInputStream final : public nearby::InputStream {
public:
explicit BluetoothInputStream(sdbus::UnixFd fd) : fd_(std::move(fd)){};
ExceptionOr<ByteArray> Read(std::int64_t size) override;
Exception Close() override;
private:
sdbus::UnixFd fd_;
};
class BluetoothOutputStream : public nearby::OutputStream {
public:
explicit BluetoothOutputStream(sdbus::UnixFd fd) : fd_(std::move(fd)){};
Exception Write(const ByteArray &data) override;
Exception Flush() override { return {Exception::kSuccess}; }
Exception Close() override;
private:
sdbus::UnixFd fd_;
};
class BluetoothSocket final : public api::BluetoothSocket {
public:
BluetoothSocket(std::shared_ptr<BluetoothDevice> device,
const sdbus::UnixFd &fd)
: device_(std::move(device)), output_stream_(fd), input_stream_(fd) {}
nearby::InputStream &GetInputStream() override { return input_stream_; }
nearby::OutputStream &GetOutputStream() override { return output_stream_; }
Exception Close() override {
input_stream_.Close();
output_stream_.Close();
return Exception{Exception::kSuccess};
}
api::BluetoothDevice *GetRemoteDevice() override { return device_.get(); };
private:
std::shared_ptr<BluetoothDevice> device_;
BluetoothOutputStream output_stream_;
BluetoothInputStream input_stream_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,192 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <algorithm>
#include <chrono>
#include <functional>
#include <optional>
#include <absl/time/clock.h>
#include <sdbus-c++/Types.h>
#include "absl/strings/substitute.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/device_client.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
static constexpr std::chrono::minutes kLostPeripheralsCleanupMinFreq(5);
std::shared_ptr<BluetoothDevice> BluetoothDevices::get_device_by_path(
const sdbus::ObjectPath &device_object_path) {
absl::ReaderMutexLock l(&devices_by_path_lock_);
if (devices_by_path_.count(device_object_path) == 0) {
return nullptr;
}
return devices_by_path_[device_object_path];
}
std::shared_ptr<BluetoothDevice> BluetoothDevices::get_device_by_address(
const std::string &addr) {
auto device_object_path =
bluez::device_object_path(adapter_object_path_, addr);
return get_device_by_path(device_object_path);
}
void BluetoothDevices::remove_device_by_path(
const sdbus::ObjectPath &device_object_path) {
absl::MutexLock l(&devices_by_path_lock_);
devices_by_path_.erase(device_object_path);
}
void BluetoothDevices::mark_peripheral_lost(
const sdbus::ObjectPath &device_object_path) {
absl::ReaderMutexLock lock(&devices_by_path_lock_);
if (devices_by_path_.count(device_object_path) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Device " << device_object_path
<< " doesn't exist";
return;
}
devices_by_path_[device_object_path]->MarkLost();
}
void BluetoothDevices::cleanup_lost_peripherals() {
auto now = std::chrono::steady_clock::now();
absl::MutexLock lock(&devices_by_path_lock_);
if ((now - last_cleanup_) < kLostPeripheralsCleanupMinFreq) {
return;
}
last_cleanup_ = now;
for (auto it = devices_by_path_.begin(), end = devices_by_path_.end();
it != end;) {
auto copy = it++;
if (copy->second->Lost()) devices_by_path_.erase(copy);
}
}
std::shared_ptr<MonitoredBluetoothDevice> BluetoothDevices::add_new_device(
sdbus::ObjectPath device_object_path) {
absl::MutexLock l(&devices_by_path_lock_);
auto [device_it, inserted] = devices_by_path_.emplace(
std::string(device_object_path),
std::make_shared<MonitoredBluetoothDevice>(
system_bus_,
std::make_shared<bluez::Device>(system_bus_, device_object_path),
observers_));
if (!inserted) device_it->second->UnmarkLost();
return device_it->second;
}
void DeviceWatcher::onInterfacesAdded(
const sdbus::ObjectPath &object,
const std::map<std::string, std::map<std::string, sdbus::Variant>>
&interfaces) {
auto path_prefix = absl::Substitute("$0/dev_", adapter_object_path_);
if (object.find(path_prefix) != 0) {
return;
}
if (interfaces.count(org::bluez::Device1_proxy::INTERFACE_NAME) == 0) return;
auto device = devices_->add_new_device(object);
device->SetDiscoveryCallback(discovery_cb_);
if (discovery_cb_ != nullptr &&
discovery_cb_->device_discovered_cb != nullptr) {
discovery_cb_->device_discovered_cb(*device);
}
if (observers_ != nullptr) {
for (const auto &observer : observers_->GetObservers()) {
observer->DeviceAdded(*device);
}
}
}
void DeviceWatcher::onInterfacesRemoved(
const sdbus::ObjectPath &object,
const std::vector<std::string> &interfaces) {
auto path_prefix = absl::Substitute("$0/dev_", adapter_object_path_);
if (object.find(path_prefix) != 0) {
return;
}
auto removed_device_it = std::find(interfaces.begin(), interfaces.end(),
org::bluez::Device1_proxy::INTERFACE_NAME);
if (removed_device_it != interfaces.end()) {
auto device = devices_->get_device_by_path(object);
if (device == nullptr) {
NEARBY_LOGS(WARNING) << __func__
<< ": received InterfacesRemoved for a device "
"we don't know about: "
<< object;
return;
}
NEARBY_LOGS(INFO) << __func__ << ": Device " << object
<< " has been removed";
if (discovery_cb_ != nullptr && discovery_cb_->device_lost_cb != nullptr) {
discovery_cb_->device_lost_cb(*device);
}
if (observers_ != nullptr) {
for (const auto &observer : observers_->GetObservers()) {
observer->DeviceRemoved(*device);
}
devices_->remove_device_by_path(object);
} else {
devices_->mark_peripheral_lost(object);
}
}
}
void DeviceWatcher::notifyExistingDevices() {
std::map<sdbus::ObjectPath,
std::map<std::string, std::map<std::string, sdbus::Variant>>>
objects;
try {
objects = GetManagedObjects();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(this, "GetManagedObjects", e);
return;
}
auto device_it =
std::find_if(objects.begin(), objects.end(), [&](auto entry) {
auto &[device_path, interfaces] = entry;
return device_path.find(
absl::Substitute("$0/dev_", adapter_object_path_)) == 0 &&
interfaces.count(org::bluez::Device1_proxy::INTERFACE_NAME) == 1;
});
for (; device_it != objects.end(); device_it++) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Adding existing device "
<< device_it->first;
auto device = devices_->add_new_device(device_it->first);
if (discovery_cb_ != nullptr) {
device->SetDiscoveryCallback(discovery_cb_);
}
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,127 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_DEVICES_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_DEVICES_H_
#include <chrono>
#include <memory>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/container/flat_hash_map.h"
#include "absl/synchronization/mutex.h"
#include "internal/base/observer_list.h"
#include "internal/platform/bluetooth_utils.h"
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
namespace nearby {
namespace linux {
class BluetoothDevices final {
public:
BluetoothDevices(
std::shared_ptr<sdbus::IConnection> system_bus,
sdbus::ObjectPath adapter_object_path,
ObserverList<api::BluetoothClassicMedium::Observer> &observers)
: system_bus_(std::move(system_bus)),
observers_(observers),
adapter_object_path_(std::move(adapter_object_path)) {}
std::shared_ptr<BluetoothDevice> get_device_by_path(const sdbus::ObjectPath &)
ABSL_LOCKS_EXCLUDED(devices_by_path_lock_);
std::shared_ptr<BluetoothDevice> get_device_by_address(const std::string &);
std::shared_ptr<BluetoothDevice> get_device_by_unique_id(
api::ble_v2::BlePeripheral::UniqueId id) {
auto addr = BluetoothUtils::FromNumber(id);
return get_device_by_address(addr);
}
std::shared_ptr<MonitoredBluetoothDevice> add_new_device(sdbus::ObjectPath)
ABSL_LOCKS_EXCLUDED(devices_by_path_lock_);
void remove_device_by_path(const sdbus::ObjectPath &)
ABSL_LOCKS_EXCLUDED(devices_by_path_lock_);
void mark_peripheral_lost(const sdbus::ObjectPath &)
ABSL_LOCKS_EXCLUDED(devices_by_path_lock_);
void cleanup_lost_peripherals() ABSL_LOCKS_EXCLUDED(devices_by_path_lock_);
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
ObserverList<api::BluetoothClassicMedium::Observer> &observers_;
sdbus::ObjectPath adapter_object_path_;
absl::Mutex devices_by_path_lock_;
absl::flat_hash_map<std::string, std::shared_ptr<MonitoredBluetoothDevice>>
devices_by_path_ ABSL_GUARDED_BY(devices_by_path_lock_);
std::chrono::time_point<std::chrono::steady_clock> last_cleanup_
ABSL_GUARDED_BY(devices_by_path_lock_);
};
class DeviceWatcher final : sdbus::ProxyInterfaces<sdbus::ObjectManager_proxy> {
public:
DeviceWatcher(const DeviceWatcher &) = delete;
DeviceWatcher(DeviceWatcher &&) = delete;
DeviceWatcher &operator=(const DeviceWatcher &) = delete;
DeviceWatcher &operator=(DeviceWatcher &&) = delete;
DeviceWatcher(
sdbus::IConnection &system_bus,
const sdbus::ObjectPath &adapter_object_path,
std::shared_ptr<BluetoothDevices> devices,
std::unique_ptr<api::BluetoothClassicMedium::DiscoveryCallback>
discovery_callback,
std::shared_ptr<ObserverList<api::BluetoothClassicMedium::Observer>>
observers)
: ProxyInterfaces(system_bus, "org.bluez", "/"),
adapter_object_path_(adapter_object_path),
devices_(std::move(devices)),
discovery_cb_(std::move(discovery_callback)),
observers_(std::move(observers)) {
notifyExistingDevices();
registerProxy();
}
DeviceWatcher(sdbus::IConnection &system_bus,
const sdbus::ObjectPath &adapter_object_path,
std::shared_ptr<BluetoothDevices> devices)
: DeviceWatcher(system_bus, adapter_object_path, std::move(devices),
nullptr, nullptr) {}
~DeviceWatcher() { unregisterProxy(); }
void onInterfacesAdded(
const sdbus::ObjectPath &object,
const std::map<std::string, std::map<std::string, sdbus::Variant>>
&interfaces) override;
void onInterfacesRemoved(const sdbus::ObjectPath &object,
const std::vector<std::string> &interfaces) override;
private:
void notifyExistingDevices();
sdbus::ObjectPath adapter_object_path_;
std::shared_ptr<BluetoothDevices> devices_;
std::shared_ptr<api::BluetoothClassicMedium::DiscoveryCallback> discovery_cb_;
std::shared_ptr<ObserverList<api::BluetoothClassicMedium::Observer>>
observers_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,105 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <utility>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include "internal/platform/implementation/bluetooth_classic.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_pairing.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
void BluetoothPairing::pairing_reply_handler(const sdbus::Error *error) {
if (error != nullptr && error->isValid()) {
const auto &name = error->getName();
api::BluetoothPairingCallback::PairingError err =
api::BluetoothPairingCallback::PairingError::kAuthFailed;
NEARBY_LOGS(ERROR) << __func__ << ": "
<< "Got error '" << error->getName()
<< "' with message '" << error->getMessage()
<< "' while pairing with device "
<< device_->GetMacAddress();
if (name == "org.bluez.Error.AuthenticationCanceled") {
err = api::BluetoothPairingCallback::PairingError::kAuthCanceled;
} else if (name == "org.bluez.Error.AuthenticationFailed") {
err = api::BluetoothPairingCallback::PairingError::kAuthFailed;
} else if (name == "org.bluez.Error.AuthenticationRejected") {
err = api::BluetoothPairingCallback::PairingError::kAuthRejected;
} else if (name == "org.bluez.Error.AuthenticationTimeout") {
err = api::BluetoothPairingCallback::PairingError::kAuthTimeout;
}
if (pairing_cb_.on_pairing_error_cb != nullptr) {
pairing_cb_.on_pairing_error_cb(err);
}
return;
}
if (pairing_cb_.on_paired_cb != nullptr) {
pairing_cb_.on_paired_cb();
}
}
BluetoothPairing::BluetoothPairing(
BluetoothAdapter &adapter, std::shared_ptr<BluetoothDevice> remote_device)
: device_(std::move(remote_device)),
device_object_path_(bluez::device_object_path(adapter.GetObjectPath(),
device_->GetAddress())),
adapter_(adapter) {}
bool BluetoothPairing::InitiatePairing(
api::BluetoothPairingCallback pairing_cb) {
pairing_cb_ = std::move(pairing_cb);
if (pairing_cb_.on_pairing_initiated_cb != nullptr)
pairing_cb_.on_pairing_initiated_cb(api::PairingParams{
api::PairingParams::PairingType::kConsent, std::string()});
return true;
}
bool BluetoothPairing::FinishPairing(
std::optional<absl::string_view> pin_code) {
device_->SetPairReplyCallback([this](const sdbus::Error *error) {
this->pairing_reply_handler(error);
});
auto call = device_->Pair();
if (!call.has_value()) return false;
pair_async_call_ = *call;
return true;
}
bool BluetoothPairing::CancelPairing() {
if (pair_async_call_.isPending()) {
pair_async_call_.cancel();
}
return device_->CancelPairing();
}
bool BluetoothPairing::Unpair() {
return adapter_.RemoveDeviceByObjectPath(device_object_path_);
}
bool BluetoothPairing::IsPaired() { return device_->Bonded(); }
} // namespace linux
} // namespace nearby
@@ -0,0 +1,57 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUETOOTH_PAIRING_H_
#define PLATFORM_IMPL_LINUX_BLUETOOTH_PAIRING_H_
#include <memory>
#include <string>
#include <sdbus-c++/Error.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <systemd/sd-bus.h>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluetooth_classic_device.h"
namespace nearby {
namespace linux {
class BluetoothPairing final : public api::BluetoothPairing {
public:
BluetoothPairing(BluetoothAdapter &adapter,
std::shared_ptr<BluetoothDevice> remote_device);
bool InitiatePairing(api::BluetoothPairingCallback pairing_cb) override;
bool FinishPairing(std::optional<absl::string_view> pin_code) override;
bool CancelPairing() override;
bool Unpair() override;
bool IsPaired() override;
private:
void pairing_reply_handler(const sdbus::Error *e);
sdbus::PendingAsyncCall pair_async_call_;
std::shared_ptr<BluetoothDevice> device_;
sdbus::ObjectPath device_object_path_;
linux::BluetoothAdapter adapter_;
api::BluetoothPairingCallback pairing_cb_;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,79 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sdbus-c++/Types.h>
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/strings/substitute.h"
#include "internal/platform/implementation/linux/bluez.h"
namespace nearby {
namespace linux {
namespace bluez {
std::string device_object_path(const sdbus::ObjectPath &adapter_object_path,
absl::string_view mac_address) {
return absl::Substitute(
"$0/dev_$1", adapter_object_path,
absl::StrReplaceAll(absl::AsciiStrToUpper(mac_address), {{":", "_"}}));
}
sdbus::ObjectPath profile_object_path(absl::string_view service_uuid) {
return absl::Substitute(
"/com/google/nearby/medium/bluetooth_classic/profiles/$0",
absl::StrReplaceAll(service_uuid, {{"-", "_"}}));
}
sdbus::ObjectPath adapter_object_path(absl::string_view name) {
return absl::Substitute("/org/bluez/$0", name);
}
sdbus::ObjectPath gatt_service_path(size_t num) {
return absl::Substitute("$0/service$1", NEARBY_BLE_GATT_PATH_ROOT, num);
}
sdbus::ObjectPath gatt_characteristic_path(
const sdbus::ObjectPath &service_path, size_t num) {
return absl::Substitute("$0/char$1", service_path, num);
}
sdbus::ObjectPath ble_advertisement_path(size_t num) {
return absl::Substitute("/com/google/nearby/medium/ble/advertisement/$0",
num);
}
sdbus::ObjectPath advertisement_monitor_path(absl::string_view uuid) {
return absl::Substitute(
"/com/google/nearby/medium/ble/advertisement/monitor/$0",
absl::StrReplaceAll(uuid, {{"-", "_"}}));
}
int16_t TxPowerLevelDbm(api::ble_v2::TxPowerLevel level) {
switch (level) {
case api::ble_v2::TxPowerLevel::kUnknown:
return 0;
case api::ble_v2::TxPowerLevel::kUltraLow:
return -3;
case api::ble_v2::TxPowerLevel::kLow:
return 0;
case api::ble_v2::TxPowerLevel::kMedium:
return 3;
case api::ble_v2::TxPowerLevel::kHigh:
return 6;
}
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,86 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_H_
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/ble_v2.h"
#include <string>
#define BLUEZ_LOG_METHOD_CALL_ERROR(proxy, method, err) \
do { \
NEARBY_LOGS(ERROR) << __func__ << ": Got error '" << (err).getName() \
<< "' with message '" << (err).getMessage() \
<< "' while calling " << method << " on object " \
<< (proxy)->getObjectPath(); \
} while (false)
namespace nearby {
namespace linux {
namespace bluez {
static constexpr const char *SERVICE_DEST = "org.bluez";
static constexpr const char *ADAPTER_INTERFACE = "org.bluez.Adapter1";
static constexpr const char *DEVICE_INTERFACE = "org.bluez.Device1";
static constexpr const char *DEVICE_PROP_ADDRESS = "Address";
static constexpr const char *DEVICE_PROP_ALIAS = "Alias";
static constexpr const char *DEVICE_PROP_PAIRED = "Paired";
static constexpr const char *DEVICE_PROP_CONNECTED = "Connected";
static constexpr const char *DEVICE_NAME = "Name";
static constexpr const char *NEARBY_BLE_GATT_PATH_ROOT =
"/com/google/nearby/medium/ble/gatt";
std::string device_object_path(const sdbus::ObjectPath &adapter_object_path,
absl::string_view mac_address);
sdbus::ObjectPath profile_object_path(absl::string_view service_uuid);
sdbus::ObjectPath adapter_object_path(absl::string_view name);
sdbus::ObjectPath gatt_service_path(size_t num);
sdbus::ObjectPath gatt_characteristic_path(
const sdbus::ObjectPath &service_path, size_t num);
sdbus::ObjectPath ble_advertisement_path(size_t num);
sdbus::ObjectPath advertisement_monitor_path(absl::string_view uuid);
int16_t TxPowerLevelDbm(api::ble_v2::TxPowerLevel level);
class BluezObjectManager
: public sdbus::ProxyInterfaces<sdbus::ObjectManager_proxy> {
public:
explicit BluezObjectManager(sdbus::IConnection &system_bus)
: ProxyInterfaces(system_bus, "org.bluez", "/") {
registerProxy();
}
virtual ~BluezObjectManager() { unregisterProxy(); }
protected:
void onInterfacesAdded(
const sdbus::ObjectPath &objectPath,
const std::map<std::string, std::map<std::string, sdbus::Variant>>
&interfacesAndProperties) override {}
void onInterfacesRemoved(
const sdbus::ObjectPath &objectPath,
const std::vector<std::string> &interfaces) override {}
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,68 @@
#include "internal/platform/implementation/linux/bluez_advertisement_monitor.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/utils.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
namespace bluez {
AdvertisementMonitor::AdvertisementMonitor(
sdbus::IConnection &system_bus, Uuid service_uuid,
api::ble_v2::TxPowerLevel tx_power_level, absl::string_view type,
std::shared_ptr<BluetoothDevices> devices,
api::ble_v2::BleMedium::ScanCallback scan_callback)
: AdvertisementMonitor(
system_bus, service_uuid, tx_power_level, type, std::move(devices),
api::ble_v2::BleMedium::ScanningCallback{
.start_scanning_result = nullptr,
.advertisement_found_cb =
std::move(scan_callback.advertisement_found_cb)}) {}
AdvertisementMonitor::AdvertisementMonitor(
sdbus::IConnection &system_bus, Uuid service_uuid,
api::ble_v2::TxPowerLevel tx_power_level, absl::string_view type,
std::shared_ptr<BluetoothDevices> devices,
api::ble_v2::BleMedium::ScanningCallback scan_callback)
: AdaptorInterfaces(system_bus, bluez::advertisement_monitor_path(
std::string{service_uuid})),
devices_(std::move(devices)),
scan_callback_{std::move(scan_callback.advertisement_found_cb)},
start_scanning_result_callback_(
std::move(scan_callback.start_scanning_result)),
type_(type),
service_uuid_(service_uuid),
tx_power_level_(tx_power_level) {
registerAdaptor();
}
void AdvertisementMonitor::DeviceFound(const sdbus::ObjectPath &device) {
devices_->cleanup_lost_peripherals();
auto peripheral = devices_->add_new_device(device);
auto service_data = peripheral->ServiceData();
if (!service_data.has_value()) return;
struct api::ble_v2::BleAdvertisementData adv_data;
for (const auto &[uuid_str, data] : *service_data) {
auto uuid = UuidFromString(uuid_str);
if (!uuid.has_value()) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Could not parse UUID string in ServiceData for peripheral "
<< peripheral->getObjectPath();
continue;
}
std::vector<uint8_t> bytes = data;
adv_data.service_data.emplace(*uuid,
std::string(bytes.begin(), bytes.end()));
}
scan_callback_.advertisement_found_cb(*peripheral, adv_data);
}
void AdvertisementMonitor::DeviceLost(const sdbus::ObjectPath &device) {
devices_->mark_peripheral_lost(device);
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,95 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_H_
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/StandardInterfaces.h>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/advertisement_monitor_server.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
namespace bluez {
class AdvertisementMonitor final
: public sdbus::AdaptorInterfaces<org::bluez::AdvertisementMonitor1_adaptor,
sdbus::ManagedObject_adaptor> {
public:
AdvertisementMonitor(const AdvertisementMonitor&) = delete;
AdvertisementMonitor(AdvertisementMonitor&&) = delete;
AdvertisementMonitor& operator=(const AdvertisementMonitor&) = delete;
AdvertisementMonitor& operator=(AdvertisementMonitor&&) = delete;
AdvertisementMonitor(sdbus::IConnection& system_bus, Uuid service_uuid,
api::ble_v2::TxPowerLevel tx_power_level,
absl::string_view type,
std::shared_ptr<BluetoothDevices> devices,
api::ble_v2::BleMedium::ScanCallback scan_callback);
AdvertisementMonitor(sdbus::IConnection& system_bus, Uuid service_uuid,
api::ble_v2::TxPowerLevel tx_power_level,
absl::string_view type,
std::shared_ptr<BluetoothDevices> devices,
api::ble_v2::BleMedium::ScanningCallback scan_callback);
~AdvertisementMonitor() { unregisterAdaptor(); }
private:
// Methods
void Release() override {}
void Activate() override {
if (start_scanning_result_callback_ != nullptr) {
start_scanning_result_callback_(absl::OkStatus());
}
}
void DeviceFound(const sdbus::ObjectPath& device) override;
void DeviceLost(const sdbus::ObjectPath& device) override;
// Properties
std::string Type() override { return type_; };
int16_t RSSILowThreshold() override { return 0; };
int16_t RSSIHighThreshold() override {
return bluez::TxPowerLevelDbm(tx_power_level_);
}
uint16_t RSSISamplingPeriod() override {
// The Windows implementation uses a sampling interval of 2 seconds.
return 20;
}
std::vector<sdbus::Struct<uint8_t, uint8_t, std::vector<uint8_t>>> Patterns()
override {
std::array<char, 16> service_id_data = service_uuid_.data();
return {{0,
0x16,
{static_cast<uint8_t>(service_id_data[3] & 0xFF),
static_cast<uint8_t>(service_id_data[2] & 0xFF)}}};
};
std::shared_ptr<BluetoothDevices> devices_;
api::ble_v2::BleMedium::ScanCallback scan_callback_;
absl::AnyInvocable<void(absl::Status)> start_scanning_result_callback_;
std::string type_;
Uuid service_uuid_;
api::ble_v2::TxPowerLevel tx_power_level_;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,87 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_ADVERTISEMENT_MONITOR_MANAGER_H_
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/advertisement_monitor_manager_client.h"
namespace nearby {
namespace linux {
namespace bluez {
class AdvertisementMonitorManager final
: public sdbus::ProxyInterfaces<
org::bluez::AdvertisementMonitorManager1_proxy> {
private:
friend std::unique_ptr<AdvertisementMonitorManager>
std::make_unique<AdvertisementMonitorManager>(sdbus::IConnection &,
const BluetoothAdapter &);
AdvertisementMonitorManager(sdbus::IConnection &system_bus,
const BluetoothAdapter &adapter)
: ProxyInterfaces(system_bus, "org.bluez", adapter.GetObjectPath()) {
registerProxy();
}
public:
AdvertisementMonitorManager(const AdvertisementMonitorManager &) = delete;
AdvertisementMonitorManager(AdvertisementMonitorManager &&) = delete;
AdvertisementMonitorManager &operator=(const AdvertisementMonitorManager &) =
delete;
AdvertisementMonitorManager &operator=(AdvertisementMonitorManager &&) =
delete;
~AdvertisementMonitorManager() { unregisterProxy(); }
static std::unique_ptr<AdvertisementMonitorManager>
DiscoverAdvertisementMonitorManager(sdbus::IConnection &system_bus,
const BluetoothAdapter &adapter) {
bluez::BluezObjectManager manager(system_bus);
std::map<sdbus::ObjectPath,
std::map<std::string, std::map<std::string, sdbus::Variant>>>
objects;
try {
objects = manager.GetManagedObjects();
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(&manager, "GetManagedObjects", e);
return nullptr;
}
if (objects.count(adapter.GetObjectPath()) == 0) {
NEARBY_LOGS(ERROR) << __func__ << ": Adapter object no longer exists "
<< adapter.GetObjectPath();
return nullptr;
}
if (objects[adapter.GetObjectPath()].count(
org::bluez::AdvertisementMonitorManager1_proxy::INTERFACE_NAME) ==
0) {
NEARBY_LOGS(ERROR)
<< __func__ << ": Adapter " << adapter.GetObjectPath()
<< " doesn't provide "
<< org::bluez::AdvertisementMonitorManager1_proxy::INTERFACE_NAME;
return nullptr;
}
return std::make_unique<AdvertisementMonitorManager>(system_bus, adapter);
}
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,65 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_DEVICE_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_DEVICE_H_
#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/device_client.h"
namespace nearby {
namespace linux {
namespace bluez {
class Device : public sdbus::ProxyInterfaces<org::bluez::Device1_proxy> {
public:
Device(std::shared_ptr<sdbus::IConnection> system_bus,
const sdbus::ObjectPath &device_path)
: ProxyInterfaces(*system_bus, "org.bluez", device_path),
system_bus(std::move(system_bus)) {
registerProxy();
}
~Device() { unregisterProxy(); }
void SetPairReplyCallback(absl::AnyInvocable<void(const sdbus::Error *)> cb)
ABSL_LOCKS_EXCLUDED(pair_callback_lock_) {
absl::MutexLock l(&pair_callback_lock_);
on_pair_reply_cb_ = std::move(cb);
}
void ResetPairReplyCallback() ABSL_LOCKS_EXCLUDED(pair_callback_lock_) {
absl::MutexLock l(&pair_callback_lock_);
on_pair_reply_cb_ = nullptr;
}
protected:
void onPairReply(const sdbus::Error *error) override
ABSL_LOCKS_EXCLUDED(pair_callback_lock_) {
absl::ReaderMutexLock l(&pair_callback_lock_);
if (on_pair_reply_cb_ != nullptr) on_pair_reply_cb_(error);
};
private:
std::shared_ptr<sdbus::IConnection> system_bus;
absl::Mutex pair_callback_lock_;
absl::AnyInvocable<void(const sdbus::Error *)> on_pair_reply_cb_
ABSL_GUARDED_BY(pair_callback_lock_) = nullptr;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,41 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <map>
#include <string>
#include <vector>
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_client.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_client.h"
namespace nearby {
namespace linux {
namespace bluez {
void SubscribedGattCharacteristicClient::onPropertiesChanged(
const std::string& interfaceName,
const std::map<std::string, sdbus::Variant>& changedProperties,
const std::vector<std::string>& invalidatedProperties) {
if (interfaceName != org::bluez::GattCharacteristic1_proxy::INTERFACE_NAME)
return;
if (changedProperties.count("Value") == 1) {
std::vector<uint8_t> value_bytes = changedProperties.at("Value");
if (notify_callback_ != nullptr) {
auto value = std::string(value_bytes.cbegin(), value_bytes.cend());
notify_callback_(value);
}
}
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,71 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_GATT_CHARACTERISTIC_CLIENT_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_GATT_CHARACTERISTIC_CLIENT_H_
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_client.h"
namespace nearby {
namespace linux {
namespace bluez {
class GattCharacteristicClient
: public sdbus::ProxyInterfaces<org::bluez::GattCharacteristic1_proxy,
sdbus::Properties_proxy> {
public:
GattCharacteristicClient(std::shared_ptr<sdbus::IConnection> system_bus,
sdbus::ObjectPath path)
: ProxyInterfaces(*system_bus, "org.bluez", std::move(path)),
system_bus_(std::move(system_bus)) {
registerProxy();
}
virtual ~GattCharacteristicClient() { unregisterProxy(); }
protected:
void onPropertiesChanged(
const std::string& interfaceName,
const std::map<std::string, sdbus::Variant>& changedProperties,
const std::vector<std::string>& invalidatedProperties) override {}
std::shared_ptr<sdbus::IConnection> system_bus_;
};
class SubscribedGattCharacteristicClient : public GattCharacteristicClient {
public:
SubscribedGattCharacteristicClient(
std::shared_ptr<sdbus::IConnection> system_bus, sdbus::ObjectPath path,
absl::AnyInvocable<void(absl::string_view value)> notify_callback)
: GattCharacteristicClient(std::move(system_bus), std::move(path)),
notify_callback_(std::move(notify_callback)) {}
protected:
void onPropertiesChanged(
const std::string& interfaceName,
const std::map<std::string, sdbus::Variant>& changedProperties,
const std::vector<std::string>& invalidatedProperties) override;
private:
absl::AnyInvocable<void(absl::string_view value)> notify_callback_;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,238 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <sdbus-c++/Error.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/Types.h>
#include "internal/platform/byte_array.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_server.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
namespace bluez {
void GattCharacteristicServer::Update(const nearby::ByteArray &value) {
std::vector<uint8_t> bytes(value.size());
const auto *buf = value.data();
for (auto i = 0; i < value.size(); i++) bytes[i] = buf[i];
absl::MutexLock static_value_lock(&static_value_mutex_);
static_value_ = std::move(bytes);
}
absl::Status GattCharacteristicServer::NotifyChanged(
bool confirm, const ByteArray &new_value) {
std::vector<uint8_t> bytes(new_value.size());
const auto *buf = new_value.data();
for (auto i = 0; i < new_value.size(); i++) bytes[i] = buf[i];
{
absl::MutexLock lock(&cached_value_mutex_);
cached_value_ = bytes;
}
if (confirm) {
auto confirmed = [&]() {
confirmed_mutex_.AssertReaderHeld();
return confirmed_;
};
{
absl::MutexLock lock(&confirmed_mutex_);
confirmed_ = false;
}
absl::ReaderMutexLock lock(&confirmed_mutex_, absl::Condition(&confirmed));
}
try {
emitPropertiesChangedSignal(GattCharacteristic1_adaptor::INTERFACE_NAME,
{"Value"});
return absl::OkStatus();
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR) << __func__
<< ": Error emitting PropertiesChanged signal on "
<< getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
return absl::UnknownError(e.getMessage());
}
}
void GattCharacteristicServer::ReadValue(
sdbus::Result<std::vector<uint8_t>> &&result,
std::map<std::string, sdbus::Variant> options) {
{
absl::ReaderMutexLock static_value_lock(&static_value_mutex_);
if (static_value_.has_value()) {
result.returnResults(*static_value_);
absl::MutexLock cached_value_lock(&cached_value_mutex_);
cached_value_ = *static_value_;
return;
}
}
uint16_t offset = options["offset"];
sdbus::ObjectPath device_path = options["device"];
auto device = devices_->get_device_by_path(device_path);
if (device == nullptr) {
result.returnError(
sdbus::Error("org.bluez.Error.NotAuthorized", "device does not exist"));
return;
}
auto characteristic = characteristic_;
server_cb_->on_characteristic_read_cb(
*device, characteristic, static_cast<int>(offset),
[result = std::move(result),
this](absl::StatusOr<absl::string_view> data) {
const auto &status = data.status();
if (status.ok()) {
auto str = data.value();
std::vector<uint8_t> bytes(str.size());
for (auto i = 0; i < str.size(); i++) {
bytes[i] = str[i];
}
result.returnResults(bytes);
absl::MutexLock lock(&cached_value_mutex_);
cached_value_ = bytes;
} else if (absl::IsPermissionDenied(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotPermitted",
std::string(status.message())));
} else if (absl::IsUnauthenticated(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotAuthorized",
std::string(status.message())));
} else if (absl::IsOutOfRange(status)) {
result.returnError(sdbus::Error("org.bluez.Error.InvalidOffset",
std::string(status.message())));
} else if (absl::IsUnimplemented(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotSupported",
std::string(status.message())));
} else {
result.returnError(sdbus::Error("org.bluez.Error.Failed",
std::string(status.message())));
}
});
}
void GattCharacteristicServer::WriteValue(
sdbus::Result<> &&result, std::vector<uint8_t> value,
std::map<std::string, sdbus::Variant> options) {
uint16_t offset = options["offset"];
sdbus::ObjectPath device_path = options["device"];
auto device = devices_->get_device_by_path(device_path);
if (device == nullptr) {
result.returnError(
sdbus::Error("org.bluez.Error.NotAuthorized", "device does not exist"));
return;
}
std::string type = options["type"];
std::string data(value.begin(), value.end());
auto characteristic = characteristic_;
// TODO: Support writes without response.
server_cb_->on_characteristic_write_cb(
*device, characteristic, static_cast<int>(offset), data,
[result = std::move(result)](absl::Status status) {
if (status.ok()) {
result.returnResults();
} else if (absl::IsPermissionDenied(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotPermitted",
std::string(status.message())));
} else if (absl::IsUnauthenticated(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotAuthorized",
std::string(status.message())));
} else if (absl::IsOutOfRange(status)) {
result.returnError(sdbus::Error("org.bluez.Error.InvalidOffset",
std::string(status.message())));
} else if (absl::IsUnimplemented(status)) {
result.returnError(sdbus::Error("org.bluez.Error.NotSupported",
std::string(status.message())));
} else {
result.returnError(sdbus::Error("org.bluez.Error.Failed",
std::string(status.message())));
}
});
}
void GattCharacteristicServer::StartNotify() {
if ((characteristic_.property |
api::ble_v2::GattCharacteristic::Property::kNotify) ==
api::ble_v2::GattCharacteristic::Property::kNotify) {
if (notify_sessions_.fetch_add(1) == 0) {
if (server_cb_->characteristic_subscription_cb != nullptr) {
server_cb_->characteristic_subscription_cb(characteristic_);
}
notifying_ = true;
}
} else {
throw(sdbus::Error("org.bluez.Error.NotSupported"));
}
}
void GattCharacteristicServer::StopNotify() {
if ((characteristic_.property |
api::ble_v2::GattCharacteristic::Property::kNotify) ==
api::ble_v2::GattCharacteristic::Property::kNotify) {
if (notify_sessions_.fetch_sub(0) == 1) {
if (server_cb_->characteristic_unsubscription_cb != nullptr) {
server_cb_->characteristic_unsubscription_cb(characteristic_);
}
notifying_ = false;
}
} else {
throw(sdbus::Error("org.bluez.Error.Failed"));
}
}
std::vector<std::string> GattCharacteristicServer::Flags() {
auto characteristic = characteristic_;
std::vector<std::string> flags;
if ((characteristic.permission &
api::ble_v2::GattCharacteristic::Permission::kRead) ==
api::ble_v2::GattCharacteristic::Permission::kRead ||
(characteristic.property &
api::ble_v2::GattCharacteristic::Property::kRead) ==
api::ble_v2::GattCharacteristic::Property::kRead)
flags.push_back("read");
if ((characteristic.permission &
api::ble_v2::GattCharacteristic::Permission::kWrite) ==
api::ble_v2::GattCharacteristic::Permission::kWrite ||
(characteristic.property &
api::ble_v2::GattCharacteristic::Property::kWrite) ==
api::ble_v2::GattCharacteristic::Property::kWrite) {
flags.push_back("write");
flags.push_back("write-without-response");
}
if ((characteristic.property &
api::ble_v2::GattCharacteristic::Property::kIndicate) ==
api::ble_v2::GattCharacteristic::Property::kIndicate)
flags.push_back("indicate");
if ((characteristic.property &
api::ble_v2::GattCharacteristic::Property::kNotify) ==
api::ble_v2::GattCharacteristic::Property::kNotify)
flags.push_back("notify");
return flags;
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,129 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_GATT_CHARACTERISTIC_SERVER_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_GATT_CHARACTERISTIC_SERVER_H_
#include <atomic>
#include <map>
#include <optional>
#include <string>
#include <vector>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/MethodResult.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluetooth_devices.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_server.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
namespace bluez {
class GattCharacteristicServer final
: public sdbus::AdaptorInterfaces<org::bluez::GattCharacteristic1_adaptor,
sdbus::Properties_adaptor,
sdbus::ManagedObject_adaptor> {
public:
GattCharacteristicServer(const GattCharacteristicServer &) = delete;
GattCharacteristicServer(GattCharacteristicServer &&) = delete;
GattCharacteristicServer &operator=(const GattCharacteristicServer &) =
delete;
GattCharacteristicServer &operator=(GattCharacteristicServer &&) = delete;
GattCharacteristicServer(
sdbus::IConnection &system_bus,
const sdbus::ObjectPath &service_object_path, size_t num,
const api::ble_v2::GattCharacteristic &characteristic,
std::shared_ptr<api::ble_v2::ServerGattConnectionCallback> server_cb,
std::shared_ptr<BluetoothDevices> devices)
: AdaptorInterfaces(system_bus, bluez::gatt_characteristic_path(
service_object_path, num)),
devices_(std::move(devices)),
server_cb_(std::move(server_cb)),
characteristic_(characteristic),
service_object_path_(service_object_path),
notifying_(false),
confirmed_(false),
notify_sessions_(0) {
registerAdaptor();
NEARBY_LOGS(VERBOSE)
<< __func__ << "Creating a "
<< org::bluez::GattCharacteristic1_adaptor::INTERFACE_NAME
<< " object at " << getObjectPath();
}
~GattCharacteristicServer() { unregisterAdaptor(); }
void Update(const nearby::ByteArray &value)
ABSL_LOCKS_EXCLUDED(static_value_mutex_);
absl::Status NotifyChanged(bool confirm, const ByteArray &new_value)
ABSL_LOCKS_EXCLUDED(confirmed_mutex_);
private:
// Methods
void ReadValue(sdbus::Result<std::vector<uint8_t>> &&result,
std::map<std::string, sdbus::Variant> options) override
ABSL_LOCKS_EXCLUDED(cached_value_mutex_, static_value_mutex_);
void WriteValue(sdbus::Result<> &&result, std::vector<uint8_t> value,
std::map<std::string, sdbus::Variant> options) override;
void StartNotify() override;
void StopNotify() override;
void Confirm() override ABSL_LOCKS_EXCLUDED(confirmed_mutex_) {
absl::MutexLock lock(&confirmed_mutex_);
confirmed_ = true;
};
// Properties
std::string UUID() override { return std::string{characteristic_.uuid}; }
sdbus::ObjectPath Service() override { return service_object_path_; }
bool Notifying() override { return notifying_; }
std::vector<std::string> Flags() override;
std::vector<uint8_t> Value() override
ABSL_LOCKS_EXCLUDED(cached_value_mutex_) {
absl::ReaderMutexLock lock(&cached_value_mutex_);
return cached_value_;
}
std::shared_ptr<BluetoothDevices> devices_;
std::shared_ptr<api::ble_v2::ServerGattConnectionCallback> server_cb_;
api::ble_v2::GattCharacteristic characteristic_;
// Set by `GattServer::UpdateCharacteristic()`
absl::Mutex static_value_mutex_;
std::optional<std::vector<uint8_t>> static_value_
ABSL_GUARDED_BY(static_value_mutex_);
sdbus::ObjectPath service_object_path_;
std::atomic_bool notifying_;
absl::Mutex cached_value_mutex_;
std::vector<uint8_t> cached_value_ ABSL_GUARDED_BY(cached_value_mutex_);
absl::Mutex confirmed_mutex_;
bool confirmed_;
std::atomic_size_t notify_sessions_;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,44 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
#define PLATFORM_IMPL_LINUX_API_BLUEZ_GATT_MANAGER_H_
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/Types.h>
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_manager_client.h"
namespace nearby {
namespace linux {
namespace bluez {
class GattManager
: public sdbus::ProxyInterfaces<org::bluez::GattManager1_proxy> {
public:
GattManager(const GattManager &) = delete;
GattManager(GattManager &&) = delete;
GattManager &operator=(const GattManager &) = delete;
GattManager &operator=(GattManager &&) = delete;
GattManager(sdbus::IConnection &system_bus,
sdbus::ObjectPath adapter_object_path)
: ProxyInterfaces(system_bus, "org.bluez",
std::move(adapter_object_path)) {
registerProxy();
}
~GattManager() { unregisterProxy(); }
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,40 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_GATT_SERVICE_CLIENT_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_GATT_SERVICE_CLIENT_H_
#include <memory>
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_service_client.h"
namespace nearby {
namespace linux {
class GattServiceClient final
: public sdbus::ProxyInterfaces<org::bluez::GattService1_proxy> {
public:
GattServiceClient(std::shared_ptr<sdbus::IConnection> system_bus,
sdbus::ObjectPath service_object_path)
: ProxyInterfaces(*system_bus, "org.bluez",
std::move(service_object_path)) {
registerProxy();
}
~GattServiceClient() { unregisterProxy(); }
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,63 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/bluez_gatt_service_server.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_server.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_server.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
namespace bluez {
bool GattServiceServer::AddCharacteristic(
const Uuid &service_uuid, const Uuid &characteristic_uuid,
api::ble_v2::GattCharacteristic::Permission permission,
api::ble_v2::GattCharacteristic::Property property) {
absl::MutexLock lock(&characterstics_mutex_);
api::ble_v2::GattCharacteristic characteristic{
characteristic_uuid, service_uuid, permission, property};
auto count = characteristics_.size();
std::shared_ptr<GattCharacteristicServer> chr =
std::make_shared<GattCharacteristicServer>(
getObject().getConnection(), getObjectPath(), count, characteristic,
server_cb_, devices_);
try {
chr->emitInterfacesAddedSignal(
{org::bluez::GattCharacteristic1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesAdded signal for object path "
<< chr->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
return false;
}
characteristics_.insert({characteristic_uuid, std::move(chr)});
return true;
}
std::shared_ptr<GattCharacteristicServer> GattServiceServer::GetCharacteristic(
const Uuid &uuid) {
absl::ReaderMutexLock lock(&characterstics_mutex_);
if (characteristics_.count(uuid) == 0) {
return nullptr;
}
return characteristics_[uuid];
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,110 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_BLUEZ_GATT_SERVICE_H_
#define PLATFORM_IMPL_LINUX_BLUEZ_GATT_SERVICE_H_
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IObject.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/bluez_gatt_characteristic_server.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_characteristic_server.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/gatt_service_server.h"
#include "internal/platform/logging.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
namespace bluez {
class GattServiceServer final
: public sdbus::AdaptorInterfaces<org::bluez::GattService1_adaptor,
sdbus::ManagedObject_adaptor,
sdbus::Properties_adaptor> {
public:
GattServiceServer(const GattServiceServer &) = delete;
GattServiceServer(GattServiceServer &&) = delete;
GattServiceServer &operator=(const GattServiceServer &) = delete;
GattServiceServer &operator=(GattServiceServer &&) = delete;
GattServiceServer(
sdbus::IConnection &system_bus, size_t num, const Uuid &service_uuid,
std::shared_ptr<api::ble_v2::ServerGattConnectionCallback> server_cb,
std::shared_ptr<BluetoothDevices> devices)
: AdaptorInterfaces(system_bus, bluez::gatt_service_path(num)),
devices_(std::move(devices)),
server_cb_(std::move(server_cb)),
uuid_(service_uuid),
primary_(true) {
registerAdaptor();
NEARBY_LOGS(VERBOSE) << __func__ << ": Created a "
<< org::bluez::GattService1_adaptor::INTERFACE_NAME
<< " object at " << getObjectPath();
}
~GattServiceServer() {
absl::MutexLock lock(&characterstics_mutex_);
for (auto &[_uuid, characteristic] : characteristics_) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Removing characteristic "
<< characteristic->getObjectPath();
try {
characteristic->emitInterfacesRemovedSignal(
{org::bluez::GattCharacteristic1_adaptor::INTERFACE_NAME});
} catch (const sdbus::Error &e) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": error emitting InterfacesRemoved signal for object path "
<< characteristic->getObjectPath() << " with name '" << e.getName()
<< "' and message '" << e.getMessage() << "'";
}
}
unregisterAdaptor();
}
bool AddCharacteristic(const Uuid &service_uuid,
const Uuid &characteristic_uuid,
api::ble_v2::GattCharacteristic::Permission permission,
api::ble_v2::GattCharacteristic::Property property)
ABSL_LOCKS_EXCLUDED(characterstics_mutex_);
std::shared_ptr<GattCharacteristicServer> GetCharacteristic(const Uuid &uuid)
ABSL_LOCKS_EXCLUDED(characterstics_mutex_);
private:
// Properties
std::string UUID() override { return uuid_; }
bool Primary() override { return primary_; }
sdbus::ObjectPath Device() override { return "/"; }
std::vector<sdbus::ObjectPath> Includes() override { return {}; }
absl::Mutex characterstics_mutex_;
absl::flat_hash_map<Uuid, std::shared_ptr<GattCharacteristicServer>>
characteristics_ ABSL_GUARDED_BY(characterstics_mutex_);
std::shared_ptr<BluetoothDevices> devices_;
std::shared_ptr<api::ble_v2::ServerGattConnectionCallback> server_cb_;
const std::string uuid_;
const bool primary_;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,54 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/bluez_le_advertisement.h"
#include "internal/platform/logging.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace linux {
namespace bluez {
LEAdvertisement::LEAdvertisement(
sdbus::IConnection& system_bus, sdbus::ObjectPath path,
const api::ble_v2::BleAdvertisementData& advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters)
: AdaptorInterfaces(system_bus, std::move(path)),
is_extended_advertisement_(advertising_data.is_extended_advertisement),
advertise_set_parameters_(advertise_set_parameters) {
for (const auto& [uuid, data] : advertising_data.service_data) {
std::string uuid_string(uuid);
std::vector<uint8_t> data_bytes(data.size());
const auto* bytes = data.data();
service_uuids_.push_back(uuid_string);
for (size_t i = 0; i < data.size(); i++) {
data_bytes[i] = bytes[i];
}
service_data_.insert({uuid_string, std::move(data_bytes)});
}
registerAdaptor();
NEARBY_LOGS(VERBOSE) << __func__
<< ": Created a org.bluez.LEAdvertisement1 instance at "
<< getObjectPath();
}
} // namespace bluez
} // namespace linux
} // namespace nearby
@@ -0,0 +1,112 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_API_BLUEZ_BLE_ADVERTISEMENT_H_
#define PLATFORM_IMPL_LINUX_API_BLUEZ_BLE_ADVERTISEMENT_H_
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/StandardInterfaces.h>
#include <sdbus-c++/Types.h>
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/linux/bluetooth_adapter.h"
#include "internal/platform/implementation/linux/bluez.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/le_advertisement_manager_client.h"
#include "internal/platform/implementation/linux/generated/dbus/bluez/le_advertisement_server.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
namespace bluez {
class LEAdvertisement final
: public sdbus::AdaptorInterfaces<org::bluez::LEAdvertisement1_adaptor,
sdbus::ObjectManager_adaptor> {
public:
LEAdvertisement(const LEAdvertisement&) = delete;
LEAdvertisement(LEAdvertisement&&) = delete;
LEAdvertisement& operator=(const LEAdvertisement&) = delete;
LEAdvertisement& operator=(LEAdvertisement&&) = delete;
LEAdvertisement(sdbus::IConnection& system_bus, sdbus::ObjectPath path,
const api::ble_v2::BleAdvertisementData& advertising_data,
api::ble_v2::AdvertiseParameters advertise_set_parameters);
static std::unique_ptr<LEAdvertisement> CreateLEAdvertisement(
sdbus::IConnection& system_bus,
const api::ble_v2::BleAdvertisementData& advertising_data,
api::ble_v2::AdvertiseParameters advertising_parameters) {
static std::atomic<size_t> adv_count = 0;
auto object_path = bluez::ble_advertisement_path(adv_count++);
return std::make_unique<LEAdvertisement>(
system_bus, object_path, advertising_data, advertising_parameters);
}
~LEAdvertisement() { unregisterAdaptor(); }
private:
// Methods
void Release() override {
NEARBY_LOGS(INFO) << __func__
<< ": LE Advertisement released: " << getObjectPath();
}
// Properties
std::string Type() override { return "peripheral"; }
std::vector<std::string> ServiceUUIDs() override { return service_uuids_; }
std::map<std::string, sdbus::Variant> ManufacturerData() override {
return {};
}
std::vector<std::string> SolicitUUIDs() override { return {}; }
std::map<std::string, sdbus::Variant> ServiceData() override {
return service_data_;
}
std::vector<std::string> Includes() override { return {}; }
std::string LocalName() override { return {}; }
uint16_t Duration() override { return 0; }
uint16_t Timeout() override { return 0; }
// Windows seems to hardcode the scan interval to 118.125 milliseconds, so
// lets just replicate that.
uint32_t MinInterval() override { return 118; }
uint32_t MaxInterval() override { return 119; }
int16_t TxPower() override {
return bluez::TxPowerLevelDbm(advertise_set_parameters_.tx_power_level);
};
bool is_extended_advertisement_;
std::vector<std::string> service_uuids_;
std::map<std::string, sdbus::Variant> service_data_;
api::ble_v2::AdvertiseParameters advertise_set_parameters_;
};
class LEAdvertisementManager final
: public sdbus::ProxyInterfaces<org::bluez::LEAdvertisingManager1_proxy> {
public:
LEAdvertisementManager(sdbus::IConnection& system_bus,
BluetoothAdapter& adapter)
: ProxyInterfaces(system_bus, "org.bluez", adapter.GetObjectPath()) {
registerProxy();
}
~LEAdvertisementManager() { unregisterProxy(); }
LEAdvertisementManager(const LEAdvertisementManager&) = delete;
LEAdvertisementManager(LEAdvertisementManager&&) = delete;
LEAdvertisementManager& operator=(const LEAdvertisementManager&) = delete;
LEAdvertisementManager& operator=(LEAdvertisementManager&&) = delete;
};
} // namespace bluez
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,50 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_
#define PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/condition_variable.h"
#include "internal/platform/implementation/linux/mutex.h"
#include "internal/platform/implementation/mutex.h"
namespace nearby {
namespace linux {
class ConditionVariable : public api::ConditionVariable {
public:
explicit ConditionVariable(api::Mutex *mutex)
: mutex_(&(static_cast<linux::Mutex*>(mutex)->GetMutex())) {}
~ConditionVariable() = default;
Exception Wait() override {
cond_var_.Wait(mutex_);
return {Exception::kSuccess};
}
Exception Wait(absl::Duration timeout) override {
cond_var_.WaitWithTimeout(mutex_, timeout);
return {Exception::kSuccess};
}
void Notify() override { cond_var_.SignalAll(); }
private:
absl::Mutex *mutex_;
absl::CondVar cond_var_;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_CONDITION_VARIABLE_H_
@@ -0,0 +1,100 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/condition_variable.h"
#include <future> // NOLINT
#include "absl/time/clock.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/linux/mutex.h"
#include "gtest/gtest.h"
class ConditionVariableTests : public testing::Test {
public:
class ConditionVariableTest {
public:
ConditionVariableTest() {}
std::future<bool> WaitForEvent(bool timedWait, // NOLINT
const absl::Duration* timeout) {
return std::async(
std::launch::async, [this, timedWait, timeout]() mutable -> bool {
if (timedWait == true) {
auto result = this->condition_variable_actual_.Wait(*timeout);
if (result.value == nearby::Exception::kSuccess) {
return true;
} else {
return false;
}
} else {
this->condition_variable_actual_.Wait();
}
return true;
});
}
void PostEvent() {
absl::MutexLock(&mutex_actual_.GetMutex());
condition_variable_actual_.Notify();
}
private:
nearby::linux::Mutex mutex_actual_ =
nearby::linux::Mutex(nearby::linux::Mutex::Mode::kRegular);
nearby::linux::Mutex& mutex_ = mutex_actual_;
nearby::linux::ConditionVariable condition_variable_actual_ =
nearby::linux::ConditionVariable(&mutex_);
nearby::linux::ConditionVariable& condition_variable_ =
condition_variable_actual_;
};
};
TEST_F(ConditionVariableTests, SuccessfulCreation) {
// Arrange
ConditionVariableTest conditionVariableTest;
auto result = conditionVariableTest.WaitForEvent(false, nullptr);
sleep(1);
// Act
conditionVariableTest.PostEvent();
// Assert
ASSERT_TRUE(result.get());
}
TEST_F(ConditionVariableTests, TimedCreation) {
// Arrange
ConditionVariableTest conditionVariableTest;
const absl::Duration duration = absl::Milliseconds(100);
// Act
auto result = conditionVariableTest.WaitForEvent(true, &duration);
// Assert
ASSERT_FALSE(result.get()); // Timed out
// Act
result = conditionVariableTest.WaitForEvent(true, &duration);
sleep(1);
conditionVariableTest.PostEvent();
// Assert
ASSERT_TRUE(result.get()); // Didn't timeout
}
@@ -0,0 +1,164 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/shared/count_down_latch.h"
#include "gtest/gtest.h"
#include "internal/platform/implementation/platform.h"
#include <atomic>
#include <thread>
#include <vector>
class CountDownLatchTests : public testing::Test {
public:
class TestData {
public:
std::unique_ptr<nearby::api::CountDownLatch>& countDownLatch;
long volatile& count;
};
class CountDownLatchTest {
public:
static unsigned int ThreadProcCountDown(void* lpParam) {
TestData* testData = static_cast<TestData*>(lpParam);
sleep(1);
__sync_fetch_and_add(&testData->count, 1);
testData->countDownLatch->CountDown();
return 0;
}
static unsigned int ThreadProcAwait(void* lpParam) {
TestData* testData = static_cast<TestData*>(lpParam);
sleep(1);
testData->countDownLatch->Await();
__sync_fetch_and_add(&testData->count, 1);
return 0;
}
};
CountDownLatchTests() {}
};
TEST_F(CountDownLatchTests, CountDownLatchAwaitSucceeds) {
// Arrange
long volatile count = 0;
std::unique_ptr<nearby::api::CountDownLatch> countDownLatch =
nearby::api::ImplementationPlatform::CreateCountDownLatch(3);
std::vector<std::thread> threads;
TestData testData{countDownLatch, count};
// Setup 3 threads
for (int i = 0; i < 3; i++) {
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
// https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread
// Could use C++ concurrency for this possibly
threads.emplace_back(CountDownLatchTest::ThreadProcCountDown, &testData);
}
// Act
nearby::Exception result = countDownLatch->Await();
for (auto& thread : threads) {
thread.join();
}
// Assert
EXPECT_EQ(result.value, nearby::Exception::kSuccess);
EXPECT_EQ(count, 3);
}
TEST_F(CountDownLatchTests, CountDownLatchAwaitTimeoutTimesOut) {
// Arrange
std::unique_ptr<nearby::api::CountDownLatch> countDownLatch =
nearby::api::ImplementationPlatform::CreateCountDownLatch(3);
// Act
nearby::ExceptionOr<bool> result =
countDownLatch->Await(absl::Milliseconds(5));
sleep(40);
// Assert
EXPECT_FALSE(result.GetResult());
// TODO(jfcarroll)I think there's a bug in the shared version of this, it's
// not returning a timeout exception, need to look at it some more.
// EXPECT_EQ(result.GetException().value,
// nearby::Exception::kTimeout);
}
TEST_F(CountDownLatchTests, CountDownLatchAwaitNoTimeoutSucceeds) {
// Arrange
long volatile count = 0;
std::unique_ptr<nearby::api::CountDownLatch> countDownLatch =
nearby::api::ImplementationPlatform::CreateCountDownLatch(3);
TestData testData{countDownLatch, count};
std::vector<std::thread> threads;
// Setup 3 threads
for (int i = 0; i < 3; i++) {
// TODO: More complex scenarios may require use of a parameter
// to the thread procedure, such as an event per thread to
// be used for synchronization.
threads.emplace_back(CountDownLatchTest::ThreadProcAwait, &testData);
}
for (auto& thread : threads) {
thread.join();
}
// Act
nearby::ExceptionOr<bool> result =
countDownLatch->Await(absl::Milliseconds(100));
// Assert
EXPECT_TRUE(result.GetResult());
EXPECT_EQ(result.GetException().value, nearby::Exception::kSuccess);
EXPECT_EQ(count, 3);
}
void test(std::string str) {
std::cout << str << std::endl;
return;
}
TEST_F(CountDownLatchTests, CountDownLatchCountDownBeforeAwaitSucceeds) {
// Arrange
long volatile count = 0;
std::unique_ptr<nearby::api::CountDownLatch> countDownLatch =
nearby::api::ImplementationPlatform::CreateCountDownLatch(1);
TestData testData{countDownLatch, count};
std::thread thread(CountDownLatchTest::ThreadProcCountDown, &testData);
// Act
countDownLatch->CountDown(); // This countdown occurs before the thread has a
// chance to run
// Assert
EXPECT_EQ(count, 1);
}
@@ -0,0 +1,64 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_CREDENTIAL_STORAGE_H_
#define PLATFORM_IMPL_LINUX_CREDENTIAL_STORAGE_H_
#include <memory>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/sdbus-c++.h>
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/credential_storage.h"
namespace nearby {
namespace linux {
class CredentialStorage : public api::CredentialStorage {
using LocalCredential = ::nearby::internal::LocalCredential;
using SharedCredential = ::nearby::internal::SharedCredential;
using PublicCredentialType = ::nearby::presence::PublicCredentialType;
using SaveCredentialsResultCallback =
::nearby::presence::SaveCredentialsResultCallback;
using CredentialSelector = ::nearby::presence::CredentialSelector;
using GetLocalCredentialsResultCallback =
::nearby::presence::GetLocalCredentialsResultCallback;
using GetPublicCredentialsResultCallback =
::nearby::presence::GetPublicCredentialsResultCallback;
CredentialStorage(sdbus::IConnection &connection);
~CredentialStorage() override = default;
void SaveCredentials(absl::string_view manager_app_id,
absl::string_view account_name,
const std::vector<LocalCredential> &Local_credentials,
const std::vector<SharedCredential> &Shared_credentials,
PublicCredentialType public_credential_type,
SaveCredentialsResultCallback callback) override;
void UpdateLocalCredential(absl::string_view manager_app_id,
absl::string_view account_name,
nearby::internal::LocalCredential credential,
SaveCredentialsResultCallback callback) override;
void GetPublicCredentials(
const CredentialSelector &credential_selector,
PublicCredentialType public_credential_type,
GetPublicCredentialsResultCallback callback) override;
private:
std::unique_ptr<sdbus::IProxy> proxy;
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,54 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/crypto.h"
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include <openssl/evp.h>
// Function implementations for platform/implementation/crypto.h.
namespace nearby {
// Initialize global crypto state.
void Crypto::Init() {}
static ByteArray Hash(absl::string_view input, const EVP_MD* algo) {
unsigned int md_out_size = EVP_MAX_MD_SIZE;
uint8_t digest_buffer[EVP_MAX_MD_SIZE];
if (input.empty()) return {};
if (!EVP_Digest(input.data(), input.size(), digest_buffer, &md_out_size, algo,
nullptr))
return {};
return ByteArray{reinterpret_cast<char*>(digest_buffer), md_out_size};
}
// Return MD5 hash of input.
ByteArray Crypto::Md5(absl::string_view input) {
return Hash(input, EVP_md5());
}
// Return SHA256 hash of input.
ByteArray Crypto::Sha256(absl::string_view input) {
return Hash(input, EVP_sha256());
}
} // namespace nearby
@@ -0,0 +1,50 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/crypto.h"
#include <string>
#include "gtest/gtest.h"
namespace nearby {
namespace {
TEST(CryptoTest, Md5Hash) {
const std::string input{"Hello Nearby Connection"};
const ByteArray expected_md5(
"\x94\xa3\xbe\xc1\x8d\x30\xe3\x24\x5f\xa1\x4c\xee\xe7\x52\xe9\x36");
ByteArray md5_hash = Crypto::Md5(input);
EXPECT_EQ(md5_hash, expected_md5);
}
TEST(CryptoTest, Md5HashOnEmptyInput) {
EXPECT_EQ(Crypto::Md5(""), ByteArray{});
}
TEST(CryptoTest, Sha256Hash) {
const std::string input("Hello Nearby Connection");
const ByteArray expected_sha256(
"\xb4\x24\xd3\xc0\x58\x12\x9a\x42\xcb\x81\xa0\x4b\x6e\x9d\xfe\x45\x45\x9f"
"\x15\xf7\xc0\xa9\x32\x2f\xfb\x9\x45\xf0\xf9\xbe\x75\xb");
ByteArray sha256_hash = Crypto::Sha256(input);
EXPECT_EQ(sha256_hash, expected_sha256);
}
TEST(CryptoTest, Sha256HashOnEmptyInput) {
EXPECT_EQ(Crypto::Sha256(""), ByteArray{});
}
} // namespace
} // namespace nearby
@@ -0,0 +1,47 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cassert>
#include <cstdlib>
#include <memory>
#include <sdbus-c++/IConnection.h>
#include "absl/base/call_once.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/linux/dbus.h"
namespace nearby {
namespace linux {
namespace {
static absl::Mutex global_system_bus_mutex;
static std::weak_ptr<sdbus::IConnection> global_system_bus_connection
ABSL_GUARDED_BY(global_system_bus_mutex);
} // namespace
std::shared_ptr<sdbus::IConnection> getSystemBusConnection() {
absl::MutexLock lock(&global_system_bus_mutex);
auto bus = global_system_bus_connection.lock();
if (bus == nullptr) {
bus =
std::shared_ptr<sdbus::IConnection>(sdbus::createSystemBusConnection());
bus->enterEventLoopAsync();
global_system_bus_connection = bus;
}
return bus;
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,63 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_DBUS_H_
#define PLATFORM_IMPL_LINUX_DBUS_H_
#include <sdbus-c++/AdaptorInterfaces.h>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/StandardInterfaces.h>
#include "internal/platform/logging.h"
#define DBUS_LOG_METHOD_CALL_ERROR(p, m, e) \
do { \
NEARBY_LOGS(ERROR) << __func__ << ": Got error '" << (e).getName() \
<< "' with message '" << (e).getMessage() \
<< "' while calling " << (m) << " on object " \
<< (p)->getObjectPath(); \
} while (false)
#define DBUS_LOG_PROPERTY_GET_ERROR(p, prop, e) \
do { \
NEARBY_LOGS(ERROR) << __func__ << ": Got error '" << (e).getName() \
<< "' with message '" << (e).getMessage() \
<< "' while getting property " << (prop) \
<< " on object " << (p)->getObjectPath(); \
} while (false)
#define DBUS_LOG_PROPERTY_SET_ERROR(p, prop, e) \
do { \
NEARBY_LOGS(ERROR) << __func__ << ": Got error '" << (e).getName() \
<< "' with message '" << (e).getMessage() \
<< "' while setting property " << (prop) \
<< " on object " << (p)->getObjectPath(); \
} while (false)
namespace nearby {
namespace linux {
extern std::shared_ptr<sdbus::IConnection> getSystemBusConnection();
class RootObjectManager final
: public sdbus::AdaptorInterfaces<sdbus::ObjectManager_adaptor,
sdbus::Properties_adaptor> {
public:
explicit RootObjectManager(sdbus::IConnection &system_bus)
: AdaptorInterfaces(system_bus, "/") {
registerAdaptor();
}
~RootObjectManager() { unregisterAdaptor(); }
};
} // namespace linux
} // namespace nearby
#endif
@@ -0,0 +1,183 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <pwd.h>
#include <sys/types.h>
#include <cstdlib>
#include <cstring>
#include <optional>
#include <string>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/device_info.h"
#include "internal/platform/implementation/linux/avahi.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/device_info.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
void CurrentUserSession::RegisterScreenLockedListener(
absl::string_view listener_name,
std::function<void(api::DeviceInfo::ScreenStatus)> callback) {
absl::MutexLock l(&screen_lock_listeners_mutex_);
screen_lock_listeners_[listener_name] = std::move(callback);
}
void CurrentUserSession::UnregisterScreenLockedListener(
absl::string_view listener_name) {
absl::MutexLock l(&screen_lock_listeners_mutex_);
screen_lock_listeners_.erase(listener_name);
}
void CurrentUserSession::onLock() {
absl::ReaderMutexLock l(&screen_lock_listeners_mutex_);
for (auto &[_, callback] : screen_lock_listeners_) {
callback(api::DeviceInfo::ScreenStatus::kLocked);
}
}
void CurrentUserSession::onUnlock() {
absl::ReaderMutexLock l(&screen_lock_listeners_mutex_);
for (auto &[_, callback] : screen_lock_listeners_) {
callback(api::DeviceInfo::ScreenStatus::kUnlocked);
}
}
DeviceInfo::DeviceInfo(std::shared_ptr<sdbus::IConnection> system_bus)
: system_bus_(std::move(system_bus)),
current_user_session_(std::make_unique<CurrentUserSession>(*system_bus_)),
login_manager_(std::make_unique<LoginManager>(*system_bus_)) {}
std::optional<std::u16string> DeviceInfo::GetOsDeviceName() const {
avahi::Server avahi(*system_bus_);
try {
std::string hostname = avahi.GetHostNameFqdn();
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.from_bytes(hostname);
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(&avahi, "GetHostNameFqdn", e);
return std::nullopt;
}
}
api::DeviceInfo::DeviceType DeviceInfo::GetDeviceType() const {
Hostnamed hostnamed(*system_bus_);
try {
std::string chasis = hostnamed.Chassis();
api::DeviceInfo::DeviceType device = api::DeviceInfo::DeviceType::kUnknown;
if (chasis == "phone" || chasis == "handset") {
device = api::DeviceInfo::DeviceType::kPhone;
} else if (chasis == "laptop" || chasis == "desktop") {
device = api::DeviceInfo::DeviceType::kLaptop;
} else if (chasis == "tablet") {
device = api::DeviceInfo::DeviceType::kTablet;
}
return device;
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(&hostnamed, "Chasis", e);
return api::DeviceInfo::DeviceType::kUnknown;
}
}
std::optional<std::u16string> DeviceInfo::GetFullName() const {
struct passwd *pwd = getpwuid(getuid());
if (pwd == nullptr) {
return std::nullopt;
}
char *name = strtok(pwd->pw_gecos, ",");
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.from_bytes(name != nullptr ? name : pwd->pw_gecos);
}
std::optional<std::string> DeviceInfo::GetProfileUserName() const {
char *logname = secure_getenv("LOGNAME");
return logname == nullptr ? std::nullopt
: std::optional(std::string(logname));
}
std::optional<std::filesystem::path> DeviceInfo::GetDownloadPath() const {
char *dir = getenv("XDG_DOWNLOAD_DIR");
return std::filesystem::path(std::string(dir));
}
std::optional<std::filesystem::path> DeviceInfo::GetLocalAppDataPath() const {
char *dir = getenv("XDG_CONFIG_HOME");
if (dir == nullptr) {
return std::filesystem::path("/tmp");
}
return std::filesystem::path(std::string(dir)) / "Google Nearby";
}
std::optional<std::filesystem::path> DeviceInfo::GetTemporaryPath() const {
char *dir = getenv("XDG_RUNTIME_PATH");
if (dir == nullptr) {
return std::filesystem::path("/tmp");
}
return std::filesystem::path(std::string(dir)) / "Google Nearby";
}
std::optional<std::filesystem::path> DeviceInfo::GetLogPath() const {
char *dir = getenv("XDG_STATE_HOME");
if (dir == nullptr) {
return std::filesystem::path("/tmp");
}
return std::filesystem::path(std::string(dir)) / "Google Nearby" / "logs";
}
std::optional<std::filesystem::path> DeviceInfo::GetCrashDumpPath() const {
char *dir = getenv("XDG_STATE_HOME");
if (dir == nullptr) {
return std::filesystem::path("/tmp");
}
return std::filesystem::path(std::string(dir)) / "Google Nearby" / "crashes";
}
bool DeviceInfo::IsScreenLocked() const {
try {
return current_user_session_->LockedHint();
} catch (const sdbus::Error &e) {
DBUS_LOG_PROPERTY_GET_ERROR(current_user_session_, "LockedHint", e);
return false;
}
}
bool DeviceInfo::PreventSleep() {
try {
inhibit_fd_ = login_manager_->Inhibit("sleep", "Google Nearby",
"Google Nearby", "block");
return true;
} catch (const sdbus::Error &e) {
DBUS_LOG_METHOD_CALL_ERROR(login_manager_, "Inhibit", e);
return false;
}
}
bool DeviceInfo::AllowSleep() {
if (!inhibit_fd_.has_value()) {
NEARBY_LOGS(ERROR) << __func__
<< "No inhibit lock is acquired at the moment";
return false;
}
inhibit_fd_.reset();
return true;
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,171 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_DEVICE_INFO_H_
#define PLATFORM_IMPL_LINUX_DEVICE_INFO_H_
#include <optional>
#include <string>
#include <sdbus-c++/IConnection.h>
#include <sdbus-c++/IProxy.h>
#include <sdbus-c++/ProxyInterfaces.h>
#include <sdbus-c++/Types.h>
#include "absl/container/flat_hash_map.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/implementation/device_info.h"
#include "internal/platform/implementation/linux/generated/dbus/hostname/hostname_client.h"
#include "internal/platform/implementation/linux/generated/dbus/login/login_manager_client.h"
#include "internal/platform/implementation/linux/generated/dbus/login/login_session_client.h"
namespace nearby {
namespace linux {
class CurrentUserSession final
: public sdbus::ProxyInterfaces<org::freedesktop::login1::Session_proxy> {
public:
CurrentUserSession(const CurrentUserSession &) = delete;
CurrentUserSession(CurrentUserSession &&) = delete;
CurrentUserSession &operator=(const CurrentUserSession &) = delete;
CurrentUserSession &operator=(CurrentUserSession &&) = delete;
~CurrentUserSession() { unregisterProxy(); }
explicit CurrentUserSession(sdbus::IConnection &system_bus)
: ProxyInterfaces(system_bus, "org.freedesktop.login1",
"/org/freedesktop/login1/session/auto") {
registerProxy();
}
void RegisterScreenLockedListener(
absl::string_view listener_name,
std::function<void(api::DeviceInfo::ScreenStatus)> callback)
ABSL_LOCKS_EXCLUDED(screen_lock_listeners_mutex_);
void UnregisterScreenLockedListener(absl::string_view listener_name)
ABSL_LOCKS_EXCLUDED(screen_lock_listeners_mutex_);
protected:
void onPauseDevice(const uint32_t &major, const uint32_t &minor,
const std::string &type) override {}
void onResumeDevice(const uint32_t &major, const uint32_t &minor,
const sdbus::UnixFd &fd) override {}
void onLock() override ABSL_LOCKS_EXCLUDED(screen_lock_listeners_mutex_);
void onUnlock() override ABSL_LOCKS_EXCLUDED(screen_lock_listeners_mutex_);
private:
absl::Mutex screen_lock_listeners_mutex_;
absl::flat_hash_map<std::string,
std::function<void(api::DeviceInfo::ScreenStatus)>>
screen_lock_listeners_ ABSL_GUARDED_BY(screen_lock_listeners_mutex_);
};
class Hostnamed
: public sdbus::ProxyInterfaces<org::freedesktop::hostname1_proxy> {
public:
Hostnamed(const Hostnamed &) = delete;
Hostnamed(Hostnamed &&) = delete;
Hostnamed &operator=(const Hostnamed &) = delete;
Hostnamed &operator=(Hostnamed &&) = delete;
explicit Hostnamed(sdbus::IConnection &system_bus)
: ProxyInterfaces(system_bus, "org.freedesktop.hostname1",
"/org/freedesktop/hostname1") {
registerProxy();
}
~Hostnamed() { unregisterProxy(); }
};
class LoginManager final
: public sdbus::ProxyInterfaces<org::freedesktop::login1::Manager_proxy> {
public:
LoginManager(const LoginManager &) = delete;
LoginManager(LoginManager &&) = delete;
LoginManager &operator=(const LoginManager &) = delete;
LoginManager &operator=(LoginManager &&) = delete;
explicit LoginManager(sdbus::IConnection &system_bus)
: ProxyInterfaces(system_bus, "org.freedesktop.login1",
"/org/freedesktop/login1") {
registerProxy();
}
~LoginManager() { unregisterProxy(); }
protected:
void onSessionNew(const std::string &session_id,
const sdbus::ObjectPath &object_path) override {}
void onSessionRemoved(const std::string &session_id,
const sdbus::ObjectPath &object_path) override {}
void onUserNew(const uint32_t &uid,
const sdbus::ObjectPath &object_path) override {}
void onUserRemoved(const uint32_t &uid,
const sdbus::ObjectPath &object_path) override {}
void onSeatNew(const std::string &seat_id,
const sdbus::ObjectPath &object_path) override {}
void onSeatRemoved(const std::string &seat_id,
const sdbus::ObjectPath &object_path) override {}
void onPrepareForShutdown(const bool &start) override {}
void onPrepareForSleep(const bool &start) override {}
};
class DeviceInfo final : public api::DeviceInfo {
public:
explicit DeviceInfo(std::shared_ptr<sdbus::IConnection> system_bus);
std::optional<std::u16string> GetOsDeviceName() const override;
api::DeviceInfo::DeviceType GetDeviceType() const override;
api::DeviceInfo::OsType GetOsType() const override {
return api::DeviceInfo::OsType::kWindows; // Or ChromeOS?
}
std::optional<std::u16string> GetFullName() const override;
std::optional<std::u16string> GetGivenName() const override {
return GetFullName();
}
std::optional<std::u16string> GetLastName() const override {
return GetFullName();
}
std::optional<std::string> GetProfileUserName() const override;
std::optional<std::filesystem::path> GetDownloadPath() const override;
std::optional<std::filesystem::path> GetLocalAppDataPath() const override;
std::optional<std::filesystem::path> GetCommonAppDataPath() const override {
return std::nullopt;
};
std::optional<std::filesystem::path> GetTemporaryPath() const override;
std::optional<std::filesystem::path> GetLogPath() const override;
std::optional<std::filesystem::path> GetCrashDumpPath() const override;
bool IsScreenLocked() const override;
void RegisterScreenLockedListener(
absl::string_view listener_name,
std::function<void(api::DeviceInfo::ScreenStatus)> callback) override {
current_user_session_->RegisterScreenLockedListener(listener_name,
std::move(callback));
}
void UnregisterScreenLockedListener(
absl::string_view listener_name) override {
current_user_session_->UnregisterScreenLockedListener(listener_name);
}
bool PreventSleep() override;
bool AllowSleep() override;
private:
std::shared_ptr<sdbus::IConnection> system_bus_;
std::unique_ptr<CurrentUserSession> current_user_session_;
std::unique_ptr<LoginManager> login_manager_;
std::optional<sdbus::UnixFd> inhibit_fd_;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_DEVICE_INFO_H_
@@ -0,0 +1,129 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/device_info.h"
#include <functional>
#include <string>
#include "absl/synchronization/notification.h"
#include "gtest/gtest.h"
#include "internal/platform/implementation/device_info.h"
namespace nearby {
namespace linux {
namespace {
TEST(DeviceInfo, DISABLED_GetComputerName) {
EXPECT_TRUE(DeviceInfo().GetOsDeviceName().has_value());
}
TEST(DeviceInfo, DISABLED_GetDeviceType) {
EXPECT_EQ(DeviceInfo().GetDeviceType(), api::DeviceInfo::DeviceType::kLaptop);
}
TEST(DeviceInfo, GetOsType) {
EXPECT_EQ(DeviceInfo().GetOsType(), api::DeviceInfo::OsType::kLinux);
}
TEST(DeviceInfo, DISABLED_GetFullName) {
EXPECT_TRUE(DeviceInfo().GetFullName().has_value());
}
TEST(DeviceInfo, DISABLED_GetGivenName) {
EXPECT_TRUE(DeviceInfo().GetGivenName().has_value());
}
TEST(DeviceInfo, DISABLED_GetLastName) {
EXPECT_TRUE(DeviceInfo().GetLastName().has_value());
}
TEST(DeviceInfo, DISABLED_GetProfileUserName) {
EXPECT_TRUE(DeviceInfo().GetProfileUserName().has_value());
}
TEST(DeviceInfo, DISABLED_GetLocalAppDataPath) {
EXPECT_TRUE(DeviceInfo().GetLocalAppDataPath().has_value());
}
TEST(DeviceInfo, DISABLED_GetDownloadPath) {
EXPECT_TRUE(DeviceInfo().GetDownloadPath().has_value());
}
TEST(DeviceInfo, DISABLED_GetTemporaryPath) {
EXPECT_TRUE(DeviceInfo().GetTemporaryPath().has_value());
}
TEST(DeviceInfo, DISABLED_IsScreenLocked) {
EXPECT_FALSE(DeviceInfo().IsScreenLocked());
}
TEST(DeviceInfo, DISABLED_RegisterScreenLockedListener) {
std::function<void(api::DeviceInfo::ScreenStatus)> listener_1 =
[](api::DeviceInfo::ScreenStatus) {};
std::function<void(api::DeviceInfo::ScreenStatus)> listener_2 =
[](api::DeviceInfo::ScreenStatus) {};
DeviceInfo device_info;
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0);
device_info.RegisterScreenLockedListener("listener_1", listener_1);
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1);
device_info.RegisterScreenLockedListener("listener_2", listener_2);
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2);
}
TEST(DeviceInfo, DISABLED_UnregisterScreenLockedListener) {
std::function<void(api::DeviceInfo::ScreenStatus)> listener_1 =
[](api::DeviceInfo::ScreenStatus) {};
std::function<void(api::DeviceInfo::ScreenStatus)> listener_2 =
[](api::DeviceInfo::ScreenStatus) {};
DeviceInfo device_info;
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0);
device_info.RegisterScreenLockedListener("listener_1", listener_1);
device_info.RegisterScreenLockedListener("listener_2", listener_2);
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2);
device_info.UnregisterScreenLockedListener("listener_1");
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1);
device_info.UnregisterScreenLockedListener("listener_2");
EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0);
}
TEST(DeviceInfo, DISABLED_UpdateScreenLockedListener) {
absl::Notification notification;
api::DeviceInfo::ScreenStatus screen_locked_tracker =
api::DeviceInfo::ScreenStatus::kUndetermined;
std::function<void(api::DeviceInfo::ScreenStatus)> listener =
[&screen_locked_tracker,
&notification](api::DeviceInfo::ScreenStatus status) {
screen_locked_tracker = api::DeviceInfo::ScreenStatus::kLocked;
notification.Notify();
};
DeviceInfo device_info;
device_info.RegisterScreenLockedListener("listener", listener);
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(5)));
EXPECT_EQ(screen_locked_tracker, api::DeviceInfo::ScreenStatus::kLocked);
}
} // namespace
} // namespace linux
} // namespace nearby
@@ -0,0 +1,52 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/executor.h"
#include <cassert>
#include "internal/platform/implementation/linux/thread_pool.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
Executor::Executor(size_t max_concurrency)
: thread_pool_(std::make_unique<ThreadPool>(max_concurrency)) {
assert(max_concurrency >= 1);
assert(thread_pool_ != nullptr);
}
void Executor::Execute(Runnable &&runnable) {
if (shut_down_) {
NEARBY_LOGS(VERBOSE) << "Warning: " << __func__
<< ": Attempt to execute on a shut down pool.";
return;
}
if (runnable == nullptr) {
NEARBY_LOGS(ERROR) << __func__ << ": Runnable was null.";
return;
}
thread_pool_->Run(std::move(runnable));
}
void Executor::Shutdown() {
shut_down_ = true;
thread_pool_->ShutDown();
thread_pool_ = nullptr;
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,48 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_EXECUTOR_H_
#define PLATFORM_IMPL_LINUX_EXECUTOR_H_
#include <atomic>
#include <memory>
#include "internal/platform/implementation/executor.h"
#include "internal/platform/implementation/linux/thread_pool.h"
namespace nearby {
namespace linux {
// This abstract class is the superclass of all classes representing an
// Executor.
class Executor : public api::Executor {
public:
Executor(size_t max_concurrency = 1);
// Before returning from destructor, executor must wait for all pending
// jobs to finish.
~Executor() override = default;
void Execute(Runnable&& runnable) override;
void Shutdown() override;
private:
std::unique_ptr<linux::ThreadPool> thread_pool_ = nullptr;
std::atomic<bool> shut_down_ = false;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_EXECUTOR_H_
@@ -0,0 +1,323 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/executor.h"
#include <algorithm>
#include <thread>
#include <utility>
#include "absl/synchronization/blocking_counter.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "gtest/gtest.h"
#include "internal/platform/implementation/linux/test_data.h"
namespace nearby {
namespace linux {
namespace {
constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200);
TEST(ExecutorTests, SingleThreadedExecutorSucceeds) {
absl::Notification notification;
// Arrange
std::string expected(RUNNABLE_0_TEXT.c_str());
auto executor = std::make_unique<Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
threadIds->push_back(std::this_thread::get_id());
// Act
executor->Execute([&]() {
threadIds->push_back(std::this_thread::get_id());
output.append(RUNNABLE_0_TEXT.c_str());
notification.Notify();
});
ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, SingleThreadedExecutorAfterShutdownFails) {
// Arrange
std::string expected("");
std::unique_ptr<Executor> executor = std::make_unique<Executor>();
std::unique_ptr<std::string> output = std::make_unique<std::string>();
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
threadIds->push_back(std::this_thread::get_id());
executor->Shutdown();
// Act
executor->Execute([&output, &threadIds]() {
threadIds->push_back(std::this_thread::get_id());
output->append(RUNNABLE_0_TEXT.c_str());
});
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 1);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests, SingleThreadedExecutorExecuteNullSucceeds) {
absl::Notification notification;
// Arrange
std::string expected(RUNNABLE_0_TEXT.c_str());
auto executor = std::make_unique<Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
threadIds->push_back(std::this_thread::get_id());
// Act
executor->Execute(nullptr);
executor->Execute([&]() {
threadIds->push_back(std::this_thread::get_id());
output.append(RUNNABLE_0_TEXT.c_str());
notification.Notify();
});
executor->Execute(nullptr);
ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
// We should've run all runnables on the worker thread
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, SingleThreadedExecutorMultipleTasksSucceeds) {
absl::BlockingCounter block_count(5);
// Arrange
std::string expected(RUNNABLE_ALL_TEXT.c_str());
auto executor = std::make_unique<Executor>();
std::string output = std::string();
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
auto parent_thread = std::this_thread::get_id();
// Act
for (int index = 0; index < 5; index++) {
executor->Execute([&, index]() {
threadIds->push_back(std::this_thread::get_id());
char buffer[128];
snprintf(buffer, sizeof(buffer), "%s%d, ", RUNNABLE_TEXT.c_str(), index);
output.append(std::string(buffer));
block_count.DecrementCount();
});
}
block_count.Wait();
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 5);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), parent_thread);
// We should've run all runnables on the worker thread
auto workerThreadId = threadIds->at(0);
for (int index = 0; index < threadIds->size(); index++) {
ASSERT_EQ(threadIds->at(index), workerThreadId);
}
// We should of run them in the order submitted
ASSERT_EQ(output, expected);
}
TEST(ExecutorTests, MultiThreadedExecutorSingleTaskSucceeds) {
absl::Notification notification;
// Arrange
std::string expected(RUNNABLE_0_TEXT.c_str());
auto executor = std::make_unique<Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(std::this_thread::get_id());
// Act
executor->Execute([&, output]() {
threadIds->push_back(std::this_thread::get_id());
output->append(RUNNABLE_0_TEXT.c_str());
notification.Notify();
});
ASSERT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 2);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
// We should've run the task
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests, MultiThreadedExecutorMultipleTasksSucceeds) {
absl::BlockingCounter block_count(5);
// Arrange
auto executor = std::make_unique<Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(std::this_thread::get_id());
// Act
for (int index = 0; index < 5; index++) {
executor->Execute([&, index]() {
threadIds->push_back(std::this_thread::get_id());
char buffer[128];
snprintf(buffer, sizeof(buffer), "%s %d, ", RUNNABLE_TEXT.c_str(), index);
output->append(std::string(buffer));
block_count.DecrementCount();
});
}
block_count.Wait();
executor->Shutdown();
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 6);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
}
TEST(ExecutorTests, MultiThreadedExecutorSingleTaskAfterShutdownFails) {
// Arrange
std::string expected("");
auto executor = std::make_unique<Executor>(2);
// Container to note threads that ran
std::unique_ptr<std::vector<std::thread::id>> threadIds =
std::make_unique<std::vector<std::thread::id>>();
std::shared_ptr<std::string> output = std::make_shared<std::string>();
threadIds->push_back(std::this_thread::get_id());
executor->Shutdown();
// Act
executor->Execute([output, &threadIds]() {
threadIds->push_back(std::this_thread::get_id());
output->append(RUNNABLE_0_TEXT.c_str());
});
// Assert
// We should've run 1 time on the main thread, and 5 times on the
// workerThread
ASSERT_EQ(threadIds->size(), 1);
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds->at(0));
// We should've run the task
ASSERT_EQ(*output.get(), expected);
}
TEST(ExecutorTests,
MultiThreadedExecutorMultipleTasksLargeNumberOfThreadsSucceeds) {
absl::BlockingCounter block_count(250);
// Arrange
auto executor = std::make_unique<Executor>(32);
// Container to note threads that ran
std::vector<std::thread::id> threadIds = std::vector<std::thread::id>();
threadIds.push_back(std::this_thread::get_id());
absl::Mutex mutex;
// Act
for (int index = 0; index < 250; index++) {
executor->Execute([&]() mutable {
std::thread::id id = std::this_thread::get_id();
{
absl::MutexLock lock(&mutex);
threadIds.push_back(id);
}
// Using rand since this is in a critical section
// and windows doesn't have a rand_r anyway
auto sleepTime = (std::rand() % 101) + 1; // NOLINT
sleep(sleepTime);
block_count.DecrementCount();
});
}
block_count.Wait();
executor->Shutdown();
// Assert
// We should still be on the main thread
ASSERT_EQ(std::this_thread::get_id(), threadIds.at(0));
// We should've run 1 time on the main thread, and 200 times on the
// workerThreads
ASSERT_EQ(threadIds.size(), 251);
}
} // namespace
} // namespace linux
} // namespace nearby
@@ -0,0 +1,113 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/file.h"
#include <algorithm>
#include <codecvt>
#include <cstddef>
#include <filesystem>
#include <ios>
#include <locale>
#include <memory>
#include <string>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/linux/utils.h"
namespace nearby {
namespace linux {
// InputFile
std::unique_ptr<IOFile> IOFile::CreateInputFile(
const absl::string_view file_path, size_t size) {
return absl::WrapUnique(new IOFile(file_path, size));
}
IOFile::IOFile(const absl::string_view file_path, size_t size)
: path_(file_path) {
// Always open input file path as wide string on Linux platform.
file_.open(std::filesystem::path(linux::string_to_wstring(path_)),
std::ios::binary | std::ios::in | std::ios::ate);
total_size_ = file_.tellg();
file_.seekg(0);
}
std::unique_ptr<IOFile> IOFile::CreateOutputFile(const absl::string_view path) {
return std::unique_ptr<IOFile>(new IOFile(path));
}
IOFile::IOFile(const absl::string_view file_path)
: file_(), path_(file_path), total_size_(0) {
// Always open input file path as wide string on Windows platform.
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
file_.open(std::filesystem::path(converter.from_bytes(path_)),
std::ios::binary | std::ios::out);
}
ExceptionOr<ByteArray> IOFile::Read(std::int64_t size) {
if (!file_.is_open()) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
if (file_.peek() == EOF) {
return ExceptionOr<ByteArray>{ByteArray{}};
}
if (!file_.good()) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
ByteArray bytes(size);
std::unique_ptr<char[]> read_bytes{new char[size]};
file_.read(read_bytes.get(), static_cast<ptrdiff_t>(size));
auto num_bytes_read = file_.gcount();
if (num_bytes_read == 0) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
return ExceptionOr<ByteArray>(ByteArray(read_bytes.get(), num_bytes_read));
}
Exception IOFile::Close() {
if (file_.is_open()) {
file_.close();
}
return {Exception::kSuccess};
}
Exception IOFile::Write(const ByteArray& data) {
if (!file_.is_open()) {
return {Exception::kIo};
}
if (!file_.good()) {
return {Exception::kIo};
}
file_.write(data.data(), data.size());
file_.flush();
return {file_.good() ? Exception::kSuccess : Exception::kIo};
}
Exception IOFile::Flush() {
file_.flush();
return {file_.good() ? Exception::kSuccess : Exception::kIo};
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,60 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_FILE_H_
#define PLATFORM_IMPL_LINUX_FILE_H_
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "internal/platform/exception.h"
#include "internal/platform/implementation/input_file.h"
#include "internal/platform/implementation/output_file.h"
namespace nearby {
namespace linux {
class IOFile final : public api::InputFile, public api::OutputFile {
public:
static std::unique_ptr<IOFile> CreateInputFile(
const absl::string_view file_path, size_t size);
static std::unique_ptr<IOFile> CreateOutputFile(const absl::string_view path);
ExceptionOr<ByteArray> Read(std::int64_t size) override;
std::string GetFilePath() const override { return path_; }
std::int64_t GetTotalSize() const override { return total_size_; }
Exception Close() override;
Exception Write(const ByteArray& data) override;
Exception Flush() override;
private:
explicit IOFile(const absl::string_view file_path, size_t size);
explicit IOFile(const absl::string_view file_path);
std::fstream file_;
std::string path_;
std::int64_t total_size_;
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_FILE_H_
@@ -0,0 +1,209 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/file_path.h"
#include <algorithm>
#include <cctype>
#include <fstream>
#include <iterator>
#include <string>
#include <vector>
#include "absl/strings/str_cat.h"
#include "internal/platform/implementation/linux/dbus.h"
#include "internal/platform/implementation/linux/device_info.h"
#include "internal/platform/implementation/linux/utils.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace linux {
const wchar_t* kUpOneLevel = L"/..";
constexpr wchar_t kPathDelimiter = L'/';
constexpr wchar_t kReplacementChar = L'_';
constexpr wchar_t kForwardSlash = L'/';
constexpr wchar_t kBackSlash = L'\\';
std::wstring FilePath::GetCustomSavePath(std::wstring parent_folder,
std::wstring file_name) {
std::wstring path;
path += parent_folder + kPathDelimiter + file_name;
return CreateOutputFileWithRename(path);
}
std::wstring FilePath::GetDownloadPath(std::wstring parent_folder,
std::wstring file_name) {
return CreateOutputFileWithRename(
GetDownloadPathInternal(parent_folder, file_name));
}
std::wstring FilePath::GetDownloadPathInternal(std::wstring parent_folder,
std::wstring file_name) {
DeviceInfo info = DeviceInfo(linux::getSystemBusConnection());
std::optional<std::filesystem::path> download_path = info.GetDownloadPath();
std::string base_path;
std::wstring wide_path(string_to_wstring(base_path));
if (!download_path) {
// If grabbing the download path fails then we make a custom one
base_path = getenv("HOME");
base_path.append("/Downloads");
} else {
base_path = download_path.value();
}
// If parent_folder starts with a \\ or /, then strip it
while (!parent_folder.empty() && (*parent_folder.begin() == kBackSlash ||
*parent_folder.begin() == kForwardSlash)) {
parent_folder.erase(0, 1);
}
// If parent_folder ends with a \\ or /, then strip it
while (!parent_folder.empty() && (*parent_folder.rbegin() == kBackSlash ||
*parent_folder.rbegin() == kForwardSlash)) {
parent_folder.erase(parent_folder.size() - 1, 1);
}
// If file_name starts with a \\, then strip it
while (!file_name.empty() && (*file_name.begin() == kBackSlash ||
*file_name.begin() == kForwardSlash)) {
file_name.erase(0, 1);
}
// If file_name ends with a \\, then strip it
while (!file_name.empty() && (*file_name.rbegin() == kBackSlash ||
*file_name.rbegin() == kForwardSlash)) {
file_name.erase(file_name.size() - 1, 1);
}
std::wstring path;
if (parent_folder.empty()) {
path =
file_name.empty() ? wide_path : wide_path + kForwardSlash + file_name;
} else {
path = file_name.empty() ? wide_path + kForwardSlash + parent_folder
: wide_path + kForwardSlash + parent_folder +
kForwardSlash + file_name;
}
// Convert to UTF8 format.
return path;
}
// If the file already exists we add " (x)", where x is an incrementing number,
// starting at 1, using the next non-existing number, to the file name, just
// before the first dot, or at the end if no dot. The absolute path is returned.
std::wstring FilePath::CreateOutputFileWithRename(std::wstring path) {
std::wstring sanitized_path(path);
// Replace any \\ with /
std::replace(sanitized_path.begin(), sanitized_path.end(), kBackSlash,
kForwardSlash);
// Remove any /..'s
SanitizePath(sanitized_path);
auto last_delimiter = sanitized_path.find_last_of(kPathDelimiter);
std::wstring folder(sanitized_path.substr(0, last_delimiter));
std::wstring file_name(sanitized_path.substr(last_delimiter));
// Locate the last dot
auto first = file_name.find_last_of('.');
if (first == std::string::npos) {
first = file_name.size();
}
// Break the string at the dot.
auto file_name1 = file_name.substr(0, first);
auto file_name2 = file_name.substr(first);
// Construct the target file name
std::wstring target(sanitized_path);
std::fstream file;
// Open file as std::wstring
file.open(wstring_to_string(target), std::fstream::binary | std::fstream::in);
// While we successfully open the file, keep incrementing the count.
int count = 0;
while (!(file.rdstate() & std::ifstream::failbit)) {
file.close();
target = (folder + file_name1 + L" (" + std::to_wstring(++count) + L")" +
file_name2);
file.clear();
file.open(wstring_to_string(target),
std::fstream::binary | std::fstream::in);
}
if (count > 0) {
NEARBY_LOGS(INFO) << "Renamed " << wstring_to_string(path) << " to "
<< wstring_to_string(target);
}
// The above leaves the file open, so close it.
file.close();
return target;
}
std::wstring FilePath::MutateForbiddenPathElements(std::wstring& str) {
// There are no forbidden paths in Linux
return str;
}
void FilePath::SanitizePath(std::wstring& path) {
size_t pos = std::wstring::npos;
// Search for the substring in string in a loop until nothing is found
while ((pos = path.find(kUpOneLevel)) != std::string::npos) {
// If found then erase it from string
path.erase(pos, wcslen(kUpOneLevel));
}
ReplaceInvalidCharacters(path);
}
// Legit the only illegal character in Linux
char kIllegalFileCharacters[] = {'/'};
void FilePath::ReplaceInvalidCharacters(std::wstring& path) {
for (auto& character : path) {
// If 0 < character < 32, it's illegal, replace it
if (character > 0 && character < 32) {
NEARBY_LOGS(INFO) << "In path " << wstring_to_string(path)
<< " replaced \'" << std::string(1, character)
<< "\' with \'" << std::string(1, kReplacementChar);
character = kReplacementChar;
}
for (auto illegal_character : kIllegalFileCharacters) {
if (character == illegal_character) {
NEARBY_LOGS(INFO) << "In path " << wstring_to_string(path)
<< " replaced \'" << std::string(1, character)
<< "\' with \'" << std::string(1, kReplacementChar);
character = kReplacementChar;
}
}
}
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,49 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
#include <string>
#include "absl/strings/string_view.h"
namespace nearby {
namespace linux {
class FilePath {
public:
static std::wstring GetCustomSavePath(std::wstring parent_folder,
std::wstring file_name);
static std::wstring GetDownloadPath(std::wstring parent_folder,
std::wstring file_name);
private:
// If the file already exists we add " (x)", where x is an incrementing
// number, starting at 1, using the next non-existing number, to the
// file name, just before the first dot, or at the end if no dot. The
// absolute path is returned.
static std::wstring CreateOutputFileWithRename(std::wstring path);
static void ReplaceInvalidCharacters(std::wstring& path);
static void SanitizePath(std::wstring& path);
static std::wstring MutateForbiddenPathElements(std::wstring& str);
static std::wstring GetDownloadPathInternal(std::wstring parent_folder,
std::wstring file_name);
};
} // namespace linux
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_LINUX_FILE_PATH_H_
@@ -0,0 +1,779 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "internal/platform/implementation/linux/file_path.h"
#include <algorithm>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <internal/platform/implementation/linux/device_info.h>
#include <internal/platform/implementation/linux/utils.h>
#include "gtest/gtest.h"
namespace nearby {
namespace linux {
namespace {
const wchar_t* kFileName(L"increment_file_test.txt");
const wchar_t* kFirstIterationFileName(L"/increment_file_test (1).txt");
const wchar_t* kSecondIterationFileName(L"/increment_file_test (2).txt");
const wchar_t* kThirdIterationFileName(L"/increment_file_test (3).txt");
const wchar_t* kNoDotsFileName(L"incrementfiletesttxt");
const wchar_t* kOneIterationNoDotsFileName(L"/incrementfiletesttxt (1)");
const wchar_t* kMultipleDotsFileName(L"increment.file.test.txt");
const wchar_t* kOneIterationMultipleDotsFileName(
L"/increment.file.test (1).txt");
const wchar_t* kImmediateEscape(L"../");
const wchar_t* kLongEscapeBackSlash(L"..\\test\\..\\..\\test");
const wchar_t* kTwoLevelFolder(L"/test/test");
const wchar_t* kLongEscapeSlash(L"../test/../../test");
const wchar_t* kLongEscapeMixedSlash(L"../test\\..\\../test");
const wchar_t* kLongEscapeEndingEscape(L"../test/../../test/..");
const wchar_t* kLongEscapeEndingEscapeWithSlash(
L"../test/../../test/../../../");
} // namespace
// Can't run on google 3, I presume the SHGetKnownFolderPath
// fails.
class FilePathTests : public testing::Test {
protected:
// You can define per-test set-up logic as usual.
FilePathTests() {
default_download_path_ =
string_to_wstring(DeviceInfo().GetDownloadPath().value_or(
std::string(getenv("HOME")).append("/Downloads")));
}
std::wstring default_download_path_;
};
TEST_F(FilePathTests, GetDownloadPathWithEmptyStringArguments\
ShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithSlashParent\
FolderArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"/");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithBackslashParent\
FolderArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"\\");
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
} // NOLINT false lint error here
TEST_F(FilePathTests, GetDownloadPathWithAttemptToEscape\
UsersDownloadFolderShouldReturnDownloadPathNotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kImmediateEscape);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithBackslashShouldReturnDownloadPath\
NotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeBackSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderShouldReturnDownloadPathNotEscapingUsers\
DownloadFolder) {
std::wstring parent_folder(kLongEscapeSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithMixedSlashShouldReturnDownloadPath\
NotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeMixedSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithEndingEscapeShouldReturnDownload\
PathNotEscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeEndingEscape);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithMultiple\
AttemptsToEscapeUsersDownloadFolderWithEndingSlashShouldReturnDownloadPathNot\
EscapingUsersDownloadFolder) {
std::wstring parent_folder(kLongEscapeEndingEscapeWithSlash);
std::wstring file_name(L"");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_ + kTwoLevelFolder);
}
TEST_F(FilePathTests, GetDownloadPathWithSlashFileName\
ArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"/");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithBackslashFile\
NameArgumentsShouldReturnBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"\\");
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
auto result_size = actual.size();
auto default_size = default_download_path_.size();
EXPECT_EQ(actual, default_download_path_);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
ShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
StartingWithSlashArgumentsShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"/test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
StartingWithBackslashArgumentsShouldReturnParentFolderAppendedToBase\
DownloadPath) {
std::wstring parent_folder(L"\\test_parent_folder");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithSlashArgumentsShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder/");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithBackslashArguments\
ShouldReturnParentFolderAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder\\");
std::wstring file_name(L"");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
BeginningWithSlashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"/test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
BeginningWithBackslashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"\\test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, path.str().c_str());
}
TEST_F(FilePathTests, GetDownloadPathWithFileNameEnding\
WithSlashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"test_file_name.name/");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileNameEnding\
WithBackslashArgumentsShouldReturnFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"");
std::wstring file_name(L"test_file_name.name\\");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolderAnd\
FileNameArgumentsShould\
ReturnParentFolderAndFileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithParentFolder\
EndingWithBackslashAndFileNameArgumentsShouldReturnParentFolderAndFileName\
AppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder\\");
std::wstring file_name(L"test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPathWithFileName\
StartingWithBackslashAndParentFolderArgumentsShouldReturnParentFolderAnd\
FileNameAppendedToBaseDownloadPath) {
std::wstring parent_folder(L"test_parent_folder");
std::wstring file_name(L"\\test_file_name.name");
std::wstringstream path(L"");
path << default_download_path_ << L"/" << "test_parent_folder"
<< "/"
<< "test_file_name.name";
std::wstring expected = path.str();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacters\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x05,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x5Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_LowestIllegalFileNameCharacter\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x01,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x1Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_HighestIllegalFileNameCharacter\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x1f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test\x1fTest");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterQuestionMark\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test?Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterAsterisk\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test*Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterLessThan\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test<Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterGreaterThan\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test>Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterVerticalBar\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test|Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_IllegalFileNameCharacterColon\
ReturnsFileNameWithUnderbarSubstituted) {
// char illegal_character_sequence[]{ 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2f,
// 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0 };
auto illegal_character_sequence(L"Test:Test");
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/Test_Test");
auto actual(FilePath::GetDownloadPath(
parent_folder, std::wstring(illegal_character_sequence)));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_FileDoesntExist\
ReturnsFileWithPassedName) {
std::wstring file_name(kFileName);
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(L"/");
expected.append(file_name);
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
}
TEST_F(FilePathTests, GetDownloadPath_FileExistsReturns\
FileWithIncrementedName) {
std::wstring file_name(kFileName);
std::wstring renamed_file_name(kFirstIterationFileName);
std::wstring parent_folder(L"");
std::wstring output_file_path(default_download_path_);
output_file_path.append(L"/");
output_file_path.append(file_name);
std::wstring expected(default_download_path_);
expected += renamed_file_name;
std::wifstream input_file;
std::wofstream output_file;
output_file.open(wstring_to_string(output_file_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(actual, expected);
// Remove the file and check that it is removed
// File 1
std::filesystem::remove(output_file_path.c_str());
input_file.open(wstring_to_string(output_file_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_MultipleFilesExist\
ReturnsNextIncrementedFileName) {
std::ofstream output_file;
std::ifstream input_file;
std::wstring file_name(kFileName);
std::wstring first_renamed_file_name(kFirstIterationFileName);
std::wstring second_renamed_file_name(kSecondIterationFileName);
std::wstring parent_folder(L"");
std::wstring expected(default_download_path_);
expected.append(second_renamed_file_name.c_str());
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(first_renamed_file_name);
// Create the test files
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
output_file.clear();
output_file.open(wstring_to_string(output_file2_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
// Remove the test files and check that it is removed
// File 1
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(output_file1_path, std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// File 2
std::filesystem::remove(wstring_to_string(output_file2_path).c_str());
input_file.clear();
input_file.open(wstring_to_string(output_file2_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameContains\
MultipleDotsReturnsIncrementBeforeFirstDot) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kMultipleDotsFileName);
std::wstring renamed_file_name(kOneIterationMultipleDotsFileName);
std::wstring parent_folder(L"");
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(renamed_file_name);
std::wstring expected(default_download_path_);
expected.append(renamed_file_name);
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameContainsNo\
DotsReturnsWithIncrementAtEnd) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kNoDotsFileName);
std::wstring renamed_file_name(kOneIterationNoDotsFileName);
std::wstring parent_folder(L"");
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(L"/" + file_name);
std::wstring output_file2_path(default_download_path_);
output_file2_path.append(L"/" + renamed_file_name);
std::wstring expected(default_download_path_);
expected.append(renamed_file_name);
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
TEST_F(FilePathTests, GetDownloadPath_FileNameExistsWith\
AHoleBetweenRenamedFiles) {
std::ifstream input_file;
std::ofstream output_file;
std::wstring file_name(kFileName);
std::wstring file_name1(kFirstIterationFileName);
std::wstring file_name2(kSecondIterationFileName);
std::wstring file_name3(kThirdIterationFileName);
std::wstring parent_folder(L"");
// Create the path for the original file name
std::wstring output_file_path(default_download_path_);
output_file_path.append(
L"/" +
file_name); // Original file name example: "increment_file_test.txt"
// Create the path for the first iteration of the original file name
std::wstring output_file1_path(default_download_path_);
output_file1_path.append(file_name1); // First iteration on original file
// name example:
// "increment_file_test (1).txt"
// Create the path for the third iteration of the original file name
std::wstring output_file3_path(default_download_path_);
output_file3_path.append(
file_name3); // Third iteration on original file
// name example: "increment_file_test (3).txt"
// Create the expected result which is the second iteration of the original
// file name
std::wstring expected(default_download_path_);
expected.append(file_name2); // Second iteration on original file name
// example: "increment_file_test (2).txt"
// Create the original file
output_file.open(wstring_to_string(output_file_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// Create the first iteration of the original file
output_file.clear();
output_file.open(wstring_to_string(output_file1_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// Create the third iteration of the original file
output_file.clear();
output_file.open(wstring_to_string(output_file3_path),
std::ofstream::binary | std::ofstream::out);
ASSERT_TRUE(output_file.rdstate() == std::ofstream::goodbit);
output_file.close();
// This should return the second iteration of the original file
auto actual(FilePath::GetDownloadPath(parent_folder, file_name));
EXPECT_EQ(expected, actual);
// Delete the original file
std::filesystem::remove(wstring_to_string(output_file_path).c_str());
input_file.open(wstring_to_string(output_file_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// Delete the first iteration of the original file
input_file.clear(); // Reset the input_file state
std::filesystem::remove(wstring_to_string(output_file1_path).c_str());
input_file.open(wstring_to_string(output_file1_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
// Delete the third iteration of the original file
input_file.clear(); // Reset the input_file state
std::filesystem::remove(wstring_to_string(output_file3_path).c_str());
input_file.open(wstring_to_string(output_file3_path),
std::ifstream::binary | std::ifstream::in);
ASSERT_FALSE(input_file.rdstate() == std::ifstream::goodbit);
}
} // namespace linux
} // namespace nearby
@@ -0,0 +1,48 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PLATFORM_IMPL_LINUX_FUTURE_H_
#define PLATFORM_IMPL_LINUX_FUTURE_H_
#include "internal/platform/implementation/future.h"
namespace nearby {
namespace linux {
// A Future represents the result of an asynchronous computation.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html
template <typename T>
class Future : public api::Future<T> {
public:
// TODO(b/184975123): replace with real implementation.
~Future() override = default;
// throws Exception::kInterrupted, Exception::kExecution
// TODO(b/184975123): replace with real implementation.
ExceptionOr<T> Get() override { return ExceptionOr<T>{Exception::kFailed}; }
// throws Exception::kInterrupted, Exception::kExecution
// throws Exception::kTimeout if timeout is exceeded while waiting for
// result.
// TODO(b/184975123): replace with real implementation.
ExceptionOr<T> Get(absl::Duration timeout) override {
return ExceptionOr<T>{Exception::kFailed};
}
};
} // namespace linux
} // namespace nearby
#endif // PLATFORM_IMPL_LINUX_FUTURE_H_
@@ -0,0 +1,22 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"])
cc_library(
name = "types",
textual_hdrs = glob(["**/*.h"]),
visibility = [
"//internal/platform/implementation/linux:__subpackages__",
],
)
@@ -0,0 +1,94 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__avahi_entrygroup_client_glue_h__proxy__H__
#define __sdbuscpp__avahi_entrygroup_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace freedesktop {
namespace Avahi {
class EntryGroup_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.freedesktop.Avahi.EntryGroup";
protected:
EntryGroup_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const int32_t& state, const std::string& error){ this->onStateChanged(state, error); });
}
~EntryGroup_proxy() = default;
virtual void onStateChanged(const int32_t& state, const std::string& error) = 0;
public:
void Free()
{
proxy_.callMethod("Free").onInterface(INTERFACE_NAME);
}
void Commit()
{
proxy_.callMethod("Commit").onInterface(INTERFACE_NAME);
}
void Reset()
{
proxy_.callMethod("Reset").onInterface(INTERFACE_NAME);
}
int32_t GetState()
{
int32_t result;
proxy_.callMethod("GetState").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
bool IsEmpty()
{
bool result;
proxy_.callMethod("IsEmpty").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void AddService(const int32_t& interface, const int32_t& protocol, const uint32_t& flags, const std::string& name, const std::string& type, const std::string& domain, const std::string& host, const uint16_t& port, const std::vector<std::vector<uint8_t>>& txt)
{
proxy_.callMethod("AddService").onInterface(INTERFACE_NAME).withArguments(interface, protocol, flags, name, type, domain, host, port, txt);
}
void AddServiceSubtype(const int32_t& interface, const int32_t& protocol, const uint32_t& flags, const std::string& name, const std::string& type, const std::string& domain, const std::string& subtype)
{
proxy_.callMethod("AddServiceSubtype").onInterface(INTERFACE_NAME).withArguments(interface, protocol, flags, name, type, domain, subtype);
}
void UpdateServiceTxt(const int32_t& interface, const int32_t& protocol, const uint32_t& flags, const std::string& name, const std::string& type, const std::string& domain, const std::vector<std::vector<uint8_t>>& txt)
{
proxy_.callMethod("UpdateServiceTxt").onInterface(INTERFACE_NAME).withArguments(interface, protocol, flags, name, type, domain, txt);
}
void AddAddress(const int32_t& interface, const int32_t& protocol, const uint32_t& flags, const std::string& name, const std::string& address)
{
proxy_.callMethod("AddAddress").onInterface(INTERFACE_NAME).withArguments(interface, protocol, flags, name, address);
}
void AddRecord(const int32_t& interface, const int32_t& protocol, const uint32_t& flags, const std::string& name, const uint16_t& clazz, const uint16_t& type, const uint32_t& ttl, const std::vector<uint8_t>& rdata)
{
proxy_.callMethod("AddRecord").onInterface(INTERFACE_NAME).withArguments(interface, protocol, flags, name, clazz, type, ttl, rdata);
}
private:
sdbus::IProxy& proxy_;
};
}}} // namespaces
#endif
@@ -0,0 +1,94 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<?xml-stylesheet type="text/xsl" href="introspect.xsl"?>
<!DOCTYPE node SYSTEM "introspect.dtd">
<!--
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-->
<node>
<interface name="org.freedesktop.Avahi.EntryGroup">
<method name="Free"/>
<method name="Commit"/>
<method name="Reset"/>
<method name="GetState">
<arg name="state" type="i" direction="out"/>
</method>
<signal name="StateChanged">
<arg name="state" type="i"/>
<arg name="error" type="s"/>
</signal>
<method name="IsEmpty">
<arg name="empty" type="b" direction="out"/>
</method>
<method name="AddService">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="host" type="s" direction="in"/>
<arg name="port" type="q" direction="in"/>
<arg name="txt" type="aay" direction="in"/>
</method>
<method name="AddServiceSubtype">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="subtype" type="s" direction="in"/>
</method>
<method name="UpdateServiceTxt">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="txt" type="aay" direction="in"/>
</method>
<method name="AddAddress">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="address" type="s" direction="in"/>
</method>
<method name="AddRecord">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="clazz" type="q" direction="in"/>
<arg name="type" type="q" direction="in"/>
<arg name="ttl" type="u" direction="in"/>
<arg name="rdata" type="ay" direction="in"/>
</method>
</interface>
</node>
@@ -0,0 +1,398 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<?xml-stylesheet type="text/xsl" href="introspect.xsl"?>
<!DOCTYPE node SYSTEM "introspect.dtd">
<!--
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-->
<node>
<interface name="org.freedesktop.Avahi.Server">
<method name="GetVersionString">
<arg name="version" type="s" direction="out"/>
</method>
<method name="GetAPIVersion">
<arg name="version" type="u" direction="out"/>
</method>
<method name="GetHostName">
<arg name="name" type="s" direction="out"/>
</method>
<method name="SetHostName">
<arg name="name" type="s" direction="in"/>
</method>
<method name="GetHostNameFqdn">
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetDomainName">
<arg name="name" type="s" direction="out"/>
</method>
<method name="IsNSSSupportAvailable">
<arg name="yes" type="b" direction="out"/>
</method>
<method name="GetState">
<arg name="state" type="i" direction="out"/>
</method>
<signal name="StateChanged">
<arg name="state" type="i"/>
<arg name="error" type="s"/>
</signal>
<method name="GetLocalServiceCookie">
<arg name="cookie" type="u" direction="out"/>
</method>
<method name="GetAlternativeHostName">
<arg name="name" type="s" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetAlternativeServiceName">
<arg name="name" type="s" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetNetworkInterfaceNameByIndex">
<arg name="index" type="i" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetNetworkInterfaceIndexByName">
<arg name="name" type="s" direction="in"/>
<arg name="index" type="i" direction="out"/>
</method>
<method name="ResolveHostName">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="ResolveAddress">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="address" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="ResolveService">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="type" type="s" direction="out"/>
<arg name="domain" type="s" direction="out"/>
<arg name="host" type="s" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="port" type="q" direction="out"/>
<arg name="txt" type="aay" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="EntryGroupNew">
<arg name="path" type="o" direction="out"/>
</method>
<method name="DomainBrowserNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="btype" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceTypeBrowserNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceBrowserNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceResolverNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="HostNameResolverNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="AddressResolverNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="address" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="RecordBrowserNew">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="clazz" type="q" direction="in"/>
<arg name="type" type="q" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
</interface>
<interface name="org.freedesktop.Avahi.Server2">
<method name="GetVersionString">
<arg name="version" type="s" direction="out"/>
</method>
<method name="GetAPIVersion">
<arg name="version" type="u" direction="out"/>
</method>
<method name="GetHostName">
<arg name="name" type="s" direction="out"/>
</method>
<method name="SetHostName">
<arg name="name" type="s" direction="in"/>
</method>
<method name="GetHostNameFqdn">
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetDomainName">
<arg name="name" type="s" direction="out"/>
</method>
<method name="IsNSSSupportAvailable">
<arg name="yes" type="b" direction="out"/>
</method>
<method name="GetState">
<arg name="state" type="i" direction="out"/>
</method>
<signal name="StateChanged">
<arg name="state" type="i"/>
<arg name="error" type="s"/>
</signal>
<method name="GetLocalServiceCookie">
<arg name="cookie" type="u" direction="out"/>
</method>
<method name="GetAlternativeHostName">
<arg name="name" type="s" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetAlternativeServiceName">
<arg name="name" type="s" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetNetworkInterfaceNameByIndex">
<arg name="index" type="i" direction="in"/>
<arg name="name" type="s" direction="out"/>
</method>
<method name="GetNetworkInterfaceIndexByName">
<arg name="name" type="s" direction="in"/>
<arg name="index" type="i" direction="out"/>
</method>
<method name="ResolveHostName">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="ResolveAddress">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="address" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="ResolveService">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="type" type="s" direction="out"/>
<arg name="domain" type="s" direction="out"/>
<arg name="host" type="s" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="port" type="q" direction="out"/>
<arg name="txt" type="aay" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</method>
<method name="EntryGroupNew">
<arg name="path" type="o" direction="out"/>
</method>
<method name="DomainBrowserPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="btype" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceTypeBrowserPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceBrowserPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="ServiceResolverPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="type" type="s" direction="in"/>
<arg name="domain" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="HostNameResolverPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="aprotocol" type="i" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="AddressResolverPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="address" type="s" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
<method name="RecordBrowserPrepare">
<arg name="interface" type="i" direction="in"/>
<arg name="protocol" type="i" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="clazz" type="q" direction="in"/>
<arg name="type" type="q" direction="in"/>
<arg name="flags" type="u" direction="in"/>
<arg name="path" type="o" direction="out"/>
</method>
</interface>
</node>
@@ -0,0 +1,58 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<?xml-stylesheet type="text/xsl" href="introspect.xsl"?>
<!DOCTYPE node SYSTEM "introspect.dtd">
<!--
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-->
<node>
<interface name="org.freedesktop.Avahi.ServiceBrowser">
<method name="Free"/>
<method name="Start"/>
<signal name="ItemNew">
<arg name="interface" type="i"/>
<arg name="protocol" type="i"/>
<arg name="name" type="s"/>
<arg name="type" type="s"/>
<arg name="domain" type="s"/>
<arg name="flags" type="u"/>
</signal>
<signal name="ItemRemove">
<arg name="interface" type="i"/>
<arg name="protocol" type="i"/>
<arg name="name" type="s"/>
<arg name="type" type="s"/>
<arg name="domain" type="s"/>
<arg name="flags" type="u"/>
</signal>
<signal name="Failure">
<arg name="error" type="s"/>
</signal>
<signal name="AllForNow"/>
<signal name="CacheExhausted"/>
</interface>
</node>
@@ -0,0 +1,57 @@
<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
<?xml-stylesheet type="text/xsl" href="introspect.xsl"?>
<!DOCTYPE node SYSTEM "introspect.dtd">
<!--
This file is part of avahi.
avahi is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
avahi is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with avahi; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.
-->
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" type="s" direction="out" />
</method>
</interface>
<interface name="org.freedesktop.Avahi.ServiceResolver">
<method name="Free"/>
<method name="Start"/>
<signal name="Found">
<arg name="interface" type="i" direction="out"/>
<arg name="protocol" type="i" direction="out"/>
<arg name="name" type="s" direction="out"/>
<arg name="type" type="s" direction="out"/>
<arg name="domain" type="s" direction="out"/>
<arg name="host" type="s" direction="out"/>
<arg name="aprotocol" type="i" direction="out"/>
<arg name="address" type="s" direction="out"/>
<arg name="port" type="q" direction="out"/>
<arg name="txt" type="aay" direction="out"/>
<arg name="flags" type="u" direction="out"/>
</signal>
<signal name="Failure">
<arg name="error" type="s"/>
</signal>
</interface>
</node>
@@ -0,0 +1,399 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__avahi_server_client_glue_h__proxy__H__
#define __sdbuscpp__avahi_server_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace freedesktop {
namespace Avahi {
class Server_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.freedesktop.Avahi.Server";
protected:
Server_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const int32_t& state, const std::string& error){ this->onStateChanged(state, error); });
}
~Server_proxy() = default;
virtual void onStateChanged(const int32_t& state, const std::string& error) = 0;
public:
std::string GetVersionString()
{
std::string result;
proxy_.callMethod("GetVersionString").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
uint32_t GetAPIVersion()
{
uint32_t result;
proxy_.callMethod("GetAPIVersion").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetHostName()
{
std::string result;
proxy_.callMethod("GetHostName").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void SetHostName(const std::string& name)
{
proxy_.callMethod("SetHostName").onInterface(INTERFACE_NAME).withArguments(name);
}
std::string GetHostNameFqdn()
{
std::string result;
proxy_.callMethod("GetHostNameFqdn").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetDomainName()
{
std::string result;
proxy_.callMethod("GetDomainName").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
bool IsNSSSupportAvailable()
{
bool result;
proxy_.callMethod("IsNSSSupportAvailable").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t GetState()
{
int32_t result;
proxy_.callMethod("GetState").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
uint32_t GetLocalServiceCookie()
{
uint32_t result;
proxy_.callMethod("GetLocalServiceCookie").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetAlternativeHostName(const std::string& name)
{
std::string result;
proxy_.callMethod("GetAlternativeHostName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::string GetAlternativeServiceName(const std::string& name)
{
std::string result;
proxy_.callMethod("GetAlternativeServiceName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::string GetNetworkInterfaceNameByIndex(const int32_t& index)
{
std::string result;
proxy_.callMethod("GetNetworkInterfaceNameByIndex").onInterface(INTERFACE_NAME).withArguments(index).storeResultsTo(result);
return result;
}
int32_t GetNetworkInterfaceIndexByName(const std::string& name)
{
int32_t result;
proxy_.callMethod("GetNetworkInterfaceIndexByName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, std::string, int32_t, std::string, uint32_t> ResolveHostName(const int32_t& interface, const int32_t& protocol, const std::string& name, const int32_t& aprotocol, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, std::string, int32_t, std::string, uint32_t> result;
proxy_.callMethod("ResolveHostName").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, aprotocol, flags).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, int32_t, std::string, std::string, uint32_t> ResolveAddress(const int32_t& interface, const int32_t& protocol, const std::string& address, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, int32_t, std::string, std::string, uint32_t> result;
proxy_.callMethod("ResolveAddress").onInterface(INTERFACE_NAME).withArguments(interface, protocol, address, flags).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, std::string, std::string, std::string, std::string, int32_t, std::string, uint16_t, std::vector<std::vector<uint8_t>>, uint32_t> ResolveService(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const int32_t& aprotocol, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, std::string, std::string, std::string, std::string, int32_t, std::string, uint16_t, std::vector<std::vector<uint8_t>>, uint32_t> result;
proxy_.callMethod("ResolveService").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, type, domain, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath EntryGroupNew()
{
sdbus::ObjectPath result;
proxy_.callMethod("EntryGroupNew").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath DomainBrowserNew(const int32_t& interface, const int32_t& protocol, const std::string& domain, const int32_t& btype, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("DomainBrowserNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, domain, btype, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceTypeBrowserNew(const int32_t& interface, const int32_t& protocol, const std::string& domain, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceTypeBrowserNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, domain, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceBrowserNew(const int32_t& interface, const int32_t& protocol, const std::string& type, const std::string& domain, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceBrowserNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, type, domain, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceResolverNew(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const int32_t& aprotocol, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceResolverNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, type, domain, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath HostNameResolverNew(const int32_t& interface, const int32_t& protocol, const std::string& name, const int32_t& aprotocol, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("HostNameResolverNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath AddressResolverNew(const int32_t& interface, const int32_t& protocol, const std::string& address, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("AddressResolverNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, address, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath RecordBrowserNew(const int32_t& interface, const int32_t& protocol, const std::string& name, const uint16_t& clazz, const uint16_t& type, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("RecordBrowserNew").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, clazz, type, flags).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& proxy_;
};
}}} // namespaces
namespace org {
namespace freedesktop {
namespace Avahi {
class Server2_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.freedesktop.Avahi.Server2";
protected:
Server2_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const int32_t& state, const std::string& error){ this->onStateChanged(state, error); });
}
~Server2_proxy() = default;
virtual void onStateChanged(const int32_t& state, const std::string& error) = 0;
public:
std::string GetVersionString()
{
std::string result;
proxy_.callMethod("GetVersionString").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
uint32_t GetAPIVersion()
{
uint32_t result;
proxy_.callMethod("GetAPIVersion").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetHostName()
{
std::string result;
proxy_.callMethod("GetHostName").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void SetHostName(const std::string& name)
{
proxy_.callMethod("SetHostName").onInterface(INTERFACE_NAME).withArguments(name);
}
std::string GetHostNameFqdn()
{
std::string result;
proxy_.callMethod("GetHostNameFqdn").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetDomainName()
{
std::string result;
proxy_.callMethod("GetDomainName").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
bool IsNSSSupportAvailable()
{
bool result;
proxy_.callMethod("IsNSSSupportAvailable").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
int32_t GetState()
{
int32_t result;
proxy_.callMethod("GetState").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
uint32_t GetLocalServiceCookie()
{
uint32_t result;
proxy_.callMethod("GetLocalServiceCookie").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
std::string GetAlternativeHostName(const std::string& name)
{
std::string result;
proxy_.callMethod("GetAlternativeHostName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::string GetAlternativeServiceName(const std::string& name)
{
std::string result;
proxy_.callMethod("GetAlternativeServiceName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::string GetNetworkInterfaceNameByIndex(const int32_t& index)
{
std::string result;
proxy_.callMethod("GetNetworkInterfaceNameByIndex").onInterface(INTERFACE_NAME).withArguments(index).storeResultsTo(result);
return result;
}
int32_t GetNetworkInterfaceIndexByName(const std::string& name)
{
int32_t result;
proxy_.callMethod("GetNetworkInterfaceIndexByName").onInterface(INTERFACE_NAME).withArguments(name).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, std::string, int32_t, std::string, uint32_t> ResolveHostName(const int32_t& interface, const int32_t& protocol, const std::string& name, const int32_t& aprotocol, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, std::string, int32_t, std::string, uint32_t> result;
proxy_.callMethod("ResolveHostName").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, aprotocol, flags).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, int32_t, std::string, std::string, uint32_t> ResolveAddress(const int32_t& interface, const int32_t& protocol, const std::string& address, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, int32_t, std::string, std::string, uint32_t> result;
proxy_.callMethod("ResolveAddress").onInterface(INTERFACE_NAME).withArguments(interface, protocol, address, flags).storeResultsTo(result);
return result;
}
std::tuple<int32_t, int32_t, std::string, std::string, std::string, std::string, int32_t, std::string, uint16_t, std::vector<std::vector<uint8_t>>, uint32_t> ResolveService(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const int32_t& aprotocol, const uint32_t& flags)
{
std::tuple<int32_t, int32_t, std::string, std::string, std::string, std::string, int32_t, std::string, uint16_t, std::vector<std::vector<uint8_t>>, uint32_t> result;
proxy_.callMethod("ResolveService").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, type, domain, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath EntryGroupNew()
{
sdbus::ObjectPath result;
proxy_.callMethod("EntryGroupNew").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
sdbus::ObjectPath DomainBrowserPrepare(const int32_t& interface, const int32_t& protocol, const std::string& domain, const int32_t& btype, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("DomainBrowserPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, domain, btype, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceTypeBrowserPrepare(const int32_t& interface, const int32_t& protocol, const std::string& domain, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceTypeBrowserPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, domain, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceBrowserPrepare(const int32_t& interface, const int32_t& protocol, const std::string& type, const std::string& domain, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceBrowserPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, type, domain, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath ServiceResolverPrepare(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const int32_t& aprotocol, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("ServiceResolverPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, type, domain, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath HostNameResolverPrepare(const int32_t& interface, const int32_t& protocol, const std::string& name, const int32_t& aprotocol, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("HostNameResolverPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, aprotocol, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath AddressResolverPrepare(const int32_t& interface, const int32_t& protocol, const std::string& address, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("AddressResolverPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, address, flags).storeResultsTo(result);
return result;
}
sdbus::ObjectPath RecordBrowserPrepare(const int32_t& interface, const int32_t& protocol, const std::string& name, const uint16_t& clazz, const uint16_t& type, const uint32_t& flags)
{
sdbus::ObjectPath result;
proxy_.callMethod("RecordBrowserPrepare").onInterface(INTERFACE_NAME).withArguments(interface, protocol, name, clazz, type, flags).storeResultsTo(result);
return result;
}
private:
sdbus::IProxy& proxy_;
};
}}} // namespaces
#endif
@@ -0,0 +1,58 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__avahi_servicebrowser_client_glue_h__proxy__H__
#define __sdbuscpp__avahi_servicebrowser_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace freedesktop {
namespace Avahi {
class ServiceBrowser_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.freedesktop.Avahi.ServiceBrowser";
protected:
ServiceBrowser_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
proxy_.uponSignal("ItemNew").onInterface(INTERFACE_NAME).call([this](const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const uint32_t& flags){ this->onItemNew(interface, protocol, name, type, domain, flags); });
proxy_.uponSignal("ItemRemove").onInterface(INTERFACE_NAME).call([this](const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const uint32_t& flags){ this->onItemRemove(interface, protocol, name, type, domain, flags); });
proxy_.uponSignal("Failure").onInterface(INTERFACE_NAME).call([this](const std::string& error){ this->onFailure(error); });
proxy_.uponSignal("AllForNow").onInterface(INTERFACE_NAME).call([this](){ this->onAllForNow(); });
proxy_.uponSignal("CacheExhausted").onInterface(INTERFACE_NAME).call([this](){ this->onCacheExhausted(); });
}
~ServiceBrowser_proxy() = default;
virtual void onItemNew(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const uint32_t& flags) = 0;
virtual void onItemRemove(const int32_t& interface, const int32_t& protocol, const std::string& name, const std::string& type, const std::string& domain, const uint32_t& flags) = 0;
virtual void onFailure(const std::string& error) = 0;
virtual void onAllForNow() = 0;
virtual void onCacheExhausted() = 0;
public:
void Free()
{
proxy_.callMethod("Free").onInterface(INTERFACE_NAME);
}
void Start()
{
proxy_.callMethod("Start").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}}} // namespaces
#endif
@@ -0,0 +1,179 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__bluez_adapter_client_glue_h__proxy__H__
#define __sdbuscpp__bluez_adapter_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class Adapter1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.Adapter1";
protected:
Adapter1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~Adapter1_proxy() = default;
public:
void StartDiscovery()
{
proxy_.callMethod("StartDiscovery").onInterface(INTERFACE_NAME);
}
void SetDiscoveryFilter(const std::map<std::string, sdbus::Variant>& properties)
{
proxy_.callMethod("SetDiscoveryFilter").onInterface(INTERFACE_NAME).withArguments(properties);
}
void StopDiscovery()
{
proxy_.callMethod("StopDiscovery").onInterface(INTERFACE_NAME);
}
void RemoveDevice(const sdbus::ObjectPath& device)
{
proxy_.callMethod("RemoveDevice").onInterface(INTERFACE_NAME).withArguments(device);
}
std::vector<std::string> GetDiscoveryFilters()
{
std::vector<std::string> result;
proxy_.callMethod("GetDiscoveryFilters").onInterface(INTERFACE_NAME).storeResultsTo(result);
return result;
}
void ConnectDevice(const std::map<std::string, sdbus::Variant>& properties)
{
proxy_.callMethod("ConnectDevice").onInterface(INTERFACE_NAME).withArguments(properties);
}
public:
std::string Address()
{
return proxy_.getProperty("Address").onInterface(INTERFACE_NAME);
}
std::string AddressType()
{
return proxy_.getProperty("AddressType").onInterface(INTERFACE_NAME);
}
std::string Name()
{
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
}
std::string Alias()
{
return proxy_.getProperty("Alias").onInterface(INTERFACE_NAME);
}
void Alias(const std::string& value)
{
proxy_.setProperty("Alias").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t Class()
{
return proxy_.getProperty("Class").onInterface(INTERFACE_NAME);
}
bool Powered()
{
return proxy_.getProperty("Powered").onInterface(INTERFACE_NAME);
}
void Powered(const bool& value)
{
proxy_.setProperty("Powered").onInterface(INTERFACE_NAME).toValue(value);
}
std::string PowerState()
{
return proxy_.getProperty("PowerState").onInterface(INTERFACE_NAME);
}
bool Discoverable()
{
return proxy_.getProperty("Discoverable").onInterface(INTERFACE_NAME);
}
void Discoverable(const bool& value)
{
proxy_.setProperty("Discoverable").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t DiscoverableTimeout()
{
return proxy_.getProperty("DiscoverableTimeout").onInterface(INTERFACE_NAME);
}
void DiscoverableTimeout(const uint32_t& value)
{
proxy_.setProperty("DiscoverableTimeout").onInterface(INTERFACE_NAME).toValue(value);
}
bool Pairable()
{
return proxy_.getProperty("Pairable").onInterface(INTERFACE_NAME);
}
void Pairable(const bool& value)
{
proxy_.setProperty("Pairable").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t PairableTimeout()
{
return proxy_.getProperty("PairableTimeout").onInterface(INTERFACE_NAME);
}
void PairableTimeout(const uint32_t& value)
{
proxy_.setProperty("PairableTimeout").onInterface(INTERFACE_NAME).toValue(value);
}
bool Discovering()
{
return proxy_.getProperty("Discovering").onInterface(INTERFACE_NAME);
}
std::vector<std::string> UUIDs()
{
return proxy_.getProperty("UUIDs").onInterface(INTERFACE_NAME);
}
std::string Modalias()
{
return proxy_.getProperty("Modalias").onInterface(INTERFACE_NAME);
}
std::vector<std::string> Roles()
{
return proxy_.getProperty("Roles").onInterface(INTERFACE_NAME);
}
std::vector<std::string> ExperimentalFeatures()
{
return proxy_.getProperty("ExperimentalFeatures").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,57 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__advertisement_monitor_manager_client_h__proxy__H__
#define __sdbuscpp__advertisement_monitor_manager_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class AdvertisementMonitorManager1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.AdvertisementMonitorManager1";
protected:
AdvertisementMonitorManager1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~AdvertisementMonitorManager1_proxy() = default;
public:
void RegisterMonitor(const sdbus::ObjectPath& application)
{
proxy_.callMethod("RegisterMonitor").onInterface(INTERFACE_NAME).withArguments(application);
}
void UnregisterMonitor(const sdbus::ObjectPath& application)
{
proxy_.callMethod("UnregisterMonitor").onInterface(INTERFACE_NAME).withArguments(application);
}
public:
std::vector<std::string> SupportedMonitorTypes()
{
return proxy_.getProperty("SupportedMonitorTypes").onInterface(INTERFACE_NAME);
}
std::vector<std::string> SupportedFeatures()
{
return proxy_.getProperty("SupportedFeatures").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,57 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__advertisement_monitor_server_h__adaptor__H__
#define __sdbuscpp__advertisement_monitor_server_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class AdvertisementMonitor1_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.AdvertisementMonitor1";
protected:
AdvertisementMonitor1_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("Release").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Release(); });
object_.registerMethod("Activate").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Activate(); });
object_.registerMethod("DeviceFound").onInterface(INTERFACE_NAME).withInputParamNames("device").implementedAs([this](const sdbus::ObjectPath& device){ return this->DeviceFound(device); });
object_.registerMethod("DeviceLost").onInterface(INTERFACE_NAME).withInputParamNames("device").implementedAs([this](const sdbus::ObjectPath& device){ return this->DeviceLost(device); });
object_.registerProperty("Type").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Type(); });
object_.registerProperty("RSSILowThreshold").onInterface(INTERFACE_NAME).withGetter([this](){ return this->RSSILowThreshold(); });
object_.registerProperty("RSSIHighThreshold").onInterface(INTERFACE_NAME).withGetter([this](){ return this->RSSIHighThreshold(); });
object_.registerProperty("RSSISamplingPeriod").onInterface(INTERFACE_NAME).withGetter([this](){ return this->RSSISamplingPeriod(); });
object_.registerProperty("Patterns").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Patterns(); });
}
~AdvertisementMonitor1_adaptor() = default;
private:
virtual void Release() = 0;
virtual void Activate() = 0;
virtual void DeviceFound(const sdbus::ObjectPath& device) = 0;
virtual void DeviceLost(const sdbus::ObjectPath& device) = 0;
private:
virtual std::string Type() = 0;
virtual int16_t RSSILowThreshold() = 0;
virtual int16_t RSSIHighThreshold() = 0;
virtual uint16_t RSSISamplingPeriod() = 0;
virtual std::vector<sdbus::Struct<uint8_t, uint8_t, std::vector<uint8_t>>> Patterns() = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif
@@ -0,0 +1,214 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__device_client_h__proxy__H__
#define __sdbuscpp__device_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class Device1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.Device1";
protected:
Device1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~Device1_proxy() = default;
virtual void onPairReply(const sdbus::Error* error) = 0;
public:
void Disconnect()
{
proxy_.callMethod("Disconnect").onInterface(INTERFACE_NAME);
}
void Connect()
{
proxy_.callMethod("Connect").onInterface(INTERFACE_NAME);
}
void ConnectProfile(const std::string& UUID)
{
proxy_.callMethod("ConnectProfile").onInterface(INTERFACE_NAME).withArguments(UUID);
}
void DisconnectProfile(const std::string& UUID)
{
proxy_.callMethod("DisconnectProfile").onInterface(INTERFACE_NAME).withArguments(UUID);
}
sdbus::PendingAsyncCall Pair()
{
return proxy_.callMethodAsync("Pair").onInterface(INTERFACE_NAME).uponReplyInvoke([this](const sdbus::Error* error){ this->onPairReply(error); });
}
void CancelPairing()
{
proxy_.callMethod("CancelPairing").onInterface(INTERFACE_NAME);
}
public:
std::string Address()
{
return proxy_.getProperty("Address").onInterface(INTERFACE_NAME);
}
std::string AddressType()
{
return proxy_.getProperty("AddressType").onInterface(INTERFACE_NAME);
}
std::string Name()
{
return proxy_.getProperty("Name").onInterface(INTERFACE_NAME);
}
std::string Alias()
{
return proxy_.getProperty("Alias").onInterface(INTERFACE_NAME);
}
void Alias(const std::string& value)
{
proxy_.setProperty("Alias").onInterface(INTERFACE_NAME).toValue(value);
}
uint32_t Class()
{
return proxy_.getProperty("Class").onInterface(INTERFACE_NAME);
}
uint16_t Appearance()
{
return proxy_.getProperty("Appearance").onInterface(INTERFACE_NAME);
}
std::string Icon()
{
return proxy_.getProperty("Icon").onInterface(INTERFACE_NAME);
}
bool Paired()
{
return proxy_.getProperty("Paired").onInterface(INTERFACE_NAME);
}
bool Bonded()
{
return proxy_.getProperty("Bonded").onInterface(INTERFACE_NAME);
}
bool Trusted()
{
return proxy_.getProperty("Trusted").onInterface(INTERFACE_NAME);
}
void Trusted(const bool& value)
{
proxy_.setProperty("Trusted").onInterface(INTERFACE_NAME).toValue(value);
}
bool Blocked()
{
return proxy_.getProperty("Blocked").onInterface(INTERFACE_NAME);
}
void Blocked(const bool& value)
{
proxy_.setProperty("Blocked").onInterface(INTERFACE_NAME).toValue(value);
}
bool LegacyPairing()
{
return proxy_.getProperty("LegacyPairing").onInterface(INTERFACE_NAME);
}
int16_t RSSI()
{
return proxy_.getProperty("RSSI").onInterface(INTERFACE_NAME);
}
bool Connected()
{
return proxy_.getProperty("Connected").onInterface(INTERFACE_NAME);
}
std::vector<std::string> UUIDs()
{
return proxy_.getProperty("UUIDs").onInterface(INTERFACE_NAME);
}
std::string Modalias()
{
return proxy_.getProperty("Modalias").onInterface(INTERFACE_NAME);
}
sdbus::ObjectPath Adapter()
{
return proxy_.getProperty("Adapter").onInterface(INTERFACE_NAME);
}
std::map<uint16_t, sdbus::Variant> ManufacturerData()
{
return proxy_.getProperty("ManufacturerData").onInterface(INTERFACE_NAME);
}
std::map<std::string, sdbus::Variant> ServiceData()
{
return proxy_.getProperty("ServiceData").onInterface(INTERFACE_NAME);
}
int16_t TxPower()
{
return proxy_.getProperty("TxPower").onInterface(INTERFACE_NAME);
}
bool ServicesResolved()
{
return proxy_.getProperty("ServicesResolved").onInterface(INTERFACE_NAME);
}
std::vector<uint8_t> AdvertisingFlags()
{
return proxy_.getProperty("AdvertisingFlags").onInterface(INTERFACE_NAME);
}
std::map<uint8_t, sdbus::Variant> AdvertisingData()
{
return proxy_.getProperty("AdvertisingData").onInterface(INTERFACE_NAME);
}
bool WakeAllowed()
{
return proxy_.getProperty("WakeAllowed").onInterface(INTERFACE_NAME);
}
void WakeAllowed(const bool& value)
{
proxy_.setProperty("WakeAllowed").onInterface(INTERFACE_NAME).toValue(value);
}
std::map<sdbus::ObjectPath, std::map<std::string, sdbus::Variant>> Sets()
{
return proxy_.getProperty("Sets").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,118 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__gatt_characteristic_client_h__proxy__H__
#define __sdbuscpp__gatt_characteristic_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class GattCharacteristic1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.GattCharacteristic1";
protected:
GattCharacteristic1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~GattCharacteristic1_proxy() = default;
public:
std::vector<uint8_t> ReadValue(const std::map<std::string, sdbus::Variant>& options)
{
std::vector<uint8_t> result;
proxy_.callMethod("ReadValue").onInterface(INTERFACE_NAME).withArguments(options).storeResultsTo(result);
return result;
}
void WriteValue(const std::vector<uint8_t>& value, const std::map<std::string, sdbus::Variant>& options)
{
proxy_.callMethod("WriteValue").onInterface(INTERFACE_NAME).withArguments(value, options);
}
std::tuple<sdbus::UnixFd, uint16_t> AcquireWrite(const std::map<std::string, sdbus::Variant>& options)
{
std::tuple<sdbus::UnixFd, uint16_t> result;
proxy_.callMethod("AcquireWrite").onInterface(INTERFACE_NAME).withArguments(options).storeResultsTo(result);
return result;
}
std::tuple<sdbus::UnixFd, uint16_t> AcquireNotify(const std::map<std::string, sdbus::Variant>& options)
{
std::tuple<sdbus::UnixFd, uint16_t> result;
proxy_.callMethod("AcquireNotify").onInterface(INTERFACE_NAME).withArguments(options).storeResultsTo(result);
return result;
}
void StartNotify()
{
proxy_.callMethod("StartNotify").onInterface(INTERFACE_NAME);
}
void StopNotify()
{
proxy_.callMethod("StopNotify").onInterface(INTERFACE_NAME);
}
public:
uint16_t Handle()
{
return proxy_.getProperty("Handle").onInterface(INTERFACE_NAME);
}
std::string UUID()
{
return proxy_.getProperty("UUID").onInterface(INTERFACE_NAME);
}
sdbus::ObjectPath Service()
{
return proxy_.getProperty("Service").onInterface(INTERFACE_NAME);
}
std::vector<uint8_t> Value()
{
return proxy_.getProperty("Value").onInterface(INTERFACE_NAME);
}
bool Notifying()
{
return proxy_.getProperty("Notifying").onInterface(INTERFACE_NAME);
}
std::vector<std::string> Flags()
{
return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME);
}
bool WriteAcquired()
{
return proxy_.getProperty("WriteAcquired").onInterface(INTERFACE_NAME);
}
bool NotifyAcquired()
{
return proxy_.getProperty("NotifyAcquired").onInterface(INTERFACE_NAME);
}
uint16_t MTU()
{
return proxy_.getProperty("MTU").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,59 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__generated_dbus_bluez_gatt_characteristic_server_h__adaptor__H__
#define __sdbuscpp__generated_dbus_bluez_gatt_characteristic_server_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class GattCharacteristic1_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.GattCharacteristic1";
protected:
GattCharacteristic1_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("ReadValue").onInterface(INTERFACE_NAME).withInputParamNames("options").withOutputParamNames("value").implementedAs([this](sdbus::Result<std::vector<uint8_t>>&& result, std::map<std::string, sdbus::Variant> options){ this->ReadValue(std::move(result), std::move(options)); });
object_.registerMethod("WriteValue").onInterface(INTERFACE_NAME).withInputParamNames("value", "options").implementedAs([this](sdbus::Result<>&& result, std::vector<uint8_t> value, std::map<std::string, sdbus::Variant> options){ this->WriteValue(std::move(result), std::move(value), std::move(options)); });
object_.registerMethod("StartNotify").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StartNotify(); });
object_.registerMethod("StopNotify").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StopNotify(); });
object_.registerMethod("Confirm").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Confirm(); });
object_.registerProperty("UUID").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUID(); });
object_.registerProperty("Service").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Service(); });
object_.registerProperty("Value").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Value(); });
object_.registerProperty("Notifying").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Notifying(); });
object_.registerProperty("Flags").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Flags(); });
}
~GattCharacteristic1_adaptor() = default;
private:
virtual void ReadValue(sdbus::Result<std::vector<uint8_t>>&& result, std::map<std::string, sdbus::Variant> options) = 0;
virtual void WriteValue(sdbus::Result<>&& result, std::vector<uint8_t> value, std::map<std::string, sdbus::Variant> options) = 0;
virtual void StartNotify() = 0;
virtual void StopNotify() = 0;
virtual void Confirm() = 0;
private:
virtual std::string UUID() = 0;
virtual sdbus::ObjectPath Service() = 0;
virtual std::vector<uint8_t> Value() = 0;
virtual bool Notifying() = 0;
virtual std::vector<std::string> Flags() = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif
@@ -0,0 +1,46 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__gatt_manager_client_h__proxy__H__
#define __sdbuscpp__gatt_manager_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class GattManager1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.GattManager1";
protected:
GattManager1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~GattManager1_proxy() = default;
public:
void RegisterApplication(const sdbus::ObjectPath& application, const std::map<std::string, sdbus::Variant>& options)
{
proxy_.callMethod("RegisterApplication").onInterface(INTERFACE_NAME).withArguments(application, options);
}
void UnregisterApplication(const sdbus::ObjectPath& application)
{
proxy_.callMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withArguments(application);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,56 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__gatt_service_client_h__proxy__H__
#define __sdbuscpp__gatt_service_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class GattService1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.GattService1";
protected:
GattService1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~GattService1_proxy() = default;
public:
std::string UUID()
{
return proxy_.getProperty("UUID").onInterface(INTERFACE_NAME);
}
bool Primary()
{
return proxy_.getProperty("Primary").onInterface(INTERFACE_NAME);
}
sdbus::ObjectPath Device()
{
return proxy_.getProperty("Device").onInterface(INTERFACE_NAME);
}
std::vector<sdbus::ObjectPath> Includes()
{
return proxy_.getProperty("Includes").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,45 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__generated_dbus_bluez_gatt_service_server_h__adaptor__H__
#define __sdbuscpp__generated_dbus_bluez_gatt_service_server_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class GattService1_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.GattService1";
protected:
GattService1_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerProperty("UUID").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUID(); });
object_.registerProperty("Primary").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Primary(); });
object_.registerProperty("Device").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Device(); });
object_.registerProperty("Includes").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Includes(); });
}
~GattService1_adaptor() = default;
private:
virtual std::string UUID() = 0;
virtual bool Primary() = 0;
virtual sdbus::ObjectPath Device() = 0;
virtual std::vector<sdbus::ObjectPath> Includes() = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif
@@ -0,0 +1,77 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__generated_dbus_bluez_le_advertisement_manager_client_h__proxy__H__
#define __sdbuscpp__generated_dbus_bluez_le_advertisement_manager_client_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class LEAdvertisingManager1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisingManager1";
protected:
LEAdvertisingManager1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~LEAdvertisingManager1_proxy() = default;
public:
void RegisterAdvertisement(const sdbus::ObjectPath& advertisement, const std::map<std::string, sdbus::Variant>& options)
{
proxy_.callMethod("RegisterAdvertisement").onInterface(INTERFACE_NAME).withArguments(advertisement, options);
}
void UnregisterAdvertisement(const sdbus::ObjectPath& service)
{
proxy_.callMethod("UnregisterAdvertisement").onInterface(INTERFACE_NAME).withArguments(service);
}
public:
uint8_t ActiveInstances()
{
return proxy_.getProperty("ActiveInstances").onInterface(INTERFACE_NAME);
}
uint8_t SupportedInstances()
{
return proxy_.getProperty("SupportedInstances").onInterface(INTERFACE_NAME);
}
std::vector<std::string> SupportedIncludes()
{
return proxy_.getProperty("SupportedIncludes").onInterface(INTERFACE_NAME);
}
std::vector<std::string> SupportedSecondaryChannels()
{
return proxy_.getProperty("SupportedSecondaryChannels").onInterface(INTERFACE_NAME);
}
std::vector<std::string> SupportedFeatures()
{
return proxy_.getProperty("SupportedFeatures").onInterface(INTERFACE_NAME);
}
std::map<std::string, sdbus::Variant> SupportedCapabilities()
{
return proxy_.getProperty("SupportedCapabilities").onInterface(INTERFACE_NAME);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif
@@ -0,0 +1,65 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__generated_dbus_bluez_le_advertisement_server_h__adaptor__H__
#define __sdbuscpp__generated_dbus_bluez_le_advertisement_server_h__adaptor__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class LEAdvertisement1_adaptor
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisement1";
protected:
LEAdvertisement1_adaptor(sdbus::IObject& object)
: object_(object)
{
object_.registerMethod("Release").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Release(); });
object_.registerProperty("Type").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Type(); });
object_.registerProperty("ServiceUUIDs").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ServiceUUIDs(); });
object_.registerProperty("ManufacturerData").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ManufacturerData(); });
object_.registerProperty("SolicitUUIDs").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SolicitUUIDs(); });
object_.registerProperty("ServiceData").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ServiceData(); });
object_.registerProperty("Includes").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Includes(); });
object_.registerProperty("LocalName").onInterface(INTERFACE_NAME).withGetter([this](){ return this->LocalName(); });
object_.registerProperty("Duration").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Duration(); });
object_.registerProperty("Timeout").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Timeout(); });
object_.registerProperty("MinInterval").onInterface(INTERFACE_NAME).withGetter([this](){ return this->MinInterval(); });
object_.registerProperty("MaxInterval").onInterface(INTERFACE_NAME).withGetter([this](){ return this->MaxInterval(); });
object_.registerProperty("TxPower").onInterface(INTERFACE_NAME).withGetter([this](){ return this->TxPower(); });
}
~LEAdvertisement1_adaptor() = default;
private:
virtual void Release() = 0;
private:
virtual std::string Type() = 0;
virtual std::vector<std::string> ServiceUUIDs() = 0;
virtual std::map<std::string, sdbus::Variant> ManufacturerData() = 0;
virtual std::vector<std::string> SolicitUUIDs() = 0;
virtual std::map<std::string, sdbus::Variant> ServiceData() = 0;
virtual std::vector<std::string> Includes() = 0;
virtual std::string LocalName() = 0;
virtual uint16_t Duration() = 0;
virtual uint16_t Timeout() = 0;
virtual uint32_t MinInterval() = 0;
virtual uint32_t MaxInterval() = 0;
virtual int16_t TxPower() = 0;
private:
sdbus::IObject& object_;
};
}} // namespaces
#endif
@@ -0,0 +1,36 @@
<?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.Adapter1">
<method name="StartDiscovery" />
<method name="SetDiscoveryFilter">
<arg name="properties" type="a{sv}" direction="in" />
</method>
<method name="StopDiscovery" />
<method name="RemoveDevice">
<arg name="device" type="o" direction="in" />
</method>
<method name="GetDiscoveryFilters">
<arg name="filters" type="as" direction="out" />
</method>
<method name="ConnectDevice">
<arg name="properties" type="a{sv}" direction="in" />
</method>
<property name="Address" type="s" access="read" />
<property name="AddressType" type="s" access="read" />
<property name="Name" type="s" access="read" />
<property name="Alias" type="s" access="readwrite" />
<property name="Class" type="u" access="read" />
<property name="Powered" type="b" access="readwrite" />
<property name="PowerState" type="s" access="read" />
<property name="Discoverable" type="b" access="readwrite" />
<property name="DiscoverableTimeout" type="u" access="readwrite" />
<property name="Pairable" type="b" access="readwrite" />
<property name="PairableTimeout" type="u" access="readwrite" />
<property name="Discovering" type="b" access="read" />
<property name="UUIDs" type="as" access="read" />
<property name="Modalias" type="s" access="read" />
<property name="Roles" type="as" access="read" />
<property name="ExperimentalFeatures" type="as" access="read" />
</interface>
</node>
@@ -0,0 +1,27 @@
<?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.AdvertisementMonitor1">
<method name="Release">
</method>
<method name="Activate">
</method>
<method name="DeviceFound">
<arg type='o' name="device" direction="in"/>
</method>
<method name="DeviceLost">
<arg type='o' name="device" direction="in"/>
</method>
<property name="Type" type="s" access="read">
</property>
<property name="RSSILowThreshold" type="n" access="read">
</property>
<property name="RSSIHighThreshold" type="n" access="read">
</property>
<property name="RSSISamplingPeriod" type="q" access="read">
</property>
<property name="Patterns" type="a(yyay)" access="read">
</property>
</interface>
</node>
@@ -0,0 +1,14 @@
<?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.AdvertisementMonitorManager1">
<method name="RegisterMonitor">
<arg name="application" type="o" direction="in" />
</method>
<method name="UnregisterMonitor">
<arg name="application" type="o" direction="in" />
</method>
<property name="SupportedMonitorTypes" type="as" access="read" />
<property name="SupportedFeatures" type="as" access="read" />
</interface>
</node>
@@ -0,0 +1,43 @@
<?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.Device1">
<method name="Disconnect" />
<method name="Connect" />
<method name="ConnectProfile">
<arg name="UUID" type="s" direction="in" />
</method>
<method name="DisconnectProfile">
<arg name="UUID" type="s" direction="in" />
</method>
<method name="Pair">
<annotation name="org.freedesktop.DBus.Method.Async" value="client"/>
</method>
<method name="CancelPairing" />
<property name="Address" type="s" access="read" />
<property name="AddressType" type="s" access="read" />
<property name="Name" type="s" access="read" />
<property name="Alias" type="s" access="readwrite" />
<property name="Class" type="u" access="read" />
<property name="Appearance" type="q" access="read" />
<property name="Icon" type="s" access="read" />
<property name="Paired" type="b" access="read" />
<property name="Bonded" type="b" access="read" />
<property name="Trusted" type="b" access="readwrite" />
<property name="Blocked" type="b" access="readwrite" />
<property name="LegacyPairing" type="b" access="read" />
<property name="RSSI" type="n" access="read" />
<property name="Connected" type="b" access="read" />
<property name="UUIDs" type="as" access="read" />
<property name="Modalias" type="s" access="read" />
<property name="Adapter" type="o" access="read" />
<property name="ManufacturerData" type="a{qv}" access="read" />
<property name="ServiceData" type="a{sv}" access="read" />
<property name="TxPower" type="n" access="read" />
<property name="ServicesResolved" type="b" access="read" />
<property name="AdvertisingFlags" type="ay" access="read" />
<property name="AdvertisingData" type="a{yv}" access="read" />
<property name="WakeAllowed" type="b" access="readwrite" />
<property name="Sets" type="a{oa{sv}}" access="read" />
</interface>
</node>
@@ -0,0 +1,35 @@
<?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.GattCharacteristic1">
<method name="ReadValue">
<arg name="options" type="a{sv}" direction="in" />
<arg name="value" type="ay" direction="out" />
</method>
<method name="WriteValue">
<arg name="value" type="ay" direction="in" />
<arg name="options" type="a{sv}" direction="in" />
</method>
<method name="AcquireWrite">
<arg name="options" type="a{sv}" direction="in" />
<arg name="fd" type="h" direction="out" />
<arg name="mtu" type="q" direction="out" />
</method>
<method name="AcquireNotify">
<arg name="options" type="a{sv}" direction="in" />
<arg name="fd" type="h" direction="out" />
<arg name="mtu" type="q" direction="out" />
</method>
<method name="StartNotify" />
<method name="StopNotify" />
<property name="Handle" type="q" access="read" />
<property name="UUID" type="s" access="read" />
<property name="Service" type="o" access="read" />
<property name="Value" type="ay" access="read" />
<property name="Notifying" type="b" access="read" />
<property name="Flags" type="as" access="read" />
<property name="WriteAcquired" type="b" access="read" />
<property name="NotifyAcquired" type="b" access="read" />
<property name="MTU" type="q" access="read" />
</interface>
</node>
@@ -0,0 +1,28 @@
<!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.GattCharacteristic1">
<method name="ReadValue">
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
<arg name="options" type="a{sv}" direction="in"/>
<arg name="value" type="ay" direction="out"/>
</method>
<method name="WriteValue">
<annotation name="org.freedesktop.DBus.Method.Async" value="server" />
<arg name="value" type="ay" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="StartNotify">
</method>
<method name="StopNotify">
</method>
<method name="Confirm">
</method>
<property name="UUID" type="s" access="read"></property>
<property name="Service" type="o" access="read"></property>
<property name="Value" type="ay" access="read"></property>
<property name="Notifying" type="b" access="read"></property>
<property name="Flags" type="as" access="read"></property>
</interface>
</node>
@@ -0,0 +1,13 @@
<?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.GattManager1">
<method name="RegisterApplication">
<arg name="application" type="o" direction="in" />
<arg name="options" type="a{sv}" direction="in" />
</method>
<method name="UnregisterApplication">
<arg name="application" type="o" direction="in" />
</method>
</interface>
</node>
@@ -0,0 +1,10 @@
<!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.GattService1">
<property name="UUID" type="s" access="read"></property>
<property name="Primary" type="b" access="read"></property>
<property name="Device" type="o" access="read"></property>
<property name="Includes" type="ao" access="read"></property>
</interface>
</node>
@@ -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.LEAdvertisement1">
<method name="Release"/>
<property name="Type" type="s" access="read"/>
<property name="ServiceUUIDs" type="as" access="read"/>
<property name="ManufacturerData" type="a{sv}" access="read"/>
<property name="SolicitUUIDs" type="as" access="read"/>
<property name="ServiceData" type="a{sv}" access="read"/>
<property name="Includes" type="as" access="read"/>
<property name="LocalName" type="s" access="read"/>
<property name="Duration" type="q" access="read"/>
<property name="Timeout" type="q" access="read"/>
<property name="MinInterval" type="u" access="read"/>
<property name="MaxInterval" type="u" access="read"/>
<property name="TxPower" type="n" access="read"/>
</interface>
</node>
@@ -0,0 +1,19 @@
<?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.LEAdvertisingManager1">
<method name="RegisterAdvertisement">
<arg name="advertisement" type="o" direction="in" />
<arg name="options" type="a{sv}" direction="in" />
</method>
<method name="UnregisterAdvertisement">
<arg name="service" type="o" direction="in" />
</method>
<property name="ActiveInstances" type="y" access="read" />
<property name="SupportedInstances" type="y" access="read" />
<property name="SupportedIncludes" type="as" access="read" />
<property name="SupportedSecondaryChannels" type="as" access="read" />
<property name="SupportedFeatures" type="as" access="read" />
<property name="SupportedCapabilities" type="a{sv}" access="read" />
</interface>
</node>
@@ -0,0 +1,17 @@
<!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.Profile1">
<method name='Release'>
</method>
<method name='NewConnection'>
<arg type='o' name='device' direction='in'/>
<arg type='h' name='fd' direction='in'/>
<arg type='a{sv}' name='fd_properties' direction='in'/>
</method>
<method name='RequestDisconnection'>
<arg type='o' name='device' direction='in'/>
</method>
</interface>
</node>
@@ -0,0 +1,14 @@
<?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.ProfileManager1">
<method name="RegisterProfile">
<arg name="profile" type="o" direction="in" />
<arg name="UUID" type="s" direction="in" />
<arg name="options" type="a{sv}" direction="in" />
</method>
<method name="UnregisterProfile">
<arg name="profile" type="o" direction="in" />
</method>
</interface>
</node>
@@ -0,0 +1,46 @@
/*
* This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT!
*/
#ifndef __sdbuscpp__bluez_profile_manager_client_glue_h__proxy__H__
#define __sdbuscpp__bluez_profile_manager_client_glue_h__proxy__H__
#include <sdbus-c++/sdbus-c++.h>
#include <string>
#include <tuple>
namespace org {
namespace bluez {
class ProfileManager1_proxy
{
public:
static constexpr const char* INTERFACE_NAME = "org.bluez.ProfileManager1";
protected:
ProfileManager1_proxy(sdbus::IProxy& proxy)
: proxy_(proxy)
{
}
~ProfileManager1_proxy() = default;
public:
void RegisterProfile(const sdbus::ObjectPath& profile, const std::string& UUID, const std::map<std::string, sdbus::Variant>& options)
{
proxy_.callMethod("RegisterProfile").onInterface(INTERFACE_NAME).withArguments(profile, UUID, options);
}
void UnregisterProfile(const sdbus::ObjectPath& profile)
{
proxy_.callMethod("UnregisterProfile").onInterface(INTERFACE_NAME).withArguments(profile);
}
private:
sdbus::IProxy& proxy_;
};
}} // namespaces
#endif

Some files were not shown because too many files have changed in this diff Show More