summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--daemon/appmanager.cpp15
-rw-r--r--daemon/appmanager.h3
-rw-r--r--daemon/appmsgmanager.cpp77
-rw-r--r--daemon/appmsgmanager.h30
-rw-r--r--daemon/daemon.pro12
-rw-r--r--daemon/jskitmanager.cpp77
-rw-r--r--daemon/jskitmanager.h41
-rw-r--r--daemon/jskitmanager_p.h15
-rw-r--r--daemon/manager.cpp15
-rw-r--r--daemon/manager.h8
-rw-r--r--daemon/unpacker.cpp88
-rw-r--r--daemon/unpacker.h29
-rw-r--r--daemon/watchconnector.h12
13 files changed, 395 insertions, 27 deletions
diff --git a/daemon/appmanager.cpp b/daemon/appmanager.cpp
index 34af3af..d06681e 100644
--- a/daemon/appmanager.cpp
+++ b/daemon/appmanager.cpp
@@ -29,6 +29,21 @@ QStringList AppManager::appPaths() const
QStandardPaths::LocateDirectory);
}
+const AppManager::AppInfo & AppManager::info(const QUuid &uuid) const
+{
+ return _apps.value(uuid);
+}
+
+const AppManager::AppInfo & AppManager::info(const QString &name) const
+{
+ QUuid uuid = _names.value(name);
+ if (!uuid.isNull()) {
+ return info(uuid);
+ } else {
+ return AppInfo();
+ }
+}
+
void AppManager::rescan()
{
QStringList watchedDirs = _watcher->directories();
diff --git a/daemon/appmanager.h b/daemon/appmanager.h
index 5e150ab..dc2a979 100644
--- a/daemon/appmanager.h
+++ b/daemon/appmanager.h
@@ -30,7 +30,8 @@ public:
QStringList appPaths() const;
- bool installPebbleApp(const QString &pbwFile);
+ const AppInfo & info(const QUuid &uuid) const;
+ const AppInfo & info(const QString &shortName) const;
public slots:
void rescan();
diff --git a/daemon/appmsgmanager.cpp b/daemon/appmsgmanager.cpp
new file mode 100644
index 0000000..9eb948e
--- /dev/null
+++ b/daemon/appmsgmanager.cpp
@@ -0,0 +1,77 @@
+#include "appmsgmanager.h"
+#include "unpacker.h"
+
+// TODO D-Bus server for non JS kit apps!!!!
+
+AppMsgManager::AppMsgManager(WatchConnector *watch, QObject *parent)
+ : QObject(parent), watch(watch)
+{
+ watch->setEndpointHandler(WatchConnector::watchLAUNCHER,
+ [this](const QByteArray &data) {
+ if (data.at(0) == WatchConnector::appmsgPUSH) {
+ logger()->debug() << "LAUNCHER is PUSHing" << data.toHex();
+ Unpacker u(data);
+ u.skip(1); // skip data.at(0) which we just already checked above.
+ uint transaction = u.read<quint8>();
+ QUuid uuid = u.readUuid();
+ WatchConnector::Dict dict = u.readDict();
+ if (u.bad() || !dict.contains(1)) {
+ logger()->warn() << "Failed to parse LAUNCHER message";
+ return true;
+ }
+
+ switch (dict.value(1).toInt()) {
+ case WatchConnector::launcherSTARTED:
+ logger()->debug() << "App starting in watch:" << uuid;
+ this->watch->sendMessage(WatchConnector::watchLAUNCHER,
+ buildAckMessage(transaction));
+ emit appStarted(uuid);
+ break;
+ case WatchConnector::launcherSTOPPED:
+ logger()->debug() << "App stopping in watch:" << uuid;
+ this->watch->sendMessage(WatchConnector::watchLAUNCHER,
+ buildAckMessage(transaction));
+ emit appStopped(uuid);
+ break;
+ default:
+ logger()->warn() << "LAUNCHER pushed unknown message:" << uuid << dict;
+ this->watch->sendMessage(WatchConnector::watchLAUNCHER,
+ buildNackMessage(transaction));
+ break;
+ }
+ }
+
+ return true;
+ });
+
+ watch->setEndpointHandler(WatchConnector::watchAPPLICATION_MESSAGE,
+ [this](const QByteArray &data) {
+ switch (data.at(0)) {
+ case WatchConnector::appmsgPUSH:
+ break;
+ }
+
+ return true;
+ });
+}
+
+void AppMsgManager::send(const QUuid &uuid, const QVariantMap &data)
+{
+ // TODO
+}
+
+QByteArray AppMsgManager::buildAckMessage(uint transaction)
+{
+ QByteArray ba(2, Qt::Uninitialized);
+ ba[0] = WatchConnector::appmsgACK;
+ ba[1] = transaction;
+ return ba;
+}
+
+QByteArray AppMsgManager::buildNackMessage(uint transaction)
+{
+ QByteArray ba(2, Qt::Uninitialized);
+ ba[0] = WatchConnector::appmsgNACK;
+ ba[1] = transaction;
+ return ba;
+}
diff --git a/daemon/appmsgmanager.h b/daemon/appmsgmanager.h
new file mode 100644
index 0000000..651d84e
--- /dev/null
+++ b/daemon/appmsgmanager.h
@@ -0,0 +1,30 @@
+#ifndef APPMSGMANAGER_H
+#define APPMSGMANAGER_H
+
+#include "watchconnector.h"
+
+class AppMsgManager : public QObject
+{
+ Q_OBJECT
+ LOG4QT_DECLARE_QCLASS_LOGGER
+
+public:
+ explicit AppMsgManager(WatchConnector *watch, QObject *parent);
+
+public slots:
+ void send(const QUuid &uuid, const QVariantMap &data);
+
+signals:
+ void appStarted(const QUuid &uuid);
+ void appStopped(const QUuid &uuid);
+ void dataReceived(const QUuid &uuid, const QVariantMap &data);
+
+private:
+ static QByteArray buildAckMessage(uint transaction);
+ static QByteArray buildNackMessage(uint transaction);
+
+private:
+ WatchConnector *watch;
+};
+
+#endif // APPMSGMANAGER_H
diff --git a/daemon/daemon.pro b/daemon/daemon.pro
index d4d7dbf..21c15c8 100644
--- a/daemon/daemon.pro
+++ b/daemon/daemon.pro
@@ -4,7 +4,7 @@ CONFIG += console
CONFIG += link_pkgconfig
QT -= gui
-QT += bluetooth dbus contacts
+QT += bluetooth dbus contacts qml
PKGCONFIG += mlite5 icu-i18n
CONFIG += c++11
@@ -23,7 +23,10 @@ SOURCES += \
dbusadaptor.cpp \
appmanager.cpp \
musicmanager.cpp \
- datalogmanager.cpp
+ datalogmanager.cpp \
+ unpacker.cpp \
+ appmsgmanager.cpp \
+ jskitmanager.cpp
HEADERS += \
manager.h \
@@ -37,7 +40,10 @@ HEADERS += \
appmanager.h \
musicmanager.h \
unpacker.h \
- datalogmanager.h
+ datalogmanager.h \
+ appmsgmanager.h \
+ jskitmanager.h \
+ jskitmanager_p.h
OTHER_FILES += \
org.pebbled.xml \
diff --git a/daemon/jskitmanager.cpp b/daemon/jskitmanager.cpp
new file mode 100644
index 0000000..698b22b
--- /dev/null
+++ b/daemon/jskitmanager.cpp
@@ -0,0 +1,77 @@
+#include <QQmlEngine>
+#include <QJSValueIterator>
+#include "jskitmanager.h"
+#include "jskitmanager_p.h"
+
+JSKitPebble::JSKitPebble(JSKitManager *mgr)
+ : QObject(mgr)
+{
+}
+
+JSKitPebble::~JSKitPebble()
+{
+}
+
+JSKitManager::JSKitManager(AppManager *apps, AppMsgManager *appmsg, QObject *parent) :
+ QObject(parent), _apps(apps), _appmsg(appmsg), _engine(0)
+{
+ connect(_appmsg, &AppMsgManager::appStarted, this, &JSKitManager::handleAppStarted);
+ connect(_appmsg, &AppMsgManager::appStopped, this, &JSKitManager::handleAppStopped);
+}
+
+JSKitManager::~JSKitManager()
+{
+ if (_engine) {
+ stopJsApp();
+ }
+}
+
+void JSKitManager::handleAppStarted(const QUuid &uuid)
+{
+ const auto &info = _apps->info(uuid);
+ if (!info.uuid.isNull() && info.isJSKit) {
+ logger()->debug() << "Preparing to start JSKit app" << info.uuid << info.shortName;
+ _curApp = info;
+ startJsApp();
+ }
+}
+
+void JSKitManager::handleAppStopped(const QUuid &uuid)
+{
+ if (!_curApp.uuid.isNull()) {
+ if (_curApp.uuid != uuid) {
+ logger()->warn() << "Closed app with invalid UUID";
+ }
+
+ _curApp = AppManager::AppInfo();
+ }
+}
+
+void JSKitManager::startJsApp()
+{
+ if (_engine) stopJsApp();
+ _engine = new QJSEngine(this);
+ _jspebble = new JSKitPebble(this);
+
+ QJSValue globalObj = _engine->globalObject();
+
+ globalObj.setProperty("Pebble", _engine->newQObject(_jspebble));
+
+ QJSValueIterator it(globalObj);
+ while (it.hasNext()) {
+ it.next();
+ logger()->debug() << "JS property:" << it.name();
+ }
+}
+
+void JSKitManager::stopJsApp()
+{
+ if (!_engine) return; // Nothing to do!
+
+ _engine->collectGarbage();
+
+ delete _engine;
+ _engine = 0;
+ delete _jspebble;
+ _jspebble = 0;
+}
diff --git a/daemon/jskitmanager.h b/daemon/jskitmanager.h
new file mode 100644
index 0000000..5e2880f
--- /dev/null
+++ b/daemon/jskitmanager.h
@@ -0,0 +1,41 @@
+#ifndef JSKITMANAGER_H
+#define JSKITMANAGER_H
+
+#include <QJSEngine>
+#include "appmanager.h"
+#include "appmsgmanager.h"
+
+class JSKitPebble;
+
+class JSKitManager : public QObject
+{
+ Q_OBJECT
+ LOG4QT_DECLARE_QCLASS_LOGGER
+
+public:
+ explicit JSKitManager(AppManager *apps, AppMsgManager *appmsg, QObject *parent = 0);
+ ~JSKitManager();
+
+signals:
+
+public slots:
+
+private slots:
+ void handleAppStarted(const QUuid &uuid);
+ void handleAppStopped(const QUuid &uuid);
+
+private:
+ void startJsApp();
+ void stopJsApp();
+
+private:
+ friend class JSKitPebble;
+
+ AppManager *_apps;
+ AppMsgManager *_appmsg;
+ AppManager::AppInfo _curApp;
+ QJSEngine *_engine;
+ QPointer<JSKitPebble> _jspebble;
+};
+
+#endif // JSKITMANAGER_H
diff --git a/daemon/jskitmanager_p.h b/daemon/jskitmanager_p.h
new file mode 100644
index 0000000..690a0ec
--- /dev/null
+++ b/daemon/jskitmanager_p.h
@@ -0,0 +1,15 @@
+#ifndef JSKITMANAGER_P_H
+#define JSKITMANAGER_P_H
+
+#include "jskitmanager.h"
+
+class JSKitPebble : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit JSKitPebble(JSKitManager *mgr);
+ ~JSKitPebble();
+};
+
+#endif // JSKITMANAGER_P_H
diff --git a/daemon/manager.cpp b/daemon/manager.cpp
index 7a02c86..778fdc6 100644
--- a/daemon/manager.cpp
+++ b/daemon/manager.cpp
@@ -1,19 +1,21 @@
-#include "manager.h"
-#include "dbusadaptor.h"
-
#include <QDebug>
#include <QtContacts/QContact>
#include <QtContacts/QContactPhoneNumber>
+#include "manager.h"
+#include "dbusadaptor.h"
+
Manager::Manager(Settings *settings, QObject *parent) :
QObject(parent), settings(settings),
watch(new WatchConnector(this)),
dbus(new DBusConnector(this)),
+ apps(new AppManager(this)),
voice(new VoiceCallManager(settings, this)),
notifications(new NotificationManager(settings, this)),
music(new MusicManager(watch, this)),
datalog(new DataLogManager(watch, this)),
- apps(new AppManager(this)),
+ appmsg(new AppMsgManager(watch, this)),
+ js(new JSKitManager(apps, appmsg, this)),
notification(MNotification::DeviceEvent)
{
connect(settings, SIGNAL(valueChanged(QString)), SLOT(onSettingChanged(const QString&)));
@@ -42,11 +44,6 @@ Manager::Manager(Settings *settings, QObject *parent) :
}
return true;
});
- watch->setEndpointHandler(WatchConnector::watchLAUNCHER,
- [this](const QByteArray &data) {
- logger()->debug() << "LAUNCHER msg:" << data.toHex();
- return true;
- });
connect(voice, SIGNAL(activeVoiceCallChanged()), SLOT(onActiveVoiceCallChanged()));
connect(voice, SIGNAL(error(const QString &)), SLOT(onVoiceError(const QString &)));
diff --git a/daemon/manager.h b/daemon/manager.h
index 8b2fd96..f1dd53e 100644
--- a/daemon/manager.h
+++ b/daemon/manager.h
@@ -7,6 +7,8 @@
#include "notificationmanager.h"
#include "musicmanager.h"
#include "datalogmanager.h"
+#include "appmsgmanager.h"
+#include "jskitmanager.h"
#include "appmanager.h"
#include "settings.h"
@@ -40,11 +42,13 @@ class Manager :
WatchConnector *watch;
DBusConnector *dbus;
+ AppManager *apps;
VoiceCallManager *voice;
NotificationManager *notifications;
MusicManager *music;
DataLogManager *datalog;
- AppManager *apps;
+ AppMsgManager *appmsg;
+ JSKitManager *js;
MNotification notification;
@@ -73,7 +77,7 @@ protected:
void transliterateMessage(const QString &text);
signals:
- void mprisMetadataChanged(QVariantMap);
+ void mprisMetadataChanged(const QVariantMap &metadata);
public slots:
void applyProfile();
diff --git a/daemon/unpacker.cpp b/daemon/unpacker.cpp
new file mode 100644
index 0000000..fc38020
--- /dev/null
+++ b/daemon/unpacker.cpp
@@ -0,0 +1,88 @@
+#include "unpacker.h"
+#include "watchconnector.h"
+
+QByteArray Unpacker::readBytes(int n)
+{
+ if (checkBad(n)) return QByteArray();
+ const char *u = &_buf.constData()[_offset];
+ _offset += n;
+ return QByteArray(u, n);
+}
+
+QString Unpacker::readFixedString(int n)
+{
+ if (checkBad(n)) return QString();
+ const char *u = &_buf.constData()[_offset];
+ _offset += n;
+ return QString::fromUtf8(u, strnlen(u, n));
+}
+
+QUuid Unpacker::readUuid()
+{
+ if (checkBad(16)) return QString();
+ _offset += 16;
+ return QUuid::fromRfc4122(_buf.mid(_offset - 16, 16));
+}
+
+QMap<int, QVariant> Unpacker::readDict()
+{
+ QMap<int, QVariant> d;
+ if (checkBad(1)) return d;
+
+ const int n = read<quint8>();
+
+ for (int i = 0; i < n; i++) {
+ if (checkBad(4 + 1 + 2)) return d;
+ const int key = readLE<qint32>(); // For some reason, this is little endian.
+ const int type = readLE<quint8>();
+ const int width = readLE<quint16>();
+
+ switch (type) {
+ case WatchConnector::typeBYTES:
+ d.insert(key, QVariant::fromValue(readBytes(width)));
+ break;
+ case WatchConnector::typeSTRING:
+ d.insert(key, QVariant::fromValue(readFixedString(width)));
+ break;
+ case WatchConnector::typeUINT:
+ switch (width) {
+ case sizeof(quint8):
+ d.insert(key, QVariant::fromValue(readLE<quint8>()));
+ break;
+ case sizeof(quint16):
+ d.insert(key, QVariant::fromValue(readLE<quint16>()));
+ break;
+ case sizeof(quint32):
+ d.insert(key, QVariant::fromValue(readLE<quint32>()));
+ break;
+ default:
+ _bad = true;
+ return d;
+ }
+
+ break;
+ case WatchConnector::typeINT:
+ switch (width) {
+ case sizeof(qint8):
+ d.insert(key, QVariant::fromValue(readLE<qint8>()));
+ break;
+ case sizeof(qint16):
+ d.insert(key, QVariant::fromValue(readLE<qint16>()));
+ break;
+ case sizeof(qint32):
+ d.insert(key, QVariant::fromValue(readLE<qint32>()));
+ break;
+ default:
+ _bad = true;
+ return d;
+ }
+
+ break;
+ default:
+ _bad = true;
+ return d;
+ }
+ }
+
+ return d;
+}
diff --git a/daemon/unpacker.h b/daemon/unpacker.h
index 94908cb..000c3e8 100644
--- a/daemon/unpacker.h
+++ b/daemon/unpacker.h
@@ -5,19 +5,30 @@
#include <QByteArray>
#include <QString>
#include <QUuid>
+#include <QVariantMap>
+#include <Log4Qt/Logger>
class Unpacker
{
+ LOG4QT_DECLARE_STATIC_LOGGER(logger, Unpacker)
+
public:
Unpacker(const QByteArray &data);
template <typename T>
T read();
+ template <typename T>
+ T readLE();
+
+ QByteArray readBytes(int n);
+
QString readFixedString(int n);
QUuid readUuid();
+ QMap<int, QVariant> readDict();
+
void skip(int n);
bool bad() const;
@@ -45,19 +56,13 @@ inline T Unpacker::read()
return qFromBigEndian<T>(u);
}
-inline QString Unpacker::readFixedString(int n)
-{
- if (checkBad(n)) return QString();
- const char *u = &_buf.constData()[_offset];
- _offset += n;
- return QString::fromUtf8(u, strnlen(u, n));
-}
-
-inline QUuid Unpacker::readUuid()
+template <typename T>
+inline T Unpacker::readLE()
{
- if (checkBad(16)) return QString();
- _offset += 16;
- return QUuid::fromRfc4122(_buf.mid(_offset - 16, 16));
+ if (checkBad(sizeof(T))) return 0;
+ const uchar *u = p();
+ _offset += sizeof(T);
+ return qFromLittleEndian<T>(u);
}
inline void Unpacker::skip(int n)
diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h
index c5ec332..8a7d574 100644
--- a/daemon/watchconnector.h
+++ b/daemon/watchconnector.h
@@ -122,6 +122,10 @@ public:
datalogTIMEOUT = 7
};
enum {
+ launcherSTARTED = 1,
+ launcherSTOPPED = 0
+ };
+ enum {
leadEMAIL = 0,
leadSMS = 1,
leadFACEBOOK = 2,
@@ -153,6 +157,14 @@ public:
DEFAULT_TIMEOUT_MSECS = 100
};
+ typedef QMap<int, QVariant> Dict;
+ enum DictItemType {
+ typeBYTES,
+ typeSTRING,
+ typeUINT,
+ typeINT
+ };
+
typedef std::function<bool(const QByteArray &)> EndpointHandlerFunc;
explicit WatchConnector(QObject *parent = 0);