Index: denali.pro =================================================================== diff -u -rf68b226e67eb500758ee94fe015df48931240013 -r4a95b249fb87bf69d909452ba374c794b89d423b --- denali.pro (.../denali.pro) (revision f68b226e67eb500758ee94fe015df48931240013) +++ denali.pro (.../denali.pro) (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -1,4 +1,4 @@ -QT += qml quick serialbus concurrent charts bluetooth +QT += qml quick serialbus concurrent charts bluetooth dbus CONFIG += c++17 warn_on # CONFIG += disable-desktop QMAKE_CXXFLAGS += -Wall -Werror -Wextra -Wimplicit-fallthrough=3 # -save-temps # see .ii @@ -107,6 +107,8 @@ common/HDDefs.h \ common/MsgDefs.h \ \ # Main + sources/bluetooth/Agent1Adapter.h \ + sources/bluetooth/BluetoothPairingAgent.h \ sources/main.h \ sources/Threads.h \ sources/MainTimer.h \ @@ -378,6 +380,8 @@ sources/MainTimer.cpp \ sources/AlarmGenerator.cpp \ sources/ApplicationPost.cpp \ + sources/bluetooth/Agent1Adapter.cpp \ + sources/bluetooth/BluetoothPairingAgent.cpp \ sources/device/DeviceError.cpp \ \ # Controllers sources/ApplicationController.cpp \ Index: sources/bluetooth/Agent1Adapter.cpp =================================================================== diff -u --- sources/bluetooth/Agent1Adapter.cpp (revision 0) +++ sources/bluetooth/Agent1Adapter.cpp (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -0,0 +1,92 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a agent1adaptor + * + * 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 "Agent1Adapter.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; +} Index: sources/bluetooth/Agent1Adapter.h =================================================================== diff -u --- sources/bluetooth/Agent1Adapter.h (revision 0) +++ sources/bluetooth/Agent1Adapter.h (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -0,0 +1,86 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp org.bluez.Agent1.xml -a agent1adaptor + * + * 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. + */ +#pragma once + +#ifndef AGENT1ADAPTOR_H +#define AGENT1ADAPTOR_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 Index: sources/bluetooth/BluetoothInterface.cpp =================================================================== diff -u -r1c97eebc80b33b429fa6e9489584ce3bf24f0bbc -r4a95b249fb87bf69d909452ba374c794b89d423b --- sources/bluetooth/BluetoothInterface.cpp (.../BluetoothInterface.cpp) (revision 1c97eebc80b33b429fa6e9489584ce3bf24f0bbc) +++ sources/bluetooth/BluetoothInterface.cpp (.../BluetoothInterface.cpp) (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -425,9 +425,47 @@ // initial scan to populate bluetooth device list startScan(); + + // It is necessary to initialize the pairing agent to assure that the pairing is + // retained after the device disconnects (Similar to how bluetoothctl does it) + initializePairingAgent(); } /*! + * \brief BluetoothInterface::initializePairingAgent + * \details Initialize and register the pairing agent components + */ +void BluetoothInterface::initializePairingAgent() { + // construct the custom bluetooth pairing agent + _pairingAgent = new BluetoothPairingAgent(); + + // construct the QDBusAbstractAdaptor class + _agentAdaptor = new Agent1Adaptor(_pairingAgent); + + // For pairing agent object to be visible on the d-bus, need to call the registerObject method + if (QDBusConnection::systemBus().registerObject(_pairingObjectPath, _pairingAgent)) { + // Get the interface reference for the org.bluez.AgentManager1 + QDBusInterface agentManager("org.bluez", "/org/bluez", "org.bluez.AgentManager1", QDBusConnection::systemBus(), this); + if (!agentManager.isValid()) { + qWarning() << Q_FUNC_INFO << agentManager.lastError().message(); + } else { + QVariant agentPath{ QVariant::fromValue(QDBusObjectPath(_pairingObjectPath)) }; + // Call the RegisterAgent method with the correct arguments, passing 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(); + } + } + } else { + // TODO need error handling + } +} + +/*! * \brief BluetoothInterface::ondoScan * \details the thread safe slot to be called when the public function doScan is called. * initializes/validates the discovery agent _agent and setups it up to start the discovery. Index: sources/bluetooth/BluetoothInterface.h =================================================================== diff -u -r1c97eebc80b33b429fa6e9489584ce3bf24f0bbc -r4a95b249fb87bf69d909452ba374c794b89d423b --- sources/bluetooth/BluetoothInterface.h (.../BluetoothInterface.h) (revision 1c97eebc80b33b429fa6e9489584ce3bf24f0bbc) +++ sources/bluetooth/BluetoothInterface.h (.../BluetoothInterface.h) (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -26,6 +26,8 @@ #include "MBluetooth.h" #include "MUIBloodPressureData.h" #include "DeviceModels.h" +#include "BluetoothPairingAgent.h" +#include "Agent1Adapter.h" // Define #define _BluetoothInterface Bluetooth::BluetoothInterface::I() @@ -63,6 +65,8 @@ const QByteArray _bloodPressureNotifyValue = QByteArray::fromHex("0100"); const QString _devicePairingBondCharacteristicUuid = "{b305b680-aee7-11e1-a730-0002a5d5c51b}"; const QString _vendorSpecificServiceUuid = "{ecbe3980-c9a2-11e1-b1bd-0002a5d5c51b}"; + const QString _pairingObjectPath = "/pairing/agent"; + const char *_invalidLocalAddress = "00:00:00:00:00:00"; const char _deviceConfirmKeyWriteCodeSuccess = '\x82'; const char *_devicePairingKeyString = "0x00EE020E"; @@ -79,6 +83,9 @@ QLowEnergyService *_serviceBattery = nullptr ; QLowEnergyService *_serviceVendorSpecific = nullptr ; + Agent1Adaptor *_agentAdaptor = nullptr ; + BluetoothPairingAgent *_pairingAgent = nullptr ; + QBluetoothDeviceInfo _temp ; quint8 _tempBatt = 0 ; @@ -184,6 +191,7 @@ void writeDevicePairingKey(); bool isVendorSpecificService (const QLowEnergyService *service ); bool isDevicePairingCharacteristicUuid (const QLowEnergyCharacteristic &vCharacteristic); + void initializePairingAgent(); signals: void didStateChange (const BluetoothData &vData); void didActionReceive (const UIBloodPressureData &vData); Index: sources/bluetooth/BluetoothPairingAgent.cpp =================================================================== diff -u --- sources/bluetooth/BluetoothPairingAgent.cpp (revision 0) +++ sources/bluetooth/BluetoothPairingAgent.cpp (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -0,0 +1,170 @@ +/*! + * \brief The Bluetooth Pairing Agent class + * \details The implementation of the interface for the generated DBus interface + * from the org.bluez.Agent1.xml. + * + * Interface Reference: + * https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt + */ + +#include "BluetoothPairingAgent.h" + +// Qt +#include + +// std +#include + +// namespace +using namespace Bluetooth; + +/*! + * \brief BluetoothPairingAgent::BluetoothPairingAgent + * \details Constructor + * \param parent - QObject parent owner object. + * Qt handles the children destruction by their parent objects life-cycle. + */ +BluetoothPairingAgent::BluetoothPairingAgent(QObject *parent) + : QObject(parent) { +} + +/*! + * \brief BluetoothPairingAgent::Release + * \details This method gets called when the service daemon + * unregisters the agent. An agent can use it to do + * cleanup tasks. There is no need to unregister the + * agent, because when this method gets called it has + * already been unregistered. + */ +void BluetoothPairingAgent::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. +} + +/*! + * \brief BluetoothPairingAgent::RequestPinCode + * \details This method gets called when the service daemon + * needs to get the passkey for an authentication. + * + * \returns The return value should be a string of 1-16 characters + * length. The string can be alphanumeric. + */ +QString BluetoothPairingAgent::RequestPinCode(const QDBusObjectPath &device) const { + Q_UNUSED(device) + return "1234"; +} + +/*! + * \brief BluetoothPairingAgent::DisplayPinCode + * \details This method gets called when the service daemon + * needs to display a pincode for an authentication. + * + * This is used during the pairing process of keyboards + * that don't support Bluetooth 2.1 Secure Simple Pairing, + * in contrast to DisplayPasskey which is used for those + * that do. + * + * This method will only ever be called once since + * older keyboards do not support typing notification. + * + * Note that the PIN will always be a 6-digit number, + * zero-padded to 6 digits. This is for harmony with + * the later specification. + * + * An empty reply should be returned. When the pincode + * needs no longer to be displayed, the Cancel method + * of the agent will be called. + */ +void BluetoothPairingAgent::DisplayPinCode(const QDBusObjectPath &device, const QString &pincode) { + Q_UNUSED(device) + emit showPinCode(pincode); +} + +/*! + * \brief BluetoothPairingAgent::RequestPasskey + * \details This method gets called when the service daemon + * needs to get the passkey for an authentication. + * + * \returns The return value should a numeric value between + * 0-999999 + */ +uint BluetoothPairingAgent::RequestPasskey(const QDBusObjectPath &device) const { + Q_UNUSED(device) + return 1234; +} + +/*! + * \brief BluetoothPairingAgent::DisplayPasskey + * \details This method gets called when the service daemon + * needs to display a passkey for an authentication. + * + * The entered parameter indicates the number of already + * typed keys on the remote side. + * + * An empty reply should be returned. When the passkey + * needs no longer to be displayed, the Cancel method + * of the agent will be called. + * + * During the pairing process this method might be + * called multiple times to update the entered value. + * + * Note that the passkey will always be a 6-digit number, + * so the display should be zero-padded at the start if + * the value contains less than 6 digits. + */ +void BluetoothPairingAgent::DisplayPasskey(const QDBusObjectPath &device, uint passkey, ushort entered) { + Q_UNUSED(device) + emit showCode(passkey, entered); +} + +/*! + * \brief BluetoothPairingAgent::RequestConfirmation + * \details This method gets called when the service daemon + * needs to confirm a passkey for an authentication. + * + * To confirm the value it should return an empty reply + * or an error in case the passkey is invalid. + * + * Note that the passkey will always be a 6-digit number, + * so the display should be zero-padded at the start if + * the value contains less than 6 digits. + */ +void BluetoothPairingAgent::RequestConfirmation(const QDBusObjectPath &device, uint passkey) { + Q_UNUSED(device) + emit showVerificationCode(passkey); +} + +/*! + * \brief BluetoothPairingAgent::RequestAuthorization + * \details This method gets called to request the user to + * authorize an incoming pairing attempt which + * would in other circumstances trigger the just-works + * model, or when the user plugged in a device that + * implements cable pairing. In the latter case, the + * device would not be connected to the adapter via + * Bluetooth yet. + */ +void BluetoothPairingAgent::RequestAuthorization(const QDBusObjectPath &device) const { + Q_UNUSED(device) + //NOTE: Not needed yet, but must be here as it is expected by bluez +} + +/*! + * \brief BluetoothPairingAgent::AuthorizeService + * \details This method gets called when the service daemon + * needs to authorize a connection/service request. + */ +void BluetoothPairingAgent::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 +} + +/*! + * \brief BluetoothPairingAgent::Cancel + * \details This method gets called to indicate that the agent + * request failed before a reply was returned. + */ +void BluetoothPairingAgent::Cancel() const { + //NOTE: Not needed, but must be here as it is expected by bluez +} Index: sources/bluetooth/BluetoothPairingAgent.h =================================================================== diff -u --- sources/bluetooth/BluetoothPairingAgent.h (revision 0) +++ sources/bluetooth/BluetoothPairingAgent.h (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -0,0 +1,41 @@ +/*! + * \brief The Bluetooth Pairing Agent class + * \details The implementation of the interface for the generated DBus interface + * from the org.bluez.Agent1.xml. + * + * Interface Reference: + * https://git.kernel.org/pub/scm/bluetooth/bluez.git/tree/doc/agent-api.txt + */ +#pragma once + +// Qt +#include +#include + +// namespace +namespace Bluetooth { + +class BluetoothPairingAgent : public QObject +{ + Q_OBJECT +public: + explicit BluetoothPairingAgent(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); +}; + +} Index: sources/model/settings/MBluetooth.h =================================================================== diff -u -rc447018bc6c50996f402e01f951d40b17a120705 -r4a95b249fb87bf69d909452ba374c794b89d423b --- sources/model/settings/MBluetooth.h (.../MBluetooth.h) (revision c447018bc6c50996f402e01f951d40b17a120705) +++ sources/model/settings/MBluetooth.h (.../MBluetooth.h) (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -58,6 +58,8 @@ eIS_Device_Done , eIS_Device_Disconnect , eIS_Device_Pairing , + eIS_Device_Pairing_Done , + eIS_Device_Pairing_Failed , eIS_Service_Start , eIS_Service_Invalid , Index: sources/view/settings/VBluetooth.cpp =================================================================== diff -u -r1c97eebc80b33b429fa6e9489584ce3bf24f0bbc -r4a95b249fb87bf69d909452ba374c794b89d423b --- sources/view/settings/VBluetooth.cpp (.../VBluetooth.cpp) (revision 1c97eebc80b33b429fa6e9489584ce3bf24f0bbc) +++ sources/view/settings/VBluetooth.cpp (.../VBluetooth.cpp) (revision 4a95b249fb87bf69d909452ba374c794b89d423b) @@ -167,6 +167,8 @@ case MBluetooth::eIS_Device_Done : case MBluetooth::eIS_Device_Disconnect : case MBluetooth::eIS_Device_Pairing : + case MBluetooth::eIS_Device_Pairing_Done : + case MBluetooth::eIS_Device_Pairing_Failed : case MBluetooth::eIS_Service_Start : case MBluetooth::eIS_Service_Invalid : @@ -267,7 +269,8 @@ case MBluetooth::eIS_Device_Done : message = tr("Device Clean Up" ); break; case MBluetooth::eIS_Device_Disconnect : message = tr("Device In Power Saving Mode" ); break; // received feedback and changed the "Disconnected" to this to avoid user confusion. case MBluetooth::eIS_Device_Pairing : message = tr("Device Is Pairing..." ); break; - + case MBluetooth::eIS_Device_Pairing_Done : message = tr("Pairing Completed..." ); break; + case MBluetooth::eIS_Device_Pairing_Failed : message = tr("Unable to Pair Device" ); break; case MBluetooth::eIS_Service_Start : message = tr("Service Scanning ..." ); break; case MBluetooth::eIS_Service_Error : message = tr("Service Error: %1" ).arg(_error ); break; case MBluetooth::eIS_Service_Invalid : message = tr("Service Invalid" ); break;