diff options
| author | Tomasz Sterna <tomek@xiaoka.com> | 2015-04-08 11:52:14 +0200 |
|---|---|---|
| committer | Tomasz Sterna <tomek@xiaoka.com> | 2015-04-09 08:45:16 +0200 |
| commit | 78d1697cd63033244304f7794cf9157029e4fdb5 (patch) | |
| tree | 3a1bdbe3d3706c34a8a7841a1790846599a45c8f | |
| parent | cbb0039fe542c0d8281601d25c04de487c84fa17 (diff) | |
Implemented firmwareUpgrade in daemon
| -rw-r--r-- | app/pebbledinterface.cpp | 11 | ||||
| -rw-r--r-- | app/pebbledinterface.h | 2 | ||||
| -rw-r--r-- | app/qml/pages/WatchPage.qml | 11 | ||||
| -rw-r--r-- | app/qml/pebble.qml | 4 | ||||
| -rw-r--r-- | app/translations/pebble-es.ts | 24 | ||||
| -rw-r--r-- | app/translations/pebble-pl.ts | 24 | ||||
| -rw-r--r-- | app/translations/pebble.ts | 24 | ||||
| -rw-r--r-- | daemon/appinfo.cpp | 72 | ||||
| -rw-r--r-- | daemon/appinfo.h | 14 | ||||
| -rw-r--r-- | daemon/bankmanager.cpp | 8 | ||||
| -rw-r--r-- | daemon/bundle.cpp | 129 | ||||
| -rw-r--r-- | daemon/bundle.h | 44 | ||||
| -rw-r--r-- | daemon/daemon.pro | 8 | ||||
| -rw-r--r-- | daemon/manager.cpp | 79 | ||||
| -rw-r--r-- | daemon/manager.h | 5 | ||||
| -rw-r--r-- | daemon/uploadmanager.cpp | 10 | ||||
| -rw-r--r-- | daemon/uploadmanager.h | 2 | ||||
| -rw-r--r-- | daemon/watchconnector.cpp | 23 | ||||
| -rw-r--r-- | daemon/watchconnector.h | 33 | ||||
| -rw-r--r-- | org.pebbled.Watch.xml | 3 |
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't be found please check it'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"/> |
