Developing for Sailfish OS: D-Bus
All kind time of day! This article continues a series of articles dedicated to developing for the mobile platform Sailfish OS. Since the operating system is the Linux kernel, the Sailfish OS will be initially available some of the "Goodies" came from the Linux world. One such "Goodies" is a system of interprocess communication with D-Bus. For this article, I will assume that the reader is familiar with the fact that this system for what it is and how to use it (otherwise, this information is easy enough to find online, such as official website or opennet).
Despite the fact that D-Bus is natively supported in Sailfish OS, to manage them is possible only from the terminal or from applications (if it is incorporated). That is why I had the idea of creating a visual client to the system D-Bus for Sailfish OS, which will allow you to browse the services registered in the system and to interact with them using a graphical interface. In other words, to create an analogue of D-Feet or Qt D-Bus Viewer for Sailfish OS.
Any application in the system can create your own service (or service) that will implement one or more interfaces that describe the methods, signals and properties. The same application can interact with other applications through their services registered at D-Bus. For example, Sailfish OS was the service net.connman that implements the interface net.connman.Technology at /net/connman/technology/bluetooth. This interface contains, in particular, and the method SetProperty(). Calling it the following way — SetProperty("Powered", true) — you can enable Bluetooth on the device.
In fact, the functionality of the application needs to repeat itself for analogues. Ie the app should allow you to view the list of services registered at D-Bus (both session and system) for each service to view a list of possible paths for each path in the list of interfaces for each interface to show a list of methods, properties and signals. In addition the application also should allow to call those same methods and to read/change properties.
In Sailfish SDK provides two options for interacting with D-Bus. First, it is Nemo QML Plugin D-Bus, which allows you to interact with D-Bus directly from QML code. Secondly, it is Qt D-Bus standard Qt mechanism that provides C++ classes to interact with D-Bus. The difference between them is that the first is quite easy to use, and the second gives you more options. In described in this article, the application will be described both ways.
For a list of services registered at D-Bus uses the element DBusInterface:
the
And calls the ListNames described above interface:
the
Upon successful invocation, it populates the list of services. When this occurs, filtering, to avoid the conclusion of the services with names of the form ":1.44," etc. In case of error when calling the method, the user is shown a dialog with the error message. The page itself looks like the following:

By clicking on the service from the list takes you to a page with a list of all the possible ways this service. To do this, as well as to get a list of interfaces, use the interface org.freedesktop.DBus.Introspectable and the method Introspect. This method returns information about the interfaces and sub-paths of the service to one specific path in the xml. However, for our purposes, it is necessary to list all possible ways of service. In other words, you need to call the method Introspect at the root path ("/"), and then recursively for all nested paths (which is described in the response method). Since the process is recursive, and the answer method is obtained in xml format, it is possible to realize such a format by means of only one of the QML code is not possible (the element XmlListModel simply is not the desired functionality).
Therefore, obtaining a list of paths it was decided to implement in C++. It looks like this:
the
This code implements the recursive algorithm described above. It is worth noting that results are removed from those paths that are only two interfaces: org.freedesktop.DBus.Introspectable and org.freedesktop.DBus.Peer. This is done in order to display only the paths for which the available interfaces, which can be really useful for the user.
Page lists routes as follows:

When you click on one of the paths open the following page with a list of interfaces implemented by this service on the selected path. This list as well as a list of paths drawn on the xml returned from the Introspect, however without any recursion by calling it once for a particular path. This functionality could be done using the Nemo QML Plugin D-Bus, but we decided to implement it in C++:
the
The list itself looks like this:

When you click on the interface to open another page displaying signals of methods and properties for this interface:

All these parameters are obtained with the help of parsing the xml that was received in the result of the method invocation Introspect. But here, for simplicity, we have identified a separate class of InterfaceMember, which essentially is a structure to store all parameters for a given interface member. This was done to ensure that such objects are easily represented in QML code in the form of non-visual elements.
The last two pages of the application are pages modifying the properties of the interface and call interface method. The first implemented is quite simple and looks as follows:

If the property is read-only, you cannot change it. If the property is available and for the record, the field for entering the new value on the page will be variable and there you can enter a new value. Reading and writing property values are carried out using the methods property() and setProperty() class QDBusInterface. Although it is also possible to implement using the methods Get() and Set interface org.freedesktop.DBus.Properties.
The page method call as follows:

The method call itself in the same interface is easily implemented using the methods call() or callWithArgumentList() - class QDBusInterface.
However, here we have a difficulty when you convert from method arguments obtained from QML to clear for the D-Bus. To implement this functionality, it was decided to take ready-made solution that was implemented in Qt D-Bus Viewer. The source code of this project can be viewed at GitHub.
This app was published in app store for the platform Sailfish OS — Jolla Harbour called Visual D-Bus and is available for download. Source code of the project can be found on GitHub.
Author: Laura Dennis
Article based on information from habrahabr.ru
Despite the fact that D-Bus is natively supported in Sailfish OS, to manage them is possible only from the terminal or from applications (if it is incorporated). That is why I had the idea of creating a visual client to the system D-Bus for Sailfish OS, which will allow you to browse the services registered in the system and to interact with them using a graphical interface. In other words, to create an analogue of D-Feet or Qt D-Bus Viewer for Sailfish OS.
Any application in the system can create your own service (or service) that will implement one or more interfaces that describe the methods, signals and properties. The same application can interact with other applications through their services registered at D-Bus. For example, Sailfish OS was the service net.connman that implements the interface net.connman.Technology at /net/connman/technology/bluetooth. This interface contains, in particular, and the method SetProperty(). Calling it the following way — SetProperty("Powered", true) — you can enable Bluetooth on the device.
In fact, the functionality of the application needs to repeat itself for analogues. Ie the app should allow you to view the list of services registered at D-Bus (both session and system) for each service to view a list of possible paths for each path in the list of interfaces for each interface to show a list of methods, properties and signals. In addition the application also should allow to call those same methods and to read/change properties.
In Sailfish SDK provides two options for interacting with D-Bus. First, it is Nemo QML Plugin D-Bus, which allows you to interact with D-Bus directly from QML code. Secondly, it is Qt D-Bus standard Qt mechanism that provides C++ classes to interact with D-Bus. The difference between them is that the first is quite easy to use, and the second gives you more options. In described in this article, the application will be described both ways.
List of services
For a list of services registered at D-Bus uses the element DBusInterface:
the
DBusInterface {
id: dbusList
service: 'org.freedesktop.DBus'
path: '/org/freedesktop/DBus'
iface: 'org.freedesktop.DBus'
bus: DBus.SessionBus
}
And calls the ListNames described above interface:
the
dbusList.typedCall('ListNames', undefined,
function(result) {
sessionServices = result.filter(function(value) {return value[0] !== ':'}).sort();
}, function() {
pageStack.push(Qt.resolvedUrl("FailedToRecieveServicesDialog.qml"));
});
Upon successful invocation, it populates the list of services. When this occurs, filtering, to avoid the conclusion of the services with names of the form ":1.44," etc. In case of error when calling the method, the user is shown a dialog with the error message. The page itself looks like the following:

List of services
By clicking on the service from the list takes you to a page with a list of all the possible ways this service. To do this, as well as to get a list of interfaces, use the interface org.freedesktop.DBus.Introspectable and the method Introspect. This method returns information about the interfaces and sub-paths of the service to one specific path in the xml. However, for our purposes, it is necessary to list all possible ways of service. In other words, you need to call the method Introspect at the root path ("/"), and then recursively for all nested paths (which is described in the response method). Since the process is recursive, and the answer method is obtained in xml format, it is possible to realize such a format by means of only one of the QML code is not possible (the element XmlListModel simply is not the desired functionality).
Therefore, obtaining a list of paths it was decided to implement in C++. It looks like this:
the
QStringList DBusServiceInspector::getPathsList(QString serviceName, bool isSystemBus) {
this->serviceName = serviceName;
dDusConnection = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
return extractPaths(introspectService("/"), "/");
}
DBusServiceInspector QString::introspectService(QString path) {
QDBusInterface interface(serviceName, path, "org.freedesktop.DBus.Introspectable", dDusConnection);
QDBusReply<QString> xmlReply = interface.call("Introspect");
if (xmlReply.isValid()) return xmlReply.value();
return "";
}
DBusServiceInspector QStringList::extractPaths(xml QString, QString pathPrefix) {
QXmlStreamReader xmlReader(xml);
QStringList pathsList;
while (!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartDocument)
continue;
if (token == QXmlStreamReader::StartElement) {
if (xmlReader.name() == "interface") {
QXmlStreamAttributes attributes = xmlReader.attributes();
if (attributes.hasAttribute("name") &&
attributes.value("name") != "org.freedesktop.DBus.Introspectable" &&
attributes.value("name") != "org.freedesktop.DBus.Peer")
if (!pathsList.contains(pathPrefix)) pathsList.append(pathPrefix);
} else if (xmlReader.name() == "node") {
QXmlStreamAttributes attributes = xmlReader.attributes();
if (attributes.hasAttribute("name") && attributes.value("name") != pathPrefix) {
QString path = attributes.value("name").toString();
if (path.at(0) == '/' || pathPrefix.at(pathPrefix.length() - 1) == '/') {
path = pathPrefix + path;
} else {
path = pathPrefix + "/" + path;
}
pathsList.append(extractPaths(introspectService(path), path));
}
}
}
}
return pathsList;
}
This code implements the recursive algorithm described above. It is worth noting that results are removed from those paths that are only two interfaces: org.freedesktop.DBus.Introspectable and org.freedesktop.DBus.Peer. This is done in order to display only the paths for which the available interfaces, which can be really useful for the user.
Page lists routes as follows:

List of interfaces
When you click on one of the paths open the following page with a list of interfaces implemented by this service on the selected path. This list as well as a list of paths drawn on the xml returned from the Introspect, however without any recursion by calling it once for a particular path. This functionality could be done using the Nemo QML Plugin D-Bus, but we decided to implement it in C++:
the
QStringList DBusServiceInspector::getInterfacesList(QString serviceName, QString path, bool isSystemBus) {
this->serviceName = serviceName;
dDusConnection = isSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus();
QXmlStreamReader xmlReader(introspectService(path));
QStringList interfacesList;
while(!xmlReader.atEnd() && !xmlReader.hasError()) {
QXmlStreamReader::TokenType token = xmlReader.readNext();
if (token == QXmlStreamReader::StartElement) {
if (xmlReader.name() == "interface") {
QXmlStreamAttributes attributes = xmlReader.attributes();
if (attributes.hasAttribute("name"))
interfacesList.append(attributes.value("name").toString());
}
}
}
return interfacesList;
}
The list itself looks like this:

When you click on the interface to open another page displaying signals of methods and properties for this interface:

All these parameters are obtained with the help of parsing the xml that was received in the result of the method invocation Introspect. But here, for simplicity, we have identified a separate class of InterfaceMember, which essentially is a structure to store all parameters for a given interface member. This was done to ensure that such objects are easily represented in QML code in the form of non-visual elements.
Calling methods and modifying interface properties
The last two pages of the application are pages modifying the properties of the interface and call interface method. The first implemented is quite simple and looks as follows:

If the property is read-only, you cannot change it. If the property is available and for the record, the field for entering the new value on the page will be variable and there you can enter a new value. Reading and writing property values are carried out using the methods property() and setProperty() class QDBusInterface. Although it is also possible to implement using the methods Get() and Set interface org.freedesktop.DBus.Properties.
The page method call as follows:

The method call itself in the same interface is easily implemented using the methods call() or callWithArgumentList() - class QDBusInterface.
However, here we have a difficulty when you convert from method arguments obtained from QML to clear for the D-Bus. To implement this functionality, it was decided to take ready-made solution that was implemented in Qt D-Bus Viewer. The source code of this project can be viewed at GitHub.
Opinion
This app was published in app store for the platform Sailfish OS — Jolla Harbour called Visual D-Bus and is available for download. Source code of the project can be found on GitHub.
Author: Laura Dennis
Комментарии
Отправить комментарий