summaryrefslogtreecommitdiff
path: root/rockworkd/libpebble/firmwaredownloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rockworkd/libpebble/firmwaredownloader.cpp')
-rw-r--r--rockworkd/libpebble/firmwaredownloader.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/rockworkd/libpebble/firmwaredownloader.cpp b/rockworkd/libpebble/firmwaredownloader.cpp
new file mode 100644
index 0000000..5d32f3b
--- /dev/null
+++ b/rockworkd/libpebble/firmwaredownloader.cpp
@@ -0,0 +1,246 @@
+#include "firmwaredownloader.h"
+#include "ziphelper.h"
+#include "pebble.h"
+#include "watchconnection.h"
+#include "uploadmanager.h"
+
+#include <QNetworkAccessManager>
+#include <QUrlQuery>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QJsonDocument>
+#include <QFile>
+#include <QDir>
+#include <QCryptographicHash>
+
+FirmwareDownloader::FirmwareDownloader(Pebble *pebble, WatchConnection *connection):
+ QObject(pebble),
+ m_pebble(pebble),
+ m_connection(connection)
+{
+ m_nam = new QNetworkAccessManager(this);
+
+ m_connection->registerEndpointHandler(WatchConnection::EndpointSystemMessage, this, "systemMessageReceived");
+}
+
+bool FirmwareDownloader::updateAvailable() const
+{
+ return m_updateAvailable;
+}
+
+QString FirmwareDownloader::candidateVersion() const
+{
+ return m_candidateVersion;
+}
+
+QString FirmwareDownloader::releaseNotes() const
+{
+ return m_releaseNotes;
+}
+
+QString FirmwareDownloader::url() const
+{
+ return m_url;
+}
+
+bool FirmwareDownloader::upgrading() const
+{
+ return m_upgradeInProgress;
+}
+
+void FirmwareDownloader::performUpgrade()
+{
+ if (!m_updateAvailable) {
+ qWarning() << "No update available";
+ return;
+ }
+
+ if (m_upgradeInProgress) {
+ qWarning() << "Upgrade already in progress. Won't start another one";
+ return;
+ }
+
+ m_upgradeInProgress = true;
+ emit upgradingChanged();
+
+ QNetworkRequest request(m_url);
+ QNetworkReply *reply = m_nam->get(request);
+ connect(reply, &QNetworkReply::finished, [this, reply](){
+ reply->deleteLater();
+
+ if (reply->error() != QNetworkReply::NoError) {
+ qWarning() << "Erorr fetching firmware" << reply->errorString();
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+
+ QByteArray data = reply->readAll();
+
+ QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex();
+
+ if (hash != m_hash) {
+ qWarning() << "Downloaded data hash doesn't match hash from target";
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+
+ QDir dir("/tmp/" + m_pebble->address().toString().replace(":", "_"));
+ if (!dir.exists() && !dir.mkpath(dir.absolutePath())) {
+ qWarning() << "Error saving file" << dir.absolutePath();
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+ QString path = "/tmp/" + m_pebble->address().toString().replace(":", "_");
+ QFile f(path + "/" + reply->request().url().fileName());
+ if (!f.open(QFile::WriteOnly | QFile::Truncate)) {
+ qWarning() << "Cannot open tmp file for writing" << f.fileName();
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+ f.write(data);
+ f.close();
+
+ if (!ZipHelper::unpackArchive(f.fileName(), path)) {
+ qWarning() << "Error unpacking firmware archive";
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+
+ Bundle firmware(path);
+ if (firmware.file(Bundle::FileTypeFirmware).isEmpty() || firmware.file(Bundle::FileTypeResources).isEmpty()) {
+ qWarning() << "Firmware bundle file missing binary or resources";
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ return;
+ }
+
+ qDebug() << "** Starting firmware upgrade **";
+ m_bundlePath = path;
+ m_connection->systemMessage(WatchConnection::SystemMessageFirmwareStart);
+
+ });
+}
+
+void FirmwareDownloader::checkForNewFirmware()
+{
+ QString platformString;
+ switch (m_pebble->hardwareRevision()) {
+ case HardwareRevisionUNKNOWN:
+ case HardwareRevisionTINTIN_EV1:
+ case HardwareRevisionTINTIN_EV2:
+ case HardwareRevisionTINTIN_EV2_3:
+ case HardwareRevisionSNOWY_EVT2:
+ case HardwareRevisionSPALDING_EVT:
+ case HardwareRevisionTINTIN_BB:
+ case HardwareRevisionTINTIN_BB2:
+ case HardwareRevisionSNOWY_BB:
+ case HardwareRevisionSNOWY_BB2:
+ case HardwareRevisionSPALDING_BB2:
+ qWarning() << "Hardware revision not supported for firmware upgrades" << m_pebble->hardwareRevision();
+ return;
+ case HardwareRevisionTINTIN_EV2_4:
+ platformString = "ev2_4";
+ break;
+ case HardwareRevisionTINTIN_V1_5:
+ platformString = "v1_5";
+ break;
+ case HardwareRevisionBIANCA:
+ platformString = "v2_0";
+ break;
+ case HardwareRevisionSNOWY_DVT:
+ platformString = "snowy_dvt";
+ break;
+ case HardwareRevisionBOBBY_SMILES:
+ platformString = "snowy_s3";
+ break;
+ case HardwareRevisionSPALDING:
+ platformString = "spalding";
+ break;
+
+ }
+
+ QString url("https://pebblefw.s3.amazonaws.com/pebble/%1/%2/latest.json");
+ url = url.arg(platformString).arg("release-v3.8");
+ QNetworkRequest request(url);
+ QNetworkReply *reply = m_nam->get(request);
+ connect(reply, &QNetworkReply::finished, [this, reply]() {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << "Error parsing firmware fetch reply" << jsonDoc.toJson(QJsonDocument::Indented);
+ return;
+ }
+ QVariantMap resultMap = jsonDoc.toVariant().toMap();
+ if (!resultMap.contains("normal")) {
+ qWarning() << "Could not find normal firmware package" << jsonDoc.toJson(QJsonDocument::Indented);
+ return;
+ }
+
+
+ QVariantMap targetFirmware;
+ if (resultMap.contains("3.x-migration") && m_pebble->softwareVersion() < "v3.0.0") {
+ targetFirmware = resultMap.value("3.x-migration").toMap();
+ } else if (m_pebble->softwareVersion() >= "v3.0.0" &&
+ resultMap.value("normal").toMap().value("friendlyVersion").toString() > m_pebble->softwareVersion()){
+ targetFirmware = resultMap.value("normal").toMap();
+ }
+
+ if (targetFirmware.isEmpty()) {
+ qDebug() << "Watch firmware is up to date";
+ m_updateAvailable = false;
+ emit updateAvailableChanged();
+ return;
+ }
+
+ qDebug() << targetFirmware;
+
+ m_candidateVersion = targetFirmware.value("friendlyVersion").toString();
+ m_releaseNotes = targetFirmware.value("notes").toString();
+ m_url = targetFirmware.value("url").toString();
+ m_hash = targetFirmware.value("sha-256").toByteArray();
+ m_updateAvailable = true;
+ qDebug() << "candidate firmware upgrade" << m_candidateVersion << m_releaseNotes << m_url;
+ emit updateAvailableChanged();
+ });
+}
+
+void FirmwareDownloader::systemMessageReceived(const QByteArray &data)
+{
+ qDebug() << "system message" << data.toHex();
+
+ if (!m_upgradeInProgress) {
+ return;
+ }
+
+ Bundle firmware(m_bundlePath);
+
+ qDebug() << "** Uploading firmware resources...";
+ m_connection->uploadManager()->uploadFirmwareResources(firmware.file(Bundle::FileTypeResources), firmware.crc(Bundle::FileTypeResources), [this, firmware]() {
+ qDebug() << "** Firmware resources uploaded. OK";
+
+ qDebug() << "** Uploading firmware binary...";
+ m_connection->uploadManager()->uploadFirmwareBinary(false, firmware.file(Bundle::FileTypeFirmware), firmware.crc(Bundle::FileTypeFirmware), [this]() {
+ qDebug() << "** Firmware binary uploaded. OK";
+ m_connection->systemMessage(WatchConnection::SystemMessageFirmwareComplete);
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ }, [this](int code) {
+ qWarning() << "** ERROR uploading firmware binary" << code;
+ m_connection->systemMessage(WatchConnection::SystemMessageFirmwareFail);
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ });
+ },
+ [this](int code) {
+ qWarning() << "** ERROR uploading firmware resources" << code;
+ m_connection->systemMessage(WatchConnection::SystemMessageFirmwareFail);
+ m_upgradeInProgress = false;
+ emit upgradingChanged();
+ });
+}
+