2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2020 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2019-12-23 16:13:23 +02:00
|
|
|
|
2020-11-28 15:44:37 +02:00
|
|
|
#include "androidconstants.h"
|
2022-10-06 17:53:35 +02:00
|
|
|
#include "androidsdkdownloader.h"
|
|
|
|
|
#include "androidtr.h"
|
2020-11-28 15:44:37 +02:00
|
|
|
|
2021-12-20 00:09:00 +01:00
|
|
|
#include <utils/archive.h>
|
|
|
|
|
#include <utils/filepath.h>
|
|
|
|
|
|
2020-11-30 15:13:49 +01:00
|
|
|
#include <coreplugin/icore.h>
|
|
|
|
|
|
2021-10-29 17:29:18 +02:00
|
|
|
#include <QCryptographicHash>
|
2019-12-23 16:13:23 +02:00
|
|
|
#include <QLoggingCategory>
|
|
|
|
|
#include <QStandardPaths>
|
|
|
|
|
|
2021-09-15 15:16:56 +02:00
|
|
|
using namespace Utils;
|
|
|
|
|
|
2019-12-23 16:13:23 +02:00
|
|
|
namespace {
|
|
|
|
|
Q_LOGGING_CATEGORY(sdkDownloaderLog, "qtc.android.sdkDownloader", QtWarningMsg)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace Android {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
/**
|
|
|
|
|
* @class SdkDownloader
|
|
|
|
|
* @brief Download Android SDK tools package from within Qt Creator.
|
|
|
|
|
*/
|
2020-03-16 20:14:56 +02:00
|
|
|
AndroidSdkDownloader::AndroidSdkDownloader()
|
2021-10-25 15:58:02 +03:00
|
|
|
: m_androidConfig(AndroidConfigurations::currentConfig())
|
2019-12-23 16:13:23 +02:00
|
|
|
{
|
|
|
|
|
connect(&m_manager, &QNetworkAccessManager::finished, this, &AndroidSdkDownloader::downloadFinished);
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-24 10:29:02 +01:00
|
|
|
AndroidSdkDownloader::~AndroidSdkDownloader() = default;
|
|
|
|
|
|
2019-12-23 16:13:23 +02:00
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
|
void AndroidSdkDownloader::sslErrors(const QList<QSslError> &sslErrors)
|
|
|
|
|
{
|
|
|
|
|
for (const QSslError &error : sslErrors)
|
|
|
|
|
qCDebug(sdkDownloaderLog, "SSL error: %s\n", qPrintable(error.errorString()));
|
2022-10-06 17:53:35 +02:00
|
|
|
cancelWithError(Tr::tr("Encountered SSL errors, download is aborted."));
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-11-28 15:44:37 +02:00
|
|
|
void AndroidSdkDownloader::downloadAndExtractSdk()
|
2019-12-23 16:13:23 +02:00
|
|
|
{
|
2020-03-16 20:14:56 +02:00
|
|
|
if (m_androidConfig.sdkToolsUrl().isEmpty()) {
|
2022-10-06 17:53:35 +02:00
|
|
|
logError(Tr::tr("The SDK Tools download URL is empty."));
|
2019-12-23 16:13:23 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 20:14:56 +02:00
|
|
|
QNetworkRequest request(m_androidConfig.sdkToolsUrl());
|
2019-12-23 16:13:23 +02:00
|
|
|
m_reply = m_manager.get(request);
|
|
|
|
|
|
|
|
|
|
#if QT_CONFIG(ssl)
|
|
|
|
|
connect(m_reply, &QNetworkReply::sslErrors, this, &AndroidSdkDownloader::sslErrors);
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-10-06 17:53:35 +02:00
|
|
|
m_progressDialog = new QProgressDialog(Tr::tr("Downloading SDK Tools package..."), Tr::tr("Cancel"),
|
2020-11-30 15:13:49 +01:00
|
|
|
0, 100, Core::ICore::dialogParent());
|
|
|
|
|
m_progressDialog->setWindowModality(Qt::ApplicationModal);
|
2019-12-23 16:13:23 +02:00
|
|
|
m_progressDialog->setWindowTitle(dialogTitle());
|
|
|
|
|
m_progressDialog->setFixedSize(m_progressDialog->sizeHint());
|
|
|
|
|
|
|
|
|
|
connect(m_reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 max) {
|
|
|
|
|
m_progressDialog->setRange(0, max);
|
|
|
|
|
m_progressDialog->setValue(received);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
connect(m_progressDialog, &QProgressDialog::canceled, this, &AndroidSdkDownloader::cancel);
|
|
|
|
|
|
2022-07-19 22:51:32 +02:00
|
|
|
connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this] {
|
2022-03-24 10:29:02 +01:00
|
|
|
if (!Archive::supportsFile(m_sdkFilename))
|
|
|
|
|
return;
|
2020-11-28 15:44:37 +02:00
|
|
|
const FilePath extractDir = m_sdkFilename.parentDir();
|
2022-03-24 10:29:02 +01:00
|
|
|
m_archive.reset(new Archive(m_sdkFilename, extractDir));
|
|
|
|
|
if (m_archive->isValid()) {
|
|
|
|
|
connect(m_archive.get(), &Archive::finished, this, [this, extractDir](bool success) {
|
2020-11-28 15:44:37 +02:00
|
|
|
if (success) {
|
|
|
|
|
// Save the extraction path temporarily which can be used by sdkmanager
|
|
|
|
|
// to install essential packages at firt time setup.
|
|
|
|
|
m_androidConfig.setTemporarySdkToolsPath(
|
|
|
|
|
extractDir.pathAppended(Constants::cmdlineToolsName));
|
2021-12-20 00:09:00 +01:00
|
|
|
emit sdkExtracted();
|
2020-11-28 15:44:37 +02:00
|
|
|
}
|
2022-03-24 10:29:02 +01:00
|
|
|
m_archive.release()->deleteLater();
|
2021-12-20 00:09:00 +01:00
|
|
|
});
|
2022-03-24 10:29:02 +01:00
|
|
|
m_archive->unarchive();
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidSdkDownloader::verifyFileIntegrity()
|
|
|
|
|
{
|
2021-09-15 15:16:56 +02:00
|
|
|
QFile f(m_sdkFilename.toString());
|
2019-12-23 16:13:23 +02:00
|
|
|
if (f.open(QFile::ReadOnly)) {
|
|
|
|
|
QCryptographicHash hash(QCryptographicHash::Sha256);
|
|
|
|
|
if (hash.addData(&f)) {
|
2020-03-16 20:14:56 +02:00
|
|
|
return hash.result() == m_androidConfig.getSdkToolsSha256();
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AndroidSdkDownloader::dialogTitle()
|
|
|
|
|
{
|
2022-10-06 17:53:35 +02:00
|
|
|
return Tr::tr("Download SDK Tools");
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidSdkDownloader::cancel()
|
|
|
|
|
{
|
|
|
|
|
if (m_reply) {
|
|
|
|
|
m_reply->abort();
|
|
|
|
|
m_reply->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
if (m_progressDialog)
|
2020-02-20 18:13:26 +02:00
|
|
|
m_progressDialog->cancel();
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidSdkDownloader::cancelWithError(const QString &error)
|
|
|
|
|
{
|
|
|
|
|
cancel();
|
|
|
|
|
logError(error);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidSdkDownloader::logError(const QString &error)
|
|
|
|
|
{
|
|
|
|
|
qCDebug(sdkDownloaderLog, "%s", error.toUtf8().data());
|
|
|
|
|
emit sdkDownloaderError(error);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-20 00:09:00 +01:00
|
|
|
FilePath AndroidSdkDownloader::getSaveFilename(const QUrl &url)
|
2019-12-23 16:13:23 +02:00
|
|
|
{
|
|
|
|
|
QString path = url.path();
|
|
|
|
|
QString basename = QFileInfo(path).fileName();
|
|
|
|
|
|
|
|
|
|
if (basename.isEmpty())
|
|
|
|
|
basename = "sdk-tools.zip";
|
|
|
|
|
|
|
|
|
|
if (QFile::exists(basename)) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
basename += '.';
|
|
|
|
|
while (QFile::exists(basename + QString::number(i)))
|
|
|
|
|
++i;
|
|
|
|
|
basename += QString::number(i);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-28 15:44:37 +02:00
|
|
|
return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::TempLocation))
|
2021-12-20 00:09:00 +01:00
|
|
|
/ basename;
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-15 15:16:56 +02:00
|
|
|
bool AndroidSdkDownloader::saveToDisk(const FilePath &filename, QIODevice *data)
|
2019-12-23 16:13:23 +02:00
|
|
|
{
|
2021-09-15 15:16:56 +02:00
|
|
|
QFile file(filename.toString());
|
2019-12-23 16:13:23 +02:00
|
|
|
if (!file.open(QIODevice::WriteOnly)) {
|
2022-10-06 17:53:35 +02:00
|
|
|
logError(QString(Tr::tr("Could not open %1 for writing: %2."))
|
2021-09-15 15:16:56 +02:00
|
|
|
.arg(filename.toUserOutput(), file.errorString()));
|
2019-12-23 16:13:23 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file.write(data->readAll());
|
|
|
|
|
file.close();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidSdkDownloader::isHttpRedirect(QNetworkReply *reply)
|
|
|
|
|
{
|
|
|
|
|
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
|
return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305
|
|
|
|
|
|| statusCode == 307 || statusCode == 308;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidSdkDownloader::downloadFinished(QNetworkReply *reply)
|
|
|
|
|
{
|
|
|
|
|
QUrl url = reply->url();
|
|
|
|
|
if (reply->error()) {
|
2022-10-06 17:53:35 +02:00
|
|
|
cancelWithError(QString(Tr::tr("Downloading Android SDK Tools from URL %1 has failed: %2."))
|
2019-12-23 16:13:23 +02:00
|
|
|
.arg(url.toString(), reply->errorString()));
|
|
|
|
|
} else {
|
|
|
|
|
if (isHttpRedirect(reply)) {
|
2022-10-06 17:53:35 +02:00
|
|
|
cancelWithError(QString(Tr::tr("Download from %1 was redirected.")).arg(url.toString()));
|
2019-12-23 16:13:23 +02:00
|
|
|
} else {
|
2021-12-20 00:09:00 +01:00
|
|
|
m_sdkFilename = getSaveFilename(url);
|
2019-12-23 16:13:23 +02:00
|
|
|
if (saveToDisk(m_sdkFilename, reply) && verifyFileIntegrity())
|
|
|
|
|
emit sdkPackageWriteFinished();
|
|
|
|
|
else
|
|
|
|
|
cancelWithError(
|
2022-10-06 17:53:35 +02:00
|
|
|
Tr::tr("Writing and verifying the integrity of the downloaded file has failed."));
|
2019-12-23 16:13:23 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reply->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // Internal
|
|
|
|
|
} // Android
|