Android: Move the AndroidRunnerWorker to the main thread

Replace AndroidRunnerWorker with runnerRecipe().

Add RunnerInterface for communication between
AndroidRunner and runnerRecipe().

Get rid of a separate thread.

Change-Id: I32a651592859b762b0e52a2d44c28652d42566bb
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
Jarek Kobus
2024-08-29 18:59:29 +02:00
parent ea77f3f02d
commit 50959611f4
4 changed files with 180 additions and 202 deletions

View File

@@ -44,14 +44,12 @@ AndroidRunner::AndroidRunner(RunControl *runControl)
this, &AndroidRunner::qmlServerPortReady); this, &AndroidRunner::qmlServerPortReady);
} }
AndroidRunner::~AndroidRunner()
{
m_thread.quit();
m_thread.wait();
}
void AndroidRunner::start() void AndroidRunner::start()
{ {
const Storage<RunnerInterface> glueStorage;
std::optional<ExecutableItem> avdRecipe;
QString deviceSerialNumber; QString deviceSerialNumber;
int apiLevel = -1; int apiLevel = -1;
@@ -69,56 +67,49 @@ void AndroidRunner::start()
if (!info.avdName.isEmpty()) { if (!info.avdName.isEmpty()) {
const Storage<QString> serialNumberStorage; const Storage<QString> serialNumberStorage;
const Group recipe { avdRecipe = Group {
serialNumberStorage, serialNumberStorage,
AndroidAvdManager::startAvdRecipe(info.avdName, serialNumberStorage) AndroidAvdManager::startAvdRecipe(info.avdName, serialNumberStorage)
}; }.withCancel([glueStorage] {
return std::make_pair(glueStorage.activeStorage(), &RunnerInterface::canceled);
m_startAvdRunner.start(recipe, {}, [this, deviceSerialNumber, apiLevel](DoneWith result) {
if (result == DoneWith::Success)
startImpl(deviceSerialNumber, apiLevel);
}); });
return;
} }
} else { } else {
deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target); deviceSerialNumber = AndroidManager::deviceSerialNumber(m_target);
apiLevel = AndroidManager::deviceApiLevel(m_target); apiLevel = AndroidManager::deviceApiLevel(m_target);
} }
startImpl(deviceSerialNumber, apiLevel);
const auto onSetup = [this, glueStorage, deviceSerialNumber, apiLevel] {
RunnerInterface *glue = glueStorage.activeStorage();
glue->setRunControl(runControl());
glue->setDeviceSerialNumber(deviceSerialNumber);
glue->setApiLevel(apiLevel);
connect(this, &AndroidRunner::canceled, glue, &RunnerInterface::cancel);
connect(glue, &RunnerInterface::started, this, &AndroidRunner::remoteStarted);
connect(glue, &RunnerInterface::finished, this, &AndroidRunner::remoteFinished);
connect(glue, &RunnerInterface::stdOut, this, &AndroidRunner::remoteStdOut);
connect(glue, &RunnerInterface::stdErr, this, &AndroidRunner::remoteStdErr);
};
const Group recipe {
glueStorage,
onGroupSetup(onSetup),
avdRecipe ? *avdRecipe : nullItem,
runnerRecipe(glueStorage)
};
m_taskTreeRunner.start(recipe);
} }
void AndroidRunner::stop() void AndroidRunner::stop()
{ {
if (m_startAvdRunner.isRunning()) { if (!m_taskTreeRunner.isRunning())
m_startAvdRunner.reset();
appendMessage("\n\n" + Tr::tr("\"%1\" terminated.").arg(AndroidManager::packageName(m_target)),
Utils::NormalMessageFormat);
return; return;
}
emit asyncStop();
}
void AndroidRunner::startImpl(const QString &deviceSerialNumber, int apiLevel) emit canceled();
{ appendMessage("\n\n" + Tr::tr("\"%1\" terminated.").arg(AndroidManager::packageName(m_target)),
if (m_worker) Utils::NormalMessageFormat);
m_worker->deleteLater();
m_worker = new AndroidRunnerWorker(runControl(), deviceSerialNumber, apiLevel);
m_worker->moveToThread(&m_thread);
QObject::connect(&m_thread, &QThread::finished, m_worker, &QObject::deleteLater);
connect(this, &AndroidRunner::asyncStop, m_worker, &AndroidRunnerWorker::asyncStop);
connect(m_worker, &AndroidRunnerWorker::remoteProcessStarted,
this, &AndroidRunner::handleRemoteProcessStarted);
connect(m_worker, &AndroidRunnerWorker::remoteProcessFinished,
this, &AndroidRunner::handleRemoteProcessFinished);
connect(m_worker, &AndroidRunnerWorker::remoteOutput, this, &AndroidRunner::remoteOutput);
connect(m_worker, &AndroidRunnerWorker::remoteErrorOutput,
this, &AndroidRunner::remoteErrorOutput);
m_thread.start();
QMetaObject::invokeMethod(m_worker, &AndroidRunnerWorker::asyncStart);
} }
void AndroidRunner::qmlServerPortReady(Port port) void AndroidRunner::qmlServerPortReady(Port port)
@@ -134,20 +125,7 @@ void AndroidRunner::qmlServerPortReady(Port port)
emit qmlServerReady(serverUrl); emit qmlServerReady(serverUrl);
} }
void AndroidRunner::remoteOutput(const QString &output) void AndroidRunner::remoteStarted(const Port &debugServerPort, const QUrl &qmlServer, qint64 pid)
{
appendMessage(output, Utils::StdOutFormat);
m_outputParser.processOutput(output);
}
void AndroidRunner::remoteErrorOutput(const QString &output)
{
appendMessage(output, Utils::StdErrFormat);
m_outputParser.processOutput(output);
}
void AndroidRunner::handleRemoteProcessStarted(Utils::Port debugServerPort,
const QUrl &qmlServer, qint64 pid)
{ {
m_pid = ProcessHandle(pid); m_pid = ProcessHandle(pid);
m_debugServerPort = debugServerPort; m_debugServerPort = debugServerPort;
@@ -155,7 +133,7 @@ void AndroidRunner::handleRemoteProcessStarted(Utils::Port debugServerPort,
reportStarted(); reportStarted();
} }
void AndroidRunner::handleRemoteProcessFinished(const QString &errString) void AndroidRunner::remoteFinished(const QString &errString)
{ {
appendMessage(errString, Utils::NormalMessageFormat); appendMessage(errString, Utils::NormalMessageFormat);
if (runControl()->isRunning()) if (runControl()->isRunning())
@@ -163,4 +141,16 @@ void AndroidRunner::handleRemoteProcessFinished(const QString &errString)
reportStopped(); reportStopped();
} }
void AndroidRunner::remoteStdOut(const QString &output)
{
appendMessage(output, Utils::StdOutFormat);
m_outputParser.processOutput(output);
}
void AndroidRunner::remoteStdErr(const QString &output)
{
appendMessage(output, Utils::StdErrFormat);
m_outputParser.processOutput(output);
}
} // namespace Android::Internal } // namespace Android::Internal

View File

@@ -12,19 +12,14 @@
#include <solutions/tasking/tasktreerunner.h> #include <solutions/tasking/tasktreerunner.h>
#include <QThread>
namespace Android::Internal { namespace Android::Internal {
class AndroidRunnerWorker;
class AndroidRunner : public ProjectExplorer::RunWorker class AndroidRunner : public ProjectExplorer::RunWorker
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit AndroidRunner(ProjectExplorer::RunControl *runControl); explicit AndroidRunner(ProjectExplorer::RunControl *runControl);
~AndroidRunner() override;
Utils::Port debugServerPort() const { return m_debugServerPort; } // GDB or LLDB Utils::Port debugServerPort() const { return m_debugServerPort; } // GDB or LLDB
QUrl qmlServer() const { return m_qmlServer; } QUrl qmlServer() const { return m_qmlServer; }
@@ -34,27 +29,24 @@ public:
void stop() override; void stop() override;
signals: signals:
void asyncStop(); void canceled();
void qmlServerReady(const QUrl &serverUrl); void qmlServerReady(const QUrl &serverUrl);
void avdDetected(); void avdDetected();
private: private:
void startImpl(const QString &deviceSerialNumber, int apiLevel);
void qmlServerPortReady(Utils::Port port); void qmlServerPortReady(Utils::Port port);
void remoteOutput(const QString &output);
void remoteErrorOutput(const QString &output);
void gotRemoteOutput(const QString &output);
void handleRemoteProcessStarted(Utils::Port debugServerPort, const QUrl &qmlServer, qint64 pid);
void handleRemoteProcessFinished(const QString &errString = QString());
QThread m_thread; void remoteStarted(const Utils::Port &debugServerPort, const QUrl &qmlServer, qint64 pid);
AndroidRunnerWorker *m_worker = nullptr; void remoteFinished(const QString &errString);
void remoteStdOut(const QString &output);
void remoteStdErr(const QString &output);
QPointer<ProjectExplorer::Target> m_target; QPointer<ProjectExplorer::Target> m_target;
Utils::Port m_debugServerPort; Utils::Port m_debugServerPort;
QUrl m_qmlServer; QUrl m_qmlServer;
Utils::ProcessHandle m_pid; Utils::ProcessHandle m_pid;
QmlDebug::QmlOutputParser m_outputParser; QmlDebug::QmlOutputParser m_outputParser;
Tasking::TaskTreeRunner m_startAvdRunner; Tasking::TaskTreeRunner m_taskTreeRunner;
}; };
} // namespace Android::Internal } // namespace Android::Internal

View File

@@ -131,10 +131,8 @@ static FilePath debugServer(bool useLldb, const Target *target)
return {}; return {};
} }
class RunnerStorage : public QObject class RunnerStorage
{ {
Q_OBJECT
public: public:
QStringList selector() const { return AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); } QStringList selector() const { return AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); }
bool isPreNougat() const { return m_apiLevel > 0 && m_apiLevel <= 23; } bool isPreNougat() const { return m_apiLevel > 0 && m_apiLevel <= 23; }
@@ -154,6 +152,8 @@ public:
return QStringList{"shell", "run-as", m_packageName} + userArgs(); return QStringList{"shell", "run-as", m_packageName} + userArgs();
} }
RunnerInterface *m_glue = nullptr;
QString m_packageName; QString m_packageName;
QString m_intentName; QString m_intentName;
QStringList m_beforeStartAdbCommands; QStringList m_beforeStartAdbCommands;
@@ -165,30 +165,21 @@ public:
bool m_useLldb = false; bool m_useLldb = false;
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
QUrl m_qmlServer; QUrl m_qmlServer;
QString m_deviceSerialNumber; QString m_deviceSerialNumber; // TODO: remove
int m_apiLevel = -1; int m_apiLevel = -1; // TODO: remove
QString m_extraAppParams; QString m_extraAppParams;
Utils::Environment m_extraEnvVars; Utils::Environment m_extraEnvVars;
Utils::FilePath m_debugServerPath; // On build device, typically as part of ndk Utils::FilePath m_debugServerPath; // On build device, typically as part of ndk
bool m_useAppParamsForQmlDebugger = false; bool m_useAppParamsForQmlDebugger = false;
signals:
void remoteProcessStarted(Utils::Port debugServerPort, const QUrl &qmlServer, qint64 pid);
void remoteProcessFinished(const QString &errString = QString());
void remoteOutput(const QString &output);
void remoteErrorOutput(const QString &output);
void cancel();
}; };
static void setupStorage(RunnerStorage *storage, RunControl *runControl, static void setupStorage(RunnerStorage *storage, RunnerInterface *glue)
const QString &deviceSerialNumber, int apiLevel)
{ {
storage->m_useLldb = Debugger::DebuggerKitAspect::engineType(runControl->kit()) storage->m_glue = glue;
storage->m_useLldb = Debugger::DebuggerKitAspect::engineType(glue->runControl()->kit())
== Debugger::LldbEngineType; == Debugger::LldbEngineType;
auto aspect = runControl->aspectData<Debugger::DebuggerRunConfigurationAspect>(); auto aspect = glue->runControl()->aspectData<Debugger::DebuggerRunConfigurationAspect>();
const Id runMode = runControl->runMode(); const Id runMode = glue->runControl()->runMode();
const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE; const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE;
storage->m_useCppDebugger = debuggingMode && aspect->useCppDebugger; storage->m_useCppDebugger = debuggingMode && aspect->useCppDebugger;
if (debuggingMode && aspect->useQmlDebugger) if (debuggingMode && aspect->useQmlDebugger)
@@ -212,30 +203,30 @@ static void setupStorage(RunnerStorage *storage, RunControl *runControl,
qCDebug(androidRunWorkerLog) << "QML server:" << storage->m_qmlServer.toDisplayString(); qCDebug(androidRunWorkerLog) << "QML server:" << storage->m_qmlServer.toDisplayString();
} }
auto target = runControl->target(); auto target = glue->runControl()->target();
storage->m_packageName = AndroidManager::packageName(target); storage->m_packageName = AndroidManager::packageName(target);
storage->m_intentName = storage->m_packageName + '/' + AndroidManager::activityName(target); storage->m_intentName = storage->m_packageName + '/' + AndroidManager::activityName(target);
storage->m_deviceSerialNumber = deviceSerialNumber; storage->m_deviceSerialNumber = glue->deviceSerialNumber();
storage->m_apiLevel = apiLevel; storage->m_apiLevel = glue->apiLevel();
qCDebug(androidRunWorkerLog) << "Intent name:" << storage->m_intentName qCDebug(androidRunWorkerLog) << "Intent name:" << storage->m_intentName
<< "Package name:" << storage->m_packageName; << "Package name:" << storage->m_packageName;
qCDebug(androidRunWorkerLog) << "Device API:" << storage->m_apiLevel; qCDebug(androidRunWorkerLog) << "Device API:" << storage->m_apiLevel;
storage->m_extraEnvVars = runControl->aspectData<EnvironmentAspect>()->environment; storage->m_extraEnvVars = glue->runControl()->aspectData<EnvironmentAspect>()->environment;
qCDebug(androidRunWorkerLog).noquote() << "Environment variables for the app" qCDebug(androidRunWorkerLog).noquote() << "Environment variables for the app"
<< storage->m_extraEnvVars.toStringList(); << storage->m_extraEnvVars.toStringList();
if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release) if (target->buildConfigurations().first()->buildType() != BuildConfiguration::BuildType::Release)
storage->m_extraAppParams = runControl->commandLine().arguments(); storage->m_extraAppParams = glue->runControl()->commandLine().arguments();
if (const Store sd = runControl->settingsData(Constants::ANDROID_AM_START_ARGS); if (const Store sd = glue->runControl()->settingsData(Constants::ANDROID_AM_START_ARGS);
!sd.isEmpty()) { !sd.isEmpty()) {
QTC_CHECK(sd.first().typeId() == QMetaType::QString); QTC_CHECK(sd.first().typeId() == QMetaType::QString);
const QString startArgs = sd.first().toString(); const QString startArgs = sd.first().toString();
storage->m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix); storage->m_amStartExtraArgs = ProcessArgs::splitArgs(startArgs, OsTypeOtherUnix);
} }
if (const Store sd = runControl->settingsData(Constants::ANDROID_PRESTARTSHELLCMDLIST); if (const Store sd = glue->runControl()->settingsData(Constants::ANDROID_PRESTARTSHELLCMDLIST);
!sd.isEmpty()) { !sd.isEmpty()) {
const QVariant &first = sd.first(); const QVariant &first = sd.first();
QTC_CHECK(first.typeId() == QMetaType::QStringList); QTC_CHECK(first.typeId() == QMetaType::QStringList);
@@ -244,7 +235,7 @@ static void setupStorage(RunnerStorage *storage, RunControl *runControl,
storage->m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd)); storage->m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
} }
if (const Store sd = runControl->settingsData(Constants::ANDROID_POSTFINISHSHELLCMDLIST); if (const Store sd = glue->runControl()->settingsData(Constants::ANDROID_POSTFINISHSHELLCMDLIST);
!sd.isEmpty()) { !sd.isEmpty()) {
const QVariant &first = sd.first(); const QVariant &first = sd.first();
QTC_CHECK(first.typeId() == QMetaType::QStringList); QTC_CHECK(first.typeId() == QMetaType::QStringList);
@@ -265,27 +256,7 @@ static void setupStorage(RunnerStorage *storage, RunControl *runControl,
storage->m_useAppParamsForQmlDebugger = version->qtVersion() >= QVersionNumber(5, 12); storage->m_useAppParamsForQmlDebugger = version->qtVersion() >= QVersionNumber(5, 12);
} }
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &deviceSerialNumber, static ExecutableItem forceStopRecipe(const Storage<RunnerStorage> &storage)
int apiLevel)
: m_storage(new RunnerStorage)
{
m_storage->setParent(this); // Move m_storage object together with *this into a separate thread.
setupStorage(m_storage.get(), runControl, deviceSerialNumber, apiLevel);
m_taskTreeRunner.setParent(this); // Move m_taskTreeRunner object together with *this into a separate thread.
connect(m_storage.get(), &RunnerStorage::remoteProcessStarted,
this, &AndroidRunnerWorker::remoteProcessStarted);
connect(m_storage.get(), &RunnerStorage::remoteProcessFinished,
this, &AndroidRunnerWorker::remoteProcessFinished);
connect(m_storage.get(), &RunnerStorage::remoteOutput,
this, &AndroidRunnerWorker::remoteOutput);
connect(m_storage.get(), &RunnerStorage::remoteErrorOutput,
this, &AndroidRunnerWorker::remoteErrorOutput);
connect(this, &AndroidRunnerWorker::cancel, m_storage.get(), &RunnerStorage::cancel);
}
static ExecutableItem forceStopRecipe(RunnerStorage *storage)
{ {
const auto onForceStopSetup = [storage](Process &process) { const auto onForceStopSetup = [storage](Process &process) {
process.setCommand(storage->adbCommand({"shell", "am", "force-stop", storage->m_packageName})); process.setCommand(storage->adbCommand({"shell", "am", "force-stop", storage->m_packageName}));
@@ -335,7 +306,7 @@ static ExecutableItem removeForwardPortRecipe(RunnerStorage *storage, const QStr
process.setCommand(storage->adbCommand({"forward", "--remove", port})); process.setCommand(storage->adbCommand({"forward", "--remove", port}));
}; };
const auto onForwardRemoveDone = [storage](const Process &process) { const auto onForwardRemoveDone = [storage](const Process &process) {
emit storage->remoteErrorOutput(process.cleanedStdErr().trimmed()); storage->m_glue->addStdErr(process.cleanedStdErr().trimmed());
return true; return true;
}; };
@@ -346,7 +317,7 @@ static ExecutableItem removeForwardPortRecipe(RunnerStorage *storage, const QStr
if (result == DoneWith::Success) if (result == DoneWith::Success)
storage->m_afterFinishAdbCommands.push_back("forward --remove " + port); storage->m_afterFinishAdbCommands.push_back("forward --remove " + port);
else else
emit storage->remoteProcessFinished(Tr::tr("Failed to forward %1 debugging ports.").arg(portType)); storage->m_glue->setFinished(Tr::tr("Failed to forward %1 debugging ports.").arg(portType));
}; };
return Group { return Group {
@@ -359,7 +330,8 @@ static ExecutableItem removeForwardPortRecipe(RunnerStorage *storage, const QStr
// The startBarrier is passed when logcat process received "Sending WAIT chunk" message. // The startBarrier is passed when logcat process received "Sending WAIT chunk" message.
// The settledBarrier is passed when logcat process received "debugger has settled" message. // The settledBarrier is passed when logcat process received "debugger has settled" message.
static ExecutableItem jdbRecipe(RunnerStorage *storage, const SingleBarrier &startBarrier, static ExecutableItem jdbRecipe(const Storage<RunnerStorage> &storage,
const SingleBarrier &startBarrier,
const SingleBarrier &settledBarrier) const SingleBarrier &settledBarrier)
{ {
const auto onSetup = [storage] { const auto onSetup = [storage] {
@@ -367,9 +339,9 @@ static ExecutableItem jdbRecipe(RunnerStorage *storage, const SingleBarrier &sta
}; };
const auto onTaskTreeSetup = [storage](TaskTree &taskTree) { const auto onTaskTreeSetup = [storage](TaskTree &taskTree) {
taskTree.setRecipe({ taskTree.setRecipe({removeForwardPortRecipe(storage.activeStorage(),
removeForwardPortRecipe(storage, "tcp:" + s_localJdbServerPort.toString(), "tcp:" + s_localJdbServerPort.toString(),
"jdwp:" + QString::number(storage->m_processPID), "JDB") "jdwp:" + QString::number(storage->m_processPID), "JDB")
}); });
}; };
@@ -403,7 +375,7 @@ static ExecutableItem jdbRecipe(RunnerStorage *storage, const SingleBarrier &sta
}; };
} }
static ExecutableItem logcatRecipe(RunnerStorage *storage) static ExecutableItem logcatRecipe(const Storage<RunnerStorage> &storage)
{ {
struct Buffer { struct Buffer {
QStringList timeArgs; QStringList timeArgs;
@@ -424,11 +396,12 @@ static ExecutableItem logcatRecipe(RunnerStorage *storage)
}; };
const auto onLogcatSetup = [storage, bufferStorage, startJdbBarrier, settledJdbBarrier](Process &process) { const auto onLogcatSetup = [storage, bufferStorage, startJdbBarrier, settledJdbBarrier](Process &process) {
RunnerStorage *storagePtr = storage.activeStorage();
Buffer *bufferPtr = bufferStorage.activeStorage(); Buffer *bufferPtr = bufferStorage.activeStorage();
const auto parseLogcat = [storage, bufferPtr, start = startJdbBarrier->barrier(), const auto parseLogcat = [storagePtr, bufferPtr, start = startJdbBarrier->barrier(),
settled = settledJdbBarrier->barrier(), processPtr = &process]( settled = settledJdbBarrier->barrier(), processPtr = &process](
QProcess::ProcessChannel channel) { QProcess::ProcessChannel channel) {
if (storage->m_processPID == -1) if (storagePtr->m_processPID == -1)
return; return;
QByteArray &buffer = channel == QProcess::StandardOutput ? bufferPtr->stdOutBuffer QByteArray &buffer = channel == QProcess::StandardOutput ? bufferPtr->stdOutBuffer
@@ -444,13 +417,13 @@ static ExecutableItem logcatRecipe(RunnerStorage *storage)
else else
buffer = lines.takeLast(); // incomplete line buffer = lines.takeLast(); // incomplete line
const QString pidString = QString::number(storage->m_processPID); const QString pidString = QString::number(storagePtr->m_processPID);
for (const QByteArray &msg : std::as_const(lines)) { for (const QByteArray &msg : std::as_const(lines)) {
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n'); const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
if (!line.contains(pidString)) if (!line.contains(pidString))
continue; continue;
if (storage->m_useCppDebugger) { if (storagePtr->m_useCppDebugger) {
if (start->current() == 0 && msg.trimmed().endsWith("Sending WAIT chunk")) if (start->current() == 0 && msg.trimmed().endsWith("Sending WAIT chunk"))
start->advance(); start->advance();
else if (settled->current() == 0 && msg.indexOf("debugger has settled") > 0) else if (settled->current() == 0 && msg.indexOf("debugger has settled") > 0)
@@ -481,16 +454,16 @@ static ExecutableItem logcatRecipe(RunnerStorage *storage)
const QString msgType = match.captured(2); const QString msgType = match.captured(2);
const QString output = line.mid(match.capturedStart(2)); const QString output = line.mid(match.capturedStart(2));
if (onlyError || msgType == "F" || msgType == "E" || msgType == "W") if (onlyError || msgType == "F" || msgType == "E" || msgType == "W")
emit storage->remoteErrorOutput(output); storagePtr->m_glue->addStdErr(output);
else else
emit storage->remoteOutput(output); storagePtr->m_glue->addStdOut(output);
} }
} else { } else {
if (onlyError || line.startsWith("F/") || line.startsWith("E/") if (onlyError || line.startsWith("F/") || line.startsWith("E/")
|| line.startsWith("W/")) { || line.startsWith("W/")) {
emit storage->remoteErrorOutput(line); storagePtr->m_glue->addStdErr(line);
} else { } else {
emit storage->remoteOutput(line); storagePtr->m_glue->addStdOut(line);
} }
} }
} }
@@ -517,12 +490,12 @@ static ExecutableItem logcatRecipe(RunnerStorage *storage)
}; };
} }
static ExecutableItem preStartRecipe(RunnerStorage *storage) static ExecutableItem preStartRecipe(const Storage<RunnerStorage> &storage)
{ {
const QString port = "tcp:" + QString::number(storage->m_qmlServer.port());
const Storage<QStringList> argsStorage; const Storage<QStringList> argsStorage;
const LoopList iterator(storage->m_beforeStartAdbCommands); const LoopUntil iterator([storage](int iteration) {
return iteration < storage->m_beforeStartAdbCommands.size();
});
const auto onArgsSetup = [storage, argsStorage] { const auto onArgsSetup = [storage, argsStorage] {
*argsStorage = {"shell", "am", "start", "-n", storage->m_intentName}; *argsStorage = {"shell", "am", "start", "-n", storage->m_intentName};
@@ -531,15 +504,20 @@ static ExecutableItem preStartRecipe(RunnerStorage *storage)
}; };
const auto onPreCommandSetup = [storage, iterator](Process &process) { const auto onPreCommandSetup = [storage, iterator](Process &process) {
process.setCommand(storage->adbCommand({iterator->split(' ', Qt::SkipEmptyParts)})); process.setCommand(storage->adbCommand(
{storage->m_beforeStartAdbCommands.at(iterator.iteration()).split(' ', Qt::SkipEmptyParts)}));
}; };
const auto onPreCommandDone = [storage](const Process &process) { const auto onPreCommandDone = [storage](const Process &process) {
emit storage->remoteErrorOutput(process.cleanedStdErr().trimmed()); storage->m_glue->addStdErr(process.cleanedStdErr().trimmed());
}; };
const auto onQmlDebugSetup = [storage] { const auto onQmlDebugSetup = [storage] {
return storage->m_qmlDebugServices == QmlDebug::NoQmlDebugServices ? SetupResult::StopWithSuccess return storage->m_qmlDebugServices == QmlDebug::NoQmlDebugServices
: SetupResult::Continue; ? SetupResult::StopWithSuccess : SetupResult::Continue;
};
const auto onTaskTreeSetup = [storage](TaskTree &taskTree) {
const QString port = "tcp:" + QString::number(storage->m_qmlServer.port());
taskTree.setRecipe({removeForwardPortRecipe(storage.activeStorage(), port, port, "QML")});
}; };
const auto onQmlDebugDone = [storage, argsStorage] { const auto onQmlDebugDone = [storage, argsStorage] {
const QString qmljsdebugger = QString("port:%1,block,services:%2") const QString qmljsdebugger = QString("port:%1,block,services:%2")
@@ -575,8 +553,8 @@ static ExecutableItem preStartRecipe(RunnerStorage *storage)
process.setCommand(storage->adbCommand({args})); process.setCommand(storage->adbCommand({args}));
}; };
const auto onActivityDone = [storage](const Process &process) { const auto onActivityDone = [storage](const Process &process) {
emit storage->remoteProcessFinished(Tr::tr("Activity Manager error: %1") storage->m_glue->setFinished(
.arg(process.cleanedStdErr().trimmed())); Tr::tr("Activity Manager error: %1").arg(process.cleanedStdErr().trimmed()));
}; };
return Group { return Group {
@@ -587,14 +565,14 @@ static ExecutableItem preStartRecipe(RunnerStorage *storage)
}, },
Group { Group {
onGroupSetup(onQmlDebugSetup), onGroupSetup(onQmlDebugSetup),
removeForwardPortRecipe(storage, port, port, "QML"), TaskTreeTask(onTaskTreeSetup),
onGroupDone(onQmlDebugDone, CallDoneIf::Success) onGroupDone(onQmlDebugDone, CallDoneIf::Success)
}, },
ProcessTask(onActivitySetup, onActivityDone, CallDoneIf::Error) ProcessTask(onActivitySetup, onActivityDone, CallDoneIf::Error)
}; };
} }
static ExecutableItem postDoneRecipe(RunnerStorage *storage) static ExecutableItem postDoneRecipe(const Storage<RunnerStorage> &storage)
{ {
const LoopUntil iterator([storage](int iteration) { const LoopUntil iterator([storage](int iteration) {
return iteration < storage->m_afterFinishAdbCommands.size(); return iteration < storage->m_afterFinishAdbCommands.size();
@@ -608,7 +586,7 @@ static ExecutableItem postDoneRecipe(RunnerStorage *storage)
const auto onDone = [storage] { const auto onDone = [storage] {
storage->m_processPID = -1; storage->m_processPID = -1;
storage->m_processUser = -1; storage->m_processUser = -1;
emit storage->remoteProcessFinished("\n\n" + Tr::tr("\"%1\" died.").arg(storage->m_packageName)); storage->m_glue->setFinished("\n\n" + Tr::tr("\"%1\" died.").arg(storage->m_packageName));
}; };
return Group { return Group {
@@ -626,7 +604,8 @@ static QString tempDebugServerPath(int count)
return tempDebugServerPathTemplate.arg(count); return tempDebugServerPathTemplate.arg(count);
} }
static ExecutableItem uploadDebugServerRecipe(RunnerStorage *storage, const QString &debugServerFileName) static ExecutableItem uploadDebugServerRecipe(const Storage<RunnerStorage> &storage,
const QString &debugServerFileName)
{ {
const Storage<QString> tempDebugServerPathStorage; const Storage<QString> tempDebugServerPathStorage;
const LoopUntil iterator([tempDebugServerPathStorage](int iteration) { const LoopUntil iterator([tempDebugServerPathStorage](int iteration) {
@@ -645,8 +624,8 @@ static ExecutableItem uploadDebugServerRecipe(RunnerStorage *storage, const QStr
const auto onTempDebugServerPath = [storage, tempDebugServerPathStorage] { const auto onTempDebugServerPath = [storage, tempDebugServerPathStorage] {
const bool tempDirOK = !tempDebugServerPathStorage->isEmpty(); const bool tempDirOK = !tempDebugServerPathStorage->isEmpty();
if (tempDirOK) { if (tempDirOK) {
emit storage->remoteProcessStarted(s_localDebugServerPort, storage->m_qmlServer, storage->m_glue->setStarted(s_localDebugServerPort, storage->m_qmlServer,
storage->m_processPID); storage->m_processPID);
} else { } else {
qCDebug(androidRunWorkerLog) << "Can not get temporary file name"; qCDebug(androidRunWorkerLog) << "Can not get temporary file name";
} }
@@ -693,7 +672,7 @@ static ExecutableItem uploadDebugServerRecipe(RunnerStorage *storage, const QStr
}; };
} }
static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage) static ExecutableItem startNativeDebuggingRecipe(const Storage<RunnerStorage> &storage)
{ {
const auto onSetup = [storage] { const auto onSetup = [storage] {
return storage->m_useCppDebugger ? SetupResult::Continue : SetupResult::StopWithSuccess; return storage->m_useCppDebugger ? SetupResult::Continue : SetupResult::StopWithSuccess;
@@ -709,7 +688,7 @@ static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage)
if (result == DoneWith::Success) if (result == DoneWith::Success)
*packageDirStorage = process.stdOut(); *packageDirStorage = process.stdOut();
else else
emit storage->remoteProcessFinished(Tr::tr("Failed to find application directory.")); storage->m_glue->setFinished(Tr::tr("Failed to find application directory."));
}; };
// Add executable flag to package dir. Gdb can't connect to running server on device on // Add executable flag to package dir. Gdb can't connect to running server on device on
@@ -723,7 +702,7 @@ static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage)
QString msg = Tr::tr("Cannot find C++ debug server in NDK installation."); QString msg = Tr::tr("Cannot find C++ debug server in NDK installation.");
if (storage->m_useLldb) if (storage->m_useLldb)
msg += "\n" + Tr::tr("The lldb-server binary has not been found."); msg += "\n" + Tr::tr("The lldb-server binary has not been found.");
emit storage->remoteProcessFinished(msg); storage->m_glue->setFinished(msg);
return false; return false;
}; };
@@ -742,7 +721,7 @@ static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage)
setDebugServer(debugServerFileName) setDebugServer(debugServerFileName)
} >> Else { } >> Else {
Sync([storage] { Sync([storage] {
emit storage->remoteProcessFinished(Tr::tr("Cannot copy C++ debug server.")); storage->m_glue->setFinished(Tr::tr("Cannot copy C++ debug server."));
return false; return false;
}) })
}; };
@@ -776,7 +755,8 @@ static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage)
const auto onTaskTreeSetup = [storage, packageDirStorage](TaskTree &taskTree) { const auto onTaskTreeSetup = [storage, packageDirStorage](TaskTree &taskTree) {
const QString gdbServerSocket = *packageDirStorage + "/debug-socket"; const QString gdbServerSocket = *packageDirStorage + "/debug-socket";
taskTree.setRecipe({removeForwardPortRecipe(storage, "tcp:" + s_localDebugServerPort.toString(), taskTree.setRecipe({removeForwardPortRecipe(storage.activeStorage(),
"tcp:" + s_localDebugServerPort.toString(),
"localfilesystem:" + gdbServerSocket, "C++")}); "localfilesystem:" + gdbServerSocket, "C++")});
}; };
@@ -813,13 +793,12 @@ static ExecutableItem startNativeDebuggingRecipe(RunnerStorage *storage)
}; };
} }
static ExecutableItem pidRecipe(RunnerStorage *storage) static ExecutableItem pidRecipe(const Storage<RunnerStorage> &storage)
{ {
const QString pidScript = storage->isPreNougat() const auto onPidSetup = [storage](Process &process) {
? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done") const QString pidScript = storage->isPreNougat()
: QString("pidof -s '%1'").arg(storage->m_packageName); ? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done")
: QString("pidof -s '%1'").arg(storage->m_packageName);
const auto onPidSetup = [storage, pidScript](Process &process) {
process.setCommand(storage->adbCommand({"shell", pidScript})); process.setCommand(storage->adbCommand({"shell", pidScript}));
}; };
const auto onPidDone = [storage](const Process &process) { const auto onPidDone = [storage](const Process &process) {
@@ -848,8 +827,8 @@ static ExecutableItem pidRecipe(RunnerStorage *storage)
storage->m_processUser = processUser; storage->m_processUser = processUser;
qCDebug(androidRunWorkerLog) << "Process ID changed to:" << storage->m_processPID; qCDebug(androidRunWorkerLog) << "Process ID changed to:" << storage->m_processPID;
if (!storage->m_useCppDebugger) { if (!storage->m_useCppDebugger) {
emit storage->remoteProcessStarted(s_localDebugServerPort, storage->m_qmlServer, storage->m_glue->setStarted(s_localDebugServerPort, storage->m_qmlServer,
storage->m_processPID); storage->m_processPID);
} }
return DoneResult::Success; return DoneResult::Success;
} }
@@ -890,42 +869,41 @@ static ExecutableItem pidRecipe(RunnerStorage *storage)
}; };
} }
AndroidRunnerWorker::~AndroidRunnerWorker() void RunnerInterface::setStarted(const Utils::Port &debugServerPort, const QUrl &qmlServer,
qint64 pid)
{ {
if (m_storage->m_processPID != -1) emit started(debugServerPort, qmlServer, pid);
TaskTree::runBlocking(Group { forceStopRecipe(m_storage.get()), postDoneRecipe(m_storage.get()) });
} }
void AndroidRunnerWorker::asyncStart() ExecutableItem runnerRecipe(const Storage<RunnerInterface> &glueStorage)
{ {
// TODO: Instead of asyncStop recipe, add a barrier storage. const Storage<RunnerStorage> storage;
const Group recipe {
const auto onSetup = [glueStorage, storage] {
setupStorage(storage.activeStorage(), glueStorage.activeStorage());
};
return Group {
finishAllAndSuccess, finishAllAndSuccess,
storage,
onGroupSetup(onSetup),
Group { Group {
forceStopRecipe(m_storage.get()), forceStopRecipe(storage),
Group { Group {
parallel, parallel,
stopOnSuccessOrError, stopOnSuccessOrError,
logcatRecipe(m_storage.get()), logcatRecipe(storage),
Group { Group {
preStartRecipe(m_storage.get()), preStartRecipe(storage),
pidRecipe(m_storage.get()) pidRecipe(storage)
} }
} }
}.withCancel([storage = m_storage.get()] { }.withCancel([glueStorage] {
return std::make_pair(storage, &RunnerStorage::cancel); return std::make_pair(glueStorage.activeStorage(), &RunnerInterface::canceled);
}), }),
forceStopRecipe(m_storage.get()), forceStopRecipe(storage),
postDoneRecipe(m_storage.get()) postDoneRecipe(storage)
}; };
m_taskTreeRunner.start(recipe);
}
void AndroidRunnerWorker::asyncStop()
{
emit cancel();
} }
} // namespace Android::Internal } // namespace Android::Internal
#include "androidrunnerworker.moc"

View File

@@ -8,34 +8,52 @@
namespace ProjectExplorer { class RunControl; } namespace ProjectExplorer { class RunControl; }
namespace Utils { class Port; } namespace Utils { class Port; }
QT_BEGIN_NAMESPACE
class QUrl;
QT_END_NAMESPACE
namespace Android::Internal { namespace Android::Internal {
class RunnerStorage; class RunnerInterface : public QObject
class AndroidRunnerWorker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
AndroidRunnerWorker(ProjectExplorer::RunControl *runControl, const QString &deviceSerialNumber, // Gui init setters
int apiLevel); void setRunControl(ProjectExplorer::RunControl *runControl) { m_runControl = runControl; }
~AndroidRunnerWorker() override; void setDeviceSerialNumber(const QString &deviceSerialNumber) { m_deviceSerialNumber = deviceSerialNumber; }
void setApiLevel(int apiLevel) { m_apiLevel = apiLevel; }
void asyncStart(); // business logic init getters
void asyncStop(); ProjectExplorer::RunControl *runControl() const { return m_runControl; }
QString deviceSerialNumber() const { return m_deviceSerialNumber; }
int apiLevel() const { return m_apiLevel; }
// GUI -> business logic
void cancel() { emit canceled(); }
// business logic -> GUI
void setStarted(const Utils::Port &debugServerPort, const QUrl &qmlServer, qint64 pid);
void setFinished(const QString &errorMessage) { emit finished(errorMessage); }
void addStdOut(const QString &data) { emit stdOut(data); }
void addStdErr(const QString &data) { emit stdErr(data); }
signals: signals:
void remoteProcessStarted(const Utils::Port &debugServerPort, const QUrl &qmlServer, qint64 pid); // GUI -> business logic
void remoteProcessFinished(const QString &errString = QString()); void canceled();
void remoteOutput(const QString &output); // business logic -> GUI
void remoteErrorOutput(const QString &output); void started(const Utils::Port &debugServerPort, const QUrl &qmlServer, qint64 pid);
void finished(const QString &errorMessage);
void cancel(); void stdOut(const QString &data);
void stdErr(const QString &data);
private: private:
std::unique_ptr<RunnerStorage> m_storage; ProjectExplorer::RunControl *m_runControl = nullptr;
Tasking::TaskTreeRunner m_taskTreeRunner; QString m_deviceSerialNumber;
int m_apiLevel = -1;
}; };
Tasking::ExecutableItem runnerRecipe(const Tasking::Storage<RunnerInterface> &storage);
} // namespace Android::Internal } // namespace Android::Internal