BlueZ Part 9: Understanding DBUS – Introspectable – (8)

Introduction:

In our journey to deepen our understanding of D-Bus using BlueZ, we’ve already delved into the org.freedesktop.DBus.Properties interface, exploring the Get, Set, GetAll methods, and the PropertiesChanged signal. These foundational concepts have paved the way for us to handle property changes and interact with various D-Bus services effectively. Now, we shift our focus to another crucial, yet straightforward, interface: org.freedesktop.DBus.Introspectable.

The Introspect method within this interface provides a way to retrieve all properties, methods, and their respective D-Bus signatures in an XML format. This is invaluable for understanding the capabilities of a D-Bus object. While using XML and GDBus to build a complete D-Bus service is outside the scope of this blog series, we’ll touch upon it as we prepare to develop a BlueZ agent using the AgentManager interface in the coming weeks.

org.freedesktop.DBus.Introspectable:

#include <gio/gio.h>
#include <stdio.h>
#include <stdlib.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#define BLUEZ_BUS_NAME		"org.bluez"
#define BLUEZ_OBJECT_PATH	"/org/bluez/hci0"
#define INTROSPECT_METHOD	"Introspect"

int main(void)
{
    GDBusConnection *connection;
    GError *error = NULL;
    GVariant *result;
    const char *xml_data;
    xmlChar *xmlbuff;
    xmlDocPtr doc;
    int buffersize;
    int ret = 0;

    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 -ENOTCONN;
    }

    result = g_dbus_connection_call_sync(connection,
            BLUEZ_BUS_NAME,
            BLUEZ_OBJECT_PATH,
            "org.freedesktop.DBus.Introspectable",
            INTROSPECT_METHOD,
            NULL,
            G_VARIANT_TYPE("(s)"),
            G_DBUS_CALL_FLAGS_NONE,
            -1,
            NULL,
            &error);

    if (error != NULL) {
        fprintf(stderr, "Error calling Introspect method: %s\n", error->message);

        if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT))
            ret = ETIMEDOUT;
        else if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
            ret = ENOENT;
        else
            ret = EIO;

        g_clear_error(&error);
        g_object_unref(connection);
        return -ret;
    }

    g_variant_get(result, "(&s)", &xml_data);

    doc = xmlReadMemory(xml_data, strlen(xml_data), "noname.xml", NULL, 0);
    if (doc == NULL) {
        fprintf(stderr, "Failed to parse XML\n");
        g_variant_unref(result);
        g_object_unref(connection);
        return -EINVAL;
    }

    xmlDocDumpFormatMemory(doc, &xmlbuff, &buffersize, 1);
    printf("Introspect XML (Pretty-printed):\n%s\n", (char *)xmlbuff);

    xmlFree(xmlbuff);
    xmlFreeDoc(doc);
    g_variant_unref(result);
    g_object_unref(connection);
    xmlCleanupParser();

    return ret;
}

There is no special or magical DBUS signatures, but we have 2 new things.

  1. Error handling
  2. XML parser

The later only helps to view the output of the DBUS interface in pretty format. Error handling on the other hand, kick starts our first part to get IO errors. We will b expanding this into handling specific errors when we invoke core BlueZ related methods. Otherwise,

<?xml version="1.0"?>
<!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.freedesktop.DBus.Introspectable">
    <method name="Introspect"><arg name="xml" type="s" direction="out"/>
</method>
  </interface>
  <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>
    <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"/>
    <property name="Manufacturer" type="q" access="read"/>
    <property name="Version" type="y" access="read"/>
  </interface>
  <interface name="org.freedesktop.DBus.Properties"><method name="Get"><arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="out"/>
</method><method name="Set"><arg name="interface" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
<arg name="value" type="v" direction="in"/>
</method><method name="GetAll"><arg name="interface" type="s" direction="in"/>
<arg name="properties" type="a{sv}" direction="out"/>
</method><signal name="PropertiesChanged"><arg name="interface" type="s"/>
<arg name="changed_properties" type="a{sv}"/>
<arg name="invalidated_properties" type="as"/>
</signal>
</interface>
  <interface name="org.bluez.BatteryProviderManager1">
    <method name="RegisterBatteryProvider"><arg name="provider" type="o" direction="in"/>
</method>
    <method name="UnregisterBatteryProvider"><arg name="provider" type="o" direction="in"/>
</method>
  </interface>
  <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>
  <interface name="org.bluez.Media1">
    <method name="RegisterEndpoint"><arg name="endpoint" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method>
    <method name="UnregisterEndpoint"><arg name="endpoint" type="o" direction="in"/>
</method>
    <method name="RegisterPlayer"><arg name="player" type="o" direction="in"/>
<arg name="properties" type="a{sv}" direction="in"/>
</method>
    <method name="UnregisterPlayer"><arg name="player" type="o" direction="in"/>
</method>
    <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>
    <property name="SupportedUUIDs" type="as" access="read"/>
  </interface>
  <interface name="org.bluez.NetworkServer1">
    <method name="Register"><arg name="uuid" type="s" direction="in"/>
<arg name="bridge" type="s" direction="in"/>
</method>
    <method name="Unregister"><arg name="uuid" type="s" direction="in"/>
</method>
  </interface>
  <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 name="dev_2C_FD_B4_2D_1F_A5"/>
  <node name="dev_74_2A_8A_B0_13_78"/>
</node>

With this we will be able to view the name of the method or property with it’s DBUS signatures.

Conclusion:

While this blog touched on the Introspectable interface, which is relatively straightforward with its basic string signature, we didn’t push too deep, keeping it simple and accessible. However, the excitement is just beginning—stay tuned as we dive into the more complex and feature-rich ObjectManager interface in our next blog, where we’ll explore its powerful capabilities. As always, we have everything available at GitHub.

Written by

No Comment

Please Post Your Comments & Reviews

Your email address will not be published. Required fields are marked *