0

I'm trying to build a Qt application for pairing / connecting bluetooth devices on a Linux device. I'm using the BlueZ API (https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc) over D-Bus as descibed in the BlueZ official site (http://www.bluez.org/page/8/).

The general procedure is described in here: https://www.kynetics.com/docs/2018/pairing_agents_bluez/

At the moment, I can use the APIs for pairing/connecting devices without problems.

BUT I cannot figure out how to use the pairing agent.

I'm trying to follow the direction pointed out here: How can i set the bluetooth pin number in linux C/C++

QDBusAbstractAdaptor object does not export implemented dbus interface

I searched a lot on the web and there are tons of code:

https://github.com/sdemario/qt-bluez-demos

https://github.com/KDE/bluez-qt

https://android.googlesource.com/platform/external/bluetooth/bluez/+/froyo-release/test/agent.c

https://github.com/pauloborges/bluez/blob/master/client/agent.c

https://www.linumiz.com/bluetooth-connectdevice-without-scanning/

but, still, I can't understand how this works. There are also a lot of scripts in python out there, but I'm working with C++.

My (pseudo)-code is the following:

agentManager = new QDBusInterface("org.bluez","/org/bluez","org.bluez.AgentManager1",QDBusConnection::systemBus(),this);
agent = new QDBusInterface("org.bluez","/org/bluez/agent","org.bluez.Agent1",QDBusConnection::systemBus(),this);


QDBusReply<void> reply;

reply = agentManager->call("RegisterAgent", QVariant::fromValue(QDBusObjectPath(path)), capability);
if (!reply.isValid()) {
    qDebug() << reply.error().message() << reply.error().name();
}

reply = agentManager->call("RequestDefaultAgent", QVariant::fromValue(QDBusObjectPath(path)));
if (!reply.isValid()) {
    qDebug() << reply.error().message() << reply.error().name();
}

QDBusReply<QString> reply_str = agent->call("RequestPinCode", QVariant::fromValue(QDBusObjectPath(path)));
if (reply_str.isValid()) {
    pincode = reply_str.value();
} else {
    qDebug() << reply_str.error().message() << reply.error().name();
}

The "RegisterAgent" and "RequestDefaultAgent" calls give a valid reply, while, the "RequestPinCode" gives an error:

"Method "RequestPinCode" with signature "o" on interface "org.bluez.Agent1" doesn't exist " "org.freedesktop.DBus.Error.UnknownObject"

Why? What I'm doing wrong?

Thanks

EDIT

I implemented a brand new service on D-Bus, which implements the org.bluez.Agent1 interface (at leat a part of it). Now, the code looks like this:

MyObject* myObject = new MyObject();
MyAdaptor* myAdaptor = new MyQDusAdaptor(myObject);

if (QDBusConnection::systemBus().registerService("org.test")) {
    qDebug() << "registerService was Succesfull!";
} else {
    qDebug() << "registerService was not Succesfull!";
}

if(QDBusConnection::systemBus().registerObject("/pairing/agent",myObject)){
    qDebug() << "registerObject was Succesfull!";
} else {
    qDebug() << "registerObject was not Succesfull!";
}

agentManager->RegisterAgent(QDBusObjectPath("/pairing/agent"),"NoInputNoOutput");
agentManager->RequestDefaultAgent(QDBusObjectPath("/pairing/agent"));


where the MyObject is


class MyObject : public QObject
{
    Q_OBJECT

public:
    MyObject();

public Q_SLOTS:
    QString RequestPinCode(const QDBusObjectPath &in0);
};


MyObject::MyObject() {

}
QString MyObject::RequestPinCode(const QDBusObjectPath& in0)
{
    qDebug() << "MyObject" << in0.path();


    QString str = "2974";
    return str;
}


and the adaptor is


class MyQDusAdaptor: 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=\"RequestPinCode\">\n"
                                                                         "      <arg direction=\"in\" type=\"o\"/>\n"
                                                                         "      <arg direction=\"out\" type=\"s\"/>\n"
                                                                         "    </method>\n"
                                                                         "  </interface>\n"
                                                                         "")

public:
        MyQDusAdaptor(QObject *parent);
        virtual ~MyQDusAdaptor();

public: // PROPERTIES
public Q_SLOTS: // METHODS
        QString RequestPinCode(const QDBusObjectPath &in0);
};


MyQDusAdaptor::MyQDusAdaptor(QObject* parent) : QDBusAbstractAdaptor(parent) {

    setAutoRelaySignals(true);
}

MyQDusAdaptor::~MyQDusAdaptor() {

}

QString MyQDusAdaptor::RequestPinCode(const QDBusObjectPath& in0)
{
    qDebug() << "MyQDusAdaptor" << in0.path();

    QString out0;
         QMetaObject::invokeMethod(parent(), "RequestPinCode", Q_RETURN_ARG(QString, out0), Q_ARG(QDBusObjectPath, in0));
         return out0;
}

When I try to pair a smartphone, the application freezes for ca 30s, I recieve the following error message:

"Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken." "org.freedesktop.DBus.Error.NoReply"

and only after that the smartphone ask to insert a PIN.

I think that has to be done asynchronously. Any tips?

NoobNoob
  • 80
  • 1
  • 9
  • I'm using qdbusviewer for exploring the objects, and I cannot find the agent – NoobNoob Sep 10 '19 at 14:18
  • If you want to implement the UI where the user e.g. inputs a pincode or accepts a connection, you have to actually write a D-Bus service that implements the whole org.bluez.Agent1 interface. – Jussi Kukkonen Sep 10 '19 at 19:01
  • @JussiKukkonen So, I cannot add the interface to the already existent org.bluez service? I have to instantiate a brand new service? – NoobNoob Sep 11 '19 at 05:58
  • Now I'm trying to figure out how qt does the job (https://code.woboq.org/qt5/qtconnectivity/src/bluetooth/bluez/agent_p.h.html). I cannot use the qt bluetooth module, because I have to work with Qt 4.8 – NoobNoob Sep 11 '19 at 08:18
  • If you want to make your own agent then yes: you do have to implement a service that implements Agent. Bluez will then call the methods on your service when information from your agent is required. – Jussi Kukkonen Sep 11 '19 at 16:51
  • @JussiKukkonen ok, I created a new service which implements the RequestPinCode method from org.bluez.Agent1. Now the problem is that the application locks when I try to pair a device (my guess is that is waiting for a response from the smartphone, but the smartphone asks for insert PIN oly after the timeout is over). Any suggestions? Thanks – NoobNoob Sep 13 '19 at 12:57
  • 1
    I managed to make it work. :) I just made the call to "Pair" asynchronous with callWithCallback – NoobNoob Sep 13 '19 at 14:06

1 Answers1

0

Ok, now it works. The way to do it is explained in QDBusAbstractAdaptor object does not export implemented dbus interface

Details of the implementation are in the last EDIT of this question. The last thing to to is to implement the "Pair" method on the Device1 interface asynchronously with callWithCallback:

void cBluetoothDevice::Pair() {

    QList<QVariant> args;
    args.clear();

    device->callWithCallback("Pair",args, this, SLOT(PairSlot(QDBusMessage)));
}

where device is a QDBusInterface*

NoobNoob
  • 80
  • 1
  • 9