From 4c28a324d99d4d3790b307fc124a8531ec604bb6 Mon Sep 17 00:00:00 2001 From: Kevin Dewald Date: Sun, 29 Sep 2024 17:45:41 -0700 Subject: [PATCH] Crude example where advertising works --- examples/simplebluez/CMakeLists.txt | 1 + examples/simplebluez/advertise/CMakeLists.txt | 7 + examples/simplebluez/advertise/advertise.cpp | 64 +++++++ simplebluez/CMakeLists.txt | 4 + simplebluez/include/simplebluez/Adapter.h | 4 + simplebluez/include/simplebluez/Bluez.h | 3 + .../simplebluez/interfaces/LEAdvertisement1.h | 53 ++++++ .../interfaces/LEAdvertisingManager1.h | 35 ++++ simplebluez/src/Adapter.cpp | 16 +- simplebluez/src/Bluez.cpp | 17 +- .../src/interfaces/LEAdvertisement1.cpp | 175 ++++++++++++++++++ .../src/interfaces/LEAdvertisingManager1.cpp | 60 ++++++ .../simpledbus/advanced/LocalInterface.h | 1 + .../include/simpledbus/advanced/LocalProxy.h | 3 + simpledbus/src/advanced/LocalInterface.cpp | 10 + simpledbus/src/advanced/LocalProxy.cpp | 78 +++++++- simpledbus/src/base/Exceptions.cpp | 7 + 17 files changed, 533 insertions(+), 5 deletions(-) create mode 100644 examples/simplebluez/advertise/CMakeLists.txt create mode 100644 examples/simplebluez/advertise/advertise.cpp create mode 100644 simplebluez/include/simplebluez/interfaces/LEAdvertisement1.h create mode 100644 simplebluez/include/simplebluez/interfaces/LEAdvertisingManager1.h create mode 100644 simplebluez/src/interfaces/LEAdvertisement1.cpp create mode 100644 simplebluez/src/interfaces/LEAdvertisingManager1.cpp diff --git a/examples/simplebluez/CMakeLists.txt b/examples/simplebluez/CMakeLists.txt index 85c55ca0..e3c1b1be 100644 --- a/examples/simplebluez/CMakeLists.txt +++ b/examples/simplebluez/CMakeLists.txt @@ -15,6 +15,7 @@ else() find_package(simplebluez CONFIG REQUIRED) endif() +add_subdirectory(advertise) add_subdirectory(list_adapters) add_subdirectory(list_paired) add_subdirectory(scan) diff --git a/examples/simplebluez/advertise/CMakeLists.txt b/examples/simplebluez/advertise/CMakeLists.txt new file mode 100644 index 00000000..e2e3d37b --- /dev/null +++ b/examples/simplebluez/advertise/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.21) + +project(EXAMPLE_ADVERTISE) + +message("-- [INFO] Building Example") +add_executable(example_advertise advertise.cpp) +target_link_libraries(example_advertise simplebluez::simplebluez pthread) diff --git a/examples/simplebluez/advertise/advertise.cpp b/examples/simplebluez/advertise/advertise.cpp new file mode 100644 index 00000000..1b412141 --- /dev/null +++ b/examples/simplebluez/advertise/advertise.cpp @@ -0,0 +1,64 @@ +#include + +#include +#include +#include +#include +#include +#include + +SimpleBluez::Bluez bluez; + +std::atomic_bool async_thread_active = true; +void async_thread_function() { + while (async_thread_active) { + bluez.run_async(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } +} + +void millisecond_delay(int ms) { + for (int i = 0; i < ms; i++) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +int main(int argc, char* argv[]) { + int selection = -1; + + bluez.init(); + std::thread* async_thread = new std::thread(async_thread_function); + + auto adapters = bluez.get_adapters(); + std::cout << "The following adapters were found:" << std::endl; + for (int i = 0; i < adapters.size(); i++) { + std::cout << "[" << i << "] " << adapters[i]->identifier() << " [" << adapters[i]->address() << "]" + << std::endl; + } + + // std::cout << "Please select an adapter to advertise: "; + // std::cin >> selection; + // if (selection < 0 || selection >= adapters.size()) { + // std::cout << "Invalid selection" << std::endl; + // return 1; + // } + + auto adapter = adapters[0]; + std::cout << "Advertising on " << adapter->identifier() << " [" << adapter->address() << "]" << std::endl; + + adapter->register_le_advertisement("/simplebluez"); + + // Sleep for a bit to allow the adapter to stop discovering. + millisecond_delay(30000); + + adapter->unregister_le_advertisement("/simplebluez"); + + async_thread_active = false; + while (!async_thread->joinable()) { + millisecond_delay(10); + } + async_thread->join(); + delete async_thread; + + return 0; +} diff --git a/simplebluez/CMakeLists.txt b/simplebluez/CMakeLists.txt index 0ad60139..e24aa40e 100644 --- a/simplebluez/CMakeLists.txt +++ b/simplebluez/CMakeLists.txt @@ -63,10 +63,14 @@ set(SIMPLEBLUEZ_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/interfaces/Device1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/interfaces/Battery1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/interfaces/AgentManager1.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/interfaces/LEAdvertisement1.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/interfaces/LEAdvertisingManager1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/ProxyBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/InterfaceBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/RemoteInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/RemoteProxy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/LocalInterface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/advanced/LocalProxy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/base/Connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/base/Exceptions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../simpledbus/src/base/Holder.cpp diff --git a/simplebluez/include/simplebluez/Adapter.h b/simplebluez/include/simplebluez/Adapter.h index 2ed1ffe9..4356e3dd 100644 --- a/simplebluez/include/simplebluez/Adapter.h +++ b/simplebluez/include/simplebluez/Adapter.h @@ -4,6 +4,7 @@ #include #include +#include #include @@ -33,11 +34,14 @@ class Adapter : public SimpleDBus::RemoteProxy { void set_on_device_updated(std::function device)> callback); void clear_on_device_updated(); + void register_le_advertisement(const std::string& advertisement_path); + void unregister_le_advertisement(const std::string& advertisement_path); private: std::shared_ptr path_create(const std::string& path) override; std::shared_ptr interfaces_create(const std::string& interface_name) override; std::shared_ptr adapter1(); + std::shared_ptr le_advertising_manager1(); }; } // namespace SimpleBluez diff --git a/simplebluez/include/simplebluez/Bluez.h b/simplebluez/include/simplebluez/Bluez.h index 643db269..bd7c100d 100644 --- a/simplebluez/include/simplebluez/Bluez.h +++ b/simplebluez/include/simplebluez/Bluez.h @@ -1,6 +1,8 @@ #pragma once #include +#include + #include #include @@ -28,6 +30,7 @@ class Bluez : public SimpleDBus::RemoteProxy { std::shared_ptr object_manager(); std::shared_ptr _agent; + std::shared_ptr _simplebluez_root_proxy; }; } // namespace SimpleBluez diff --git a/simplebluez/include/simplebluez/interfaces/LEAdvertisement1.h b/simplebluez/include/simplebluez/interfaces/LEAdvertisement1.h new file mode 100644 index 00000000..59eca045 --- /dev/null +++ b/simplebluez/include/simplebluez/interfaces/LEAdvertisement1.h @@ -0,0 +1,53 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace SimpleBluez { + +class LEAdvertisement1 : public SimpleDBus::LocalInterface { + public: + // ----- CONSTRUCTORS ----- + + LEAdvertisement1(std::shared_ptr conn, std::string path); + virtual ~LEAdvertisement1() = default; + + // ----- PROPERTIES ----- + + void SetType(const std::string& type); + void SetServiceUUIDs(const std::vector& uuids); + void SetManufacturerData(const std::map>& data); + void SetServiceData(const std::map>& data); + void SetSolicitUUIDs(const std::vector& uuids); + void SetData(const std::map>& data); + void SetDiscoverable(bool discoverable); + void SetDiscoverableTimeout(uint16_t timeout); + void SetIncludes(const std::vector& includes); + void SetLocalName(const std::string& name); + void SetAppearance(uint16_t appearance); + void SetDuration(uint16_t duration); + void SetTimeout(uint16_t timeout); + void SetSecondaryChannel(const std::string& channel); + void SetMinInterval(uint32_t interval); + void SetMaxInterval(uint32_t interval); + void SetTxPower(int16_t power); + void SetIncludeTxPower(bool include); + + // ----- METHODS ----- + + void Release(); + + protected: + void message_handle(SimpleDBus::Message& msg) override; + + private: + // You might want to add private member variables to store the property values + // std::string _type; + // std::vector _serviceUUIDs; + // ... (other properties) +}; + +} // namespace SimpleBluez \ No newline at end of file diff --git a/simplebluez/include/simplebluez/interfaces/LEAdvertisingManager1.h b/simplebluez/include/simplebluez/interfaces/LEAdvertisingManager1.h new file mode 100644 index 00000000..32367a93 --- /dev/null +++ b/simplebluez/include/simplebluez/interfaces/LEAdvertisingManager1.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include +#include +#include + +namespace SimpleBluez { + +class LEAdvertisingManager1 : public SimpleDBus::RemoteInterface { + + public: + + // ----- TYPES ----- + + // ----- CONSTRUCTORS ----- + + LEAdvertisingManager1(std::shared_ptr conn, std::string path); + virtual ~LEAdvertisingManager1() = default; + + // ----- METHODS ----- + + void RegisterAdvertisement(std::string advertisement_path); + void UnregisterAdvertisement(std::string advertisement_path); + + // ----- PROPERTIES ----- + + uint8_t ActiveInstances(bool refresh = true); + uint8_t SupportedInstances(bool refresh = true); + std::vector SupportedIncludes(bool refresh = true); + + protected: + void property_changed(std::string option_name) override; +}; + +} // namespace SimpleBluez \ No newline at end of file diff --git a/simplebluez/src/Adapter.cpp b/simplebluez/src/Adapter.cpp index da3edbe8..929c0bde 100644 --- a/simplebluez/src/Adapter.cpp +++ b/simplebluez/src/Adapter.cpp @@ -1,8 +1,6 @@ #include #include -#include - using namespace SimpleBluez; Adapter::Adapter(std::shared_ptr conn, const std::string& bus_name, const std::string& path) @@ -18,6 +16,8 @@ std::shared_ptr Adapter::path_create(const std::string& std::shared_ptr Adapter::interfaces_create(const std::string& interface_name) { if (interface_name == "org.bluez.Adapter1") { return std::static_pointer_cast(std::make_shared(_conn, _path)); + } else if (interface_name == "org.bluez.LEAdvertisingManager1") { + return std::static_pointer_cast(std::make_shared(_conn, _path)); } auto interface = std::make_shared(_conn, _bus_name, _path, interface_name); @@ -28,6 +28,10 @@ std::shared_ptr Adapter::adapter1() { return std::dynamic_pointer_cast(interface_get("org.bluez.Adapter1")); } +std::shared_ptr Adapter::le_advertising_manager1() { + return std::dynamic_pointer_cast(interface_get("org.bluez.LEAdvertisingManager1")); +} + std::string Adapter::identifier() const { std::size_t start = _path.find_last_of("/"); return _path.substr(start + 1); @@ -85,3 +89,11 @@ void Adapter::clear_on_device_updated() { on_child_created.unload(); on_child_signal_received.unload(); } + +void Adapter::register_le_advertisement(const std::string& advertisement_path) { + le_advertising_manager1()->RegisterAdvertisement(advertisement_path); +} + +void Adapter::unregister_le_advertisement(const std::string& advertisement_path) { + le_advertising_manager1()->UnregisterAdvertisement(advertisement_path); +} diff --git a/simplebluez/src/Bluez.cpp b/simplebluez/src/Bluez.cpp index 58ccaf35..69bd7e28 100644 --- a/simplebluez/src/Bluez.cpp +++ b/simplebluez/src/Bluez.cpp @@ -1,7 +1,7 @@ #include #include #include - +#include #include using namespace SimpleBluez; @@ -20,6 +20,12 @@ Bluez::Bluez() : RemoteProxy(std::make_shared(DBUS_BUS), object_manager()->InterfacesRemoved = [&](std::string path, SimpleDBus::Holder options) { path_remove(path, options); }; + + _simplebluez_root_proxy = std::make_shared(_conn, "org.simplebluez", "/simplebluez"); + + auto advertisement = std::make_shared(_conn, "/simplebluez"); + _simplebluez_root_proxy->interfaces_load("org.bluez.LEAdvertisement1", + std::static_pointer_cast(advertisement)); } Bluez::~Bluez() { @@ -48,7 +54,14 @@ void Bluez::run_async() { _conn->read_write(); SimpleDBus::Message message = _conn->pop_message(); while (message.is_valid()) { - message_forward(message); + std::cout << "Message: " << message.to_string() << " on path " << message.get_path() << std::endl; + + if (_simplebluez_root_proxy->path_belongs(message.get_path())) { + _simplebluez_root_proxy->message_handle(message); + } else { + message_forward(message); + } + message = _conn->pop_message(); } } diff --git a/simplebluez/src/interfaces/LEAdvertisement1.cpp b/simplebluez/src/interfaces/LEAdvertisement1.cpp new file mode 100644 index 00000000..5924076c --- /dev/null +++ b/simplebluez/src/interfaces/LEAdvertisement1.cpp @@ -0,0 +1,175 @@ +#include "simplebluez/interfaces/LEAdvertisement1.h" + +using namespace SimpleBluez; + +LEAdvertisement1::LEAdvertisement1(std::shared_ptr conn, std::string path) + : SimpleDBus::LocalInterface(conn, "org.bluez", path, "org.bluez.LEAdvertisement1") { + + // Set all fields to null or empty values + SetType("peripheral"); // This is the only required field, so we set a default + // SetServiceUUIDs({}); // Empty vector + // SetManufacturerData({}); // Empty map + // SetServiceData({}); // Empty map + // SetSolicitUUIDs({}); // Empty vector + // SetData({}); // Empty map + // SetDiscoverable(false); + // SetDiscoverableTimeout(0); + // SetIncludes({}); // Empty vector + SetLocalName("SimpleBluez"); + // SetAppearance(0); + // SetDuration(180); + SetTimeout(180); + // SetSecondaryChannel(""); + // SetMinInterval(0); + // SetMaxInterval(0); + // SetTxPower(0); + // SetIncludeTxPower(false); +} + +void LEAdvertisement1::SetType(const std::string& type) { + _properties["Type"] = SimpleDBus::Holder::create_string(type); +} + +void LEAdvertisement1::SetServiceUUIDs(const std::vector& uuids) { + SimpleDBus::Holder uuids_holder = SimpleDBus::Holder::create_array(); + for (const auto& uuid : uuids) { + uuids_holder.array_append(SimpleDBus::Holder::create_string(uuid)); + } + _properties["ServiceUUIDs"] = uuids_holder; +} + +void LEAdvertisement1::SetManufacturerData(const std::map>& data) { + SimpleDBus::Holder data_holder = SimpleDBus::Holder::create_dict(); + for (const auto& [key, value] : data) { + SimpleDBus::Holder value_holder = SimpleDBus::Holder::create_array(); + for (uint8_t byte : value) { + value_holder.array_append(SimpleDBus::Holder::create_byte(byte)); + } + data_holder.dict_append(SimpleDBus::Holder::Type::UINT16, key, value_holder); + } + _properties["ManufacturerData"] = data_holder; +} + +void LEAdvertisement1::SetServiceData(const std::map>& data) { + SimpleDBus::Holder data_holder = SimpleDBus::Holder::create_dict(); + for (const auto& [key, value] : data) { + SimpleDBus::Holder value_holder = SimpleDBus::Holder::create_array(); + for (uint8_t byte : value) { + value_holder.array_append(SimpleDBus::Holder::create_byte(byte)); + } + data_holder.dict_append(SimpleDBus::Holder::Type::STRING, key, value_holder); + } + _properties["ServiceData"] = data_holder; +} + +void LEAdvertisement1::SetIncludeTxPower(bool include) { + _properties["IncludeTxPower"] = SimpleDBus::Holder::create_boolean(include); +} + +void LEAdvertisement1::Release() { + // This method is called when the advertisement is unregistered + // Implement any cleanup logic here +} + +#include + +void LEAdvertisement1::message_handle(SimpleDBus::Message& msg) { + std::cout << "LEAdvertisement1::message_handle: " << msg.to_string() << std::endl; + // if (msg.get_type() == SimpleDBus::Message::Type::METHOD_CALL) { + // SimpleDBus::Message reply = SimpleDBus::Message::create_method_return(msg); + + // if (msg.get_member() == "GetAll" && msg.get_interface() == "org.freedesktop.DBus.Properties") { + // SimpleDBus::Holder properties = SimpleDBus::Holder::create_dict(); + // for (const auto& [key, value] : _properties) { + // properties.dict_append(SimpleDBus::Holder::Type::STRING, key, value); + // } + // reply.append_argument(properties, "a{sv}"); + // } else if (msg.get_member() == "Get" && msg.get_interface() == "org.freedesktop.DBus.Properties") { + // std::string interface_name = msg.extract().get_string(); + // std::string property_name = msg.extract().get_string(); + // if (_properties.find(property_name) != _properties.end()) { + // reply.append_argument(_properties[property_name], "v"); + // } else { + // // Property not found, create an error reply + // reply = SimpleDBus::Message::create_error(msg, "org.freedesktop.DBus.Error.UnknownProperty", "Unknown property: " + property_name); + // } + // } else if (msg.get_member() == "Release") { + // Release(); + // } else { + // // Unknown method, create an error reply + // reply = SimpleDBus::Message::create_error(msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method: " + msg.get_member()); + // } + + // _conn->send(reply); + // } +} + +void LEAdvertisement1::SetSolicitUUIDs(const std::vector& uuids) { + SimpleDBus::Holder uuids_holder = SimpleDBus::Holder::create_array(); + for (const auto& uuid : uuids) { + uuids_holder.array_append(SimpleDBus::Holder::create_string(uuid)); + } + _properties["SolicitUUIDs"] = uuids_holder; +} + +void LEAdvertisement1::SetData(const std::map>& data) { + SimpleDBus::Holder data_holder = SimpleDBus::Holder::create_dict(); + for (const auto& [key, value] : data) { + SimpleDBus::Holder value_holder = SimpleDBus::Holder::create_array(); + for (uint8_t byte : value) { + value_holder.array_append(SimpleDBus::Holder::create_byte(byte)); + } + data_holder.dict_append(SimpleDBus::Holder::Type::BYTE, key, value_holder); + } + _properties["Data"] = data_holder; +} + +void LEAdvertisement1::SetDiscoverable(bool discoverable) { + _properties["Discoverable"] = SimpleDBus::Holder::create_boolean(discoverable); +} + +void LEAdvertisement1::SetDiscoverableTimeout(uint16_t timeout) { + _properties["DiscoverableTimeout"] = SimpleDBus::Holder::create_uint16(timeout); +} + +void LEAdvertisement1::SetIncludes(const std::vector& includes) { + SimpleDBus::Holder includes_holder = SimpleDBus::Holder::create_array(); + for (const auto& include : includes) { + includes_holder.array_append(SimpleDBus::Holder::create_string(include)); + } + _properties["Includes"] = includes_holder; +} + +void LEAdvertisement1::SetLocalName(const std::string& name) { + _properties["LocalName"] = SimpleDBus::Holder::create_string(name); +} + +void LEAdvertisement1::SetAppearance(uint16_t appearance) { + _properties["Appearance"] = SimpleDBus::Holder::create_uint16(appearance); +} + +void LEAdvertisement1::SetDuration(uint16_t duration) { + _properties["Duration"] = SimpleDBus::Holder::create_uint16(duration); +} + +void LEAdvertisement1::SetTimeout(uint16_t timeout) { + _properties["Timeout"] = SimpleDBus::Holder::create_uint16(timeout); +} + +void LEAdvertisement1::SetSecondaryChannel(const std::string& channel) { + _properties["SecondaryChannel"] = SimpleDBus::Holder::create_string(channel); +} + +void LEAdvertisement1::SetMinInterval(uint32_t interval) { + _properties["MinInterval"] = SimpleDBus::Holder::create_uint32(interval); +} + +void LEAdvertisement1::SetMaxInterval(uint32_t interval) { + _properties["MaxInterval"] = SimpleDBus::Holder::create_uint32(interval); +} + +void LEAdvertisement1::SetTxPower(int16_t power) { + _properties["TxPower"] = SimpleDBus::Holder::create_int16(power); +} + +// ... existing code ... \ No newline at end of file diff --git a/simplebluez/src/interfaces/LEAdvertisingManager1.cpp b/simplebluez/src/interfaces/LEAdvertisingManager1.cpp new file mode 100644 index 00000000..2983f84f --- /dev/null +++ b/simplebluez/src/interfaces/LEAdvertisingManager1.cpp @@ -0,0 +1,60 @@ +#include "simplebluez/interfaces/LEAdvertisingManager1.h" + +using namespace SimpleBluez; + +LEAdvertisingManager1::LEAdvertisingManager1(std::shared_ptr conn, std::string path) + : SimpleDBus::RemoteInterface(conn, "org.bluez", path, "org.bluez.LEAdvertisingManager1") {} + +void LEAdvertisingManager1::RegisterAdvertisement(std::string advertisement_path) { + SimpleDBus::Holder properties = SimpleDBus::Holder::create_dict(); + + // NOTE: The current documentation doesn't specify any options. Using a placeholder for now. + + auto msg = create_method_call("RegisterAdvertisement"); + msg.append_argument(SimpleDBus::Holder::create_object_path(advertisement_path), "o"); + msg.append_argument(properties, "a{sv}"); + + // TODO: We need a way to get an async reply for this. + _conn->send(msg); +} + +void LEAdvertisingManager1::UnregisterAdvertisement(std::string advertisement_path) { + auto msg = create_method_call("UnregisterAdvertisement"); + msg.append_argument(SimpleDBus::Holder::create_object_path(advertisement_path), "o"); + _conn->send_with_reply_and_block(msg); +} + +uint8_t LEAdvertisingManager1::ActiveInstances(bool refresh) { + if (refresh) { + property_refresh("ActiveInstances"); + } + + std::scoped_lock lock(_property_update_mutex); + return _properties["ActiveInstances"].get_byte(); +} + +uint8_t LEAdvertisingManager1::SupportedInstances(bool refresh) { + if (refresh) { + property_refresh("SupportedInstances"); + } + + std::scoped_lock lock(_property_update_mutex); + return _properties["SupportedInstances"].get_byte(); +} + +std::vector LEAdvertisingManager1::SupportedIncludes(bool refresh) { + if (refresh) { + property_refresh("SupportedIncludes"); + } + + std::scoped_lock lock(_property_update_mutex); + std::vector result; + for (auto& item : _properties["SupportedIncludes"].get_array()) { + result.push_back(item.get_string()); + } + + return result; +} + + +void LEAdvertisingManager1::property_changed(std::string option_name) {} \ No newline at end of file diff --git a/simpledbus/include/simpledbus/advanced/LocalInterface.h b/simpledbus/include/simpledbus/advanced/LocalInterface.h index 216719c5..99439190 100644 --- a/simpledbus/include/simpledbus/advanced/LocalInterface.h +++ b/simpledbus/include/simpledbus/advanced/LocalInterface.h @@ -23,6 +23,7 @@ class LocalInterface : public InterfaceBase { Message create_method_call(const std::string& method_name); // ----- PROPERTIES ----- + Holder property_get_all(); Holder property_get(const std::string& property_name); void property_set(const std::string& property_name, const Holder& value); diff --git a/simpledbus/include/simpledbus/advanced/LocalProxy.h b/simpledbus/include/simpledbus/advanced/LocalProxy.h index 349ccf67..bbdbf3b4 100644 --- a/simpledbus/include/simpledbus/advanced/LocalProxy.h +++ b/simpledbus/include/simpledbus/advanced/LocalProxy.h @@ -36,6 +36,7 @@ class LocalProxy : public ProxyBase { void interfaces_unload(const std::string& interface_name); // ----- CHILD HANDLING ----- + bool path_belongs(const std::string& path); void path_add(const std::string& path, std::shared_ptr child); bool path_remove(const std::string& path); bool path_prune(); @@ -78,6 +79,8 @@ class LocalProxy : public ProxyBase { std::recursive_mutex _interface_access_mutex; std::recursive_mutex _child_access_mutex; + + SimpleDBus::Holder _collect_managed_objects(const std::string& base_path); }; } // namespace SimpleDBus \ No newline at end of file diff --git a/simpledbus/src/advanced/LocalInterface.cpp b/simpledbus/src/advanced/LocalInterface.cpp index c4141310..7e838fb9 100644 --- a/simpledbus/src/advanced/LocalInterface.cpp +++ b/simpledbus/src/advanced/LocalInterface.cpp @@ -17,6 +17,16 @@ Message LocalInterface::create_method_call(const std::string& method_name) { // ----- PROPERTIES ----- +Holder LocalInterface::property_get_all() { + _property_update_mutex.lock(); + Holder result = Holder::create_dict(); + for (const auto& [name, value] : _properties) { + result.dict_append(Holder::Type::STRING, name, value); + } + _property_update_mutex.unlock(); + return result; +} + Holder LocalInterface::property_get(const std::string& property_name) { _property_update_mutex.lock(); auto property = _properties.find(property_name); diff --git a/simpledbus/src/advanced/LocalProxy.cpp b/simpledbus/src/advanced/LocalProxy.cpp index 3d620c09..6e818d10 100644 --- a/simpledbus/src/advanced/LocalProxy.cpp +++ b/simpledbus/src/advanced/LocalProxy.cpp @@ -60,6 +60,10 @@ void LocalProxy::interfaces_unload(const std::string& interface_name) { // ----- CHILD HANDLING ----- +bool LocalProxy::path_belongs(const std::string& path) { + return _path == path || Path::is_descendant(_path, path); +} + bool LocalProxy::path_exists(const std::string& path) { std::scoped_lock lock(_child_access_mutex); return _children.find(path) != _children.end(); @@ -170,9 +174,56 @@ void LocalProxy::message_handle(Message& msg) { if (interface_exists(msg.get_interface())) { interface_get(msg.get_interface())->message_handle(msg); return; + } else if (msg.is_method_call("org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) { + SimpleDBus::Holder result = _collect_managed_objects(_path); + + std::cout << "Sending managed objects: " << result.represent() << std::endl; + + SimpleDBus::Message reply = SimpleDBus::Message::create_method_return(msg); + reply.append_argument(result, "a{oa{sa{sv}}}"); + _conn->send(reply); + + std::cout << "Sent managed objects" << std::endl; + return; + } else if (msg.is_method_call("org.freedesktop.DBus.Properties", "GetAll")) { + Holder interface_h = msg.extract(); + std::string iface_name = interface_h.get_string(); + std::cout << "GetAll: " << iface_name << std::endl; + + SimpleDBus::Holder result = interface_get(iface_name)->property_get_all(); + + std::cout << "Result: " << result.represent() << std::endl; + + SimpleDBus::Message reply = SimpleDBus::Message::create_method_return(msg); + reply.append_argument(result, "a{sv}"); + _conn->send(reply); + return; + } else if (msg.is_method_call("org.freedesktop.DBus.Properties", "Set")) { + Holder interface_h = msg.extract(); + std::string iface_name = interface_h.get_string(); + msg.extract_next(); + + Holder property_h = msg.extract(); + std::string property_name = property_h.get_string(); + msg.extract_next(); + + Holder value_h = msg.extract(); + + std::cout << "Set: " << iface_name << "." << property_name << " = " << value_h.represent() << std::endl; + + + + // SimpleDBus::Holder result = interface_get(iface_name)->property_get_all(); + + // std::cout << "Result: " << result.represent() << std::endl; + + // SimpleDBus::Message reply = SimpleDBus::Message::create_method_return(msg); + // reply.append_argument(result, "a{sv}"); + // _conn->send(reply); + return; } - std::cout << "LocalProxy::Unhandled message: " << msg.get_path() << std::endl; + std::cout << "LocalProxy::Unhandled message: " << msg.to_string() << std::endl; } void LocalProxy::message_forward(Message& msg) { @@ -198,3 +249,28 @@ void LocalProxy::message_forward(Message& msg) { } } } + + +SimpleDBus::Holder LocalProxy::_collect_managed_objects(const std::string& base_path) { + SimpleDBus::Holder result = SimpleDBus::Holder::create_dict(); + SimpleDBus::Holder interfaces = SimpleDBus::Holder::create_dict(); + + for (const auto& [interface_name, interface_ptr] : _interfaces) { + SimpleDBus::Holder properties = interface_ptr->property_get_all(); + interfaces.dict_append(SimpleDBus::Holder::Type::STRING, interface_name, std::move(properties)); + } + + if (!interfaces.get_dict_string().empty()) { + result.dict_append(SimpleDBus::Holder::Type::OBJ_PATH, _path, std::move(interfaces)); + } + + for (const auto& [child_path, child] : _children) { + SimpleDBus::Holder child_result = child->_collect_managed_objects(base_path); + // Merge child_result into result + for (auto&& [path, child_interfaces] : child_result.get_dict_object_path()) { + result.dict_append(SimpleDBus::Holder::Type::OBJ_PATH, std::move(path), std::move(child_interfaces)); + } + } + + return std::move(result); +} \ No newline at end of file diff --git a/simpledbus/src/base/Exceptions.cpp b/simpledbus/src/base/Exceptions.cpp index acb1fe73..950bfd3f 100644 --- a/simpledbus/src/base/Exceptions.cpp +++ b/simpledbus/src/base/Exceptions.cpp @@ -34,6 +34,13 @@ PathNotFoundException::PathNotFoundException(const std::string& path, const std: const char* PathNotFoundException::what() const noexcept { return _message.c_str(); } +PropertyNotFoundException::PropertyNotFoundException(const std::string& path, const std::string& interface, const std::string& property) { + _message = fmt::format("Property {} not found in interface {} at path {}", property, interface, path); +} + +const char* PropertyNotFoundException::what() const noexcept { return _message.c_str(); } + + } // namespace Exception } // namespace SimpleDBus