diff options
| -rw-r--r-- | daemon/bankmanager.cpp | 134 | ||||
| -rw-r--r-- | daemon/bankmanager.h | 14 | ||||
| -rw-r--r-- | daemon/daemon.pro | 8 | ||||
| -rw-r--r-- | daemon/manager.cpp | 14 | ||||
| -rw-r--r-- | daemon/manager.h | 3 | ||||
| -rw-r--r-- | daemon/stm32crc.cpp | 119 | ||||
| -rw-r--r-- | daemon/stm32crc.h | 24 | ||||
| -rw-r--r-- | daemon/uploadmanager.cpp | 266 | ||||
| -rw-r--r-- | daemon/uploadmanager.h | 65 | ||||
| -rw-r--r-- | daemon/watchconnector.h | 16 | ||||
| -rw-r--r-- | org.pebbled.Watch.xml | 4 |
11 files changed, 643 insertions, 24 deletions
diff --git a/daemon/bankmanager.cpp b/daemon/bankmanager.cpp index 194ec77..fe5dc21 100644 --- a/daemon/bankmanager.cpp +++ b/daemon/bankmanager.cpp @@ -1,11 +1,19 @@ +#include <QFile> +#include <QDir> #include "unpacker.h" #include "packer.h" #include "bankmanager.h" -BankManager::BankManager(WatchConnector *watch, AppManager *apps, QObject *parent) : - QObject(parent), watch(watch), apps(apps) +BankManager::BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent) : + QObject(parent), watch(watch), upload(upload), apps(apps), _refresh(new QTimer(this)) { - connect(watch, &WatchConnector::connectedChanged, this, &BankManager::handleWatchConnected); + connect(watch, &WatchConnector::connectedChanged, + this, &BankManager::handleWatchConnected); + + _refresh->setInterval(0); + _refresh->setSingleShot(true); + connect(_refresh, &QTimer::timeout, + this, &BankManager::refresh); } int BankManager::numSlots() const @@ -32,9 +40,82 @@ bool BankManager::uploadApp(const QUuid &uuid, int slot) return false; } - // TODO + QDir appDir(info.path()); + + logger()->debug() << "about to install app from" << appDir.absolutePath() << "into slot" << slot; + + QFile *binaryFile = new QFile(appDir.absoluteFilePath("pebble-app.bin"), this); + if (!binaryFile->open(QIODevice::ReadOnly)) { + logger()->warn() << "failed to open" << binaryFile->fileName() << ":" << binaryFile->errorString(); + delete binaryFile; + return false; + } + + logger()->debug() << "binary file size is" << binaryFile->size(); + + QFile *resourceFile = 0; + if (appDir.exists("app_resources.pbpack")) { + resourceFile = new QFile(appDir.absoluteFilePath("app_resources.pbpack"), this); + if (!resourceFile->open(QIODevice::ReadOnly)) { + logger()->warn() << "failed to open" << resourceFile->fileName() << ":" << resourceFile->errorString(); + delete resourceFile; + return false; + } + } + + // Mark the slot as used, but without any app, just in case. + _slots[slot].used = true; + _slots[slot].name.clear(); + _slots[slot].uuid = QUuid(); + + upload->upload(WatchConnector::uploadBINARY, slot, binaryFile, -1, + [this, binaryFile, resourceFile, slot]() { + logger()->debug() << "app binary upload succesful"; + delete binaryFile; + + // Proceed to upload the resource file + if (resourceFile) { + upload->upload(WatchConnector::uploadRESOURCES, slot, resourceFile, -1, + [this, resourceFile, slot]() { + logger()->debug() << "app resources upload succesful"; + delete resourceFile; + + // Upload succesful + // Tell the watch to reload the slot + refreshWatchApp(slot, [this]() { + logger()->debug() << "app refresh succesful"; + _refresh->start(); + }, [this](int code) { + logger()->warn() << "app refresh failed" << code; + _refresh->start(); + }); + }, [this, resourceFile](int code) { + logger()->warn() << "app resources upload failed" << code; + delete resourceFile; + + _refresh->start(); + }); + + } else { + // No resource file + // Tell the watch to reload the slot + refreshWatchApp(slot, [this]() { + logger()->debug() << "app refresh succesful"; + _refresh->start(); + }, [this](int code) { + logger()->warn() << "app refresh failed" << code; + _refresh->start(); + }); + } + }, [this, binaryFile, resourceFile](int code) { + logger()->warn() << "app binary upload failed" << code; + delete binaryFile; + delete resourceFile; + + _refresh->start(); + }); - return false; + return true; } bool BankManager::unloadApp(int slot) @@ -76,7 +157,7 @@ bool BankManager::unloadApp(int slot) break; } - QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + _refresh->start(); return true; }); @@ -169,24 +250,43 @@ int BankManager::findUnusedSlot() const return -1; } +void BankManager::refreshWatchApp(int slot, std::function<void ()> successCallback, std::function<void (int)> errorCallback) +{ + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::appmgrREFRESH_APP); + p.write<quint32>(slot); + + watch->sendMessage(WatchConnector::watchAPP_MANAGER, msg, + [this, successCallback, errorCallback](const QByteArray &data) { + Unpacker u(data); + if (u.read<quint8>() != WatchConnector::appmgrREFRESH_APP) { + return false; + } + int code = u.read<quint32>(); + if (code == Success) { + if (successCallback) { + successCallback(); + } + } else { + if (errorCallback) { + errorCallback(code); + } + } + + return true; + }); +} + void BankManager::handleWatchConnected() { if (watch->isConnected()) { - refresh(); + _refresh->start(); } } #if 0 - -void WatchConnector::getAppbankStatus(const std::function<void(const QString &s)>& callback) -{ - sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_STATUS), - [this, callback](const QByteArray &data) { - - }); -} - -void WatchConnector::getAppbankUuids(const function<void(const QList<QUuid> &)>& callback) +void BankManager::getAppbankUuids(const function<void(const QList<QUuid> &)>& callback) { sendMessage(watchAPP_MANAGER, QByteArray(1, appmgrGET_APPBANK_UUIDS), [this, callback](const QByteArray &data) { diff --git a/daemon/bankmanager.h b/daemon/bankmanager.h index 28729b9..6abedc8 100644 --- a/daemon/bankmanager.h +++ b/daemon/bankmanager.h @@ -2,6 +2,7 @@ #define BANKMANAGER_H #include "watchconnector.h" +#include "uploadmanager.h" #include "appmanager.h" class BankManager : public QObject @@ -10,11 +11,10 @@ class BankManager : public QObject LOG4QT_DECLARE_QCLASS_LOGGER public: - explicit BankManager(WatchConnector *watch, AppManager *apps, QObject *parent = 0); + explicit BankManager(WatchConnector *watch, UploadManager *upload, AppManager *apps, QObject *parent = 0); int numSlots() const; - signals: void slotsChanged(); @@ -26,6 +26,7 @@ public slots: private: int findUnusedSlot() const; + void refreshWatchApp(int slot, std::function<void()> successCallback, std::function<void(int)> errorCallback); private slots: @@ -33,8 +34,16 @@ private slots: private: WatchConnector *watch; + UploadManager *upload; AppManager *apps; + enum ResultCodes { + Success = 1, + BankInUse = 2, + InvalidCommand = 3, + GeneralFailure = 4 + }; + struct SlotInfo { bool used; quint32 id; @@ -46,6 +55,7 @@ private: }; QVector<SlotInfo> _slots; + QTimer *_refresh; }; #endif // BANKMANAGER_H diff --git a/daemon/daemon.pro b/daemon/daemon.pro index 21ebffa..6eea288 100644 --- a/daemon/daemon.pro +++ b/daemon/daemon.pro @@ -29,7 +29,9 @@ SOURCES += \ appinfo.cpp \ jskitobjects.cpp \ packer.cpp \ - bankmanager.cpp + bankmanager.cpp \ + uploadmanager.cpp \ + stm32crc.cpp HEADERS += \ manager.h \ @@ -48,7 +50,9 @@ HEADERS += \ appinfo.h \ jskitobjects.h \ packer.h \ - bankmanager.h + bankmanager.h \ + uploadmanager.h \ + stm32crc.h OTHER_FILES += \ ../log4qt-debug.conf \ diff --git a/daemon/manager.cpp b/daemon/manager.cpp index 27bb870..136d7f3 100644 --- a/daemon/manager.cpp +++ b/daemon/manager.cpp @@ -10,8 +10,9 @@ Manager::Manager(Settings *settings, QObject *parent) : proxy(new PebbledProxy(this)), watch(new WatchConnector(this)), dbus(new DBusConnector(this)), + upload(new UploadManager(watch, this)), apps(new AppManager(this)), - bank(new BankManager(watch, apps, this)), + bank(new BankManager(watch, upload, apps, this)), voice(new VoiceCallManager(settings, this)), notifications(new NotificationManager(settings, this)), music(new MusicManager(watch, this)), @@ -510,3 +511,14 @@ void PebbledProxy::UnloadApp(uint slot) "Cannot unload application"); } } + +void PebbledProxy::UploadApp(const QString &uuid, uint slot) +{ + Q_ASSERT(calledFromDBus()); + const QDBusMessage msg = message(); + + if (!manager()->bank->uploadApp(QUuid(uuid), slot)) { + sendErrorReply(msg.interface() + ".Error.CannotUpload", + "Cannot upload application"); + } +} diff --git a/daemon/manager.h b/daemon/manager.h index 0e55190..f27da98 100644 --- a/daemon/manager.h +++ b/daemon/manager.h @@ -3,6 +3,7 @@ #include "watchconnector.h" #include "dbusconnector.h" +#include "uploadmanager.h" #include "voicecallmanager.h" #include "notificationmanager.h" #include "musicmanager.h" @@ -45,6 +46,7 @@ class Manager : public QObject, protected QDBusContext WatchConnector *watch; DBusConnector *dbus; + UploadManager *upload; AppManager *apps; BankManager *bank; VoiceCallManager *voice; @@ -146,6 +148,7 @@ public slots: void SendAppConfigurationData(const QString &uuid, const QString &data); void UnloadApp(uint slot); + void UploadApp(const QString &uuid, uint slot); signals: void NameChanged(); diff --git a/daemon/stm32crc.cpp b/daemon/stm32crc.cpp new file mode 100644 index 0000000..dd09f38 --- /dev/null +++ b/daemon/stm32crc.cpp @@ -0,0 +1,119 @@ +#include "stm32crc.h" + +/** Precomputed CRC polynomial + * Generated by pycrc v0.8.2, http://www.tty1.net/pycrc/ + * using the configuration: + * Width = 32 + * Poly = 0x04c11db7 + * XorIn = 0xffffffff + * ReflectIn = False + * XorOut = 0xffffffff + * ReflectOut = False + * Algorithm = table-driven + * Modified to use STM32-like word size + *****************************************************************************/ +static const quint32 crc_table[256] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +Stm32Crc::Stm32Crc() +{ + reset(); +} + +void Stm32Crc::reset() +{ + crc = 0xFFFFFFFFU; +} + +void Stm32Crc::addData(const char *data, int length) +{ + const int word_len = sizeof(quint32); + int i = 0; + + Q_ASSERT(length % word_len == 0); + Q_ASSERT(quintptr(data) % word_len == 0); + + for (; i < (length/word_len)*word_len; i+=word_len) { + const quint32 *word = reinterpret_cast<const quint32*>(&data[i]); + + crc ^= *word; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + crc = (crc << 8) ^ crc_table[(crc >> 24) & 0xFF]; + } +} + +void Stm32Crc::addData(const QByteArray &data) +{ + addData(data.constData(), data.length()); +} + +quint32 Stm32Crc::result() const +{ + return crc; +} diff --git a/daemon/stm32crc.h b/daemon/stm32crc.h new file mode 100644 index 0000000..1361de3 --- /dev/null +++ b/daemon/stm32crc.h @@ -0,0 +1,24 @@ +#ifndef STM32CRC_H +#define STM32CRC_H + +#include <QByteArray> + +class Stm32Crc +{ +public: + Stm32Crc(); + + void reset(); + + // Data size must be multiple of 4, data must be aligned to 4. + + void addData(const char *data, int length); + void addData(const QByteArray &data); + + quint32 result() const; + +private: + quint32 crc; +}; + +#endif // STM32CRC_H diff --git a/daemon/uploadmanager.cpp b/daemon/uploadmanager.cpp new file mode 100644 index 0000000..89d70f7 --- /dev/null +++ b/daemon/uploadmanager.cpp @@ -0,0 +1,266 @@ +#include "uploadmanager.h" +#include "unpacker.h" +#include "packer.h" +#include "stm32crc.h" + +static const int CHUNK_SIZE = 2000; +using std::function; + +UploadManager::UploadManager(WatchConnector *watch, QObject *parent) : + QObject(parent), watch(watch), _lastUploadId(0), _state(StateNotStarted) +{ + watch->setEndpointHandler(WatchConnector::watchPUTBYTES, + [this](const QByteArray &msg) { + if (_pending.empty()) { + logger()->warn() << "putbytes message, but queue is empty!"; + return false; + } + handleMessage(msg); + return true; + }); +} + +uint UploadManager::upload(WatchConnector::UploadType type, int index, QIODevice *device, int size, + function<void()> successCallback, function<void(int)> errorCallback) +{ + PendingUpload upload; + upload.id = ++_lastUploadId; + upload.type = type; + upload.index = index; + upload.device = device; + if (size < 0) { + upload.remaining = device->size(); + } else { + upload.remaining = size; + } + upload.successCallback = successCallback; + upload.errorCallback = errorCallback; + + if (upload.remaining <= 0) { + logger()->warn() << "upload is empty"; + if (errorCallback) { + errorCallback(-1); + return -1; + } + } + + _pending.enqueue(upload); + + if (_pending.size() == 1) { + startNextUpload(); + } + + return upload.id; +} + +void UploadManager::cancel(uint id, int code) +{ + if (_pending.empty()) { + logger()->warn() << "cannot cancel, empty queue"; + return; + } + + if (id == _pending.head().id) { + PendingUpload upload = _pending.dequeue(); + logger()->debug() << "aborting current upload" << id << "(code:" << code << ")"; + + if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) { + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::putbytesABORT); + p.write<quint32>(_token); + + logger()->debug() << "sending abort for upload" << id; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + } + + _state = StateNotStarted; + _token = 0; + + if (upload.errorCallback) { + upload.errorCallback(code); + } + + if (!_pending.empty()) { + startNextUpload(); + } + } else { + for (int i = 1; i < _pending.size(); ++i) { + if (_pending[i].id == id) { + logger()->debug() << "cancelling upload" << id << "(code:" << code << ")"; + if (_pending[i].errorCallback) { + _pending[i].errorCallback(code); + } + _pending.removeAt(i); + return; + } + } + logger()->warn() << "cannot cancel, id" << id << "not found"; + } +} + +void UploadManager::startNextUpload() +{ + Q_ASSERT(!_pending.empty()); + Q_ASSERT(_state == StateNotStarted); + + PendingUpload &upload = _pending.head(); + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::putbytesINIT); + p.write<quint32>(upload.remaining); + p.write<quint8>(upload.type); + p.write<quint8>(upload.index); + + _state = StateWaitForToken; + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); +} + +void UploadManager::handleMessage(const QByteArray &msg) +{ + Q_ASSERT(!_pending.empty()); + PendingUpload &upload = _pending.head(); + + logger()->debug() << "get message" << msg.toHex(); + + Unpacker u(msg); + int status = u.read<quint8>(); + + if (u.bad() || status != 1) { + logger()->warn() << "upload" << upload.id << "got error code=" << status; + cancel(upload.id, status); + return; + } + + quint32 recv_token = u.read<quint32>(); + + if (u.bad()) { + logger()->warn() << "upload" << upload.id << ": could not read the token"; + cancel(upload.id, -1); + return; + } + + if (_state != StateNotStarted && _state != StateWaitForToken && _state != StateComplete) { + if (recv_token != _token) { + logger()->warn() << "upload" << upload.id << ": invalid token"; + cancel(upload.id, -1); + return; + } + } + + switch (_state) { + case StateNotStarted: + logger()->warn() << "got packet when upload is not started"; + break; + case StateWaitForToken: + logger()->debug() << "token received"; + _token = recv_token; + _state = StateInProgress; + + /* fallthrough */ + case StateInProgress: + logger()->debug() << "moving to the next chunk"; + if (upload.remaining > 0) { + if (!uploadNextChunk(upload)) { + cancel(upload.id, -1); + return; + } + } else { + logger()->debug() << "no additional chunks, commit"; + _state = StateCommit; + if (!commit(upload)) { + cancel(upload.id, -1); + return; + } + } + break; + case StateCommit: + logger()->debug() << "commited succesfully"; + _state = StateComplete; + if (!complete(upload)) { + cancel(upload.id, -1); + return; + } + break; + case StateComplete: + logger()->debug() << "upload" << upload.id << "succesful, invoking callback"; + if (upload.successCallback) { + upload.successCallback(); + } + _pending.dequeue(); + _token = 0; + _state = StateNotStarted; + if (!_pending.empty()) { + startNextUpload(); + } + break; + } +} + +bool UploadManager::uploadNextChunk(PendingUpload &upload) +{ + QByteArray chunk = upload.device->read(qMin<int>(upload.remaining, CHUNK_SIZE)); + + if (upload.remaining < CHUNK_SIZE && chunk.size() < upload.remaining) { + // Short read! + logger()->warn() << "short read during upload" << upload.id; + return false; + } + + Q_ASSERT(!chunk.isEmpty()); + Q_ASSERT(_state = StateInProgress); + + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::putbytesSEND); + p.write<quint32>(_token); + p.write<quint32>(chunk.size()); + msg.append(chunk); + + logger()->debug() << "sending a chunk of" << chunk.size() << "bytes"; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + upload.remaining -= chunk.size(); + upload.crc.addData(chunk); + + logger()->debug() << "remaining" << upload.remaining << "bytes"; + + return true; +} + +bool UploadManager::commit(PendingUpload &upload) +{ + Q_ASSERT(_state == StateCommit); + Q_ASSERT(upload.remaining == 0); + + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::putbytesCOMMIT); + p.write<quint32>(_token); + p.write<quint32>(upload.crc.result()); + + logger()->debug() << "commiting upload" << upload.id + << "with crc" << qPrintable(QString("0x%1").arg(upload.crc.result(), 0, 16)); + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + return true; +} + +bool UploadManager::complete(PendingUpload &upload) +{ + Q_ASSERT(_state == StateComplete); + + QByteArray msg; + Packer p(&msg); + p.write<quint8>(WatchConnector::putbytesCOMPLETE); + p.write<quint32>(_token); + + logger()->debug() << "completing upload" << upload.id; + + watch->sendMessage(WatchConnector::watchPUTBYTES, msg); + + return true; +} diff --git a/daemon/uploadmanager.h b/daemon/uploadmanager.h new file mode 100644 index 0000000..1d42237 --- /dev/null +++ b/daemon/uploadmanager.h @@ -0,0 +1,65 @@ +#ifndef UPLOADMANAGER_H +#define UPLOADMANAGER_H + +#include <functional> +#include <QQueue> +#include "watchconnector.h" +#include "stm32crc.h" + +class UploadManager : public QObject +{ + Q_OBJECT + LOG4QT_DECLARE_QCLASS_LOGGER + +public: + explicit UploadManager(WatchConnector *watch, QObject *parent = 0); + + typedef std::function<void()> Callback; + + uint upload(WatchConnector::UploadType type, int index, QIODevice *device, int size = -1, + std::function<void()> successCallback = std::function<void()>(), + std::function<void(int)> errorCallback = std::function<void(int)>()); + void cancel(uint id, int code = 0); + +signals: + +public slots: + + +private: + enum State { + StateNotStarted, + StateWaitForToken, + StateInProgress, + StateCommit, + StateComplete + }; + + struct PendingUpload { + uint id; + + WatchConnector::UploadType type; + int index; + QIODevice *device; + int remaining; + Stm32Crc crc; + + std::function<void()> successCallback; + std::function<void(int)> errorCallback; + }; + + void startNextUpload(); + void handleMessage(const QByteArray &msg); + bool uploadNextChunk(PendingUpload &upload); + bool commit(PendingUpload &upload); + bool complete(PendingUpload &upload); + +private: + WatchConnector *watch; + QQueue<PendingUpload> _pending; + uint _lastUploadId; + State _state; + quint32 _token; +}; + +#endif // UPLOADMANAGER_H diff --git a/daemon/watchconnector.h b/daemon/watchconnector.h index a5fe1ea..1aaf39d 100644 --- a/daemon/watchconnector.h +++ b/daemon/watchconnector.h @@ -108,6 +108,7 @@ public: enum AppManager { appmgrGET_APPBANK_STATUS = 1, appmgrREMOVE_APP = 2, + appmgrREFRESH_APP = 3, appmgrGET_APPBANK_UUIDS = 5 }; enum AppMessage { @@ -154,8 +155,19 @@ public: osLINUX = 4, osWINDOWS = 5 }; - enum { - DEFAULT_TIMEOUT_MSECS = 100 + enum UploadType { + uploadFIRMWARE = 1, + uploadRECOVERY = 2, + uploadSYS_RESOURCES = 3, + uploadRESOURCES = 4, + uploadBINARY = 5 + }; + enum PutBytesCommand { + putbytesINIT = 1, + putbytesSEND = 2, + putbytesCOMMIT = 3, + putbytesABORT = 4, + putbytesCOMPLETE = 5 }; typedef QMap<int, QVariant> Dict; diff --git a/org.pebbled.Watch.xml b/org.pebbled.Watch.xml index ce71248..2c5202f 100644 --- a/org.pebbled.Watch.xml +++ b/org.pebbled.Watch.xml @@ -54,5 +54,9 @@ <method name="UnloadApp"> <arg name="slot" type="u" direction="in"/> </method> + <method name="UploadApp"> + <arg name="uuid" type="s" direction="in"/> + <arg name="slot" type="u" direction="in"/> + </method> </interface> </node> |
