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:
Jarek Kobus
2024-05-14 11:10:15 +02:00
parent f7120daa2e
commit f0eeb67975
5 changed files with 101 additions and 93 deletions

View File

@@ -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))

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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;

View File

@@ -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;
} }