forked from qt-creator/qt-creator
Android: Use avdmanager tool
android tool is deprecated since sdk tools version 25.3.0. Use the new avdmanager tool Task-number: QTCREATORBUG-17814 Change-Id: Id6f495f14e12d0069df08164cac1929b76d9e932 Reviewed-by: BogDan Vatra <bogdan@kdab.com>
This commit is contained in:
@@ -49,7 +49,8 @@ HEADERS += \
|
|||||||
androidbuildapkwidget.h \
|
androidbuildapkwidget.h \
|
||||||
androidrunnable.h \
|
androidrunnable.h \
|
||||||
androidtoolmanager.h \
|
androidtoolmanager.h \
|
||||||
androidsdkmanager.h
|
androidsdkmanager.h \
|
||||||
|
androidavdmanager.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
androidconfigurations.cpp \
|
androidconfigurations.cpp \
|
||||||
@@ -92,7 +93,8 @@ SOURCES += \
|
|||||||
androidqtsupport.cpp \
|
androidqtsupport.cpp \
|
||||||
androidrunnable.cpp \
|
androidrunnable.cpp \
|
||||||
androidtoolmanager.cpp \
|
androidtoolmanager.cpp \
|
||||||
androidsdkmanager.cpp
|
androidsdkmanager.cpp \
|
||||||
|
androidavdmanager.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
androidsettingswidget.ui \
|
androidsettingswidget.ui \
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ Project {
|
|||||||
"android.qrc",
|
"android.qrc",
|
||||||
"androidanalyzesupport.cpp",
|
"androidanalyzesupport.cpp",
|
||||||
"androidanalyzesupport.h",
|
"androidanalyzesupport.h",
|
||||||
|
"androidavdmanager.cpp",
|
||||||
|
"androidavdmanager.h",
|
||||||
"androidconfigurations.cpp",
|
"androidconfigurations.cpp",
|
||||||
"androidconfigurations.h",
|
"androidconfigurations.h",
|
||||||
"androidconstants.h",
|
"androidconstants.h",
|
||||||
|
|||||||
441
src/plugins/android/androidavdmanager.cpp
Normal file
441
src/plugins/android/androidavdmanager.cpp
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
|
#include "androidtoolmanager.h"
|
||||||
|
|
||||||
|
#include "utils/algorithm.h"
|
||||||
|
#include "utils/qtcassert.h"
|
||||||
|
#include "utils/runextensions.h"
|
||||||
|
#include "utils/synchronousprocess.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager")
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// Avd list keys to parse avd
|
||||||
|
const char avdInfoNameKey[] = "Name:";
|
||||||
|
const char avdInfoPathKey[] = "Path:";
|
||||||
|
const char avdInfoAbiKey[] = "abi.type";
|
||||||
|
const char avdInfoTargetKey[] = "target";
|
||||||
|
const char avdInfoErrorKey[] = "Error:";
|
||||||
|
|
||||||
|
const QVersionNumber avdManagerIntroVersion(25, 3 ,0);
|
||||||
|
|
||||||
|
const int avdCreateTimeoutMs = 30000;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs the \c avdmanager tool specific to configuration \a config with arguments \a args. Returns
|
||||||
|
\c true if the command is successfully executed. Output is copied into \a output. The function
|
||||||
|
blocks the calling thread.
|
||||||
|
*/
|
||||||
|
static bool avdManagerCommand(const AndroidConfig config, const QStringList &args, QString *output)
|
||||||
|
{
|
||||||
|
QString avdManagerToolPath = config.avdManagerToolPath().toString();
|
||||||
|
Utils::SynchronousProcess proc;
|
||||||
|
Utils::SynchronousProcessResponse response = proc.runBlocking(avdManagerToolPath, args);
|
||||||
|
if (response.result == Utils::SynchronousProcessResponse::Finished) {
|
||||||
|
if (output)
|
||||||
|
*output = response.allOutput();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
|
||||||
|
\c true if the key is found, \c false otherwise. The value is copied into \a value.
|
||||||
|
*/
|
||||||
|
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
|
||||||
|
{
|
||||||
|
auto trimmedInput = line.trimmed();
|
||||||
|
if (trimmedInput.startsWith(key)) {
|
||||||
|
if (value)
|
||||||
|
*value = trimmedInput.section(key, 1, 1).trimmed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkForTimeout(const chrono::steady_clock::time_point &start,
|
||||||
|
int msecs = 3000)
|
||||||
|
{
|
||||||
|
bool timedOut = false;
|
||||||
|
auto end = chrono::steady_clock::now();
|
||||||
|
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
|
||||||
|
timedOut = true;
|
||||||
|
return timedOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config,
|
||||||
|
const AndroidConfig::CreateAvdInfo &info)
|
||||||
|
{
|
||||||
|
AndroidConfig::CreateAvdInfo result = info;
|
||||||
|
|
||||||
|
if (!result.isValid()) {
|
||||||
|
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
|
||||||
|
<< result.target.name << result.target.apiLevel;
|
||||||
|
result.error = QApplication::translate("AndroidAvdManager",
|
||||||
|
"Cannot create AVD. Invalid input.");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name});
|
||||||
|
|
||||||
|
if (!result.abi.isEmpty()) {
|
||||||
|
SystemImage image = Utils::findOrDefault(result.target.systemImages,
|
||||||
|
Utils::equal(&SystemImage::abiName, result.abi));
|
||||||
|
if (image.isValid()) {
|
||||||
|
arguments << "-k" << image.package;
|
||||||
|
} else {
|
||||||
|
qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform"
|
||||||
|
<< result.abi << result.target.name;
|
||||||
|
result.error = QApplication::translate("AndroidAvdManager",
|
||||||
|
"Cannot create AVD. Cannot find system image for "
|
||||||
|
"the ABI %1(%2).").arg(result.abi).arg(result.target.name);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
arguments << "-k" << result.target.package;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.sdcardSize > 0)
|
||||||
|
arguments << "-c" << QString::fromLatin1("%1M").arg(result.sdcardSize);
|
||||||
|
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(config.avdManagerToolPath().toString(), arguments);
|
||||||
|
if (!proc.waitForStarted()) {
|
||||||
|
result.error = QApplication::translate("AndroidAvdManager",
|
||||||
|
"Could not start process \"%1 %2\"")
|
||||||
|
.arg(config.avdManagerToolPath().toString(), arguments.join(' '));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
QTC_CHECK(proc.state() == QProcess::Running);
|
||||||
|
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
|
||||||
|
|
||||||
|
auto start = chrono::steady_clock::now();
|
||||||
|
QString errorOutput;
|
||||||
|
QByteArray question;
|
||||||
|
while (errorOutput.isEmpty()) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
// The exit code is always 0, so we need to check stderr
|
||||||
|
// For now assume that any output at all indicates a error
|
||||||
|
errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
|
||||||
|
if (proc.state() != QProcess::Running)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// For a sane input and command, process should finish before timeout.
|
||||||
|
if (checkForTimeout(start, avdCreateTimeoutMs)) {
|
||||||
|
result.error = QApplication::translate("AndroidAvdManager",
|
||||||
|
"Cannot create AVD. Command timed out.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill the running process.
|
||||||
|
if (proc.state() != QProcess::NotRunning) {
|
||||||
|
proc.terminate();
|
||||||
|
if (!proc.waitForFinished(3000))
|
||||||
|
proc.kill();
|
||||||
|
}
|
||||||
|
|
||||||
|
QTC_CHECK(proc.state() == QProcess::NotRunning);
|
||||||
|
result.error = errorOutput;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class AvdManagerOutputParser
|
||||||
|
\brief The AvdManagerOutputParser class is a helper class to parse the output of the avdmanager
|
||||||
|
commands.
|
||||||
|
*/
|
||||||
|
class AvdManagerOutputParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config);
|
||||||
|
AndroidDeviceInfoList parseAvdList(const QString &output);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AndroidAvdManager::AndroidAvdManager(const AndroidConfig &config):
|
||||||
|
m_config(config),
|
||||||
|
m_androidTool(new AndroidToolManager(m_config)),
|
||||||
|
m_parser(new AvdManagerOutputParser)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidAvdManager::~AndroidAvdManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidAvdManager::avdManagerUiToolAvailable() const
|
||||||
|
{
|
||||||
|
return m_config.sdkToolsVersion() < avdManagerIntroVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidAvdManager::launchAvdManagerUiTool() const
|
||||||
|
{
|
||||||
|
if (avdManagerUiToolAvailable()) {
|
||||||
|
m_androidTool->launchAvdManager();
|
||||||
|
} else {
|
||||||
|
qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available"
|
||||||
|
<< m_config.sdkToolsVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<AndroidConfig::CreateAvdInfo>
|
||||||
|
AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const
|
||||||
|
{
|
||||||
|
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
|
||||||
|
return m_androidTool->createAvd(info);
|
||||||
|
|
||||||
|
return Utils::runAsync(&createAvdCommand, m_config, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidAvdManager::removeAvd(const QString &name) const
|
||||||
|
{
|
||||||
|
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
|
||||||
|
return m_androidTool->removeAvd(name);
|
||||||
|
|
||||||
|
Utils::SynchronousProcess proc;
|
||||||
|
proc.setTimeoutS(5);
|
||||||
|
Utils::SynchronousProcessResponse response
|
||||||
|
= proc.runBlocking(m_config.avdManagerToolPath().toString(),
|
||||||
|
QStringList({"delete", "avd", "-n", name}));
|
||||||
|
return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
|
||||||
|
{
|
||||||
|
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
|
||||||
|
return m_androidTool->androidVirtualDevicesFuture();
|
||||||
|
|
||||||
|
return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidAvdManager::startAvd(const QString &name) const
|
||||||
|
{
|
||||||
|
if (!findAvd(name).isEmpty() || startAvdAsync(name))
|
||||||
|
return waitForAvd(name);
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidAvdManager::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 << "-force-32bit";
|
||||||
|
|
||||||
|
arguments << "-partition-size" << QString::number(m_config.partitionSize())
|
||||||
|
<< "-avd" << avdName;
|
||||||
|
avdProcess->start(m_config.emulatorToolPath().toString(), arguments);
|
||||||
|
if (!avdProcess->waitForStarted(-1)) {
|
||||||
|
delete avdProcess;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidAvdManager::findAvd(const QString &avdName) const
|
||||||
|
{
|
||||||
|
QVector<AndroidDeviceInfo> devices = m_config.connectedDevices();
|
||||||
|
foreach (AndroidDeviceInfo device, devices) {
|
||||||
|
if (device.type != AndroidDeviceInfo::Emulator)
|
||||||
|
continue;
|
||||||
|
if (device.avdname == avdName)
|
||||||
|
return device.serialNumber;
|
||||||
|
}
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidAvdManager::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 AndroidAvdManager::isAvdBooted(const QString &device) const
|
||||||
|
{
|
||||||
|
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
|
||||||
|
arguments << "shell" << "getprop" << "init.svc.bootanim";
|
||||||
|
|
||||||
|
Utils::SynchronousProcess adbProc;
|
||||||
|
adbProc.setTimeoutS(10);
|
||||||
|
Utils::SynchronousProcessResponse response =
|
||||||
|
adbProc.runBlocking(m_config.adbToolPath().toString(), arguments);
|
||||||
|
if (response.result != Utils::SynchronousProcessResponse::Finished)
|
||||||
|
return false;
|
||||||
|
QString value = response.allOutput().trimmed();
|
||||||
|
return value == "stopped";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidAvdManager::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 (isAvdBooted(serialNumber)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
QThread::sleep(2);
|
||||||
|
if (!m_config.isConnected(serialNumber)) // device was disconnected
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidConfig &config)
|
||||||
|
{
|
||||||
|
QString output;
|
||||||
|
if (!avdManagerCommand(config, QStringList({"list", "avd"}), &output)) {
|
||||||
|
qCDebug(avdManagerLog) << "Avd list command failed" << output << config.sdkToolsVersion();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return parseAvdList(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidDeviceInfoList AvdManagerOutputParser::parseAvdList(const QString &output)
|
||||||
|
{
|
||||||
|
AndroidDeviceInfoList avdList;
|
||||||
|
QStringList avdInfo;
|
||||||
|
auto parseAvdInfo = [&avdInfo, &avdList, this] () {
|
||||||
|
AndroidDeviceInfo avd;
|
||||||
|
if (parseAvd(avdInfo, &avd)) {
|
||||||
|
// armeabi-v7a devices can also run armeabi code
|
||||||
|
if (avd.cpuAbi.contains("armeabi-v7a"))
|
||||||
|
avd.cpuAbi << "armeabi";
|
||||||
|
avd.state = AndroidDeviceInfo::OkState;
|
||||||
|
avd.type = AndroidDeviceInfo::Emulator;
|
||||||
|
avdList << avd;
|
||||||
|
} else {
|
||||||
|
qCDebug(avdManagerLog) << "Avd Parsing: Parsing failed: " << avdInfo;
|
||||||
|
}
|
||||||
|
avdInfo.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (QString line, output.split('\n')) {
|
||||||
|
if (line.startsWith("---------") || line.isEmpty()) {
|
||||||
|
parseAvdInfo();
|
||||||
|
} else {
|
||||||
|
avdInfo << line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!avdInfo.isEmpty())
|
||||||
|
parseAvdInfo();
|
||||||
|
|
||||||
|
Utils::sort(avdList);
|
||||||
|
|
||||||
|
return avdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AvdManagerOutputParser::parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(avd, return false);
|
||||||
|
foreach (const QString &line, deviceInfo) {
|
||||||
|
QString value;
|
||||||
|
if (valueForKey(avdInfoErrorKey, line)) {
|
||||||
|
qCDebug(avdManagerLog) << "Avd Parsing: Skip avd device. Error key found:" << line;
|
||||||
|
return false;
|
||||||
|
} else if (valueForKey(avdInfoNameKey, line, &value)) {
|
||||||
|
avd->avdname = value;
|
||||||
|
} else if (valueForKey(avdInfoPathKey, line, &value)) {
|
||||||
|
const Utils::FileName avdPath = Utils::FileName::fromString(value);
|
||||||
|
if (avdPath.exists())
|
||||||
|
{
|
||||||
|
// Get ABI.
|
||||||
|
Utils::FileName configFile = avdPath;
|
||||||
|
configFile.appendPath("config.ini");
|
||||||
|
QSettings config(configFile.toString(), QSettings::IniFormat);
|
||||||
|
value = config.value(avdInfoAbiKey).toString();
|
||||||
|
if (!value.isEmpty())
|
||||||
|
avd->cpuAbi << value;
|
||||||
|
else
|
||||||
|
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find ABI:" << configFile;
|
||||||
|
|
||||||
|
// Get Target
|
||||||
|
Utils::FileName avdInfoFile = avdPath.parentDir();
|
||||||
|
QString avdInfoFileName = avdPath.toFileInfo().baseName() + ".ini";
|
||||||
|
avdInfoFile.appendPath(avdInfoFileName);
|
||||||
|
QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat);
|
||||||
|
value = avdInfo.value(avdInfoTargetKey).toString();
|
||||||
|
if (!value.isEmpty())
|
||||||
|
avd->sdk = value.section('-', -1).toInt();
|
||||||
|
else
|
||||||
|
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
||||||
66
src/plugins/android/androidavdmanager.h
Normal file
66
src/plugins/android/androidavdmanager.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "androidconfigurations.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidToolManager;
|
||||||
|
class AvdManagerOutputParser;
|
||||||
|
|
||||||
|
class AndroidAvdManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig());
|
||||||
|
~AndroidAvdManager();
|
||||||
|
|
||||||
|
bool avdManagerUiToolAvailable() const;
|
||||||
|
void launchAvdManagerUiTool() const;
|
||||||
|
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
|
||||||
|
bool removeAvd(const QString &name) const;
|
||||||
|
QFuture<AndroidDeviceInfoList> avdList() const;
|
||||||
|
|
||||||
|
QString startAvd(const QString &name) const;
|
||||||
|
bool startAvdAsync(const QString &avdName) const;
|
||||||
|
QString findAvd(const QString &avdName) const;
|
||||||
|
QString waitForAvd(const QString &avdName,
|
||||||
|
const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
|
||||||
|
bool isAvdBooted(const QString &device) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const AndroidConfig &m_config;
|
||||||
|
std::unique_ptr<AndroidToolManager> m_androidTool;
|
||||||
|
std::unique_ptr<AvdManagerOutputParser> m_parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
||||||
@@ -386,7 +386,10 @@ FileName AndroidConfig::antToolPath() const
|
|||||||
FileName AndroidConfig::emulatorToolPath() const
|
FileName AndroidConfig::emulatorToolPath() const
|
||||||
{
|
{
|
||||||
FileName path = m_sdkLocation;
|
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
|
FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const
|
||||||
@@ -409,6 +412,16 @@ FileName AndroidConfig::sdkManagerToolPath() const
|
|||||||
return sdkPath;
|
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,
|
FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang,
|
||||||
const QString &ndkToolChainVersion) const
|
const QString &ndkToolChainVersion) const
|
||||||
{
|
{
|
||||||
@@ -515,46 +528,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
bool AndroidConfig::isConnected(const QString &serialNumber) const
|
||||||
{
|
{
|
||||||
QVector<AndroidDeviceInfo> devices = connectedDevices();
|
QVector<AndroidDeviceInfo> devices = connectedDevices();
|
||||||
@@ -565,39 +538,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
|
|||||||
return false;
|
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
|
bool AndroidConfig::isBootToQt(const QString &device) const
|
||||||
{
|
{
|
||||||
return isBootToQt(adbToolPath().toString(), device);
|
return isBootToQt(adbToolPath().toString(), device);
|
||||||
@@ -720,21 +660,6 @@ QString AndroidConfig::getProductModel(const QString &device) const
|
|||||||
return model;
|
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
|
QStringList AndroidConfig::getAbis(const QString &device) const
|
||||||
{
|
{
|
||||||
return getAbis(adbToolPath().toString(), device);
|
return getAbis(adbToolPath().toString(), device);
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ public:
|
|||||||
bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
|
bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
|
||||||
bool operator<(const AndroidDeviceInfo &other) const;
|
bool operator<(const AndroidDeviceInfo &other) const;
|
||||||
};
|
};
|
||||||
|
using AndroidDeviceInfoList = QList<AndroidDeviceInfo>;
|
||||||
|
|
||||||
//! Defines an Android system image.
|
//! Defines an Android system image.
|
||||||
class SystemImage
|
class SystemImage
|
||||||
@@ -89,6 +90,7 @@ using SystemImageList = QList<SystemImage>;
|
|||||||
class SdkPlatform
|
class SdkPlatform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
bool isValid() const { return !name.isEmpty() && apiLevel != -1; }
|
||||||
bool operator <(const SdkPlatform &other) const;
|
bool operator <(const SdkPlatform &other) const;
|
||||||
int apiLevel = -1;
|
int apiLevel = -1;
|
||||||
QString name;
|
QString name;
|
||||||
@@ -141,6 +143,7 @@ public:
|
|||||||
Utils::FileName antToolPath() const;
|
Utils::FileName antToolPath() const;
|
||||||
Utils::FileName emulatorToolPath() const;
|
Utils::FileName emulatorToolPath() const;
|
||||||
Utils::FileName sdkManagerToolPath() const;
|
Utils::FileName sdkManagerToolPath() const;
|
||||||
|
Utils::FileName avdManagerToolPath() const;
|
||||||
|
|
||||||
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
|
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
|
||||||
const QString &ndkToolChainVersion) const;
|
const QString &ndkToolChainVersion) const;
|
||||||
@@ -152,7 +155,8 @@ public:
|
|||||||
class CreateAvdInfo
|
class CreateAvdInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QString target;
|
bool isValid() const { return target.isValid() && !name.isEmpty(); }
|
||||||
|
SdkPlatform target;
|
||||||
QString name;
|
QString name;
|
||||||
QString abi;
|
QString abi;
|
||||||
int sdcardSize = 0;
|
int sdcardSize = 0;
|
||||||
@@ -164,10 +168,6 @@ public:
|
|||||||
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
|
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
|
||||||
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
|
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
|
||||||
|
|
||||||
QString startAVD(const QString &name) const;
|
|
||||||
bool startAVDAsync(const QString &avdName) const;
|
|
||||||
QString findAvd(const QString &avdName) const;
|
|
||||||
QString waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
|
|
||||||
QString bestNdkPlatformMatch(int target) const;
|
QString bestNdkPlatformMatch(int target) const;
|
||||||
|
|
||||||
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
|
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
|
||||||
@@ -178,8 +178,6 @@ public:
|
|||||||
QString getProductModel(const QString &device) const;
|
QString getProductModel(const QString &device) const;
|
||||||
enum class OpenGl { Enabled, Disabled, Unknown };
|
enum class OpenGl { Enabled, Disabled, Unknown };
|
||||||
OpenGl getOpenGLEnabled(const QString &emulator) const;
|
OpenGl getOpenGLEnabled(const QString &emulator) const;
|
||||||
bool hasFinishedBooting(const QString &device) const;
|
|
||||||
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
|
|
||||||
bool isConnected(const QString &serialNumber) const;
|
bool isConnected(const QString &serialNumber) const;
|
||||||
|
|
||||||
SdkPlatform highestAndroidSdk() const;
|
SdkPlatform highestAndroidSdk() const;
|
||||||
@@ -257,3 +255,5 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
Q_DECLARE_METATYPE(Android::SdkPlatform)
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidglobal.h"
|
#include "androidglobal.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/fileutils.h>
|
#include <coreplugin/fileutils.h>
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -262,8 +263,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
|
|||||||
|
|
||||||
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
||||||
|
|
||||||
if (AndroidConfigurations::currentConfig().findAvd(m_avdName).isEmpty())
|
AndroidAvdManager avdManager;
|
||||||
AndroidConfigurations::currentConfig().startAVDAsync(m_avdName);
|
if (avdManager.findAvd(m_avdName).isEmpty())
|
||||||
|
avdManager.startAvdAsync(m_avdName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +416,7 @@ void AndroidDeployQtStep::slotSetSerialNumber(const QString &serialNumber)
|
|||||||
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
|
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
|
||||||
{
|
{
|
||||||
if (!m_avdName.isEmpty()) {
|
if (!m_avdName.isEmpty()) {
|
||||||
QString serialNumber = AndroidConfigurations::currentConfig().waitForAvd(m_avdName, fi);
|
QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
|
||||||
if (serialNumber.isEmpty()) {
|
if (serialNumber.isEmpty()) {
|
||||||
reportRunResult(fi, false);
|
reportRunResult(fi, false);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#include "androiddevicedialog.h"
|
#include "androiddevicedialog.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
#include "ui_androiddevicedialog.h"
|
#include "ui_androiddevicedialog.h"
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
@@ -424,7 +425,7 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro
|
|||||||
m_apiLevel(apiLevel),
|
m_apiLevel(apiLevel),
|
||||||
m_abi(abi),
|
m_abi(abi),
|
||||||
m_defaultDevice(serialNumber),
|
m_defaultDevice(serialNumber),
|
||||||
m_androidToolManager(new AndroidToolManager(AndroidConfigurations::currentConfig()))
|
m_avdManager(new AndroidAvdManager)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->deviceView->setModel(m_model);
|
m_ui->deviceView->setModel(m_model);
|
||||||
@@ -516,7 +517,7 @@ void AndroidDeviceDialog::refreshDeviceList()
|
|||||||
m_ui->refreshDevicesButton->setEnabled(false);
|
m_ui->refreshDevicesButton->setEnabled(false);
|
||||||
m_progressIndicator->show();
|
m_progressIndicator->show();
|
||||||
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
||||||
m_futureWatcherRefreshDevices.setFuture(m_androidToolManager->androidVirtualDevicesFuture());
|
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::devicesRefreshed()
|
void AndroidDeviceDialog::devicesRefreshed()
|
||||||
@@ -531,7 +532,7 @@ void AndroidDeviceDialog::devicesRefreshed()
|
|||||||
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
|
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<AndroidDeviceInfo> devices = m_futureWatcherRefreshDevices.result();
|
AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result();
|
||||||
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
|
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
|
||||||
[] (const AndroidDeviceInfo &info) {
|
[] (const AndroidDeviceInfo &info) {
|
||||||
return info.avdname;
|
return info.avdname;
|
||||||
@@ -584,12 +585,12 @@ void AndroidDeviceDialog::createAvd()
|
|||||||
m_ui->createAVDButton->setEnabled(false);
|
m_ui->createAVDButton->setEnabled(false);
|
||||||
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
|
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
|
||||||
|
|
||||||
if (info.target.isEmpty()) {
|
if (!info.target.isValid()) {
|
||||||
m_ui->createAVDButton->setEnabled(true);
|
m_ui->createAVDButton->setEnabled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcherAddDevice.setFuture(m_androidToolManager->createAvd(info));
|
m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::avdAdded()
|
void AndroidDeviceDialog::avdAdded()
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidtoolmanager.h"
|
|
||||||
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
@@ -44,6 +43,7 @@ namespace Utils { class ProgressIndicator; }
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidAvdManager;
|
||||||
class AndroidDeviceModel;
|
class AndroidDeviceModel;
|
||||||
namespace Ui { class AndroidDeviceDialog; }
|
namespace Ui { class AndroidDeviceDialog; }
|
||||||
|
|
||||||
@@ -77,10 +77,10 @@ private:
|
|||||||
QString m_abi;
|
QString m_abi;
|
||||||
QString m_avdNameFromAdd;
|
QString m_avdNameFromAdd;
|
||||||
QString m_defaultDevice;
|
QString m_defaultDevice;
|
||||||
std::unique_ptr<AndroidToolManager> m_androidToolManager;
|
std::unique_ptr<AndroidAvdManager> m_avdManager;
|
||||||
QVector<AndroidDeviceInfo> m_connectedDevices;
|
QVector<AndroidDeviceInfo> m_connectedDevices;
|
||||||
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
||||||
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices;
|
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "androidqtsupport.h"
|
#include "androidqtsupport.h"
|
||||||
#include "androidqtversion.h"
|
#include "androidqtversion.h"
|
||||||
#include "androidbuildapkstep.h"
|
#include "androidbuildapkstep.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <coreplugin/documentmanager.h>
|
#include <coreplugin/documentmanager.h>
|
||||||
#include <coreplugin/messagemanager.h>
|
#include <coreplugin/messagemanager.h>
|
||||||
@@ -345,7 +346,7 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
|
|||||||
QString deviceSerialNumber = info.serialNumber;
|
QString deviceSerialNumber = info.serialNumber;
|
||||||
|
|
||||||
if (info.type == AndroidDeviceInfo::Emulator) {
|
if (info.type == AndroidDeviceInfo::Emulator) {
|
||||||
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
|
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
|
||||||
if (deviceSerialNumber.isEmpty())
|
if (deviceSerialNumber.isEmpty())
|
||||||
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
||||||
}
|
}
|
||||||
@@ -374,7 +375,7 @@ void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const Q
|
|||||||
|
|
||||||
QString deviceSerialNumber = info.serialNumber;
|
QString deviceSerialNumber = info.serialNumber;
|
||||||
if (info.type == AndroidDeviceInfo::Emulator) {
|
if (info.type == AndroidDeviceInfo::Emulator) {
|
||||||
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname);
|
deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
|
||||||
if (deviceSerialNumber.isEmpty())
|
if (deviceSerialNumber.isEmpty())
|
||||||
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
Core::MessageManager::write(tr("Starting Android virtual device failed."));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "androidglobal.h"
|
#include "androidglobal.h"
|
||||||
#include "androidrunconfiguration.h"
|
#include "androidrunconfiguration.h"
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <debugger/debuggerrunconfigurationaspect.h>
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
@@ -791,8 +792,9 @@ void AndroidRunner::launchAVD()
|
|||||||
emit adbParametersChanged(m_androidRunnable.packageName,
|
emit adbParametersChanged(m_androidRunnable.packageName,
|
||||||
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
||||||
if (info.isValid()) {
|
if (info.isValid()) {
|
||||||
if (AndroidConfigurations::currentConfig().findAvd(info.avdname).isEmpty()) {
|
AndroidAvdManager avdManager;
|
||||||
bool launched = AndroidConfigurations::currentConfig().startAVDAsync(info.avdname);
|
if (avdManager.findAvd(info.avdname).isEmpty()) {
|
||||||
|
bool launched = avdManager.startAvdAsync(info.avdname);
|
||||||
m_launchedAVDName = launched ? info.avdname:"";
|
m_launchedAVDName = launched ? info.avdname:"";
|
||||||
} else {
|
} else {
|
||||||
m_launchedAVDName.clear();
|
m_launchedAVDName.clear();
|
||||||
@@ -803,11 +805,12 @@ void AndroidRunner::launchAVD()
|
|||||||
void AndroidRunner::checkAVD()
|
void AndroidRunner::checkAVD()
|
||||||
{
|
{
|
||||||
const AndroidConfig &config = AndroidConfigurations::currentConfig();
|
const AndroidConfig &config = AndroidConfigurations::currentConfig();
|
||||||
QString serialNumber = config.findAvd(m_launchedAVDName);
|
AndroidAvdManager avdManager(config);
|
||||||
|
QString serialNumber = avdManager.findAvd(m_launchedAVDName);
|
||||||
if (!serialNumber.isEmpty())
|
if (!serialNumber.isEmpty())
|
||||||
return; // try again on next timer hit
|
return; // try again on next timer hit
|
||||||
|
|
||||||
if (config.hasFinishedBooting(serialNumber)) {
|
if (avdManager.isAvdBooted(serialNumber)) {
|
||||||
m_checkAVDTimer.stop();
|
m_checkAVDTimer.stop();
|
||||||
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
|
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
|
||||||
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);
|
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidtoolchain.h"
|
#include "androidtoolchain.h"
|
||||||
#include "androidtoolmanager.h"
|
#include "androidavdmanager.h"
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
void AvdModel::setAvdList(const QVector<AndroidDeviceInfo> &list)
|
void AvdModel::setAvdList(const AndroidDeviceInfoList &list)
|
||||||
{
|
{
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
m_list = list;
|
m_list = list;
|
||||||
@@ -130,7 +130,7 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
|
|||||||
m_javaState(NotSet),
|
m_javaState(NotSet),
|
||||||
m_ui(new Ui_AndroidSettingsWidget),
|
m_ui(new Ui_AndroidSettingsWidget),
|
||||||
m_androidConfig(AndroidConfigurations::currentConfig()),
|
m_androidConfig(AndroidConfigurations::currentConfig()),
|
||||||
m_androidToolManager(new AndroidToolManager(m_androidConfig))
|
m_avdManager(new AndroidAvdManager(m_androidConfig))
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
@@ -465,7 +465,7 @@ void AndroidSettingsWidget::enableAvdControls()
|
|||||||
void AndroidSettingsWidget::startUpdateAvd()
|
void AndroidSettingsWidget::startUpdateAvd()
|
||||||
{
|
{
|
||||||
disableAvdControls();
|
disableAvdControls();
|
||||||
m_virtualDevicesWatcher.setFuture(m_androidToolManager->androidVirtualDevicesFuture());
|
m_virtualDevicesWatcher.setFuture(m_avdManager->avdList());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::updateAvds()
|
void AndroidSettingsWidget::updateAvds()
|
||||||
@@ -589,12 +589,12 @@ void AndroidSettingsWidget::addAVD()
|
|||||||
disableAvdControls();
|
disableAvdControls();
|
||||||
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
|
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
|
||||||
|
|
||||||
if (info.target.isEmpty()) {
|
if (!info.target.isValid()) {
|
||||||
enableAvdControls();
|
enableAvdControls();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcher.setFuture(m_androidToolManager->createAvd(info));
|
m_futureWatcher.setFuture(m_avdManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::avdAdded()
|
void AndroidSettingsWidget::avdAdded()
|
||||||
@@ -622,13 +622,13 @@ void AndroidSettingsWidget::removeAVD()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_androidToolManager->removeAvd(avdName);
|
m_avdManager->removeAvd(avdName);
|
||||||
startUpdateAvd();
|
startUpdateAvd();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::startAVD()
|
void AndroidSettingsWidget::startAVD()
|
||||||
{
|
{
|
||||||
m_androidConfig.startAVDAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex()));
|
m_avdManager->startAvdAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::avdActivated(const QModelIndex &index)
|
void AndroidSettingsWidget::avdActivated(const QModelIndex &index)
|
||||||
@@ -673,7 +673,15 @@ void AndroidSettingsWidget::showGdbWarningDialog()
|
|||||||
|
|
||||||
void AndroidSettingsWidget::manageAVD()
|
void AndroidSettingsWidget::manageAVD()
|
||||||
{
|
{
|
||||||
m_androidToolManager->launchAvdManager();
|
if (m_avdManager->avdManagerUiToolAvailable()) {
|
||||||
|
m_avdManager->launchAvdManagerUiTool();
|
||||||
|
} else {
|
||||||
|
QMessageBox::warning(this, tr("AVD Manager Not Available"),
|
||||||
|
tr("AVD manager UI tool is not available in the installed SDK tools"
|
||||||
|
"(version %1). Use the command line tool \"avdmanager\" for "
|
||||||
|
"advanced AVD management.")
|
||||||
|
.arg(m_androidConfig.sdkToolsVersion().toString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ QT_END_NAMESPACE
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
class AndroidToolManager;
|
class AndroidAvdManager;
|
||||||
|
|
||||||
class AvdModel: public QAbstractTableModel
|
class AvdModel: public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
void setAvdList(const QVector<AndroidDeviceInfo> &list);
|
void setAvdList(const AndroidDeviceInfoList &list);
|
||||||
QString avdName(const QModelIndex &index) const;
|
QString avdName(const QModelIndex &index) const;
|
||||||
QModelIndex indexForAvdName(const QString &avdName) const;
|
QModelIndex indexForAvdName(const QString &avdName) const;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ protected:
|
|||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QVector<AndroidDeviceInfo> m_list;
|
AndroidDeviceInfoList m_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AndroidSettingsWidget : public QWidget
|
class AndroidSettingsWidget : public QWidget
|
||||||
@@ -116,14 +116,14 @@ private:
|
|||||||
|
|
||||||
Ui_AndroidSettingsWidget *m_ui;
|
Ui_AndroidSettingsWidget *m_ui;
|
||||||
AndroidConfig m_androidConfig;
|
AndroidConfig m_androidConfig;
|
||||||
std::unique_ptr<AndroidToolManager> m_androidToolManager;
|
|
||||||
AvdModel m_AVDModel;
|
AvdModel m_AVDModel;
|
||||||
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcher;
|
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcher;
|
||||||
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
|
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
|
||||||
QStringList m_gdbCheckPaths;
|
QStringList m_gdbCheckPaths;
|
||||||
|
|
||||||
QFutureWatcher<QVector<AndroidDeviceInfo>> m_virtualDevicesWatcher;
|
QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher;
|
||||||
QString m_lastAddedAvd;
|
QString m_lastAddedAvd;
|
||||||
|
std::unique_ptr<AndroidAvdManager> m_avdManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ bool AndroidToolManager::removeAvd(const QString &name) const
|
|||||||
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
|
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFuture<QVector<AndroidDeviceInfo>> AndroidToolManager::androidVirtualDevicesFuture() const
|
QFuture<AndroidDeviceInfoList> AndroidToolManager::androidVirtualDevicesFuture() const
|
||||||
{
|
{
|
||||||
return Utils::runAsync(&AndroidToolManager::androidVirtualDevices,
|
return Utils::runAsync(&AndroidToolManager::androidVirtualDevices,
|
||||||
m_config.androidToolPath(), m_config.sdkLocation(),
|
m_config.androidToolPath(), m_config.sdkLocation(),
|
||||||
@@ -161,7 +161,7 @@ AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::Cr
|
|||||||
proc.setProcessEnvironment(env.toProcessEnvironment());
|
proc.setProcessEnvironment(env.toProcessEnvironment());
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
arguments << QLatin1String("create") << QLatin1String("avd")
|
arguments << QLatin1String("create") << QLatin1String("avd")
|
||||||
<< QLatin1String("-t") << info.target
|
<< QLatin1String("-t") << info.target.name
|
||||||
<< QLatin1String("-n") << info.name
|
<< QLatin1String("-n") << info.name
|
||||||
<< QLatin1String("-b") << info.abi;
|
<< QLatin1String("-b") << info.abi;
|
||||||
if (info.sdcardSize > 0)
|
if (info.sdcardSize > 0)
|
||||||
@@ -206,12 +206,11 @@ AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::Cr
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<AndroidDeviceInfo>
|
AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
|
||||||
AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
|
const FileName &sdkLocationPath,
|
||||||
const FileName &sdkLocationPath,
|
const Environment &environment)
|
||||||
const Environment &environment)
|
|
||||||
{
|
{
|
||||||
QVector<AndroidDeviceInfo> devices;
|
AndroidDeviceInfoList devices;
|
||||||
QString output;
|
QString output;
|
||||||
if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output))
|
if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output))
|
||||||
return devices;
|
return devices;
|
||||||
|
|||||||
@@ -53,14 +53,14 @@ public:
|
|||||||
|
|
||||||
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
|
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
|
||||||
bool removeAvd(const QString &name) const;
|
bool removeAvd(const QString &name) const;
|
||||||
QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const;
|
QFuture<AndroidDeviceInfoList> androidVirtualDevicesFuture() const;
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
private:
|
private:
|
||||||
Utils::Environment androidToolEnvironment() const;
|
Utils::Environment androidToolEnvironment() const;
|
||||||
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
|
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
|
||||||
Utils::FileName androidToolPath, Utils::Environment env);
|
Utils::FileName androidToolPath, Utils::Environment env);
|
||||||
static QVector<AndroidDeviceInfo> androidVirtualDevices(const Utils::FileName &androidTool,
|
static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool,
|
||||||
const Utils::FileName &sdkLlocationPath,
|
const Utils::FileName &sdkLlocationPath,
|
||||||
const Utils::Environment &environment);
|
const Utils::Environment &environment);
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -68,12 +68,12 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo
|
|||||||
|
|
||||||
bool AvdDialog::isValid() const
|
bool AvdDialog::isValid() const
|
||||||
{
|
{
|
||||||
return !name().isEmpty() && !target().isEmpty() && !abi().isEmpty();
|
return !name().isEmpty() && target().isValid() && !abi().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AvdDialog::target() const
|
SdkPlatform AvdDialog::target() const
|
||||||
{
|
{
|
||||||
return m_avdDialog.targetComboBox->currentText();
|
return m_avdDialog.targetComboBox->currentData().value<SdkPlatform>();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AvdDialog::name() const
|
QString AvdDialog::name() const
|
||||||
@@ -93,8 +93,8 @@ int AvdDialog::sdcardSize() const
|
|||||||
|
|
||||||
void AvdDialog::updateApiLevelComboBox()
|
void AvdDialog::updateApiLevelComboBox()
|
||||||
{
|
{
|
||||||
QList<SdkPlatform> filteredList;
|
SdkPlatformList filteredList;
|
||||||
QList<SdkPlatform> platforms = m_config->sdkTargets(m_minApiLevel);
|
SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel);
|
||||||
|
|
||||||
QString selectedAbi = abi();
|
QString selectedAbi = abi();
|
||||||
auto hasAbi = [selectedAbi](const SystemImage &image) {
|
auto hasAbi = [selectedAbi](const SystemImage &image) {
|
||||||
@@ -106,7 +106,10 @@ void AvdDialog::updateApiLevelComboBox()
|
|||||||
});
|
});
|
||||||
|
|
||||||
m_avdDialog.targetComboBox->clear();
|
m_avdDialog.targetComboBox->clear();
|
||||||
m_avdDialog.targetComboBox->addItems(AndroidConfig::apiLevelNamesFor(filteredList));
|
foreach (const SdkPlatform &platform, filteredList) {
|
||||||
|
m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform),
|
||||||
|
QVariant::fromValue<SdkPlatform>(platform));
|
||||||
|
}
|
||||||
|
|
||||||
if (platforms.isEmpty()) {
|
if (platforms.isEmpty()) {
|
||||||
m_avdDialog.warningIcon->setVisible(true);
|
m_avdDialog.warningIcon->setVisible(true);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
namespace Android {
|
namespace Android {
|
||||||
class AndroidConfig;
|
class AndroidConfig;
|
||||||
|
class SdkPlatform;
|
||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ public:
|
|||||||
explicit AvdDialog(int minApiLevel, const QString &targetArch,
|
explicit AvdDialog(int minApiLevel, const QString &targetArch,
|
||||||
const AndroidConfig *config, QWidget *parent = 0);
|
const AndroidConfig *config, QWidget *parent = 0);
|
||||||
|
|
||||||
QString target() const;
|
Android::SdkPlatform target() const;
|
||||||
QString name() const;
|
QString name() const;
|
||||||
QString abi() const;
|
QString abi() const;
|
||||||
int sdcardSize() const;
|
int sdcardSize() const;
|
||||||
|
|||||||
Reference in New Issue
Block a user