2017-04-03 11:11:17 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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"
|
|
|
|
|
|
2018-04-18 12:23:02 +02:00
|
|
|
#include "coreplugin/icore.h"
|
2017-04-03 11:11:17 +02:00
|
|
|
#include "utils/algorithm.h"
|
|
|
|
|
#include "utils/qtcassert.h"
|
|
|
|
|
#include "utils/runextensions.h"
|
|
|
|
|
#include "utils/synchronousprocess.h"
|
|
|
|
|
|
|
|
|
|
#include <QApplication>
|
|
|
|
|
#include <QFileInfo>
|
|
|
|
|
#include <QLoggingCategory>
|
2018-04-18 12:23:02 +02:00
|
|
|
#include <QMessageBox>
|
2017-04-03 11:11:17 +02:00
|
|
|
#include <QSettings>
|
|
|
|
|
|
|
|
|
|
#include <chrono>
|
2018-04-19 09:18:42 +02:00
|
|
|
#include <functional>
|
2017-04-03 11:11:17 +02:00
|
|
|
|
|
|
|
|
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:";
|
2018-04-18 10:40:54 +02:00
|
|
|
const char googleApiTag[] = "google_apis";
|
2017-04-03 11:11:17 +02:00
|
|
|
|
|
|
|
|
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;
|
2018-04-19 12:19:03 +02:00
|
|
|
auto env = AndroidConfigurations::toolsEnvironment(config).toStringList();
|
|
|
|
|
proc.setEnvironment(env);
|
2017-04-03 11:11:17 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 08:22:34 +02:00
|
|
|
static CreateAvdInfo createAvdCommand(const AndroidConfig config, const CreateAvdInfo &info)
|
2017-04-03 11:11:17 +02:00
|
|
|
{
|
2017-08-18 08:22:34 +02:00
|
|
|
CreateAvdInfo result = info;
|
2017-04-03 11:11:17 +02:00
|
|
|
|
|
|
|
|
if (!result.isValid()) {
|
|
|
|
|
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
|
2017-08-18 08:22:34 +02:00
|
|
|
<< result.sdkPlatform->displayText() << result.sdkPlatform->apiLevel();
|
2017-04-03 11:11:17 +02:00
|
|
|
result.error = QApplication::translate("AndroidAvdManager",
|
|
|
|
|
"Cannot create AVD. Invalid input.");
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-18 10:40:54 +02:00
|
|
|
QStringList arguments({"create", "avd", "-n", result.name});
|
2017-04-03 11:11:17 +02:00
|
|
|
|
|
|
|
|
if (!result.abi.isEmpty()) {
|
2017-08-18 08:22:34 +02:00
|
|
|
SystemImage *image = Utils::findOrDefault(result.sdkPlatform->systemImages(),
|
2017-04-03 11:11:17 +02:00
|
|
|
Utils::equal(&SystemImage::abiName, result.abi));
|
2017-08-18 08:22:34 +02:00
|
|
|
if (image && image->isValid()) {
|
|
|
|
|
arguments << "-k" << image->sdkStylePath();
|
2018-04-18 10:40:54 +02:00
|
|
|
// Google api system images requires explicit abi as
|
|
|
|
|
// google-apis/ABI or --tag "google-apis"
|
|
|
|
|
if (image->sdkStylePath().contains(googleApiTag))
|
|
|
|
|
arguments << "--tag" << googleApiTag;
|
2017-04-03 11:11:17 +02:00
|
|
|
} else {
|
2017-08-18 08:22:34 +02:00
|
|
|
QString name = result.sdkPlatform->displayText();
|
2017-04-03 11:11:17 +02:00
|
|
|
qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform"
|
2017-08-18 08:22:34 +02:00
|
|
|
<< result.abi << name;
|
2017-04-03 11:11:17 +02:00
|
|
|
result.error = QApplication::translate("AndroidAvdManager",
|
|
|
|
|
"Cannot create AVD. Cannot find system image for "
|
2017-08-18 08:22:34 +02:00
|
|
|
"the ABI %1(%2).").arg(result.abi).arg(name);
|
2017-04-03 11:11:17 +02:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2017-08-18 08:22:34 +02:00
|
|
|
arguments << "-k" << result.sdkPlatform->sdkStylePath();
|
2017-04-03 11:11:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-19 09:18:42 +02:00
|
|
|
static void avdProcessFinished(int exitCode, QProcess *p)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(p, return);
|
|
|
|
|
if (exitCode) {
|
|
|
|
|
QString title = QCoreApplication::translate("Android::Internal::AndroidAvdManager",
|
|
|
|
|
"AVD Start Error");
|
|
|
|
|
QMessageBox::critical(Core::ICore::dialogParent(), title,
|
|
|
|
|
QString::fromLatin1(p->readAll()));
|
|
|
|
|
}
|
|
|
|
|
p->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-03 11:11:17 +02:00
|
|
|
/*!
|
|
|
|
|
\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()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidAvdManager::launchAvdManagerUiTool() const
|
|
|
|
|
{
|
2017-07-14 19:35:48 +02:00
|
|
|
if (m_config.useNativeUiTools()) {
|
2017-04-03 11:11:17 +02:00
|
|
|
m_androidTool->launchAvdManager();
|
|
|
|
|
} else {
|
|
|
|
|
qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available"
|
|
|
|
|
<< m_config.sdkToolsVersion();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-18 08:22:34 +02:00
|
|
|
QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const
|
2017-04-03 11:11:17 +02:00
|
|
|
{
|
2017-07-14 19:35:48 +02:00
|
|
|
if (m_config.useNativeUiTools())
|
2017-04-03 11:11:17 +02:00
|
|
|
return m_androidTool->createAvd(info);
|
|
|
|
|
|
|
|
|
|
return Utils::runAsync(&createAvdCommand, m_config, info);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidAvdManager::removeAvd(const QString &name) const
|
|
|
|
|
{
|
2017-07-14 19:35:48 +02:00
|
|
|
if (m_config.useNativeUiTools())
|
2017-04-03 11:11:17 +02:00
|
|
|
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
|
|
|
|
|
{
|
2017-07-14 19:35:48 +02:00
|
|
|
if (m_config.useNativeUiTools())
|
2017-04-03 11:11:17 +02:00
|
|
|
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
|
|
|
|
|
{
|
2018-04-18 12:23:02 +02:00
|
|
|
QFileInfo info(m_config.emulatorToolPath().toString());
|
|
|
|
|
if (!info.exists()) {
|
|
|
|
|
QMessageBox::critical(Core::ICore::dialogParent(),
|
|
|
|
|
tr("Emulator Tool Is Missing"),
|
|
|
|
|
tr("Install the missing emulator tool (%1) to the"
|
|
|
|
|
" installed Android SDK.")
|
|
|
|
|
.arg(m_config.emulatorToolPath().toString()));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2017-04-03 11:11:17 +02:00
|
|
|
QProcess *avdProcess = new QProcess();
|
2018-05-18 15:05:46 +02:00
|
|
|
avdProcess->setReadChannelMode(QProcess::MergedChannels);
|
2018-04-19 09:18:42 +02:00
|
|
|
QObject::connect(avdProcess,
|
|
|
|
|
static_cast<void (QProcess::*)(int)>(&QProcess::finished),
|
|
|
|
|
avdProcess,
|
|
|
|
|
std::bind(&avdProcessFinished, std::placeholders::_1, avdProcess));
|
2017-04-03 11:11:17 +02:00
|
|
|
|
|
|
|
|
// 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
|