# 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](#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](./images/media/pairing2.png) 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](https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt) 2. Try to get hold of the XML describing the org.bluez.Agent1 interface [org.bluez.Agent1](#interface-description-of-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. ```c++ // 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) ```c++ /* * 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 #include QT_BEGIN_NAMESPACE class QByteArray; template class QList; template 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", "" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \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) ```c++ /* * 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 #include #include #include #include #include #include /* * 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) ```c++ #ifndef BLUEZPAIRINGAGENT_H #define BLUEZPAIRINGAGENT_H #include #include /* * 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) ```c++ #include "bluezpairingagent.h" #include #include /* * 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 ```xml ```