summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2014-11-16 16:40:06 +0100
committerJavier <dev.git@javispedro.com>2014-11-16 16:40:06 +0100
commit5244f7909e04b23fbd5706dc6bcadafba21f7600 (patch)
tree88d8730dc70b68586d2d31845dc51eead9e526cb
parentd8d8fc7a0d139e7b864eee3b573bd208f823ad4f (diff)
downloadsapd-5244f7909e04b23fbd5706dc6bcadafba21f7600.tar.gz
sapd-5244f7909e04b23fbd5706dc6bcadafba21f7600.zip
initial notification support
-rw-r--r--endianhelpers.h27
-rw-r--r--hfpag.cc6
-rw-r--r--hfpag.h1
-rw-r--r--main.cc11
-rw-r--r--notificationagent.cc57
-rw-r--r--notificationagent.h27
-rw-r--r--notificationconn.cc139
-rw-r--r--notificationconn.h71
-rw-r--r--sapbtlistener.cc19
-rw-r--r--sapbtlistener.h1
-rw-r--r--sapd.pro9
-rw-r--r--saprotocol.cc23
12 files changed, 362 insertions, 29 deletions
diff --git a/endianhelpers.h b/endianhelpers.h
new file mode 100644
index 0000000..f11c669
--- /dev/null
+++ b/endianhelpers.h
@@ -0,0 +1,27 @@
+#ifndef ENDIANHELPERS_H
+#define ENDIANHELPERS_H
+
+#include <QtCore/QtEndian>
+
+namespace
+{
+
+template<typename T>
+inline T read(const QByteArray &data, int &offset)
+{
+ T unswapped;
+ qMemCopy(&unswapped, &data.constData()[offset], sizeof(T)); // Unaligned access warning!
+ offset += sizeof(T);
+ return qFromBigEndian<T>(unswapped);
+}
+
+template<typename T>
+inline void append(QByteArray &data, const T &value)
+{
+ T swapped = qToBigEndian<T>(value);
+ data.append(reinterpret_cast<const char*>(&swapped), sizeof(T));
+}
+
+}
+
+#endif // ENDIANHELPERS_H
diff --git a/hfpag.cc b/hfpag.cc
index 1d84c8f..2d0abf4 100644
--- a/hfpag.cc
+++ b/hfpag.cc
@@ -7,9 +7,15 @@ HfpAg::HfpAg(const QBluetoothAddress &addr, QObject *parent) :
connect(_socket, SIGNAL(connected()), SLOT(handleConnected()));
connect(_socket, SIGNAL(disconnected()), SLOT(handleDisconnected()));
connect(_socket, SIGNAL(readyRead()), SLOT(handleReadyRead()));
+ qDebug() << "Starting HFP connection to " << addr.toString();
_socket->connectToService(addr, 7); // HFP AG
}
+HfpAg::~HfpAg()
+{
+ qDebug() << "Destroying HFP";
+}
+
void HfpAg::send(const QString &cmd)
{
_socket->write("\r\n", 2);
diff --git a/hfpag.h b/hfpag.h
index 46e326c..e005c8a 100644
--- a/hfpag.h
+++ b/hfpag.h
@@ -10,6 +10,7 @@ class HfpAg : public QObject
Q_OBJECT
public:
explicit HfpAg(const QBluetoothAddress &addr, QObject *parent = 0);
+ ~HfpAg();
private:
void send(const QString& cmd);
diff --git a/main.cc b/main.cc
index fd042f3..b30e9b5 100644
--- a/main.cc
+++ b/main.cc
@@ -1,3 +1,4 @@
+#include <iostream>
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
#include "sapmanager.h"
@@ -5,6 +6,10 @@
#include "capabilityagent.h"
#include "hostmanageragent.h"
+#include "notificationagent.h"
+
+using std::cerr;
+using std::endl;
int main(int argc, char *argv[])
{
@@ -13,10 +18,16 @@ int main(int argc, char *argv[])
app.setOrganizationDomain("com.javispedro");
app.setApplicationName("sapd");
+ if (app.arguments().size() != 2) {
+ cerr << "Usage:: sapd <bt address>" << endl;
+ return EXIT_FAILURE;
+ }
+
SAPManager *manager = SAPManager::instance();
CapabilityAgent::registerServices(manager);
HostManagerAgent::registerServices(manager);
+ NotificationAgent::registerServices(manager);
QScopedPointer<SAPBTListener> sap_listener(new SAPBTListener);
diff --git a/notificationagent.cc b/notificationagent.cc
new file mode 100644
index 0000000..70358d0
--- /dev/null
+++ b/notificationagent.cc
@@ -0,0 +1,57 @@
+#include "sapsocket.h"
+#include "sapconnectionrequest.h"
+#include "sapserviceinfo.h"
+#include "sapchannelinfo.h"
+#include "notificationconn.h"
+#include "notificationagent.h"
+
+static NotificationAgent *agent = 0;
+static const QLatin1String notification_profile("/system/NotificationService");
+
+NotificationAgent::NotificationAgent(QObject *parent)
+ : QObject(parent), _peer(0), _socket(0)
+{
+}
+
+NotificationAgent* NotificationAgent::instance()
+{
+ if (!agent) {
+ agent = new NotificationAgent;
+ }
+ return agent;
+}
+
+void NotificationAgent::peerFound(SAPPeer *peer)
+{
+ qDebug() << "Notification peer found" << peer->peerName();
+}
+
+void NotificationAgent::requestConnection(SAPConnectionRequest *request)
+{
+ qDebug() << "Notification request connection from" << request->peer()->peerName();
+ SAPConnection *conn = request->connection();
+ new NotificationConn(conn, this);
+
+ request->accept();
+}
+
+void NotificationAgent::registerServices(SAPManager *manager)
+{
+ SAPServiceInfo service;
+ SAPChannelInfo channel;
+
+ service.setProfile(notification_profile);
+ service.setFriendlyName("Notification");
+ service.setRole(SAPServiceInfo::RoleProvider);
+ service.setVersion(1, 0);
+ service.setConnectionTimeout(0);
+
+ channel.setChannelId(104);
+ channel.setPayloadType(SAPChannelInfo::PayloadBinary);
+ channel.setQoSType(SAPChannelInfo::QoSReliabilityDisable);
+ channel.setQoSDataRate(SAPChannelInfo::QoSDataRateLow);
+ channel.setQoSPriority(SAPChannelInfo::QoSPriorityLow);
+ service.addChannel(channel);
+
+ manager->registerServiceAgent(service, instance());
+}
diff --git a/notificationagent.h b/notificationagent.h
new file mode 100644
index 0000000..1b9c9cb
--- /dev/null
+++ b/notificationagent.h
@@ -0,0 +1,27 @@
+#ifndef NOTIFICATIONAGENT_H
+#define NOTIFICATIONAGENT_H
+
+#include <QtCore/QObject>
+#include "sappeer.h"
+#include "sapmanager.h"
+#include "sapagent.h"
+
+class NotificationAgent : public QObject, public SAPAgent
+{
+ Q_OBJECT
+
+ explicit NotificationAgent(QObject *parent = 0);
+
+public:
+ static NotificationAgent * instance();
+ static void registerServices(SAPManager *manager);
+
+ void peerFound(SAPPeer *peer);
+ void requestConnection(SAPConnectionRequest *request);
+
+private:
+ SAPPeer *_peer;
+ SAPSocket *_socket;
+};
+
+#endif // NOTIFICATIONAGENT_H
diff --git a/notificationconn.cc b/notificationconn.cc
new file mode 100644
index 0000000..fde3303
--- /dev/null
+++ b/notificationconn.cc
@@ -0,0 +1,139 @@
+#include <QtCore/QDebug>
+#include <QtCore/QTimer>
+#include <QtCore/QFile>
+
+#include "sappeer.h"
+#include "endianhelpers.h"
+#include "notificationconn.h"
+
+NotificationConn::Notification::Notification()
+ : type(NotificationPopup), sequenceNumber(0), urgent(false),
+ applicationId(0), category(0),
+ count(0), sender(0),
+ notificationId(-1)
+{
+}
+
+NotificationConn::NotificationConn(SAPConnection *conn, QObject *parent)
+ : QObject(parent), _conn(conn), _socket(conn->getSocket(104))
+{
+ connect(_conn, SIGNAL(disconnected()), SLOT(deleteLater()));
+ Q_ASSERT(_socket);
+ connect(_socket, SIGNAL(connected()), SLOT(handleConnected()));
+ connect(_socket, SIGNAL(messageReceived()), SLOT(handleMessageReceived()));
+}
+
+QByteArray NotificationConn::packNotification(const Notification &n)
+{
+ QByteArray data;
+
+ append<quint8>(data, 0x10 | n.type);
+ append<quint16>(data, n.sequenceNumber | (n.urgent ? 0x4000 : 0));
+
+ append<quint16>(data, n.applicationId);
+ append<quint8>(data, n.category);
+
+ int attributeCount = 0;
+
+ // Let's count attributes first
+ if (!n.packageName.isEmpty()) attributeCount++;
+ if (n.count >= 0) attributeCount++;
+ if (!n.title.isEmpty()) attributeCount++;
+ if (n.time.isValid()) attributeCount++;
+ if (n.sender >= 0) attributeCount++;
+ if (!n.body.isEmpty()) attributeCount++;
+ if (!n.thumbnail.isEmpty()) attributeCount++;
+ if (!n.applicationName.isEmpty()) attributeCount++;
+ //TODO if (n.openInHost) attributeCount++;
+ //if (n.openInWatch) attributeCount++;
+ if (n.notificationId != -1) attributeCount++;
+
+ append<quint8>(data, attributeCount);
+
+ if (n.time.isValid()) {
+ append<quint32>(data, (NotificationTime << 24) | sizeof(qint64));
+ append<qint64>(data, n.time.toMSecsSinceEpoch());
+ }
+ if (!n.thumbnail.isEmpty()) {
+ append<quint32>(data, (NotificationThumbnail << 24) | n.thumbnail.size());
+ data.append(n.thumbnail);
+ }
+ if (!n.applicationName.isEmpty()) {
+ QByteArray str = n.applicationName.toUtf8();
+ append<quint32>(data, (NotificationApplicationName << 24) | str.length());
+ data.append(str);
+ }
+ if (!n.packageName.isEmpty()) {
+ QByteArray str = n.packageName.toUtf8();
+ append<quint32>(data, (NotificationPackageName << 24) | str.length());
+ data.append(str);
+ }
+ if (!n.title.isEmpty()) {
+ QByteArray str = n.title.toUtf8();
+ append<quint32>(data, (NotificationTitle << 24) | str.length());
+ data.append(str);
+ }
+ if (!n.body.isEmpty()) {
+ QByteArray str = n.body.toUtf8();
+ append<quint32>(data, (NotificationBody << 24) | str.length());
+ data.append(str);
+ }
+ if (n.count >= 0) {
+ append<quint32>(data, (NotificationCount << 24) | sizeof(quint16));
+ append<quint16>(data, n.count);
+ }
+ if (n.sender >= 0) {
+ append<quint32>(data, (NotificationSender << 24) | sizeof(quint16));
+ append<quint16>(data, n.sender);
+ }
+ if (n.notificationId != -1) {
+ append<quint32>(data, (NotificationId << 24) | sizeof(quint32));
+ append<quint32>(data, n.notificationId);
+ }
+
+ return data;
+}
+
+void NotificationConn::handleConnected()
+{
+ qDebug() << "Manager socket now connected!";
+
+ QTimer::singleShot(2000, this, SLOT(performTest()));
+
+}
+
+void NotificationConn::handleMessageReceived()
+{
+ QByteArray data = _socket->receive();
+ data.remove(0, 1); // Remove first null byte
+
+ qDebug() << "Got notif msg" << data.toHex();
+
+ // TODO Seems that we receive the notification ID that we should act upon.
+}
+
+void NotificationConn::performTest()
+{
+ qDebug() << "Performing notif test";
+
+ Notification n;
+ n.sequenceNumber = 1;
+ n.type = NotificationPopup;
+ n.time = QDateTime::currentDateTime();
+ n.title = "A title";
+ n.packageName = "com.example.package";
+ n.applicationName = "Example package";
+ n.body = "A long body";
+ n.sender = 0;
+ n.count = 13;
+ n.category = 0;
+ n.applicationId = 0xc2d7;
+ n.notificationId = 1;
+
+ QByteArray packet = packNotification(n);
+ packet.prepend('\0');
+
+ qDebug() << packet.toHex();
+
+ _socket->send(packet);
+}
diff --git a/notificationconn.h b/notificationconn.h
new file mode 100644
index 0000000..9be3e20
--- /dev/null
+++ b/notificationconn.h
@@ -0,0 +1,71 @@
+#ifndef NOTIFICATIONCONN_H
+#define NOTIFICATIONCONN_H
+
+#include <QtCore/QObject>
+#include <QtCore/QDateTime>
+#include "sapconnection.h"
+#include "sapsocket.h"
+
+class NotificationConn : public QObject
+{
+ Q_OBJECT
+
+public:
+ NotificationConn(SAPConnection *conn, QObject *parent = 0);
+
+protected:
+ enum NotificationType {
+ NotificationPopup = 0
+ };
+
+ enum NotificationAttributeType {
+ NotificationPackageName = 0,
+ NotificationCount = 1,
+ NotificationTitle = 2,
+ NotificationTime = 3,
+ NotificationSender = 4,
+ NotificationBody = 5,
+ NotificationThumbnail = 6,
+ NotificationApplicationName = 7,
+ NotificationOpenInWatch = 9,
+ NotificationOpenInHost = 10,
+ NotificationId = 11
+ };
+
+ struct Notification {
+ Notification();
+
+ NotificationType type;
+ int sequenceNumber;
+ bool urgent;
+ int applicationId;
+ int category;
+ QString packageName;
+ int count;
+ QString title;
+ QDateTime time;
+ int sender;
+ QString body;
+ QByteArray thumbnail;
+ QString applicationName;
+ //bool openInWatch;
+ //bool openInHost;
+ int notificationId;
+ };
+
+ QByteArray packNotification(const Notification &n);
+
+private:
+
+
+private slots:
+ void handleConnected();
+ void handleMessageReceived();
+ void performTest();
+
+private:
+ SAPConnection *_conn;
+ SAPSocket *_socket;
+};
+
+#endif // NOTIFICATIONCONN_H
diff --git a/sapbtlistener.cc b/sapbtlistener.cc
index d5cd071..1b4550a 100644
--- a/sapbtlistener.cc
+++ b/sapbtlistener.cc
@@ -169,17 +169,18 @@ void SAPBTListener::nudge(const QBluetoothAddress &address)
{
QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket, this);
+ connect(socket, SIGNAL(connected()), socket, SLOT(deleteLater()));
+ connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)),
+ this, SLOT(handleNudgeError(QBluetoothSocket::SocketError)));
+
qDebug() << "Nudging" << address.toString();
socket->connectToService(address, 2); //SAPProtocol::nudgeServiceUuid);
#if 1
- HfpAg *ag = new HfpAg(address, this);
- connect(socket, SIGNAL(disconnected()), ag, SLOT(deleteLater()));
+ // At the same time set up and HFP connection to the watch.
+ new HfpAg(address, this);
#endif
-
- connect(socket, SIGNAL(connected()), socket, SLOT(deleteLater()));
- connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), socket, SLOT(deleteLater()));
}
void SAPBTListener::acceptConnection()
@@ -196,3 +197,11 @@ void SAPBTListener::acceptConnection()
// TODO Why am I hardcoding the role here
new SAPBTPeer(SAProtocol::ClientRole, socket, this);
}
+
+void SAPBTListener::handleNudgeError(QBluetoothSocket::SocketError error)
+{
+ QBluetoothSocket *socket = static_cast<QBluetoothSocket*>(sender());
+ qWarning() << "Cannot nudge:" << error << socket->errorString();
+ socket->abort();
+ socket->deleteLater();
+}
diff --git a/sapbtlistener.h b/sapbtlistener.h
index ee2fb7e..a59a5e1 100644
--- a/sapbtlistener.h
+++ b/sapbtlistener.h
@@ -32,6 +32,7 @@ private:
private slots:
void acceptConnection();
+ void handleNudgeError(QBluetoothSocket::SocketError error);
private:
diff --git a/sapd.pro b/sapd.pro
index 7b5a154..ee30305 100644
--- a/sapd.pro
+++ b/sapd.pro
@@ -48,7 +48,9 @@ SOURCES += main.cc \
sapconnectionrequest.cc \
capabilitypeer.cc \
hostmanageragent.cc \
- hostmanagerconn.cc
+ hostmanagerconn.cc \
+ notificationagent.cc \
+ notificationconn.cc
HEADERS += \
sapbtlistener.h \
@@ -70,4 +72,7 @@ HEADERS += \
sapconnectionrequest.h \
capabilitypeer.h \
hostmanageragent.h \
- hostmanagerconn.h
+ hostmanagerconn.h \
+ notificationagent.h \
+ notificationconn.h \
+ endianhelpers.h
diff --git a/saprotocol.cc b/saprotocol.cc
index 5a21762..541f0af 100644
--- a/saprotocol.cc
+++ b/saprotocol.cc
@@ -1,5 +1,5 @@
#include <QtCore/QDebug>
-#include <QtEndian>
+#include "endianhelpers.h"
#include "saprotocol.h"
const QBluetoothUuid SAProtocol::dataServiceUuid(QLatin1String("a49eb41e-cb06-495c-9f4f-aa80a90cdf4a"));
@@ -7,27 +7,6 @@ const QBluetoothUuid SAProtocol::nudgeServiceUuid(QLatin1String("a49eb41e-cb06-4
const QLatin1String SAProtocol::capabilityDiscoveryProfile("/System/Reserved/ServiceCapabilityDiscovery");
-namespace
-{
-
-template<typename T>
-inline T read(const QByteArray &data, int &offset)
-{
- T unswapped;
- qMemCopy(&unswapped, &data.constData()[offset], sizeof(T)); // Unaligned access warning!
- offset += sizeof(T);
- return qFromBigEndian<T>(unswapped);
-}
-
-template<typename T>
-inline void append(QByteArray &data, const T &value)
-{
- T swapped = qToBigEndian<T>(value);
- data.append(reinterpret_cast<const char*>(&swapped), sizeof(T));
-}
-
-}
-
SAProtocol::SAProtocol()
{
}