Advanced bluetooth code example

Proxy objects D-Bus, Qt adaptor

By using adaptors you can access an interface via an object instead of calling methods via QDbusInterace class. You create a QDBusAbstractAdaptor object which is used for relaying calls into the real object and the signals from it.

Using a Q_CLASSINFO macro you define the D-BUS interface it is implementing.
Some times you can get the interface description from introspecting the D-BUS. You can also write it yourself or if it is a known interface you can usually find an XML description of it.

Look at the interface description of org.bluez.Agent1

How to implement custom UI for displaying pairing information.

Suppose you would like to show a UI with a pincode to verify when you activate pairing on a unit.

Example pincode

The steps involved would be.

  1. Look at the D-Bus api for your version of Bluez. The agent-api is what we are looking for. Agent-api

  2. Try to get hold of the XML describing the org.bluez.Agent1 interface org.bluez.Agent1

  3. Use the tool that comes with Qt called qdbusxml2cpp. In this example having the xml file in the same folder as the tool. Run the command qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor

  4. Import the generated class bluezagent1adaptor to your Qt project.

  5. Do your own implementation in another class (Below named BluezPairingAgent

  6. Register your implemented object with a call to the org.bluez.AgentManager1 interface

After completing the above steps you will automatically get the proper callbacks to your object that implements the interface.

Registering your implementation with org.bluez.AgentManager1

From your setup code you will need something very similar to this code below.

// Setup and register bluetooth pairing agent
m_agent = new BluezPairingAgent();

// Give your agent an object path
QString objectPath("/pairing/agent");
m_agentAdaptor = new Agent1Adaptor(m_agent);

// For your object to be visible on d-bus, call the registerObject method
if (QDBusConnection::systemBus().registerObject(objectPath, m_agent)) {
    // Get the interface reference for the org.bluez.AgentManager1
    QDBusInterface agentManager("org.bluez", "/org/bluez", "org.bluez.AgentManager1", QDBusConnection::systemBus());
    if (!agentManager.isValid())
        qWarning() << Q_FUNC_INFO << agentManager.lastError().message();
    else {
        QVariant agentPath{ QVariant::fromValue(QDBusObjectPath(objectPath)) };
        // Call the RegisterAgent method with the correct arguments, since we don't have a keyboard connected we submit DisplayOnly
        QDBusMessage msg{ agentManager.call("RegisterAgent", agentPath, "DisplayOnly") };
        if (msg.type() == QDBusMessage::ErrorMessage)
            qWarning() << Q_FUNC_INFO << msg.errorMessage();
        else {
            msg = agentManager.call("RequestDefaultAgent", agentPath);
            if (msg.type() == QDBusMessage::ErrorMessage)
                qWarning() << Q_FUNC_INFO << msg.errorMessage();
        }
    }
}

Now when someone initiates pairing bluez will call our agent object!
It is now up to you to write a good looking UI for it. Below you can see the generated class and a simple implementation.

Agent1Adaptor.h (Generated via qdbusxml2cpp)

/*
 * This file was generated by qdbusxml2cpp version 0.8
 * Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor
 *
 * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
 *
 * This is an auto-generated file.
 * This file may have been hand-edited. Look for HAND-EDIT comments
 * before re-generating it.
 */

#ifndef BLUEZAGENT1ADAPTOR_H
#define BLUEZAGENT1ADAPTOR_H

#include <QtCore/QObject>
#include <QtDBus/QtDBus>
QT_BEGIN_NAMESPACE
class QByteArray;
template<class T> class QList;
template<class Key, class Value> class QMap;
class QString;
class QStringList;
class QVariant;
QT_END_NAMESPACE

/*
 * Adaptor class for interface org.bluez.Agent1
 */
class Agent1Adaptor: public QDBusAbstractAdaptor
{
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "org.bluez.Agent1")
    Q_CLASSINFO("D-Bus Introspection", ""
"  <interface name=\"org.bluez.Agent1\">\n"
"    <method name=\"Release\"/>\n"
"    <method name=\"RequestPinCode\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"out\" type=\"s\"/>\n"
"    </method>\n"
"    <method name=\"DisplayPinCode\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"in\" type=\"s\"/>\n"
"    </method>\n"
"    <method name=\"RequestPasskey\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"out\" type=\"u\"/>\n"
"    </method>\n"
"    <method name=\"DisplayPasskey\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"in\" type=\"u\"/>\n"
"      <arg direction=\"in\" type=\"q\"/>\n"
"    </method>\n"
"    <method name=\"RequestConfirmation\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"in\" type=\"u\"/>\n"
"    </method>\n"
"    <method name=\"RequestAuthorization\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"    </method>\n"
"    <method name=\"AuthorizeService\">\n"
"      <arg direction=\"in\" type=\"o\"/>\n"
"      <arg direction=\"in\" type=\"s\"/>\n"
"    </method>\n"
"    <method name=\"Cancel\"/>\n"
"  </interface>\n"
        "")
public:
    Agent1Adaptor(QObject *parent);
    virtual ~Agent1Adaptor();

public: // PROPERTIES
public Q_SLOTS: // METHODS
    void AuthorizeService(const QDBusObjectPath &in0, const QString &in1);
    void Cancel();
    void DisplayPasskey(const QDBusObjectPath &in0, uint in1, ushort in2);
    void DisplayPinCode(const QDBusObjectPath &in0, const QString &in1);
    void Release();
    void RequestAuthorization(const QDBusObjectPath &in0);
    void RequestConfirmation(const QDBusObjectPath &in0, uint in1);
    uint RequestPasskey(const QDBusObjectPath &in0);
    QString RequestPinCode(const QDBusObjectPath &in0);
Q_SIGNALS: // SIGNALS
};

#endif

Agent1Adaptor.cpp (Generated via qdbusxml2cpp)

/*
 * This file was generated by qdbusxml2cpp version 0.8
 * Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a bluezagent1adaptor
 *
 * qdbusxml2cpp is Copyright (C) 2017 The Qt Company Ltd.
 *
 * This is an auto-generated file.
 * Do not edit! All changes made to it will be lost.
 */

#include "bluezagent1adaptor.h"
#include <QtCore/QMetaObject>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

/*
 * Implementation of adaptor class Agent1Adaptor
 */

Agent1Adaptor::Agent1Adaptor(QObject *parent)
    : QDBusAbstractAdaptor(parent)
{
    // constructor
    setAutoRelaySignals(true);
}

Agent1Adaptor::~Agent1Adaptor()
{
    // destructor
}

void Agent1Adaptor::AuthorizeService(const QDBusObjectPath &in0, const QString &in1)
{
    // handle method call org.bluez.Agent1.AuthorizeService
    QMetaObject::invokeMethod(parent(), "AuthorizeService", Q_ARG(QDBusObjectPath, in0), Q_ARG(QString, in1));
}

void Agent1Adaptor::Cancel()
{
    // handle method call org.bluez.Agent1.Cancel
    QMetaObject::invokeMethod(parent(), "Cancel");
}

void Agent1Adaptor::DisplayPasskey(const QDBusObjectPath &in0, uint in1, ushort in2)
{
    // handle method call org.bluez.Agent1.DisplayPasskey
    QMetaObject::invokeMethod(parent(), "DisplayPasskey", Q_ARG(QDBusObjectPath, in0), Q_ARG(uint, in1), Q_ARG(ushort, in2));
}

void Agent1Adaptor::DisplayPinCode(const QDBusObjectPath &in0, const QString &in1)
{
    // handle method call org.bluez.Agent1.DisplayPinCode
    QMetaObject::invokeMethod(parent(), "DisplayPinCode", Q_ARG(QDBusObjectPath, in0), Q_ARG(QString, in1));
}

void Agent1Adaptor::Release()
{
    // handle method call org.bluez.Agent1.Release
    QMetaObject::invokeMethod(parent(), "Release");
}

void Agent1Adaptor::RequestAuthorization(const QDBusObjectPath &in0)
{
    // handle method call org.bluez.Agent1.RequestAuthorization
    QMetaObject::invokeMethod(parent(), "RequestAuthorization", Q_ARG(QDBusObjectPath, in0));
}

void Agent1Adaptor::RequestConfirmation(const QDBusObjectPath &in0, uint in1)
{
    // handle method call org.bluez.Agent1.RequestConfirmation
    QMetaObject::invokeMethod(parent(), "RequestConfirmation", Q_ARG(QDBusObjectPath, in0), Q_ARG(uint, in1));
}

uint Agent1Adaptor::RequestPasskey(const QDBusObjectPath &in0)
{
    // handle method call org.bluez.Agent1.RequestPasskey
    uint out0;
    QMetaObject::invokeMethod(parent(), "RequestPasskey", Q_RETURN_ARG(uint, out0), Q_ARG(QDBusObjectPath, in0));
    return out0;
}

QString Agent1Adaptor::RequestPinCode(const QDBusObjectPath &in0)
{
    // handle method call org.bluez.Agent1.RequestPinCode
    QString out0;
    QMetaObject::invokeMethod(parent(), "RequestPinCode", Q_RETURN_ARG(QString, out0), Q_ARG(QDBusObjectPath, in0));
    return out0;
}

BluezPairingAgent.h (Our implementation)

#ifndef BLUEZPAIRINGAGENT_H
#define BLUEZPAIRINGAGENT_H

#include <QObject>
#include <QtDBus>

/*
 * This class implements the interfaces for the generated D-Bus
 * interface
 *
 * The interface was generated from org.bluez.Agent1.xml
 */

class BluezPairingAgent : public QObject
{
    Q_OBJECT
public:
    explicit BluezPairingAgent(QObject *parent = nullptr);

public slots:
    void Release() const;
    QString RequestPinCode(const QDBusObjectPath &device) const;
    void DisplayPinCode(const QDBusObjectPath &device, const QString &pincode);
    uint RequestPasskey(const QDBusObjectPath &device) const;
    void DisplayPasskey(const QDBusObjectPath &device, uint passkey, ushort entered);
    void RequestConfirmation(const QDBusObjectPath &device, uint passkey);
    void RequestAuthorization(const QDBusObjectPath &device) const;
    void AuthorizeService(const QDBusObjectPath &device, const QString &uuid) const;
    void Cancel() const;

signals:
    void showCode(const uint &code, const ushort &entered);
    void showVerificationCode(const uint &code);
    void showPinCode(const QString &code);
};

#endif // BLUEZPAIRINGAGENT_H

BluezPairingAgent.cpp (Our implementation)

#include "bluezpairingagent.h"

#include <QDebug>
#include <iostream>

/*
 * This class implements the interfaces for the generated D-Bus
 * interface
 *
 * The interface was generated from org.bluez.Agent1.xml
 */

BluezPairingAgent::BluezPairingAgent(QObject *parent)
    : QObject(parent) {
}

void BluezPairingAgent::Release() const {
    //NOTE: Not needed yet, but must be here as it is expected by bluez.
    //      This call is for unregistering agents, which we never do.
}

QString BluezPairingAgent::RequestPinCode(const QDBusObjectPath &device) const {
    Q_UNUSED(device)
    return "1234";
}

void BluezPairingAgent::DisplayPinCode(const QDBusObjectPath &device, const QString &pincode) {
    Q_UNUSED(device)
    emit showPinCode(pincode);
}

uint BluezPairingAgent::RequestPasskey(const QDBusObjectPath &device) const {
    Q_UNUSED(device)
    return 1234;
}

void BluezPairingAgent::DisplayPasskey(const QDBusObjectPath &device, uint passkey, ushort entered) {
    Q_UNUSED(device)
    emit showCode(passkey, entered);
}

void BluezPairingAgent::RequestConfirmation(const QDBusObjectPath &device, uint passkey) {
    Q_UNUSED(device)
    emit showVerificationCode(passkey);
}

void BluezPairingAgent::RequestAuthorization(const QDBusObjectPath &device) const {
    Q_UNUSED(device)
    //NOTE: Not needed yet, but must be here as it is expected by bluez
}

void BluezPairingAgent::AuthorizeService(const QDBusObjectPath &device, const QString &uuid) const {
    Q_UNUSED(device)
    Q_UNUSED(uuid)
    //NOTE: Not needed yet, but must be here as it is expected by bluez
}

void BluezPairingAgent::Cancel() const {
    //NOTE: Not needed, but must be here as it is expected by bluez
}

Interface description of org.bluez.Agent1

<!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.bluez.Agent1">
        <method name="Release" />
        <method name="RequestPinCode">
            <arg direction="in" type="o" />
            <arg direction="out" type="s" />
        </method>
        <method name="DisplayPinCode">
            <arg direction="in" type="o" />
            <arg direction="in" type="s" />
        </method>
        <method name="RequestPasskey">
            <arg direction="in" type="o" />
            <arg direction="out" type="u" />
        </method>
        <method name="DisplayPasskey">
            <arg direction="in" type="o" />
            <arg direction="in" type="u" />
            <arg direction="in" type="q" />
        </method>
        <method name="RequestConfirmation">
            <arg direction="in" type="o" />
            <arg direction="in" type="u" />
        </method>
        <method name="RequestAuthorization">
            <arg direction="in" type="o" />
        </method>
        <method name="AuthorizeService">
            <arg direction="in" type="o" />
            <arg direction="in" type="s" />
        </method>
        <method name="Cancel" />
    </interface>
</node>