diff --git a/rpm/nemo-qml-plugin-systemsettings.spec b/rpm/nemo-qml-plugin-systemsettings.spec index 762762e..2cf24db 100644 --- a/rpm/nemo-qml-plugin-systemsettings.spec +++ b/rpm/nemo-qml-plugin-systemsettings.spec @@ -4,7 +4,7 @@ Version: 0.5.27 Release: 1 Group: System/Libraries License: BSD -URL: https://git.merproject.org/mer-core/nemo-qml-plugin-systemsettings +URL: https://git.sailfishos.org/mer-core/nemo-qml-plugin-systemsettings Source0: %{name}-%{version}.tar.bz2 Requires(post): /sbin/ldconfig Requires(postun): /sbin/ldconfig diff --git a/src/developermodesettings.cpp b/src/developermodesettings.cpp index f7e1340..0975bb5 100644 --- a/src/developermodesettings.cpp +++ b/src/developermodesettings.cpp @@ -55,6 +55,7 @@ /* A file that is provided by the developer mode package */ #define DEVELOPER_MODE_PROVIDED_FILE "/usr/bin/devel-su" #define DEVELOPER_MODE_PACKAGE "jolla-developer-mode" +#define DEVELOPER_MODE_PACKAGE_PRELOAD_DIR "/var/lib/jolla-developer-mode/preloaded/" /* D-Bus service */ #define USB_MODED_SERVICE "com.meego.usb_moded" @@ -84,6 +85,17 @@ static QMap enumerate_network_interfaces() return result; } +static QString get_cached_package(const QString &version) +{ + QDir dir(DEVELOPER_MODE_PACKAGE_PRELOAD_DIR); + QStringList filters; + filters << QStringLiteral("%1-%2.*.rpm").arg(DEVELOPER_MODE_PACKAGE).arg(version); + auto preloaded = dir.entryList(filters, QDir::Files, QDir::Name); + if (preloaded.empty()) + return QString(); + return dir.absoluteFilePath(preloaded.last()); +} + DeveloperModeSettings::DeveloperModeSettings(QObject *parent) : QObject(parent) , m_usbModeDaemon(USB_MODED_SERVICE, USB_MODED_PATH, USB_MODED_INTERFACE, QDBusConnection::systemBus()) @@ -97,6 +109,8 @@ DeveloperModeSettings::DeveloperModeSettings(QObject *parent) , m_transactionRole(PackageKit::Transaction::RoleUnknown) , m_transactionStatus(PackageKit::Transaction::StatusUnknown) , m_refreshedForInstall(false) + , m_localInstallFailed(false) + , m_localDeveloperModePackagePath(get_cached_package(QStringLiteral("*"))) // Initialized to possibly incompatible package { int uid = getdef_num("UID_MIN", -1); struct passwd *pwd; @@ -106,6 +120,23 @@ DeveloperModeSettings::DeveloperModeSettings(QObject *parent) qCWarning(lcDeveloperModeLog) << "Failed to return username using getpwuid()"; } + // Resolve and update local package path + if (!m_localDeveloperModePackagePath.isEmpty()) { + PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled); + connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode); + connect(resolvePackage, &PackageKit::Transaction::package, + this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) { + Q_UNUSED(summary) + Q_ASSERT(info == PackageKit::Transaction::InfoInstalled); + const QString version = PackageKit::Transaction::packageVersion(packageID); + m_localDeveloperModePackagePath = get_cached_package(version); + if (m_localDeveloperModePackagePath.isEmpty()) { + emit repositoryAccessRequiredChanged(); + } + qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath; + }); + } + refresh(); // TODO: Watch WLAN / USB IP addresses for changes @@ -146,6 +177,12 @@ int DeveloperModeSettings::workProgress() const return m_workProgress; } +bool DeveloperModeSettings::repositoryAccessRequired() const +{ + // Aka local-install-of-developer-mode-package-is-not-possible + return m_localInstallFailed || m_localDeveloperModePackagePath.isEmpty(); +} + void DeveloperModeSettings::setDeveloperMode(bool enabled) { if (m_developerModeEnabled != enabled) { @@ -233,72 +270,114 @@ void DeveloperModeSettings::refreshPackageCacheAndInstall() void DeveloperModeSettings::resolveAndExecute(Command command) { setWorkStatus(Preparing); + m_workProgress = 0; m_developerModePackageId.clear(); // might differ between installed/available - PackageKit::Transaction::Filters filters; - if (command == RemoveCommand) { - filters = PackageKit::Transaction::FilterInstalled; - } else { - filters = PackageKit::Transaction::FilterNewest; - } - PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE, filters); + if (command == InstallCommand && !m_localInstallFailed && !m_localDeveloperModePackagePath.isEmpty()) { + // Resolve which version of developer mode package is expected + PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled); + connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode); + connect(resolvePackage, &PackageKit::Transaction::package, + this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) { + Q_UNUSED(summary) + Q_ASSERT(info == PackageKit::Transaction::InfoInstalled); + const QString version = PackageKit::Transaction::packageVersion(packageID); + m_localDeveloperModePackagePath = get_cached_package(version); + emit repositoryAccessRequiredChanged(); + qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath; + }); + + connect(resolvePackage, &PackageKit::Transaction::finished, + this, [this](PackageKit::Transaction::Exit status, uint runtime) { + Q_UNUSED(runtime) + if (status != PackageKit::Transaction::ExitSuccess || m_localDeveloperModePackagePath.isEmpty()) { + qCDebug(lcDeveloperModeLog) << "Preloaded package not found, must use remote package"; + // No cached package => install from repos + resolveAndExecute(InstallCommand); + } else { + PackageKit::Transaction *tx = PackageKit::Daemon::installFiles(QStringList() << m_localDeveloperModePackagePath); + connectCommandSignals(tx); + connect(tx, &PackageKit::Transaction::finished, + this, [this](PackageKit::Transaction::Exit status, uint runtime) { + if (status == PackageKit::Transaction::ExitSuccess) { + qCDebug(lcDeveloperModeLog) << "Developer mode installation from local package transaction done:" << status << runtime; + resetState(); + } else if (status == PackageKit::Transaction::ExitFailed) { + qCWarning(lcDeveloperModeLog) << "Developer mode installation from local package failed, trying from repos"; + m_localInstallFailed = true; + emit repositoryAccessRequiredChanged(); + resolveAndExecute(InstallCommand); // TODO: If repo access is not available this can not bail out + } // else ExitUnknown (ignored) + }); + } + }); - connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode); - connect(resolvePackage, &PackageKit::Transaction::package, - this, [this](PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) { - qCDebug(lcDeveloperModeLog) << "Package transaction:" << info << packageId << "summary:" << summary; - m_developerModePackageId = packageId; - }); + } else { + PackageKit::Transaction::Filters filters; + if (command == RemoveCommand) { + filters = PackageKit::Transaction::FilterInstalled; + } else { + filters = PackageKit::Transaction::FilterNewest; + } + PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE, filters); + + connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode); + connect(resolvePackage, &PackageKit::Transaction::package, + this, [this](PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) { + qCDebug(lcDeveloperModeLog) << "Package transaction:" << info << packageId << "summary:" << summary; + m_developerModePackageId = packageId; + }); + + connect(resolvePackage, &PackageKit::Transaction::finished, + this, [this, command](PackageKit::Transaction::Exit status, uint runtime) { + Q_UNUSED(runtime) + + if (status != PackageKit::Transaction::ExitSuccess || m_developerModePackageId.isEmpty()) { + if (command == InstallCommand) { + if (m_refreshedForInstall) { + qCWarning(lcDeveloperModeLog) << "Failed to install developer mode, package didn't resolve."; + resetState(); + } else { + refreshPackageCacheAndInstall(); // try once if it helps + } + } else if (command == RemoveCommand) { + qCWarning(lcDeveloperModeLog) << "Removing developer mode but package didn't resolve into anything. Shouldn't happen."; + resetState(); + } - connect(resolvePackage, &PackageKit::Transaction::finished, - this, [this, command](PackageKit::Transaction::Exit status, uint runtime) { - Q_UNUSED(runtime) + } else if (command == InstallCommand) { + PackageKit::Transaction *tx = PackageKit::Daemon::installPackage(m_developerModePackageId); + connectCommandSignals(tx); - if (status != PackageKit::Transaction::ExitSuccess || m_developerModePackageId.isEmpty()) { - if (command == InstallCommand) { if (m_refreshedForInstall) { - qCWarning(lcDeveloperModeLog) << "Failed to install developer mode, package didn't resolve."; - resetState(); + connect(tx, &PackageKit::Transaction::finished, + this, [this](PackageKit::Transaction::Exit status, uint runtime) { + qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done (with refresh):" << status << runtime; + resetState(); + }); } else { - refreshPackageCacheAndInstall(); // try once if it helps + connect(tx, &PackageKit::Transaction::finished, + this, [this](PackageKit::Transaction::Exit status, uint runtime) { + if (status == PackageKit::Transaction::ExitSuccess) { + qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done:" << status << runtime; + resetState(); + } else { + qCDebug(lcDeveloperModeLog) << "Developer mode installation failed, trying again after refresh"; + refreshPackageCacheAndInstall(); + } + }); } - } else if (command == RemoveCommand) { - qCWarning(lcDeveloperModeLog) << "Removing developer mode but package didn't resolve into anything. Shouldn't happen."; - resetState(); - } - - } else if (command == InstallCommand) { - PackageKit::Transaction *tx = PackageKit::Daemon::installPackage(m_developerModePackageId); - connectCommandSignals(tx); - - if (m_refreshedForInstall) { - connect(tx, &PackageKit::Transaction::finished, - this, [this](PackageKit::Transaction::Exit status, uint runtime) { - qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done (with refresh):" << status << runtime; - resetState(); - }); } else { + PackageKit::Transaction *tx = PackageKit::Daemon::removePackage(m_developerModePackageId, true, true); + connectCommandSignals(tx); connect(tx, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status, uint runtime) { - if (status == PackageKit::Transaction::ExitSuccess) { - qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done:" << status << runtime; - resetState(); - } else { - qCDebug(lcDeveloperModeLog) << "Developer mode installation failed, trying again after refresh"; - refreshPackageCacheAndInstall(); - } + qCDebug(lcDeveloperModeLog) << "Developer mode removal transaction done:" << status << runtime; + resetState(); }); } - } else { - PackageKit::Transaction *tx = PackageKit::Daemon::removePackage(m_developerModePackageId, true, true); - connectCommandSignals(tx); - connect(tx, &PackageKit::Transaction::finished, - this, [this](PackageKit::Transaction::Exit status, uint runtime) { - qCDebug(lcDeveloperModeLog) << "Developer mode removal transaction done:" << status << runtime; - resetState(); - }); - } - }); + }); + } } void DeveloperModeSettings::connectCommandSignals(PackageKit::Transaction *transaction) @@ -319,19 +398,16 @@ void DeveloperModeSettings::connectCommandSignals(PackageKit::Transaction *trans void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction::Status status, PackageKit::Transaction::Role role) { - // Do not update progress when finished. - if (status == PackageKit::Transaction::StatusFinished) { - return; - } - // Expected changes from PackageKit when installing packages: - // 1. Change to 'install packages' role + // 1. Change to 'install packages' role or 'install files' if installing from local package file // 2. Status changes: // setup -> refresh cache -> query -> resolve deps -> install (refer to as 'Preparing' status) // -> download ('DownloadingPackages' status) // -> install ('InstallingPackages' status) // -> finished // + // If installing from local package fails, it starts over! + // // Expected changes from PackageKit when removing packages: // 1. Change to 'remove packages' role // 2. Status changes: @@ -347,10 +423,17 @@ void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction: m_transactionRole = role; m_transactionStatus = status; + // Do not update progress when finished or role is unknown. + if (m_transactionStatus == PackageKit::Transaction::StatusFinished + || m_transactionRole == PackageKit::Transaction::RoleUnknown) { + return; + } + if (percentage >= 0 && percentage <= 100) { int rangeStart = 0; int rangeEnd = 0; - if (m_transactionRole == PackageKit::Transaction::RoleInstallPackages) { + if (m_transactionRole == PackageKit::Transaction::RoleInstallPackages + || m_transactionRole == PackageKit::Transaction::RoleInstallFiles) { switch (m_transactionStatus) { case PackageKit::Transaction::StatusRefreshCache: // 0-10 % rangeStart = 0; @@ -362,7 +445,10 @@ void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction: rangeEnd = 20; break; case PackageKit::Transaction::StatusDownload: // 20-60 % - workStatus = DownloadingPackages; + // Skip downloading when installing from local file + if (m_transactionRole != PackageKit::Transaction::RoleInstallFiles) { + workStatus = DownloadingPackages; + } rangeStart = 20; rangeEnd = 60; break; diff --git a/src/developermodesettings.h b/src/developermodesettings.h index 35654b3..36bea30 100644 --- a/src/developermodesettings.h +++ b/src/developermodesettings.h @@ -55,6 +55,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject Q_PROPERTY(bool developerModeEnabled READ developerModeEnabled NOTIFY developerModeEnabledChanged) Q_PROPERTY(enum DeveloperModeSettings::Status workStatus READ workStatus NOTIFY workStatusChanged) Q_PROPERTY(int workProgress READ workProgress NOTIFY workProgressChanged) + Q_PROPERTY(bool repositoryAccessRequired READ repositoryAccessRequired NOTIFY repositoryAccessRequiredChanged) public: explicit DeveloperModeSettings(QObject *parent = NULL); @@ -74,6 +75,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject bool developerModeEnabled() const; enum DeveloperModeSettings::Status workStatus() const; int workProgress() const; + bool repositoryAccessRequired() const; Q_INVOKABLE void setDeveloperMode(bool enabled); Q_INVOKABLE void setUsbIpAddress(const QString &usbIpAddress); @@ -85,6 +87,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject void developerModeEnabledChanged(); void workStatusChanged(); void workProgressChanged(); + void repositoryAccessRequiredChanged(); private slots: void reportTransactionErrorCode(PackageKit::Transaction::Error code, const QString &details); @@ -117,6 +120,8 @@ private slots: PackageKit::Transaction::Role m_transactionRole; PackageKit::Transaction::Status m_transactionStatus; bool m_refreshedForInstall; + bool m_localInstallFailed; + QString m_localDeveloperModePackagePath; }; Q_DECLARE_METATYPE(DeveloperModeSettings::Status)