Wenn man DBus versteht (was a…

(2010-09-01)

Wenn man DBus versteht (was am Anfang nicht einfach ist, insbesondere wenn man nur mal kurz reinschauen will) und insbesondere die DBus-API der Programmiersprache der Wahl versteht, ist das ja ganz in Ordnung. Scheint schnell zu sein und man kann damit recht stabil IPC bauen (auch wenn ich Teile der schwarzen Magie in dem ganzen Konstrukt, was heutzutage einen Desktop auszumachen scheint, noch nicht verstanden habe).

Nokia hingegen scheint DBus teilweise noch nicht wirklich verstanden zu haben. Man braucht sich dazu nur mal die Internet Connectivity-APIs auf Maemo anzusehen. Diese sind dafür da, eine Internetverbindung herzustellen, bei Veränderungen der Konnektivität Anwendungen zu benachrichtigen oder Statistiken über die derzeitige Verbindung bereitzustellen (per GPRS übertragenes Datenvolumen z.B.).

Dieser IC-API fehlt nun leider die Möglichkeit, auf die derzeit vorhandenen WLANs zuzugreifen. Ein Blick ins syslog offenbart allerdings, dass der wlancond für die Verwaltung der WLAN-Verbindung zuständig ist. Dieser spricht selbstverständlich via DBus mit dem icd (Internet Connectivity Daemon). Also kann ich den wlancond auch von meiner eigenen Anwendung aus ansprechen.

Zunächst fehlt dem wlancond leider die XML-Beschreibung über die verfügbaren Pfade, Interfaces, Methoden und Signale. Würde diese Datei existieren, könnte man zum einen leichter programmieren, zum anderen wäre Nokia dann bestimmt auch aufgefallen, was für einen Müll sie eigentlich durch die Gegend schicken. Hier mal ein Ausschnitt aus dem Code, den man braucht, um die derzeit verfügbaren ESSIDs inklusive Signalstärke auszulesen:

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QDBusConnection conn = QDBusConnection::systemBus();
    QDBusInterface wlancond(WLANCOND_SERVICE, WLANCOND_REQ_PATH, WLANCOND_REQ_INTERFACE, conn);
    /* Arguments are power level, SSID, length of SSID */
    QDBusMessage reply = wlancond.call(WLANCOND_SCAN_REQ, WLANCOND_TX_POWER10, QByteArray(""), 0);
    if (reply.type() == QDBusMessage::ErrorMessage)
        qDebug() << reply.errorMessage();

    conn.connect(
        WLANCOND_SERVICE,
        WLANCOND_SIG_PATH,
        WLANCOND_SIG_INTERFACE,
        WLANCOND_SCAN_RESULTS_SIG,
        &app,
        SLOT(scan_results(const QDBusMessage&))
    );

    return a.exec();
}

void app::scan_results(const QDBusMessage &msg) {
    QList args = msg.arguments();

    int networks = args.value(0).value < int > ();
    for (int c = 0; c < networks; c++) {
        qDebug() << "network" << c << "of" << networks << ":";
        int pos = 1 + (c * 5);
        qDebug() << "ESSID:" << args.value(pos).value < QByteArray > ();
        qDebug() << "RSSI:" << args.value(pos+2).value < int > ();
    }
}

Für die weniger DBus-Bewanderten: Nachdem man den scan request gesendet hat (wlancond.call()), der merkwürdigerweise den DBus-Datentyp string ignoriert und stattdessen length-suffixed (!) byte arrays für die Übergabe der SSID nutzt, bekommt man, sobald der Scan fertig ist, ein Signal mit den gefundenen Netzen. Soweit ist das alles kein Problem. Der Clou ist jedoch, dass dieses Signal direkt alle gefunden Netze als Argumente enthält. Soll heißen am Anfang findet sich die Anzahl der gefundenen Netze, anschließend folgt die ESSID als Byte-Array, dann die BSSID, dann die RSSI, der Kanal, was das Netz kann und danach geht’s mit dem nächsten Netz weiter. Man muss sich das also alles selber zusammenfummeln. Wie hübsch der Code aussieht, sieht man ja.

Sinnvoller wäre gewesen, ein Array zu versenden (dann kann man nämlich auch direkt die Signatur des Signals nutzen und Qt kann Typprüfung vornehmen), welches pro Netz ein Dictionary enthält (dann muss man sich nicht selbst Stück für Stück durch die Argumente wühlen).

Hoffentlich sieht Nokia in QtMobility eine einfachere Möglichkeit vor, sich die verfügbaren Netze anzusehen…