diff options
Diffstat (limited to 'src/plugins/android/androidconfigurations.cpp')
-rw-r--r-- | src/plugins/android/androidconfigurations.cpp | 432 |
1 files changed, 82 insertions, 350 deletions
diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 851b0f2350..42027743b8 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -28,8 +28,11 @@ #include "androidtoolchain.h" #include "androiddevice.h" #include "androidgdbserverkitinformation.h" +#include "androidmanager.h" #include "androidqtversion.h" #include "androiddevicedialog.h" +#include "androidsdkmanager.h" +#include "androidtoolmanager.h" #include "avddialog.h" #include <coreplugin/icore.h> @@ -73,6 +76,9 @@ namespace Android { using namespace Internal; namespace { + + const QVersionNumber sdkToolsAntMissingVersion(25, 3, 0); + const QLatin1String SettingsGroup("AndroidConfigurations"); const QLatin1String SDKLocationKey("SDKLocation"); const QLatin1String NDKLocationKey("NDKLocation"); @@ -107,39 +113,14 @@ namespace { const QLatin1String keytoolName("keytool"); const QLatin1String changeTimeStamp("ChangeTimeStamp"); + const QLatin1String sdkToolsVersionKey("Pkg.Revision"); + static QString sdkSettingsFileName() { return QFileInfo(Core::ICore::settings(QSettings::SystemScope)->fileName()).absolutePath() + QLatin1String("/qtcreator/android.xml"); } - bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2) - { - if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????"))) - return !dev1.serialNumber.contains(QLatin1String("????")); - if (dev1.type != dev2.type) - return dev1.type == AndroidDeviceInfo::Hardware; - if (dev1.sdk != dev2.sdk) - return dev1.sdk < dev2.sdk; - if (dev1.avdname != dev2.avdname) - return dev1.avdname < dev2.avdname; - - return dev1.serialNumber < dev2.serialNumber; - } - - static QStringList cleanAndroidABIs(const QStringList &abis) - { - QStringList res; - foreach (const QString &abi, abis) { - int index = abi.lastIndexOf(QLatin1Char('/')); - if (index == -1) - res << abi; - else - res << abi.mid(index + 1); - } - return res; - } - static bool is32BitUserSpace() { // Do the exact same check as android's emulator is doing: @@ -162,25 +143,6 @@ namespace { } return false; } - - // Some preview sdks use a non integer version - int apiLevelFromAndroidList(const QString &string) - { - bool ok; - int result = string.toInt(&ok); - if (ok) - return result; - Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation(); - sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties")); - result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok); - if (ok) - return result; - if (string == QLatin1String("L")) - return 21; - if (string == QLatin1String("MNC")) - return 22; - return 23; // At least - } } ////////////////////////////////// @@ -359,61 +321,14 @@ void AndroidConfig::updateNdkInformation() const m_NdkInformationUpToDate = true; } -bool AndroidConfig::sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b) -{ - if (a.apiLevel != b.apiLevel) - return a.apiLevel > b.apiLevel; - if (a.name != b.name) - return a.name < b.name; - return false; -} - void AndroidConfig::updateAvailableSdkPlatforms() const { if (m_availableSdkPlatformsUpToDate) return; - m_availableSdkPlatforms.clear(); - - SynchronousProcess proc; - proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); - SynchronousProcessResponse response - = proc.runBlocking(androidToolPath().toString(), - QStringList({"list", "target"})); // list available AVDs - if (response.result != SynchronousProcessResponse::Finished) - return; - - SdkPlatform platform; - foreach (const QString &l, response.allOutput().split('\n')) { - const QString line = l.trimmed(); - if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) { - int index = line.indexOf(QLatin1String("\"android-")); - if (index == -1) - continue; - QString androidTarget = line.mid(index + 1, line.length() - index - 2); - const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1); - platform.apiLevel = apiLevelFromAndroidList(tmp); - } else if (line.startsWith(QLatin1String("Name:"))) { - platform.name = line.mid(6); - } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) { - platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("ABIs"))) { - platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) { - if (platform.apiLevel == -1) - continue; - auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(), - platform, sortSdkPlatformByApiLevel); - m_availableSdkPlatforms.insert(it, platform); - platform = SdkPlatform(); - } - } - - if (platform.apiLevel != -1) { - auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(), - platform, sortSdkPlatformByApiLevel); - m_availableSdkPlatforms.insert(it, platform); - } + m_availableSdkPlatforms.clear(); + AndroidSdkManager sdkManager(*this); + m_availableSdkPlatforms = sdkManager.availableSdkPlatforms(); m_availableSdkPlatformsUpToDate = true; } @@ -446,18 +361,6 @@ FileName AndroidConfig::adbToolPath() const return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX)); } -Environment AndroidConfig::androidToolEnvironment() const -{ - Environment env = Environment::systemEnvironment(); - if (!m_openJDKLocation.isEmpty()) { - env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput()); - Utils::FileName binPath = m_openJDKLocation; - binPath.appendPath(QLatin1String("bin")); - env.prependOrSetPath(binPath.toUserOutput()); - } - return env; -} - FileName AndroidConfig::androidToolPath() const { if (HostOsInfo::isWindowsHost()) { @@ -486,7 +389,10 @@ FileName AndroidConfig::antToolPath() const FileName AndroidConfig::emulatorToolPath() const { FileName path = m_sdkLocation; - return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX)); + QString relativePath = "emulator/emulator"; + if (sdkToolsVersion() < QVersionNumber(25, 3, 0)) + relativePath = "tools/emulator"; + return path.appendPath(relativePath + QTC_HOST_EXE_SUFFIX); } FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const @@ -499,6 +405,26 @@ FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVers .arg(toolsPrefix(abi))); } +FileName AndroidConfig::sdkManagerToolPath() const +{ + FileName sdkPath = m_sdkLocation; + QString toolPath = "tools/bin/sdkmanager"; + if (HostOsInfo::isWindowsHost()) + toolPath += ANDROID_BAT_SUFFIX; + sdkPath = sdkPath.appendPath(toolPath); + return sdkPath; +} + +FileName AndroidConfig::avdManagerToolPath() const +{ + FileName avdManagerPath = m_sdkLocation; + QString toolPath = "tools/bin/avdmanager"; + if (HostOsInfo::isWindowsHost()) + toolPath += ANDROID_BAT_SUFFIX; + avdManagerPath = avdManagerPath.appendPath(toolPath); + return avdManagerPath; +} + FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang, const QString &ndkToolChainVersion) const { @@ -583,7 +509,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo devices.push_back(dev); } - Utils::sort(devices, androidDevicesLessThan); + Utils::sort(devices); if (devices.isEmpty() && error) *error = QApplication::translate("AndroidConfiguration", "No devices found in output of: %1") @@ -605,197 +531,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, return result; } -QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const -{ - return Utils::runAsync(&AndroidConfig::createAVDImpl, info, - androidToolPath(), androidToolEnvironment()); -} - -AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env) -{ - QProcess proc; - proc.setProcessEnvironment(env.toProcessEnvironment()); - QStringList arguments; - arguments << QLatin1String("create") << QLatin1String("avd") - << QLatin1String("-t") << info.target - << QLatin1String("-n") << info.name - << QLatin1String("-b") << info.abi; - if (info.sdcardSize > 0) - arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize); - proc.start(androidToolPath.toString(), arguments); - if (!proc.waitForStarted()) { - info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"") - .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' '))); - return info; - } - QTC_CHECK(proc.state() == QProcess::Running); - proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile" - - QByteArray question; - while (true) { - proc.waitForReadyRead(500); - question += proc.readAllStandardOutput(); - if (question.endsWith(QByteArray("]:"))) { - // truncate to last line - int index = question.lastIndexOf(QByteArray("\n")); - if (index != -1) - question = question.mid(index); - if (question.contains("hw.gpu.enabled")) - proc.write(QByteArray("yes\n")); - else - proc.write(QByteArray("\n")); - question.clear(); - } - - if (proc.state() != QProcess::Running) - break; - } - QTC_CHECK(proc.state() == QProcess::NotRunning); - - QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError()); - // The exit code is always 0, so we need to check stderr - // For now assume that any output at all indicates a error - if (!errorOutput.isEmpty()) { - info.error = errorOutput; - } - - return info; -} - -bool AndroidConfig::removeAVD(const QString &name) const -{ - SynchronousProcess proc; - proc.setTimeoutS(5); - proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment()); - SynchronousProcessResponse response - = proc.runBlocking(androidToolPath().toString(), QStringList({"delete", "avd", "-n", name})); - return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0; -} - -QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture() const -{ - return Utils::runAsync(&AndroidConfig::androidVirtualDevices, - androidToolPath().toString(), androidToolEnvironment()); -} - -QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment) -{ - QVector<AndroidDeviceInfo> devices; - SynchronousProcess proc; - proc.setTimeoutS(20); - proc.setProcessEnvironment(environment.toProcessEnvironment()); - SynchronousProcessResponse response = proc.run(androidTool, {"list", "avd"}); // list available AVDs - if (response.result != SynchronousProcessResponse::Finished) - return devices; - - QStringList avds = response.allOutput().split('\n'); - if (avds.empty()) - return devices; - - while (avds.first().startsWith(QLatin1String("* daemon"))) - avds.removeFirst(); // remove the daemon logs - avds.removeFirst(); // remove "List of devices attached" header line - - bool nextLineIsTargetLine = false; - - AndroidDeviceInfo dev; - for (int i = 0; i < avds.size(); i++) { - QString line = avds.at(i); - if (!line.contains(QLatin1String("Name:"))) - continue; - - int index = line.indexOf(QLatin1Char(':')) + 2; - if (index >= line.size()) - break; - dev.avdname = line.mid(index).trimmed(); - dev.sdk = -1; - dev.cpuAbi.clear(); - ++i; - for (; i < avds.size(); ++i) { - line = avds.at(i); - if (line.contains(QLatin1String("---------"))) - break; - - if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) { - if (line.contains(QLatin1String("Google APIs"))) { - nextLineIsTargetLine = true; - continue; - } - - nextLineIsTargetLine = false; - - int lastIndex = line.lastIndexOf(QLatin1Char(' ')); - if (lastIndex == -1) // skip line - break; - QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed(); - dev.sdk = apiLevelFromAndroidList(tmp); - } - if (line.contains(QLatin1String("Tag/ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex)); - } else if (line.contains(QLatin1String("ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed()); - } - } - // armeabi-v7a devices can also run armeabi code - if (dev.cpuAbi == QStringList("armeabi-v7a")) - dev.cpuAbi << QLatin1String("armeabi"); - dev.state = AndroidDeviceInfo::OkState; - dev.type = AndroidDeviceInfo::Emulator; - if (dev.cpuAbi.isEmpty() || dev.sdk == -1) - continue; - devices.push_back(dev); - } - Utils::sort(devices, androidDevicesLessThan); - - return devices; -} - -QString AndroidConfig::startAVD(const QString &name) const -{ - if (!findAvd(name).isEmpty() || startAVDAsync(name)) - return waitForAvd(name); - return QString(); -} - -bool AndroidConfig::startAVDAsync(const QString &avdName) const -{ - QProcess *avdProcess = new QProcess(); - QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished), - avdProcess, &QObject::deleteLater); - - // start the emulator - QStringList arguments; - if (AndroidConfigurations::force32bitEmulator()) - arguments << QLatin1String("-force-32bit"); - - arguments << QLatin1String("-partition-size") << QString::number(partitionSize()) - << QLatin1String("-avd") << avdName; - avdProcess->start(emulatorToolPath().toString(), arguments); - if (!avdProcess->waitForStarted(-1)) { - delete avdProcess; - return false; - } - return true; -} - -QString AndroidConfig::findAvd(const QString &avdName) const -{ - QVector<AndroidDeviceInfo> devices = connectedDevices(); - foreach (AndroidDeviceInfo device, devices) { - if (device.type != AndroidDeviceInfo::Emulator) - continue; - if (device.avdname == avdName) - return device.serialNumber; - } - return QString(); -} - bool AndroidConfig::isConnected(const QString &serialNumber) const { QVector<AndroidDeviceInfo> devices = connectedDevices(); @@ -806,39 +541,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const return false; } -bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const -{ - // found a serial number, now wait until it's done booting... - for (int i = 0; i < 60; ++i) { - if (fi.isCanceled()) - return false; - if (hasFinishedBooting(serialNumber)) { - return true; - } else { - QThread::sleep(2); - if (!isConnected(serialNumber)) // device was disconnected - return false; - } - } - return false; -} - -QString AndroidConfig::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const -{ - // we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running - // 60 rounds of 2s sleeping, two minutes for the avd to start - QString serialNumber; - for (int i = 0; i < 60; ++i) { - if (fi.isCanceled()) - return QString(); - serialNumber = findAvd(avdName); - if (!serialNumber.isEmpty()) - return waitForBooted(serialNumber, fi) ? serialNumber : QString(); - QThread::sleep(2); - } - return QString(); -} - bool AndroidConfig::isBootToQt(const QString &device) const { return isBootToQt(adbToolPath().toString(), device); @@ -961,21 +663,6 @@ QString AndroidConfig::getProductModel(const QString &device) const return model; } -bool AndroidConfig::hasFinishedBooting(const QString &device) const -{ - QStringList arguments = AndroidDeviceInfo::adbSelector(device); - arguments << QLatin1String("shell") << QLatin1String("getprop") - << QLatin1String("init.svc.bootanim"); - - SynchronousProcess adbProc; - adbProc.setTimeoutS(10); - SynchronousProcessResponse response = adbProc.runBlocking(adbToolPath().toString(), arguments); - if (response.result != SynchronousProcessResponse::Finished) - return false; - QString value = response.allOutput().trimmed(); - return value == QLatin1String("stopped"); -} - QStringList AndroidConfig::getAbis(const QString &device) const { return getAbis(adbToolPath().toString(), device); @@ -1053,6 +740,19 @@ void AndroidConfig::setSdkLocation(const FileName &sdkLocation) m_availableSdkPlatformsUpToDate = false; } +QVersionNumber AndroidConfig::sdkToolsVersion() const +{ + QVersionNumber version; + if (m_sdkLocation.exists()) { + Utils::FileName sdkToolsPropertiesPath(m_sdkLocation); + sdkToolsPropertiesPath.appendPath("tools/source.properties"); + QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); + auto versionStr = settings.value(sdkToolsVersionKey).toString(); + version = QVersionNumber::fromString(versionStr); + } + return version; +} + FileName AndroidConfig::ndkLocation() const { return m_ndkLocation; @@ -1126,9 +826,18 @@ void AndroidConfig::setAutomaticKitCreation(bool b) m_automaticKitCreation = b; } +bool AndroidConfig::antScriptsAvailable() const +{ + return sdkToolsVersion() < sdkToolsAntMissingVersion; +} + bool AndroidConfig::useGrandle() const { - return m_useGradle; + if (antScriptsAvailable()) { + return m_useGradle; + } + // Force gradle builds. + return true; } void AndroidConfig::setUseGradle(bool b) @@ -1353,6 +1062,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber) return QStringList({"-s", serialNumber}); } +bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const +{ + if (serialNumber.contains("????") != other.serialNumber.contains("????")) + return !serialNumber.contains("????"); + if (type != other.type) + return type == AndroidDeviceInfo::Hardware; + if (sdk != other.sdk) + return sdk < other.sdk; + if (avdname != other.avdname) + return avdname < other.avdname; + + return serialNumber < other.serialNumber; +} + const AndroidConfig &AndroidConfigurations::currentConfig() { return m_instance->m_config; // ensure that m_instance is initialized @@ -1494,4 +1217,13 @@ void AndroidConfigurations::updateAndroidDevice() AndroidConfigurations *AndroidConfigurations::m_instance = 0; +bool SdkPlatform::operator <(const SdkPlatform &other) const +{ + if (apiLevel != other.apiLevel) + return apiLevel > other.apiLevel; + if (name != other.name) + return name < other.name; + return false; +} + } // namespace Android |