Introduction:
In our previous blog, we delved into DBus methods like Get
and GetAll
, unraveling the complexities of DBus type systems and how to decode them effectively. These methods allowed us to query the state of various properties on DBus interfaces, but this was just the beginning. Now, we’re going to take things a step further by moving from static queries to dynamic event handling. This shift opens up new possibilities for real-time interaction with Bluetooth devices and other system components.
Today, we’ll focus on one of the key features of DBus: asynchronous signal handling. Specifically, we’ll explore how the PropertiesChanged
signal works, allowing us to receive automatic updates whenever a property changes on the BlueZ Bluetooth adapter or related interfaces. This is particularly useful for scenarios like connecting to a new Bluetooth device, where we’ll receive notifications immediately when the connection is established or when any property is updated.
PropertiesChanged:
As always, we will start with code,
#include <gio/gio.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define BLUEZ_BUS_NAME "org.bluez" #define ADAPTER_INTERFACE "org.bluez.Adapter1" #define ADAPTER_OBJECT_PATH "/org/bluez/hci0" #define PROPERTIES_CHANGED_SIGNAL "PropertiesChanged" #define ALIAS_PROPERTY "Alias" static void on_properties_changed(GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { (void)connection; (void)sender_name; (void)object_path; (void)interface_name; (void)signal_name; (void)user_data; GVariantIter *properties_iter; const gchar *key; GVariant *value; g_variant_get(parameters, "(sa{sv}as)", NULL, &properties_iter, NULL); while (g_variant_iter_next(properties_iter, "{sv}", &key, &value)) { if (g_strcmp0(key, ALIAS_PROPERTY) == 0) { const gchar *alias; alias = g_variant_get_string(value, NULL); printf("Property Changed: %s -> %s\n", key, alias); } g_variant_unref(value); } g_variant_iter_free(properties_iter); } static void set_alias_property(GDBusConnection *connection, const gchar *alias) { GError *error = NULL; GVariant *result; result = g_dbus_connection_call_sync(connection, BLUEZ_BUS_NAME, ADAPTER_OBJECT_PATH, "org.freedesktop.DBus.Properties", "Set", g_variant_new("(ssv)", ADAPTER_INTERFACE, ALIAS_PROPERTY, g_variant_new_string(alias)), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error != NULL) { fprintf(stderr, "Error setting alias: %s\n", error->message); g_clear_error(&error); } else printf("Alias set to: %s\n", alias); g_variant_unref(result); } int main(int argc, char **argv) { GDBusConnection *connection; GError *error = NULL; guint subscription_id; if (argc != 2) { fprintf(stderr, "%s <alias name>", argv[0]); return 1; } connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (error != NULL) { fprintf(stderr, "Error connecting to system bus: %s\n", error->message); g_clear_error(&error); return 1; } subscription_id = g_dbus_connection_signal_subscribe(connection, BLUEZ_BUS_NAME, "org.freedesktop.DBus.Properties", PROPERTIES_CHANGED_SIGNAL, ADAPTER_OBJECT_PATH, ADAPTER_INTERFACE, G_DBUS_SIGNAL_FLAGS_NONE, on_properties_changed, NULL, NULL); /* Set the alias property */ set_alias_property(connection, argv[1]); GMainLoop *loop = g_main_loop_new(NULL, FALSE); g_main_loop_run(loop); g_dbus_connection_signal_unsubscribe(connection, subscription_id); g_main_loop_unref(loop); g_object_unref(connection); return 1; }
Except the (sa{sv}as)
signature, all other parts of the code should be familiar by now. So we will stick with expanding it,The DBus signal PropertiesChanged
typically uses the signature (sa{sv}as)
. This signature describes the structure of the data passed by the signal, which consists of several nested types. Let’s break it down:
Signature Breakdown
( ... )
: This indicates that the signal carries a tuple (or a structure) containing several values grouped together.s
(string): The first element in the tuple is a single string, which represents the interface name. In the case of BlueZ, this would be something like"org.bluez.Adapter1"
or another interface related to Bluetooth properties.a{sv}
(array of dict entries: string -> variant):- This is an associative array (or dictionary) where the key is a string (
s
), and the value is avariant
(v
). - Each key in this dictionary corresponds to the name of a property that has changed (e.g.,
"Alias"
,"Powered"
,"Connected"
). - The value for each key is a
variant
, which can hold any DBus-supported type. This allows flexibility since properties can have different data types (strings, integers, booleans, etc.). For example, the value for the key"Alias"
might be a string, while"Powered"
might be a boolean.
- This is an associative array (or dictionary) where the key is a string (
as
(array of strings):- The last part of the tuple is an array of strings, which represents a list of property names that were invalidated. These are properties that have become outdated or removed, and the signal informs the receiver that they are no longer valid. This array might be empty if no properties were invalidated.
Example
Here’s an example of how the data might look for a PropertiesChanged
signal with this signature:
("org.bluez.Adapter1", { "Alias": <"Linumiz">, "Powered": <true> }, [])
"org.bluez.Adapter1"
: The interface name where the property change occurred.{"Alias": "Linumiz", "Powered": true}
: A dictionary containing the properties that changed, in this case, theAlias
was updated to"Linumiz"
, andPowered
was set totrue
.[]
: An empty array of invalidated properties, meaning no properties were invalidated during this change.
Recap:
s
: Interface name.a{sv}
: A dictionary of properties that changed (key-value pairs where the key is a string and the value is a variant).as
: A list of property names that were invalidated.
This format provides an efficient way to send detailed updates on multiple properties simultaneously while handling different data types and supporting dynamic updates.
UML:
All our code samples, including those related to DBus signal handling and BlueZ properties, are now available on our GitHub repository. Along with the code, we’ve added UML sequence diagrams that visualize the flow of the PropertiesChanged
signal and other operations. These UML diagrams are included in .uml
format, and we’ve made it easy to generate PNG images using the PlantUML command line. Whether you’re new to PlantUML or an experienced user, you can simply run the command and generate PNG representations of these diagrams for a clearer visual understanding of the process.
@startuml actor User as u participant Application as app #LightBlue participant "DBus Connection" as dbus #LightYellow participant "BlueZ Adapter" as bluez #LightGreen u -> app: Run Application activate app app -> dbus: Connect to System Bus activate dbus dbus -> app: Connection established deactivate dbus app -> dbus: Subscribe to PropertiesChanged signal\n using g_dbus_connection_signal_subscribe activate dbus dbus -> app: Subscription confirmed deactivate dbus app -> dbus: Set Alias("Linumiz")\n using org.freedesktop.DBus.Properties.Set activate dbus dbus -> bluez: Set Alias("Linumiz") activate bluez bluez -> dbus: Confirm Alias set to "Linumiz" deactivate bluez dbus -> app: Return from Set Alias deactivate dbus note right of app: Alias set successfully \n in application bluez -> dbus: Emit PropertiesChanged signal\n for Alias property activate bluez dbus -> app: Send PropertiesChanged signal activate dbus deactivate bluez dbus -> app: PropertiesChanged signal received deactivate dbus app -> app: Handle PropertiesChanged callback app -> u: Display "Property Changed: Alias -> Linumiz" deactivate app @enduml
Conclusion:
Handling asynchronous signals like PropertiesChanged
adds a whole new dimension to working with BlueZ and DBus. Instead of querying for property states, we can now receive real-time updates on changes, enabling us to react dynamically to events like Bluetooth device connections and status changes. This approach not only streamlines our applications but also enhances their responsiveness and interactivity.
No Comment