forked from qt-creator/qt-creator
Android: Employ TaskTree for running avdList command
Run the command from the caller thread. Change-Id: I710c41c6da93e27d79c351e1dc5e7f9f01d6cc51 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
#include "androidavdmanager.h"
|
#include "androidavdmanager.h"
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidtr.h"
|
#include "androidtr.h"
|
||||||
#include "avdmanageroutputparser.h"
|
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -44,71 +43,6 @@ bool AndroidAvdManager::avdManagerCommand(const QStringList &args, QString *outp
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TagModification { CommentOut, Uncomment };
|
|
||||||
|
|
||||||
static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification)
|
|
||||||
{
|
|
||||||
if (!avdPath.exists())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const FilePath configFilePath = avdPath / "config.ini";
|
|
||||||
FileReader reader;
|
|
||||||
if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text))
|
|
||||||
return;
|
|
||||||
|
|
||||||
FileSaver saver(configFilePath);
|
|
||||||
QTextStream textStream(reader.data());
|
|
||||||
while (!textStream.atEnd()) {
|
|
||||||
QString line = textStream.readLine();
|
|
||||||
if (line.contains("hw.device.manufacturer")) {
|
|
||||||
if (modification == Uncomment)
|
|
||||||
line.replace("#", "");
|
|
||||||
else
|
|
||||||
line.prepend("#");
|
|
||||||
}
|
|
||||||
line.append("\n");
|
|
||||||
saver.write(line.toUtf8());
|
|
||||||
}
|
|
||||||
saver.finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
static AndroidDeviceInfoList listVirtualDevices()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
Currenly avdmanager tool fails to parse some AVDs because the correct
|
|
||||||
device definitions at devices.xml does not have some of the newest devices.
|
|
||||||
Particularly, failing because of tag "hw.device.manufacturer", thus removing
|
|
||||||
it would make paring successful. However, it has to be returned afterwards,
|
|
||||||
otherwise, Android Studio would give an error during parsing also. So this fix
|
|
||||||
aim to keep support for Qt Creator and Android Studio.
|
|
||||||
*/
|
|
||||||
FilePaths allAvdErrorPaths;
|
|
||||||
while (true) {
|
|
||||||
QString output;
|
|
||||||
if (!AndroidAvdManager::avdManagerCommand({"list", "avd"}, &output)) {
|
|
||||||
qCDebug(avdManagerLog)
|
|
||||||
<< "Avd list command failed" << output << androidConfig().sdkToolsVersion();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto parsedAvdList = parseAvdList(output);
|
|
||||||
if (parsedAvdList.errorPaths.isEmpty()) {
|
|
||||||
for (const FilePath &avdPath : std::as_const(allAvdErrorPaths))
|
|
||||||
modifyManufacturerTag(avdPath, Uncomment);
|
|
||||||
return parsedAvdList.avdList;
|
|
||||||
}
|
|
||||||
allAvdErrorPaths << parsedAvdList.errorPaths;
|
|
||||||
for (const FilePath &avdPath : parsedAvdList.errorPaths)
|
|
||||||
modifyManufacturerTag(avdPath, CommentOut);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
|
|
||||||
{
|
|
||||||
return Utils::asyncRun(listVirtualDevices);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AndroidAvdManager::startAvd(const QString &name) const
|
QString AndroidAvdManager::startAvd(const QString &name) const
|
||||||
{
|
{
|
||||||
if (!findAvd(name).isEmpty() || startAvdAsync(name))
|
if (!findAvd(name).isEmpty() || startAvdAsync(name))
|
||||||
|
@@ -2,8 +2,6 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "androiddeviceinfo.h"
|
|
||||||
|
|
||||||
#include <QFuture>
|
#include <QFuture>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -13,8 +11,6 @@ namespace Android::Internal {
|
|||||||
class AndroidAvdManager
|
class AndroidAvdManager
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QFuture<AndroidDeviceInfoList> avdList() const;
|
|
||||||
|
|
||||||
QString startAvd(const QString &name) const;
|
QString startAvd(const QString &name) const;
|
||||||
bool startAvdAsync(const QString &avdName) const;
|
bool startAvdAsync(const QString &avdName) const;
|
||||||
QString findAvd(const QString &avdName) const;
|
QString findAvd(const QString &avdName) const;
|
||||||
|
@@ -2,14 +2,16 @@
|
|||||||
// Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
|
// Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
|
||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include "androiddevice.h"
|
||||||
|
|
||||||
#include "androidavdmanager.h"
|
#include "androidavdmanager.h"
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androiddevice.h"
|
|
||||||
#include "androidmanager.h"
|
#include "androidmanager.h"
|
||||||
#include "androidsignaloperation.h"
|
#include "androidsignaloperation.h"
|
||||||
#include "androidtr.h"
|
#include "androidtr.h"
|
||||||
#include "avddialog.h"
|
#include "avddialog.h"
|
||||||
|
#include "avdmanageroutputparser.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
@@ -21,6 +23,8 @@
|
|||||||
#include <projectexplorer/projectmanager.h>
|
#include <projectexplorer/projectmanager.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
|
#include <solutions/tasking/tasktree.h>
|
||||||
|
|
||||||
#include <utils/async.h>
|
#include <utils/async.h>
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -410,10 +414,10 @@ void AndroidDevice::initAvdSettings()
|
|||||||
m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat));
|
m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceManager::updateAvdsList()
|
void AndroidDeviceManager::updateAvdList()
|
||||||
{
|
{
|
||||||
if (!m_avdsFutureWatcher.isRunning() && androidConfig().adbToolPath().exists())
|
if (androidConfig().adbToolPath().exists())
|
||||||
m_avdsFutureWatcher.setFuture(m_avdManager.avdList());
|
m_avdListRunner.start(m_avdListRecipe);
|
||||||
}
|
}
|
||||||
|
|
||||||
IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial,
|
IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial,
|
||||||
@@ -687,7 +691,7 @@ void AndroidDeviceManager::setupDevicesWatcher()
|
|||||||
m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) {
|
m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) {
|
||||||
qCDebug(androidDeviceLog) << "ADB device watcher error" << error; });
|
qCDebug(androidDeviceLog) << "ADB device watcher error" << error; });
|
||||||
m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) {
|
m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) {
|
||||||
HandleDevicesListChange(output);
|
handleDevicesListChange(output);
|
||||||
});
|
});
|
||||||
|
|
||||||
const CommandLine command{androidConfig().adbToolPath(), {"track-devices"}};
|
const CommandLine command{androidConfig().adbToolPath(), {"track-devices"}};
|
||||||
@@ -699,17 +703,12 @@ void AndroidDeviceManager::setupDevicesWatcher()
|
|||||||
// Setup AVD filesystem watcher to listen for changes when an avd is created/deleted,
|
// Setup AVD filesystem watcher to listen for changes when an avd is created/deleted,
|
||||||
// or started/stopped
|
// or started/stopped
|
||||||
m_avdFileSystemWatcher.addPath(avdFilePath().toString());
|
m_avdFileSystemWatcher.addPath(avdFilePath().toString());
|
||||||
connect(&m_avdsFutureWatcher, &QFutureWatcherBase::finished,
|
|
||||||
this, &AndroidDeviceManager::HandleAvdsListChange);
|
|
||||||
connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] {
|
connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] {
|
||||||
if (m_avdPathGuard.isLocked())
|
if (!m_avdPathGuard.isLocked())
|
||||||
return;
|
updateAvdList();
|
||||||
// If the avd list upate command is running no need to call it again.
|
|
||||||
if (!m_avdsFutureWatcher.isRunning())
|
|
||||||
updateAvdsList();
|
|
||||||
});
|
});
|
||||||
// Call initial update
|
// Call initial update
|
||||||
updateAvdsList();
|
updateAvdList();
|
||||||
}
|
}
|
||||||
|
|
||||||
IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info)
|
IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info)
|
||||||
@@ -731,7 +730,7 @@ IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &inf
|
|||||||
return IDevice::Ptr(dev);
|
return IDevice::Ptr(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceManager::HandleAvdsListChange()
|
void AndroidDeviceManager::handleAvdListChange(const AndroidDeviceInfoList &avdList)
|
||||||
{
|
{
|
||||||
DeviceManager *const devMgr = DeviceManager::instance();
|
DeviceManager *const devMgr = DeviceManager::instance();
|
||||||
|
|
||||||
@@ -744,7 +743,7 @@ void AndroidDeviceManager::HandleAvdsListChange()
|
|||||||
}
|
}
|
||||||
|
|
||||||
QList<Id> connectedDevs;
|
QList<Id> connectedDevs;
|
||||||
for (const AndroidDeviceInfo &item : m_avdsFutureWatcher.result()) {
|
for (const AndroidDeviceInfo &item : avdList) {
|
||||||
const Id deviceId = AndroidDevice::idFromDeviceInfo(item);
|
const Id deviceId = AndroidDevice::idFromDeviceInfo(item);
|
||||||
const QString displayName = AndroidDevice::displayNameFromInfo(item);
|
const QString displayName = AndroidDevice::displayNameFromInfo(item);
|
||||||
IDevice::ConstPtr dev = devMgr->find(deviceId);
|
IDevice::ConstPtr dev = devMgr->find(deviceId);
|
||||||
@@ -803,7 +802,7 @@ void AndroidDeviceManager::HandleAvdsListChange()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber)
|
void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber)
|
||||||
{
|
{
|
||||||
DeviceManager *const devMgr = DeviceManager::instance();
|
DeviceManager *const devMgr = DeviceManager::instance();
|
||||||
const QStringList serialBits = serialNumber.split('\t');
|
const QStringList serialBits = serialNumber.split('\t');
|
||||||
@@ -879,16 +878,93 @@ AndroidDeviceManager *AndroidDeviceManager::instance()
|
|||||||
return s_instance;
|
return s_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum TagModification { CommentOut, Uncomment };
|
||||||
|
|
||||||
|
static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification)
|
||||||
|
{
|
||||||
|
if (!avdPath.exists())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const FilePath configFilePath = avdPath / "config.ini";
|
||||||
|
FileReader reader;
|
||||||
|
if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
FileSaver saver(configFilePath);
|
||||||
|
QTextStream textStream(reader.data());
|
||||||
|
while (!textStream.atEnd()) {
|
||||||
|
QString line = textStream.readLine();
|
||||||
|
if (line.contains("hw.device.manufacturer")) {
|
||||||
|
if (modification == Uncomment)
|
||||||
|
line.replace("#", "");
|
||||||
|
else
|
||||||
|
line.prepend("#");
|
||||||
|
}
|
||||||
|
line.append("\n");
|
||||||
|
saver.write(line.toUtf8());
|
||||||
|
}
|
||||||
|
saver.finalize();
|
||||||
|
}
|
||||||
|
|
||||||
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
|
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
, m_avdListRecipe{}
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!s_instance, return);
|
QTC_ASSERT(!s_instance, return);
|
||||||
s_instance = this;
|
s_instance = this;
|
||||||
|
|
||||||
|
using namespace Tasking;
|
||||||
|
|
||||||
|
const Storage<FilePaths> storage;
|
||||||
|
|
||||||
|
const LoopUntil iterator([storage](int iteration) {
|
||||||
|
return iteration == 0 || storage->count() > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto onProcessSetup = [](Process &process) {
|
||||||
|
const CommandLine cmd(androidConfig().avdManagerToolPath(), {"list", "avd"});
|
||||||
|
qCDebug(androidDeviceLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
|
||||||
|
process.setEnvironment(androidConfig().toolsEnvironment());
|
||||||
|
process.setCommand(cmd);
|
||||||
|
};
|
||||||
|
const auto onProcessDone = [this, storage](const Process &process, DoneWith result) {
|
||||||
|
const QString output = process.allOutput();
|
||||||
|
if (result != DoneWith::Success) {
|
||||||
|
qCDebug(androidDeviceLog)
|
||||||
|
<< "Avd list command failed" << output << androidConfig().sdkToolsVersion();
|
||||||
|
return DoneResult::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto parsedAvdList = parseAvdList(output);
|
||||||
|
if (parsedAvdList.errorPaths.isEmpty()) {
|
||||||
|
for (const FilePath &avdPath : *storage)
|
||||||
|
modifyManufacturerTag(avdPath, Uncomment);
|
||||||
|
storage->clear(); // Don't repeat anymore
|
||||||
|
handleAvdListChange(parsedAvdList.avdList);
|
||||||
|
} else {
|
||||||
|
for (const FilePath &avdPath : parsedAvdList.errorPaths)
|
||||||
|
modifyManufacturerTag(avdPath, CommentOut);
|
||||||
|
storage->append(parsedAvdList.errorPaths);
|
||||||
|
}
|
||||||
|
return DoneResult::Success; // Repeat
|
||||||
|
};
|
||||||
|
|
||||||
|
// Currenly avdmanager tool fails to parse some AVDs because the correct
|
||||||
|
// device definitions at devices.xml does not have some of the newest devices.
|
||||||
|
// Particularly, failing because of tag "hw.device.manufacturer", thus removing
|
||||||
|
// it would make paring successful. However, it has to be returned afterwards,
|
||||||
|
// otherwise, Android Studio would give an error during parsing also. So this fix
|
||||||
|
// aim to keep support for Qt Creator and Android Studio.
|
||||||
|
|
||||||
|
m_avdListRecipe = Group {
|
||||||
|
storage,
|
||||||
|
iterator,
|
||||||
|
ProcessTask(onProcessSetup, onProcessDone)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidDeviceManager::~AndroidDeviceManager()
|
AndroidDeviceManager::~AndroidDeviceManager()
|
||||||
{
|
{
|
||||||
m_avdsFutureWatcher.waitForFinished();
|
|
||||||
QTC_ASSERT(s_instance == this, return);
|
QTC_ASSERT(s_instance == this, return);
|
||||||
s_instance = nullptr;
|
s_instance = nullptr;
|
||||||
}
|
}
|
||||||
|
@@ -11,9 +11,10 @@
|
|||||||
#include <projectexplorer/devicesupport/idevice.h>
|
#include <projectexplorer/devicesupport/idevice.h>
|
||||||
#include <projectexplorer/devicesupport/idevicefactory.h>
|
#include <projectexplorer/devicesupport/idevicefactory.h>
|
||||||
|
|
||||||
|
#include <solutions/tasking/tasktreerunner.h>
|
||||||
|
|
||||||
#include <utils/guard.h>
|
#include <utils/guard.h>
|
||||||
|
|
||||||
#include <QFutureWatcher>
|
|
||||||
#include <QFileSystemWatcher>
|
#include <QFileSystemWatcher>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ class AndroidDeviceManager : public QObject
|
|||||||
public:
|
public:
|
||||||
static AndroidDeviceManager *instance();
|
static AndroidDeviceManager *instance();
|
||||||
void setupDevicesWatcher();
|
void setupDevicesWatcher();
|
||||||
void updateAvdsList();
|
void updateAvdList();
|
||||||
IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const;
|
IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const;
|
||||||
void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device);
|
void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device);
|
||||||
|
|
||||||
@@ -95,12 +96,13 @@ private:
|
|||||||
explicit AndroidDeviceManager(QObject *parent);
|
explicit AndroidDeviceManager(QObject *parent);
|
||||||
~AndroidDeviceManager();
|
~AndroidDeviceManager();
|
||||||
|
|
||||||
void HandleDevicesListChange(const QString &serialNumber);
|
void handleDevicesListChange(const QString &serialNumber);
|
||||||
void HandleAvdsListChange();
|
void handleAvdListChange(const AndroidDeviceInfoList &avdList);
|
||||||
|
|
||||||
QString emulatorName(const QString &serialNumber) const;
|
QString emulatorName(const QString &serialNumber) const;
|
||||||
|
|
||||||
QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher;
|
Tasking::Group m_avdListRecipe;
|
||||||
|
Tasking::TaskTreeRunner m_avdListRunner;
|
||||||
std::unique_ptr<Utils::Process> m_removeAvdProcess;
|
std::unique_ptr<Utils::Process> m_removeAvdProcess;
|
||||||
QFileSystemWatcher m_avdFileSystemWatcher;
|
QFileSystemWatcher m_avdFileSystemWatcher;
|
||||||
Utils::Guard m_avdPathGuard;
|
Utils::Guard m_avdPathGuard;
|
||||||
|
@@ -141,7 +141,7 @@ int AvdDialog::exec()
|
|||||||
return QDialog::Rejected;
|
return QDialog::Rejected;
|
||||||
}
|
}
|
||||||
m_createdAvdInfo = avdInfo;
|
m_createdAvdInfo = avdInfo;
|
||||||
AndroidDeviceManager::instance()->updateAvdsList();
|
AndroidDeviceManager::instance()->updateAvdList();
|
||||||
}
|
}
|
||||||
return execResult;
|
return execResult;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user