summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Sterna <tomek@xiaoka.com>2015-04-08 11:52:14 +0200
committerTomasz Sterna <tomek@xiaoka.com>2015-04-09 08:45:16 +0200
commit78d1697cd63033244304f7794cf9157029e4fdb5 (patch)
tree3a1bdbe3d3706c34a8a7841a1790846599a45c8f
parentcbb0039fe542c0d8281601d25c04de487c84fa17 (diff)
Implemented firmwareUpgrade in daemon
-rw-r--r--app/pebbledinterface.cpp11
-rw-r--r--app/pebbledinterface.h2
-rw-r--r--app/qml/pages/WatchPage.qml11
-rw-r--r--app/qml/pebble.qml4
-rw-r--r--app/translations/pebble-es.ts24
-rw-r--r--app/translations/pebble-pl.ts24
-rw-r--r--app/translations/pebble.ts24
-rw-r--r--daemon/appinfo.cpp72
-rw-r--r--daemon/appinfo.h14
-rw-r--r--daemon/bankmanager.cpp8
-rw-r--r--daemon/bundle.cpp129
-rw-r--r--daemon/bundle.h44
-rw-r--r--daemon/daemon.pro8
-rw-r--r--daemon/manager.cpp79
-rw-r--r--daemon/manager.h5
-rw-r--r--daemon/uploadmanager.cpp10
-rw-r--r--daemon/uploadmanager.h2
-rw-r--r--daemon/watchconnector.cpp23
-rw-r--r--daemon/watchconnector.h33
-rw-r--r--org.pebbled.Watch.xml3
20 files changed, 364 insertions, 166 deletions
diff --git a/app/pebbledinterface.cpp b/app/pebbledinterface.cpp
index 28f0581..2099c9e 100644
--- a/app/pebbledinterface.cpp
+++ b/app/pebbledinterface.cpp
@@ -304,6 +304,12 @@ QVariantMap PebbledInterface::appInfoByUuid(const QString &uuid) const
}
}
+void PebbledInterface::notifyFirmware(bool ok)
+{
+ qDebug() << Q_FUNC_INFO << ok;
+ watch->NotifyFirmware(ok);
+}
+
void PebbledInterface::uploadFirmware(const QString &file)
{
qDebug() << Q_FUNC_INFO << file;
@@ -311,11 +317,6 @@ void PebbledInterface::uploadFirmware(const QString &file)
reply.waitForFinished();
}
-void PebbledInterface::notifyFirmware(const QString &version)
-{
- qDebug() << Q_FUNC_INFO << version;
-}
-
void PebbledInterface::onWatchConnectedChanged()
{
qDebug() << Q_FUNC_INFO;
diff --git a/app/pebbledinterface.h b/app/pebbledinterface.h
index 740df75..df0b722 100644
--- a/app/pebbledinterface.h
+++ b/app/pebbledinterface.h
@@ -73,8 +73,8 @@ public slots:
void uploadApp(const QString &uuid, int slot);
void unloadApp(int slot);
+ void notifyFirmware(bool ok);
void uploadFirmware(const QString &file);
- void notifyFirmware(const QString &version);
private slots:
void onWatchConnectedChanged();
diff --git a/app/qml/pages/WatchPage.qml b/app/qml/pages/WatchPage.qml
index 6c24ef0..55f4db0 100644
--- a/app/qml/pages/WatchPage.qml
+++ b/app/qml/pages/WatchPage.qml
@@ -5,15 +5,7 @@ import Sailfish.Silica 1.0
Page {
id: watchPage
- property bool firmwareVersionOK: false
-
- Component.onCompleted: {
- pebbled.info.firmware.forEach(function(firmware){
- if (!firmware.recovery) {
- firmwareVersionOK = (firmware.version.indexOf("v1.") !== 0)
- }
- })
- }
+ property bool firmwareVersionOK: app.firmwareVersion && app.firmwareVersion.indexOf("v1.") !== 0
SilicaFlickable {
id: flickable
@@ -78,7 +70,6 @@ Page {
from: Theme.primaryColor; to: Theme.highlightColor
duration: 2500
loops: Animation.Infinite
- easing: { type: Easing.InOutQuint }
}
}
diff --git a/app/qml/pebble.qml b/app/qml/pebble.qml
index a4b8b1f..380ff7e 100644
--- a/app/qml/pebble.qml
+++ b/app/qml/pebble.qml
@@ -32,8 +32,8 @@ ApplicationWindow
function notifyNewFirmware() {
firmwareLatest = pebbleFirmware.latest.friendlyVersion || ""
- if (firmwareLatest && firmwareVersion && firmwareVersion !== firmwareLatest) {
- pebbled.notifyFirmware(firmwareLatest);
+ if (firmwareLatest && firmwareVersion) {
+ pebbled.notifyFirmware(firmwareVersion === firmwareLatest);
}
}
diff --git a/app/translations/pebble-es.ts b/app/translations/pebble-es.ts
index 013942e..005bfe3 100644
--- a/app/translations/pebble-es.ts
+++ b/app/translations/pebble-es.ts
@@ -332,62 +332,62 @@ Si esto tarda mucho, comprueba que el reloj esté emparejado correctamente.</tra
<context>
<name>WatchPage</name>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="42"/>
+ <location filename="../qml/pages/WatchPage.qml" line="34"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="48"/>
+ <location filename="../qml/pages/WatchPage.qml" line="40"/>
<source>Ping</source>
<translation>Ping</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="54"/>
+ <location filename="../qml/pages/WatchPage.qml" line="46"/>
<source>Sync Time</source>
<translation>Ajustar hora</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="87"/>
+ <location filename="../qml/pages/WatchPage.qml" line="78"/>
<source>Installed applications</source>
<translation>Aplicaciones instaladas</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="76"/>
+ <location filename="../qml/pages/WatchPage.qml" line="68"/>
<source>Your firmware is too old to support SDKv2 applications</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="124"/>
+ <location filename="../qml/pages/WatchPage.qml" line="115"/>
<source>Uninstalling</source>
<translation>Desinstalando</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(empty slot)</source>
<translation>(hueco libre)</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(slot in use by unknown app)</source>
<translation>(hueco en uso)</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="205"/>
+ <location filename="../qml/pages/WatchPage.qml" line="196"/>
<source>Install app...</source>
<translation>Instalar app...</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="210"/>
+ <location filename="../qml/pages/WatchPage.qml" line="201"/>
<source>Companion app missing</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="223"/>
+ <location filename="../qml/pages/WatchPage.qml" line="214"/>
<source>Configure...</source>
<translation>Configurar...</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="228"/>
+ <location filename="../qml/pages/WatchPage.qml" line="219"/>
<source>Uninstall</source>
<translation>Desinstalar</translation>
</message>
diff --git a/app/translations/pebble-pl.ts b/app/translations/pebble-pl.ts
index 0291ab8..facc044 100644
--- a/app/translations/pebble-pl.ts
+++ b/app/translations/pebble-pl.ts
@@ -328,62 +328,62 @@ Jeśli nie zostaje znaleziony sprawdź czy jest w zasięgu i czy jest sparowany
<context>
<name>WatchPage</name>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="42"/>
+ <location filename="../qml/pages/WatchPage.qml" line="34"/>
<source>Info</source>
<translation>Info</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="48"/>
+ <location filename="../qml/pages/WatchPage.qml" line="40"/>
<source>Ping</source>
<translation>Ping</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="54"/>
+ <location filename="../qml/pages/WatchPage.qml" line="46"/>
<source>Sync Time</source>
<translation>Synch.Czas</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="87"/>
+ <location filename="../qml/pages/WatchPage.qml" line="78"/>
<source>Installed applications</source>
<translation>Zainstalowane aplikacje</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="76"/>
+ <location filename="../qml/pages/WatchPage.qml" line="68"/>
<source>Your firmware is too old to support SDKv2 applications</source>
<translation>Twój firmware jest zbyt stary aby obsłużyć aplikacje SDKv2</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="124"/>
+ <location filename="../qml/pages/WatchPage.qml" line="115"/>
<source>Uninstalling</source>
<translation>Odinstalowywanie</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(empty slot)</source>
<translation>(pusty slot)</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(slot in use by unknown app)</source>
<translation>(slot w użyciu przez nieznaną aplikację)</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="205"/>
+ <location filename="../qml/pages/WatchPage.qml" line="196"/>
<source>Install app...</source>
<translation>Zainstaluj aplikację...</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="210"/>
+ <location filename="../qml/pages/WatchPage.qml" line="201"/>
<source>Companion app missing</source>
<translation>Brakuje companion app</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="223"/>
+ <location filename="../qml/pages/WatchPage.qml" line="214"/>
<source>Configure...</source>
<translation>Konfiguruj...</translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="228"/>
+ <location filename="../qml/pages/WatchPage.qml" line="219"/>
<source>Uninstall</source>
<translation>Odinstaluj</translation>
</message>
diff --git a/app/translations/pebble.ts b/app/translations/pebble.ts
index dcb9a34..b9c74d3 100644
--- a/app/translations/pebble.ts
+++ b/app/translations/pebble.ts
@@ -327,62 +327,62 @@ If it can&apos;t be found please check it&apos;s available and paired in Bluetoo
<context>
<name>WatchPage</name>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="42"/>
+ <location filename="../qml/pages/WatchPage.qml" line="34"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="48"/>
+ <location filename="../qml/pages/WatchPage.qml" line="40"/>
<source>Ping</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="54"/>
+ <location filename="../qml/pages/WatchPage.qml" line="46"/>
<source>Sync Time</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="87"/>
+ <location filename="../qml/pages/WatchPage.qml" line="78"/>
<source>Installed applications</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="76"/>
+ <location filename="../qml/pages/WatchPage.qml" line="68"/>
<source>Your firmware is too old to support SDKv2 applications</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="124"/>
+ <location filename="../qml/pages/WatchPage.qml" line="115"/>
<source>Uninstalling</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(empty slot)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="196"/>
+ <location filename="../qml/pages/WatchPage.qml" line="187"/>
<source>(slot in use by unknown app)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="205"/>
+ <location filename="../qml/pages/WatchPage.qml" line="196"/>
<source>Install app...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="210"/>
+ <location filename="../qml/pages/WatchPage.qml" line="201"/>
<source>Companion app missing</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="223"/>
+ <location filename="../qml/pages/WatchPage.qml" line="214"/>
<source>Configure...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../qml/pages/WatchPage.qml" line="228"/>
+ <location filename="../qml/pages/WatchPage.qml" line="219"/>
<source>Uninstall</source>
<translation type="unfinished"></translation>
</message>
diff --git a/daemon/appinfo.cpp b/daemon/appinfo.cpp
index 91ce0be..7e726c3 100644
--- a/daemon/appinfo.cpp
+++ b/daemon/appinfo.cpp
@@ -7,7 +7,6 @@
#include "appinfo.h"
#include "unpacker.h"
#include "stm32crc.h"
-#include <quazip/quazipfile.h>
namespace {
struct ResourceEntry {
@@ -32,12 +31,11 @@ struct AppInfoData : public QSharedData {
QHash<int, QString> keyNames;
bool menuIcon;
int menuIconResource;
- QString path;
};
QLoggingCategory AppInfo::l("AppInfo");
-AppInfo::AppInfo() : d(new AppInfoData)
+AppInfo::AppInfo() : Bundle(), d(new AppInfoData)
{
d->versionCode = 0;
d->watchface = false;
@@ -47,9 +45,11 @@ AppInfo::AppInfo() : d(new AppInfoData)
d->menuIconResource = -1;
}
-AppInfo::AppInfo(const AppInfo &rhs) : d(rhs.d)
-{
-}
+AppInfo::AppInfo(const AppInfo &rhs) : Bundle(rhs), d(rhs.d)
+{}
+
+AppInfo::AppInfo(const Bundle &rhs) : Bundle(rhs), d(new AppInfoData)
+{}
AppInfo &AppInfo::operator=(const AppInfo &rhs)
{
@@ -59,12 +59,11 @@ AppInfo &AppInfo::operator=(const AppInfo &rhs)
}
AppInfo::~AppInfo()
-{
-}
+{}
bool AppInfo::isLocal() const
{
- return ! d->path.isEmpty();
+ return ! path().isEmpty();
}
bool AppInfo::isValid() const
@@ -182,7 +181,7 @@ QString AppInfo::getJSApp() const
QScopedPointer<QIODevice> appJS(openFile(AppInfo::APPJS, QIODevice::Text));
if (!appJS) {
- qCWarning(l) << "cannot find app" << d->path << "app.js";
+ qCWarning(l) << "cannot find app" << d->shortName << "app.js";
return QString();
}
@@ -192,16 +191,12 @@ QString AppInfo::getJSApp() const
AppInfo AppInfo::fromPath(const QString &path)
{
- AppInfo info;
+ AppInfo info(Bundle::fromPath(path));
- QFileInfo appPath(path);
- if (!appPath.isReadable()) {
- qCWarning(l) << "app" << appPath.absolutePath() << "is not readable";
+ if (!info.isValid()) {
return AppInfo();
}
- info.d->path = path;
-
QScopedPointer<QIODevice> appInfoJSON(info.openFile(AppInfo::INFO, QIODevice::Text));
if (!appInfoJSON) {
qCWarning(l) << "cannot find app" << path << "info json";
@@ -367,48 +362,3 @@ QImage AppInfo::decodeResourceImage(const QByteArray &data) const
return img;
}
-
-QIODevice *AppInfo::openFile(enum AppInfo::File file, QIODevice::OpenMode mode) const
-{
- QString fileName;
- switch (file) {
- case AppInfo::INFO:
- fileName = "appinfo.json";
- break;
- case AppInfo::APPJS:
- fileName = "pebble-js-app.js";
- break;
- case AppInfo::BINARY:
- fileName = "pebble-app.bin";
- break;
- case AppInfo::RESOURCES:
- fileName = "app_resources.pbpack";
- break;
- }
-
- QIODevice *dev = 0;
- QFileInfo appPath(d->path);
- if (appPath.isDir()) {
- QDir appDir(d->path);
- if (appDir.exists(fileName)) {
- dev = new QFile(appDir.absoluteFilePath(fileName));
- }
- } else if (appPath.isFile()) {
- dev = new QuaZipFile(d->path, fileName);
- }
-
- if (dev && !dev->open(QIODevice::ReadOnly | mode)) {
- delete dev;
- return 0;
- }
-
- return dev;
-}
-
-bool AppInfo::fileExists(enum AppInfo::File file) const
-{
- QIODevice *dev = openFile(file);
- bool exists = dev && dev->isOpen();
- delete dev;
- return exists;
-}
diff --git a/daemon/appinfo.h b/daemon/appinfo.h
index 849e09e..c168e21 100644
--- a/daemon/appinfo.h
+++ b/daemon/appinfo.h
@@ -6,11 +6,12 @@
#include <QHash>
#include <QImage>
#include <QLoggingCategory>
+#include "bundle.h"
#include "bankmanager.h"
class AppInfoData;
-class AppInfo
+class AppInfo : public Bundle
{
Q_GADGET
@@ -23,13 +24,6 @@ public:
};
Q_DECLARE_FLAGS(Capabilities, Capability)
- enum File {
- INFO,
- BINARY,
- RESOURCES,
- APPJS
- };
-
Q_PROPERTY(bool local READ isLocal)
Q_PROPERTY(bool valid READ isValid)
Q_PROPERTY(QUuid uuid READ uuid)
@@ -50,6 +44,7 @@ public:
public:
AppInfo();
AppInfo(const AppInfo &);
+ AppInfo(const Bundle &);
AppInfo &operator=(const AppInfo &);
~AppInfo();
@@ -77,9 +72,6 @@ public:
QByteArray getMenuIconPng() const;
QString getJSApp() const;
- QIODevice *openFile(enum File, QIODevice::OpenMode = 0) const;
- bool fileExists(enum File) const;
-
void setInvalid();
protected:
diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp
index 86577e6..6f520b8 100644
--- a/daemon/bankmanager.cpp
+++ b/daemon/bankmanager.cpp
@@ -96,6 +96,7 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot)
upload->uploadAppBinary(slot, binaryFile.data(),
[this, info, binaryFile, slot]() {
qCDebug(l) << "app binary upload succesful";
+ binaryFile->close();
// Proceed to upload the resource file
QSharedPointer<QIODevice> resourceFile(info.openFile(AppInfo::RESOURCES));
@@ -103,7 +104,7 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot)
upload->uploadAppResources(slot, resourceFile.data(),
[this, resourceFile, slot]() {
qCDebug(l) << "app resources upload succesful";
-
+ resourceFile->close();
// Upload succesful
// Tell the watch to reload the slot
refreshWatchApp(slot, [this]() {
@@ -114,10 +115,10 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot)
_refresh->start();
});
}, [this, resourceFile](int code) {
+ resourceFile->close();
qCWarning(l) << "app resources upload failed" << code;
_refresh->start();
});
-
} else {
// No resource file
// Tell the watch to reload the slot
@@ -129,7 +130,8 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot)
_refresh->start();
});
}
- }, [this](int code) {
+ }, [this, binaryFile](int code) {
+ binaryFile->close();
qCWarning(l) << "app binary upload failed" << code;
_refresh->start();
});
diff --git a/daemon/bundle.cpp b/daemon/bundle.cpp
new file mode 100644
index 0000000..bfd4b83
--- /dev/null
+++ b/daemon/bundle.cpp
@@ -0,0 +1,129 @@
+#include "bundle.h"
+#include <QSharedData>
+#include <QScopedPointer>
+#include <QDir>
+#include <QFileInfo>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <quazip/quazipfile.h>
+
+class BundleData : public QSharedData {
+public:
+ bool isValid;
+ QString path;
+ QJsonObject manifest;
+
+ BundleData() : isValid(false) {}
+};
+
+QLoggingCategory Bundle::l("Bundle");
+
+Bundle::Bundle() : b(new BundleData)
+{}
+
+Bundle::Bundle(const Bundle &rhs) : b(rhs.b)
+{}
+
+Bundle &Bundle::operator=(const Bundle &rhs)
+{
+ if (this != &rhs)
+ b.operator=(rhs.b);
+ return *this;
+}
+
+Bundle::~Bundle()
+{}
+
+QString Bundle::type() const
+{
+ return b->manifest.value("type").toString();
+}
+
+QString Bundle::path() const
+{
+ return b->path;
+}
+
+bool Bundle::isValid() const
+{
+ return b->isValid;
+}
+
+Bundle Bundle::fromPath(const QString &path)
+{
+ Bundle bundle;
+
+ QFileInfo bundlePath(path);
+ if (!bundlePath.isReadable()) {
+ qCWarning(l) << "bundle" << bundlePath.absolutePath() << "is not readable";
+ return Bundle();
+ }
+
+ bundle.b->path = path;
+
+ QScopedPointer<QIODevice> manifestJSON(bundle.openFile(Bundle::MANIFEST, QIODevice::Text));
+ if (!manifestJSON) {
+ qCWarning(l) << "cannot find" << path << "manifest json";
+ return Bundle();
+ }
+
+ QJsonParseError parseError;
+ QJsonDocument doc = QJsonDocument::fromJson(manifestJSON->readAll(), &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ qCWarning(l) << "cannot parse" << path << "manifest json" << parseError.errorString();
+ return Bundle();
+ }
+ manifestJSON->close();
+ bundle.b->manifest = doc.object();
+
+ bundle.b->isValid = true;
+ return bundle;
+}
+
+QIODevice *Bundle::openFile(enum Bundle::File file, QIODevice::OpenMode mode) const
+{
+ QString fileName;
+ switch (file) {
+ case Bundle::MANIFEST:
+ fileName = "manifest.json";
+ break;
+ case Bundle::INFO:
+ fileName = "appinfo.json";
+ break;
+ case Bundle::APPJS:
+ fileName = "pebble-js-app.js";
+ break;
+ case Bundle::BINARY:
+ fileName = b->manifest.value(type()).toObject().value("name").toString();
+ break;
+ case Bundle::RESOURCES:
+ fileName = b->manifest.value("resources").toObject().value("name").toString();
+ break;
+ }
+
+ QIODevice *dev = 0;
+ QFileInfo bundlePath(path());
+ if (bundlePath.isDir()) {
+ QDir bundleDir(path());
+ if (bundleDir.exists(fileName)) {
+ dev = new QFile(bundleDir.absoluteFilePath(fileName));
+ }
+ } else if (bundlePath.isFile()) {
+ dev = new QuaZipFile(path(), fileName);
+ }
+
+ if (dev && !dev->open(QIODevice::ReadOnly | mode)) {
+ delete dev;
+ return 0;
+ }
+
+ return dev;
+}
+
+bool Bundle::fileExists(enum Bundle::File file) const
+{
+ QIODevice *dev = openFile(file);
+ bool exists = dev && dev->isOpen();
+ delete dev;
+ return exists;
+}
diff --git a/daemon/bundle.h b/daemon/bundle.h
new file mode 100644
index 0000000..c2dc86b
--- /dev/null
+++ b/daemon/bundle.h
@@ -0,0 +1,44 @@
+#ifndef BUNDLE_H
+#define BUNDLE_H
+
+#include <QLoggingCategory>
+#include <QSharedDataPointer>
+#include <QString>
+#include <QIODevice>
+
+class BundleData;
+
+class Bundle
+{
+ Q_GADGET
+
+ static QLoggingCategory l;
+
+public:
+ enum File {
+ MANIFEST,
+ INFO,
+ BINARY,
+ RESOURCES,
+ APPJS
+ };
+
+ static Bundle fromPath(const QString &path);
+
+ Bundle();
+ Bundle(const Bundle &);
+ Bundle &operator=(const Bundle &);
+ ~Bundle();
+
+ QString type() const;
+ QString path() const;
+ bool isValid() const;
+
+ QIODevice *openFile(enum File, QIODevice::OpenMode = 0) const;
+ bool fileExists(enum File) const;
+
+private:
+ QSharedDataPointer<BundleData> b;
+};
+
+#endif // BUNDLE_H
diff --git a/daemon/daemon.pro b/daemon/daemon.pro
index 35eb302..4d693ae 100644
--- a/daemon/daemon.pro
+++ b/daemon/daemon.pro
@@ -28,7 +28,8 @@ SOURCES += \
packer.cpp \
bankmanager.cpp \
uploadmanager.cpp \
- stm32crc.cpp
+ stm32crc.cpp \
+ bundle.cpp
HEADERS += \
manager.h \
@@ -49,7 +50,10 @@ HEADERS += \
packer.h \
bankmanager.h \
uploadmanager.h \
- stm32crc.h
+ stm32crc.h \
+ bundle.h
+
+DBUS_ADAPTORS += ../org.pebbled.Watch.xml
OTHER_FILES += js/typedarray.js
diff --git a/daemon/manager.cpp b/daemon/manager.cpp
index ca3830f..345fb1d 100644
--- a/daemon/manager.cpp
+++ b/daemon/manager.cpp
@@ -4,6 +4,7 @@
#include "manager.h"
#include "watch_adaptor.h"
+#include "bundle.h"
Manager::Manager(Settings *settings, QObject *parent) :
QObject(parent), l(metaObject()->className()), settings(settings),
@@ -348,6 +349,67 @@ void Manager::onAppClosed(const QUuid &uuid)
emit proxy->AppUuidChanged();
}
+bool Manager::uploadFirmware(bool recovery, const QString &file)
+{
+ qCDebug(l) << "about to upload" << (recovery ? "recovery" : "normal")
+ << "firmware:" << file;
+
+ Bundle bundle(Bundle::fromPath(file));
+ if (!bundle.isValid()) {
+ qCWarning(l) << file << "is invalid bundle";
+ return false;
+ }
+
+ if (!bundle.fileExists(Bundle::BINARY) || !bundle.fileExists(Bundle::RESOURCES)) {
+ qCWarning(l) << file << "is missing binary or resource";
+ return false;
+ }
+
+ watch->systemMessage(WatchConnector::systemFIRMWARE_START,
+ [this, recovery, bundle](const QByteArray &data) {
+ qCDebug(l) << "got response to firmware start" << data.toHex();
+
+ QSharedPointer<QIODevice> resourceFile(bundle.openFile(Bundle::RESOURCES));
+ if (!resourceFile) {
+ qCWarning(l) << "failed to open" << bundle.path() << "resource";
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ return true;
+ }
+
+ upload->uploadFirmwareResources(resourceFile.data(),
+ [this, recovery, bundle, resourceFile]() {
+ qCDebug(l) << "firmware resource upload succesful";
+ resourceFile->close();
+ // Proceed to upload the resource file
+ QSharedPointer<QIODevice> binaryFile(bundle.openFile(Bundle::BINARY));
+ if (binaryFile) {
+ upload->uploadFirmwareBinary(recovery, binaryFile.data(),
+ [this, binaryFile]() {
+ binaryFile->close();
+ qCDebug(l) << "firmware binary upload succesful";
+ // Upload succesful
+ watch->systemMessage(WatchConnector::systemFIRMWARE_COMPLETE);
+ }, [this, binaryFile](int code) {
+ binaryFile->close();
+ qCWarning(l) << "firmware binary upload failed" << code;
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ });
+ } else {
+ qCWarning(l) << "failed to open" << bundle.path() << "binary";
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ }
+ }, [this, resourceFile](int code) {
+ resourceFile->close();
+ qCWarning(l) << "firmware resource upload failed" << code;
+ watch->systemMessage(WatchConnector::systemFIRMWARE_FAIL);
+ });
+
+ return true;
+ });
+
+ return true;
+}
+
QStringList PebbledProxy::AppSlots() const
{
const int num_slots = manager()->bank->numSlots();
@@ -512,3 +574,20 @@ void PebbledProxy::UploadApp(const QString &uuid, int slot)
"Cannot upload application");
}
}
+
+void PebbledProxy::NotifyFirmware(bool ok)
+{
+ Q_ASSERT(calledFromDBus());
+ manager()->watch->sendFirmwareState(ok);
+}
+
+void PebbledProxy::UploadFirmware(bool recovery, const QString &file)
+{
+ Q_ASSERT(calledFromDBus());
+ const QDBusMessage msg = message();
+
+ if (!manager()->uploadFirmware(recovery, file)) {
+ sendErrorReply(msg.interface() + ".Error.CannotUpload",
+ "Cannot upload firmware");
+ }
+}
diff --git a/daemon/manager.h b/daemon/manager.h
index a5e05ad..d04b730 100644
--- a/daemon/manager.h
+++ b/daemon/manager.h
@@ -71,6 +71,8 @@ public:
QString findPersonByNumber(QString number);
QString getCurrentProfile() const;
+ bool uploadFirmware(bool recovery, const QString &file);
+
protected:
void transliterateMessage(const QString &text);
@@ -147,6 +149,9 @@ public slots:
void UnloadApp(int slot);
void UploadApp(const QString &uuid, int slot);
+ void NotifyFirmware(bool ok);
+ void UploadFirmware(bool recovery, const QString &file);
+
signals:
void NameChanged();
void AddressChanged();
diff --git a/daemon/uploadmanager.cpp b/daemon/uploadmanager.cpp
index b379880..a894194 100644
--- a/daemon/uploadmanager.cpp
+++ b/daemon/uploadmanager.cpp
@@ -73,6 +73,16 @@ uint UploadManager::uploadFile(const QString &filename, QIODevice *device, Succe
return upload(WatchConnector::uploadFILE, 0, filename, device, -1, successCallback, errorCallback, progressCallback);
}
+uint UploadManager::uploadFirmwareBinary(bool recovery, QIODevice *device, SuccessCallback successCallback, ErrorCallback errorCallback, ProgressCallback progressCallback)
+{
+ return upload(recovery ? WatchConnector::uploadRECOVERY : WatchConnector::uploadFIRMWARE, 0, QString(), device, -1, successCallback, errorCallback, progressCallback);
+}
+
+uint UploadManager::uploadFirmwareResources(QIODevice *device, SuccessCallback successCallback, ErrorCallback errorCallback, ProgressCallback progressCallback)
+{
+ return upload(WatchConnector::uploadSYS_RESOURCES, 0, QString(), device, -1, successCallback, errorCallback, progressCallback);
+}
+
void UploadManager::cancel(uint id, int code)
{
if (_pending.empty()) {
diff --git a/daemon/uploadmanager.h b/daemon/uploadmanager.h
index 1980f96..22e4f80 100644
--- a/daemon/uploadmanager.h
+++ b/daemon/uploadmanager.h
@@ -24,6 +24,8 @@ public:
uint uploadAppBinary(int slot, QIODevice *device, SuccessCallback successCallback = SuccessCallback(), ErrorCallback errorCallback = ErrorCallback(), ProgressCallback progressCallback = ProgressCallback());
uint uploadAppResources(int slot, QIODevice *device, SuccessCallback successCallback = SuccessCallback(), ErrorCallback errorCallback = ErrorCallback(), ProgressCallback progressCallback = ProgressCallback());
uint uploadFile(const QString &filename, QIODevice *device, SuccessCallback successCallback = SuccessCallback(), ErrorCallback errorCallback = ErrorCallback(), ProgressCallback progressCallback = ProgressCallback());
+ uint uploadFirmwareBinary(bool recovery, QIODevice *device, SuccessCallback successCallback = SuccessCallback(), ErrorCallback errorCallback = ErrorCallback(), ProgressCallback progressCallback = ProgressCallback());
+ uint uploadFirmwareResources(QIODevice *device, SuccessCallback successCallback = SuccessCallback(), ErrorCallback errorCallback = ErrorCallback(), ProgressCallback progressCallback = ProgressCallback());
void cancel(uint id, int code = 0);
diff --git a/daemon/watchconnector.cpp b/daemon/watchconnector.cpp
index ec9bc20..54048de 100644
--- a/daemon/watchconnector.cpp
+++ b/daemon/watchconnector.cpp
@@ -433,15 +433,23 @@ void WatchConnector::sendPhoneVersion()
sendMessage(watchPHONE_VERSION, res);
}
-void WatchConnector::ping(uint val)
+void WatchConnector::systemMessage(SystemMessage msg, const EndpointHandlerFunc &callback)
{
QByteArray res;
res.append((char)0);
+ res.append((char)msg);
+ sendMessage(watchSYSTEM_MESSAGE, res, callback);
+}
- res.append((char)((val >> 24) & 0xff));
- res.append((char)((val >> 16) & 0xff));
- res.append((char)((val >> 8) & 0xff));
- res.append((char)(val & 0xff));
+void WatchConnector::ping(uint cookie)
+{
+ QByteArray res;
+ res.append((char)0);
+
+ res.append((char)((cookie >> 24) & 0xff));
+ res.append((char)((cookie >> 16) & 0xff));
+ res.append((char)((cookie >> 8) & 0xff));
+ res.append((char)(cookie & 0xff));
sendMessage(watchPING, res);
}
@@ -513,6 +521,11 @@ void WatchConnector::sendMusicNowPlaying(QString track, QString album, QString a
sendMessage(watchMUSIC_CONTROL, res);
}
+void WatchConnector::sendFirmwareState(bool ok)
+{
+ systemMessage(ok ? systemFIRMWARE_UP_TO_DATE : systemFIRMWARE_OUT_OF_DATE);
+}
+
void WatchConnector::phoneControl(char act, uint cookie, QStringList datas)
{
QByteArray head;
diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h
index 7ee9bc8..8d78356 100644
--- a/daemon/watchconnector.h
+++ b/daemon/watchconnector.h
@@ -1,32 +1,3 @@
-/*
- Copyright (C) 2014 Jouni Roivas
- All rights reserved.
-
- You may use this file under the terms of BSD license as follows:
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the authors nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
#ifndef WATCHCONNECTOR_H
#define WATCHCONNECTOR_H
@@ -246,7 +217,8 @@ public slots:
void reconnect();
void sendMessage(uint endpoint, const QByteArray &data, const EndpointHandlerFunc &callback = EndpointHandlerFunc());
- void ping(uint val);
+ void ping(uint cookie);
+ void systemMessage(SystemMessage msg, const EndpointHandlerFunc &callback = EndpointHandlerFunc());
void time();
void sendNotification(uint lead, QString sender, QString data, QString subject);
@@ -256,6 +228,7 @@ public slots:
void sendTwitterNotification(QString sender, QString data);
void sendMusicNowPlaying(QString track, QString album, QString artist);
void sendPhoneVersion();
+ void sendFirmwareState(bool ok);
void buildData(QByteArray &res, QStringList data);
QByteArray buildMessageData(uint lead, QStringList data);
diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml
index 3eae1ab..4c10bdb 100644
--- a/org.pebbled.Watch.xml
+++ b/org.pebbled.Watch.xml
@@ -72,6 +72,9 @@
<signal name="AllAppsChanged"/>
<!-- Firmware management methods -->
+ <method name="NotifyFirmware">
+ <arg name="ok" type="b" direction="in"/>
+ </method>
<method name="UploadFirmware">
<arg name="recovery" type="b" direction="in"/>
<arg name="file" type="s" direction="in"/>