2018-03-17 09:31:56 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** Copyright (C) 2018 BogDan Vatra <bog_dan_ro@yahoo.com>
|
|
|
|
|
** 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 "androidrunnerworker.h"
|
|
|
|
|
|
2018-05-15 10:08:16 +02:00
|
|
|
#include "androidconfigurations.h"
|
|
|
|
|
#include "androidconstants.h"
|
|
|
|
|
#include "androidmanager.h"
|
|
|
|
|
#include "androidrunconfiguration.h"
|
2018-10-01 14:22:49 +03:00
|
|
|
#include "androidgdbserverkitinformation.h"
|
2018-03-17 09:31:56 +02:00
|
|
|
|
|
|
|
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
2018-08-02 11:00:06 +02:00
|
|
|
#include <projectexplorer/environmentaspect.h>
|
2018-07-31 12:21:47 +02:00
|
|
|
#include <projectexplorer/runconfigurationaspects.h>
|
2018-03-17 09:31:56 +02:00
|
|
|
#include <projectexplorer/target.h>
|
|
|
|
|
#include <qtsupport/baseqtversion.h>
|
|
|
|
|
#include <qtsupport/qtkitinformation.h>
|
|
|
|
|
#include <utils/hostosinfo.h>
|
|
|
|
|
#include <utils/runextensions.h>
|
|
|
|
|
#include <utils/synchronousprocess.h>
|
|
|
|
|
#include <utils/temporaryfile.h>
|
2018-07-31 12:21:47 +02:00
|
|
|
#include <utils/qtcprocess.h>
|
2018-03-17 09:31:56 +02:00
|
|
|
#include <utils/url.h>
|
2018-10-01 14:22:49 +03:00
|
|
|
#include <utils/fileutils.h>
|
2018-03-17 09:31:56 +02:00
|
|
|
|
2018-06-18 11:49:14 +02:00
|
|
|
#include <QLoggingCategory>
|
2018-03-17 09:31:56 +02:00
|
|
|
#include <QTcpServer>
|
2018-05-15 10:08:16 +02:00
|
|
|
#include <QThread>
|
|
|
|
|
|
2018-03-17 09:31:56 +02:00
|
|
|
#include <chrono>
|
|
|
|
|
|
2018-06-18 11:49:14 +02:00
|
|
|
namespace {
|
2018-10-12 09:33:30 +03:00
|
|
|
Q_LOGGING_CATEGORY(androidRunWorkerLog, "qtc.android.run.androidrunnerworker", QtWarningMsg)
|
2018-12-20 18:40:34 +01:00
|
|
|
static const int GdbTempFileMaxCounter = 20;
|
2018-06-18 11:49:14 +02:00
|
|
|
}
|
|
|
|
|
|
2018-03-17 09:31:56 +02:00
|
|
|
using namespace std;
|
|
|
|
|
using namespace std::placeholders;
|
|
|
|
|
using namespace ProjectExplorer;
|
|
|
|
|
|
|
|
|
|
namespace Android {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const QString pidScript = "pidof -s \"%1\"";
|
|
|
|
|
static const QString pidScriptPreNougat = QStringLiteral("for p in /proc/[0-9]*; "
|
|
|
|
|
"do cat <$p/cmdline && echo :${p##*/}; done");
|
|
|
|
|
static const QString pidPollingScript = QStringLiteral("while [ -d /proc/%1 ]; do sleep 1; done");
|
|
|
|
|
|
|
|
|
|
static const QString regExpLogcat = QStringLiteral("[0-9\\-]*" // date
|
|
|
|
|
"\\s+"
|
|
|
|
|
"[0-9\\-:.]*"// time
|
|
|
|
|
"\\s*"
|
|
|
|
|
"(\\d*)" // pid 1. capture
|
|
|
|
|
"\\s+"
|
|
|
|
|
"\\d*" // unknown
|
|
|
|
|
"\\s+"
|
|
|
|
|
"(\\w)" // message type 2. capture
|
|
|
|
|
"\\s+"
|
|
|
|
|
"(.*): " // source 3. capture
|
|
|
|
|
"(.*)" // message 4. capture
|
|
|
|
|
"[\\n\\r]*"
|
|
|
|
|
);
|
|
|
|
|
static int APP_START_TIMEOUT = 45000;
|
|
|
|
|
|
|
|
|
|
static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
|
|
|
|
|
int msecs = APP_START_TIMEOUT)
|
|
|
|
|
{
|
|
|
|
|
bool timedOut = false;
|
|
|
|
|
auto end = chrono::high_resolution_clock::now();
|
|
|
|
|
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
|
|
|
|
|
timedOut = true;
|
|
|
|
|
return timedOut;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 11:12:45 +02:00
|
|
|
static qint64 extractPID(const QString &output, const QString &packageName)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
qint64 pid = -1;
|
|
|
|
|
foreach (auto tuple, output.split('\n')) {
|
|
|
|
|
tuple = tuple.simplified();
|
|
|
|
|
if (!tuple.isEmpty()) {
|
|
|
|
|
auto parts = tuple.split(':');
|
2018-08-07 11:12:45 +02:00
|
|
|
QString commandName = parts.first();
|
2018-03-17 09:31:56 +02:00
|
|
|
if (parts.length() == 2 && commandName == packageName) {
|
|
|
|
|
pid = parts.last().toLongLong();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return pid;
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 11:12:45 +02:00
|
|
|
static void findProcessPID(QFutureInterface<qint64> &fi, QStringList selector,
|
|
|
|
|
const QString &packageName, bool preNougat)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Finding PID. PreNougat:" << preNougat;
|
2018-03-17 09:31:56 +02:00
|
|
|
if (packageName.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
qint64 processPID = -1;
|
|
|
|
|
chrono::high_resolution_clock::time_point start = chrono::high_resolution_clock::now();
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
selector.append("shell");
|
|
|
|
|
selector.append(preNougat ? pidScriptPreNougat : pidScript.arg(packageName));
|
2018-03-17 09:31:56 +02:00
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
QThread::msleep(200);
|
2018-08-07 11:12:45 +02:00
|
|
|
SdkToolResult result = AndroidManager::runAdbCommand(selector);
|
2018-05-09 12:20:54 +02:00
|
|
|
if (preNougat) {
|
2018-08-07 11:12:45 +02:00
|
|
|
processPID = extractPID(result.stdOut(), packageName);
|
2018-05-09 12:20:54 +02:00
|
|
|
} else {
|
2018-08-07 11:12:45 +02:00
|
|
|
if (!result.stdOut().isEmpty())
|
|
|
|
|
processPID = result.stdOut().trimmed().toLongLong();
|
2018-05-09 12:20:54 +02:00
|
|
|
}
|
2018-03-17 09:31:56 +02:00
|
|
|
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
|
|
|
|
|
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "PID found:" << processPID;
|
2018-03-17 09:31:56 +02:00
|
|
|
if (!fi.isCanceled())
|
|
|
|
|
fi.reportResult(processPID);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-08 11:54:32 +02:00
|
|
|
static void deleter(QProcess *p)
|
|
|
|
|
{
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Killing process:" << p->objectName();
|
2018-05-08 11:54:32 +02:00
|
|
|
p->terminate();
|
|
|
|
|
if (!p->waitForFinished(1000)) {
|
|
|
|
|
p->kill();
|
|
|
|
|
p->waitForFinished();
|
|
|
|
|
}
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Done killing process:" << p->objectName();
|
2018-05-08 11:54:32 +02:00
|
|
|
// Might get deleted from its own signal handler.
|
|
|
|
|
p->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 12:50:41 +02:00
|
|
|
AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packageName)
|
|
|
|
|
: m_packageName(packageName)
|
2018-03-17 09:31:56 +02:00
|
|
|
, m_adbLogcatProcess(nullptr, deleter)
|
|
|
|
|
, m_psIsAlive(nullptr, deleter)
|
|
|
|
|
, m_logCatRegExp(regExpLogcat)
|
|
|
|
|
, m_gdbServerProcess(nullptr, deleter)
|
|
|
|
|
, m_jdbProcess(nullptr, deleter)
|
|
|
|
|
|
|
|
|
|
{
|
2018-05-15 12:39:56 +02:00
|
|
|
auto runConfig = runner->runControl()->runConfiguration();
|
2018-09-07 13:29:45 +02:00
|
|
|
auto aspect = runConfig->aspect<Debugger::DebuggerRunConfigurationAspect>();
|
2018-05-15 12:39:56 +02:00
|
|
|
Core::Id runMode = runner->runMode();
|
2018-03-17 09:31:56 +02:00
|
|
|
const bool debuggingMode = runMode == ProjectExplorer::Constants::DEBUG_RUN_MODE;
|
|
|
|
|
m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
|
|
|
|
|
if (debuggingMode && aspect->useQmlDebugger())
|
|
|
|
|
m_qmlDebugServices = QmlDebug::QmlDebuggerServices;
|
|
|
|
|
else if (runMode == ProjectExplorer::Constants::QML_PROFILER_RUN_MODE)
|
|
|
|
|
m_qmlDebugServices = QmlDebug::QmlProfilerServices;
|
|
|
|
|
else if (runMode == ProjectExplorer::Constants::QML_PREVIEW_RUN_MODE)
|
|
|
|
|
m_qmlDebugServices = QmlDebug::QmlPreviewServices;
|
|
|
|
|
else
|
|
|
|
|
m_qmlDebugServices = QmlDebug::NoQmlDebugServices;
|
|
|
|
|
m_localGdbServerPort = Utils::Port(5039);
|
|
|
|
|
QTC_CHECK(m_localGdbServerPort.isValid());
|
|
|
|
|
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "QML debugging enabled";
|
2018-03-17 09:31:56 +02:00
|
|
|
QTcpServer server;
|
2018-07-05 08:13:08 +02:00
|
|
|
QTC_ASSERT(server.listen(QHostAddress::LocalHost),
|
2018-03-17 09:31:56 +02:00
|
|
|
qDebug() << tr("No free ports available on host for QML debugging."));
|
|
|
|
|
m_qmlServer.setScheme(Utils::urlTcpScheme());
|
|
|
|
|
m_qmlServer.setHost(server.serverAddress().toString());
|
|
|
|
|
m_qmlServer.setPort(server.serverPort());
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "QML server:" << m_qmlServer.toDisplayString();
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
m_localJdbServerPort = Utils::Port(5038);
|
|
|
|
|
QTC_CHECK(m_localJdbServerPort.isValid());
|
2018-05-07 17:13:52 +02:00
|
|
|
|
|
|
|
|
auto target = runConfig->target();
|
|
|
|
|
m_deviceSerialNumber = AndroidManager::deviceSerialNumber(target);
|
|
|
|
|
m_apiLevel = AndroidManager::deviceApiLevel(target);
|
2018-05-15 10:08:16 +02:00
|
|
|
|
2018-09-07 13:29:45 +02:00
|
|
|
m_extraEnvVars = runConfig->aspect<EnvironmentAspect>()->environment();
|
2018-08-02 11:00:06 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Environment variables for the app"
|
|
|
|
|
<< m_extraEnvVars.toStringList();
|
|
|
|
|
|
2018-07-31 12:21:47 +02:00
|
|
|
m_extraAppParams = runConfig->runnable().commandLineArguments;
|
|
|
|
|
|
2018-09-07 13:29:45 +02:00
|
|
|
if (auto aspect = runConfig->aspect(Constants::ANDROID_AMSTARTARGS))
|
2018-05-15 10:08:16 +02:00
|
|
|
m_amStartExtraArgs = static_cast<BaseStringAspect *>(aspect)->value().split(' ');
|
2018-05-15 12:11:54 +02:00
|
|
|
|
2018-09-07 13:29:45 +02:00
|
|
|
if (auto aspect = runConfig->aspect(Constants::ANDROID_PRESTARTSHELLCMDLIST)) {
|
2018-05-15 12:11:54 +02:00
|
|
|
for (const QString &shellCmd : static_cast<BaseStringListAspect *>(aspect)->value())
|
|
|
|
|
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
|
|
|
|
|
}
|
|
|
|
|
for (const QString &shellCmd : runner->recordedData(Constants::ANDROID_PRESTARTSHELLCMDLIST).toStringList())
|
|
|
|
|
m_beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
|
|
|
|
|
|
2018-09-07 13:29:45 +02:00
|
|
|
if (auto aspect = runConfig->aspect(Constants::ANDROID_POSTFINISHSHELLCMDLIST)) {
|
2018-05-15 12:11:54 +02:00
|
|
|
for (const QString &shellCmd : static_cast<BaseStringListAspect *>(aspect)->value())
|
|
|
|
|
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
|
|
|
|
}
|
|
|
|
|
for (const QString &shellCmd : runner->recordedData(Constants::ANDROID_POSTFINISHSHELLCMDLIST).toStringList())
|
|
|
|
|
m_afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
2018-06-18 11:49:14 +02:00
|
|
|
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Device Serial:" << m_deviceSerialNumber
|
|
|
|
|
<< "API level:" << m_apiLevel
|
|
|
|
|
<< "Extra Start Args:" << m_amStartExtraArgs
|
|
|
|
|
<< "Before Start ADB cmds:" << m_beforeStartAdbCommands
|
|
|
|
|
<< "After finish ADB cmds:" << m_afterFinishAdbCommands;
|
2019-02-06 12:50:51 +01:00
|
|
|
m_gdbserverPath = AndroidGdbServerKitAspect::gdbServer(target->kit()).toString();
|
|
|
|
|
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit());
|
2018-12-03 11:25:49 +02:00
|
|
|
m_useAppParamsForQmlDebugger = version->qtVersion() >= QtSupport::QtVersionNumber(5, 12);
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
AndroidRunnerWorker::~AndroidRunnerWorker()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
forceStop();
|
|
|
|
|
|
|
|
|
|
if (!m_pidFinder.isFinished())
|
|
|
|
|
m_pidFinder.cancel();
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-07 11:12:45 +02:00
|
|
|
bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *stdOut,
|
|
|
|
|
const QByteArray &writeData)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-06-18 11:49:14 +02:00
|
|
|
QStringList adbArgs = selector() + args;
|
2018-08-07 11:12:45 +02:00
|
|
|
SdkToolResult result = AndroidManager::runAdbCommand(adbArgs, writeData);
|
|
|
|
|
if (!result.success())
|
|
|
|
|
emit remoteErrorOutput(result.exitMessage() + "\n" + result.stdErr());
|
|
|
|
|
if (stdOut)
|
|
|
|
|
*stdOut = result.stdOut();
|
|
|
|
|
return result.success();
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-12-20 18:40:34 +01:00
|
|
|
bool AndroidRunnerWorker::uploadGdbServer()
|
2018-10-01 14:22:49 +03:00
|
|
|
{
|
2018-12-20 18:40:34 +01:00
|
|
|
// Push the gdbserver to temp location and then to package dir.
|
|
|
|
|
// the files can't be pushed directly to package because of permissions.
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Uploading GdbServer";
|
|
|
|
|
|
|
|
|
|
bool foundUnique = true;
|
|
|
|
|
auto cleanUp = [this, &foundUnique] (QString *p) {
|
|
|
|
|
if (foundUnique && !runAdb({"shell", "rm", "-f", *p}))
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Gdbserver cleanup failed.";
|
|
|
|
|
delete p;
|
|
|
|
|
};
|
|
|
|
|
std::unique_ptr<QString, decltype (cleanUp)>
|
|
|
|
|
tempGdbServerPath(new QString("/data/local/tmp/%1"), cleanUp);
|
|
|
|
|
|
|
|
|
|
// Get a unique temp file name for gdbserver copy
|
|
|
|
|
int count = 0;
|
|
|
|
|
while (deviceFileExists(tempGdbServerPath->arg(++count))) {
|
|
|
|
|
if (count > GdbTempFileMaxCounter) {
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Can not get temporary file name";
|
|
|
|
|
foundUnique = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*tempGdbServerPath = tempGdbServerPath->arg(count);
|
|
|
|
|
|
|
|
|
|
// Copy gdbserver to temp location
|
|
|
|
|
if (!runAdb({"push", m_gdbserverPath , *tempGdbServerPath})) {
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Gdbserver upload to temp directory failed";
|
2018-10-01 14:22:49 +03:00
|
|
|
return false;
|
2018-12-20 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy gdbserver from temp location to app directory
|
|
|
|
|
if (!runAdb({"shell", "run-as", m_packageName, "cp" , *tempGdbServerPath, "./gdbserver"})) {
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Gdbserver copy from temp directory failed";
|
2018-10-01 14:22:49 +03:00
|
|
|
return false;
|
2018-12-20 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
QTC_ASSERT(runAdb({"shell", "run-as", m_packageName, "chmod", "+x", "./gdbserver"}),
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Gdbserver chmod +x failed.");
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidRunnerWorker::deviceFileExists(const QString &filePath)
|
|
|
|
|
{
|
2019-02-04 15:21:55 +01:00
|
|
|
QString output;
|
|
|
|
|
const bool success = runAdb({"shell", "ls", filePath, "2>/dev/null"}, &output);
|
|
|
|
|
return success && !output.trimmed().isEmpty();
|
2018-12-20 18:40:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AndroidRunnerWorker::packageFileExists(const QString &filePath)
|
|
|
|
|
{
|
2019-02-04 15:21:55 +01:00
|
|
|
QString output;
|
|
|
|
|
const bool success = runAdb({"shell", "run-as", m_packageName, "ls", filePath, "2>/dev/null"}, &output);
|
|
|
|
|
return success && !output.trimmed().isEmpty();
|
2018-10-01 14:22:49 +03:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::adbKill(qint64 pid)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
runAdb({"shell", "kill", "-9", QString::number(pid)});
|
2018-05-15 12:50:41 +02:00
|
|
|
runAdb({"shell", "run-as", m_packageName, "kill", "-9", QString::number(pid)});
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
QStringList AndroidRunnerWorker::selector() const
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-05-07 17:13:52 +02:00
|
|
|
return AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::forceStop()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-08-07 11:12:45 +02:00
|
|
|
runAdb({"shell", "am", "force-stop", m_packageName});
|
2018-03-17 09:31:56 +02:00
|
|
|
|
|
|
|
|
// try killing it via kill -9
|
2018-08-07 11:12:45 +02:00
|
|
|
QString out;
|
|
|
|
|
runAdb({"shell", pidScriptPreNougat}, &out);
|
2018-05-15 12:50:41 +02:00
|
|
|
qint64 pid = extractPID(out.simplified(), m_packageName);
|
2018-08-07 11:12:45 +02:00
|
|
|
if (pid != -1)
|
2018-03-17 09:31:56 +02:00
|
|
|
adbKill(pid);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::logcatReadStandardError()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
logcatProcess(m_adbLogcatProcess->readAllStandardError(), m_stderrBuffer, true);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::logcatReadStandardOutput()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
logcatProcess(m_adbLogcatProcess->readAllStandardOutput(), m_stdoutBuffer, false);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
QList<QByteArray> lines = text.split('\n');
|
|
|
|
|
// lines always contains at least one item
|
|
|
|
|
lines[0].prepend(buffer);
|
|
|
|
|
if (!lines.last().endsWith('\n')) {
|
|
|
|
|
// incomplete line
|
|
|
|
|
buffer = lines.last();
|
|
|
|
|
lines.removeLast();
|
|
|
|
|
} else {
|
|
|
|
|
buffer.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString pidString = QString::number(m_processPID);
|
|
|
|
|
foreach (const QByteArray &msg, lines) {
|
|
|
|
|
const QString line = QString::fromUtf8(msg).trimmed() + QLatin1Char('\n');
|
|
|
|
|
if (!line.contains(pidString))
|
|
|
|
|
continue;
|
|
|
|
|
if (m_useCppDebugger) {
|
|
|
|
|
switch (m_jdbState) {
|
|
|
|
|
case JDBState::Idle:
|
|
|
|
|
if (msg.trimmed().endsWith("Sending WAIT chunk")) {
|
|
|
|
|
m_jdbState = JDBState::Waiting;
|
|
|
|
|
handleJdbWaiting();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case JDBState::Waiting:
|
|
|
|
|
if (msg.indexOf("debugger has settled") > 0) {
|
|
|
|
|
m_jdbState = JDBState::Settled;
|
|
|
|
|
handleJdbSettled();
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (m_logCatRegExp.exactMatch(line)) {
|
|
|
|
|
// Android M
|
|
|
|
|
if (m_logCatRegExp.cap(1) == pidString) {
|
|
|
|
|
const QString &messagetype = m_logCatRegExp.cap(2);
|
|
|
|
|
QString output = line.mid(m_logCatRegExp.pos(2));
|
|
|
|
|
|
|
|
|
|
if (onlyError
|
|
|
|
|
|| messagetype == QLatin1String("F")
|
|
|
|
|
|| messagetype == QLatin1String("E")
|
|
|
|
|
|| messagetype == QLatin1String("W"))
|
|
|
|
|
emit remoteErrorOutput(output);
|
|
|
|
|
else
|
|
|
|
|
emit remoteOutput(output);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (onlyError || line.startsWith("F/")
|
|
|
|
|
|| line.startsWith("E/")
|
|
|
|
|
|| line.startsWith("W/"))
|
|
|
|
|
emit remoteErrorOutput(line);
|
|
|
|
|
else
|
|
|
|
|
emit remoteOutput(line);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::setAndroidDeviceInfo(const AndroidDeviceInfo &info)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-05-07 17:13:52 +02:00
|
|
|
m_deviceSerialNumber = info.serialNumber;
|
|
|
|
|
m_apiLevel = info.sdk;
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Android Device Info changed"
|
|
|
|
|
<< m_deviceSerialNumber << m_apiLevel;
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::asyncStartHelper()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
forceStop();
|
|
|
|
|
|
|
|
|
|
// Its assumed that the device or avd returned by selector() is online.
|
2018-08-07 11:12:45 +02:00
|
|
|
// Start the logcat process before app starts.
|
2018-03-17 09:31:56 +02:00
|
|
|
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
2018-08-07 11:12:45 +02:00
|
|
|
m_adbLogcatProcess.reset(AndroidManager::runAdbCommandDetached(selector() << "logcat"));
|
|
|
|
|
if (m_adbLogcatProcess) {
|
|
|
|
|
m_adbLogcatProcess->setObjectName("AdbLogcatProcess");
|
|
|
|
|
connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardOutput,
|
|
|
|
|
this, &AndroidRunnerWorker::logcatReadStandardOutput);
|
|
|
|
|
connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardError,
|
|
|
|
|
this, &AndroidRunnerWorker::logcatReadStandardError);
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-15 12:11:54 +02:00
|
|
|
for (const QString &entry : m_beforeStartAdbCommands)
|
2018-03-17 09:31:56 +02:00
|
|
|
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
|
|
|
|
|
|
|
|
|
QStringList args({"shell", "am", "start"});
|
2018-05-15 10:08:16 +02:00
|
|
|
args << m_amStartExtraArgs;
|
2018-05-15 09:52:09 +02:00
|
|
|
args << "-n" << m_intentName;
|
2018-03-17 09:31:56 +02:00
|
|
|
if (m_useCppDebugger) {
|
|
|
|
|
args << "-D";
|
|
|
|
|
// run-as <package-name> pwd fails on API 22 so route the pwd through shell.
|
2018-08-07 11:12:45 +02:00
|
|
|
QString packageDir;
|
|
|
|
|
if (!runAdb({"shell", "run-as", m_packageName, "/system/bin/sh", "-c", "pwd"},
|
|
|
|
|
&packageDir)) {
|
|
|
|
|
emit remoteProcessFinished(tr("Failed to find application directory."));
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2018-08-06 11:56:20 +02:00
|
|
|
|
|
|
|
|
// Add executable flag to package dir. Gdb can't connect to running server on device on
|
|
|
|
|
// e.g. on Android 8 with NDK 10e
|
2018-08-07 11:12:45 +02:00
|
|
|
runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir.trimmed()});
|
2018-03-17 09:31:56 +02:00
|
|
|
|
2018-12-20 18:40:34 +01:00
|
|
|
QString gdbServerExecutable = "gdbserver";
|
2018-11-28 09:59:19 +01:00
|
|
|
QString gdbServerPrefix = "./lib/";
|
2018-12-20 18:40:34 +01:00
|
|
|
auto findGdbServer = [this, &gdbServerExecutable, gdbServerPrefix](const QString& gdbEx) {
|
|
|
|
|
if (!packageFileExists(gdbServerPrefix + gdbEx))
|
|
|
|
|
return false;
|
|
|
|
|
gdbServerExecutable = gdbEx;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!findGdbServer("gdbserver") && !findGdbServer("libgdbserver.so")) {
|
|
|
|
|
// Armv8. symlink lib is not available.
|
|
|
|
|
// Kill the previous instances of gdbserver. Do this before copying the gdbserver.
|
|
|
|
|
runAdb({"shell", "run-as", m_packageName, "killall", gdbServerExecutable});
|
|
|
|
|
if (!m_gdbserverPath.isEmpty() && uploadGdbServer()) {
|
|
|
|
|
gdbServerPrefix = "./";
|
|
|
|
|
} else {
|
2019-03-01 17:10:38 +01:00
|
|
|
emit remoteProcessFinished(tr("Cannot find or copy C++ debug server."));
|
2018-11-28 09:59:19 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-12-20 18:40:34 +01:00
|
|
|
qCDebug(androidRunWorkerLog) << "Found GDB server under ./lib";
|
|
|
|
|
runAdb({"shell", "run-as", m_packageName, "killall", gdbServerExecutable});
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-07 11:12:45 +02:00
|
|
|
QString debuggerServerErr;
|
2018-12-02 22:07:22 +02:00
|
|
|
if (!startDebuggerServer(packageDir, gdbServerPrefix, gdbServerExecutable, &debuggerServerErr)) {
|
2018-08-07 11:12:45 +02:00
|
|
|
emit remoteProcessFinished(debuggerServerErr);
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
|
|
|
|
|
// currently forward to same port on device and host
|
|
|
|
|
const QString port = QString("tcp:%1").arg(m_qmlServer.port());
|
|
|
|
|
QStringList removeForward{{"forward", "--remove", port}};
|
|
|
|
|
runAdb(removeForward);
|
|
|
|
|
if (!runAdb({"forward", port, port})) {
|
2018-08-07 11:12:45 +02:00
|
|
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2018-05-15 12:11:54 +02:00
|
|
|
m_afterFinishAdbCommands.push_back(removeForward.join(' '));
|
2018-03-17 09:31:56 +02:00
|
|
|
|
2018-12-03 11:25:49 +02:00
|
|
|
const QString qmljsdebugger = QString("port:%1,block,services:%2")
|
2018-03-17 09:31:56 +02:00
|
|
|
.arg(m_qmlServer.port()).arg(QmlDebug::qmlDebugServices(m_qmlDebugServices));
|
2018-12-03 11:25:49 +02:00
|
|
|
|
|
|
|
|
if (m_useAppParamsForQmlDebugger) {
|
|
|
|
|
if (!m_extraAppParams.isEmpty())
|
|
|
|
|
m_extraAppParams.prepend(' ');
|
|
|
|
|
m_extraAppParams.prepend("-qmljsdebugger=" + qmljsdebugger);
|
|
|
|
|
} else {
|
|
|
|
|
args << "-e" << "qml_debug" << "true"
|
|
|
|
|
<< "-e" << "qmljsdebugger"
|
|
|
|
|
<< qmljsdebugger;
|
|
|
|
|
}
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-31 12:21:47 +02:00
|
|
|
|
2018-05-07 18:06:30 +02:00
|
|
|
if (!m_extraAppParams.isEmpty()) {
|
2018-07-31 12:21:47 +02:00
|
|
|
QStringList appArgs =
|
|
|
|
|
Utils::QtcProcess::splitArgs(m_extraAppParams, Utils::OsType::OsTypeLinux);
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Using application arguments: " << appArgs;
|
2018-04-17 10:09:35 +02:00
|
|
|
args << "-e" << "extraappparams"
|
2018-07-31 12:21:47 +02:00
|
|
|
<< QString::fromLatin1(appArgs.join(' ').toUtf8().toBase64());
|
2018-04-17 10:09:35 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-07 18:06:30 +02:00
|
|
|
if (m_extraEnvVars.size() > 0) {
|
2018-04-17 10:09:35 +02:00
|
|
|
args << "-e" << "extraenvvars"
|
2018-05-07 18:06:30 +02:00
|
|
|
<< QString::fromLatin1(m_extraEnvVars.toStringList().join('\t')
|
2018-04-17 10:09:35 +02:00
|
|
|
.toUtf8().toBase64());
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-17 09:31:56 +02:00
|
|
|
if (!runAdb(args)) {
|
2019-03-01 17:10:38 +01:00
|
|
|
emit remoteProcessFinished(tr("Failed to start the activity."));
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-02 22:07:22 +02:00
|
|
|
bool AndroidRunnerWorker::startDebuggerServer(const QString &packageDir,
|
|
|
|
|
const QString &gdbServerPrefix,
|
|
|
|
|
const QString &gdbServerExecutable,
|
|
|
|
|
QString *errorStr)
|
2018-08-07 11:12:45 +02:00
|
|
|
{
|
|
|
|
|
QString gdbServerSocket = packageDir + "/debug-socket";
|
|
|
|
|
runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket});
|
|
|
|
|
|
|
|
|
|
QString gdbProcessErr;
|
|
|
|
|
QStringList gdbServerArgs = selector();
|
2018-12-02 22:07:22 +02:00
|
|
|
gdbServerArgs << "shell" << "run-as" << m_packageName << gdbServerPrefix + gdbServerExecutable
|
|
|
|
|
<< "--multi" << "+" + gdbServerSocket;
|
2018-08-07 11:12:45 +02:00
|
|
|
m_gdbServerProcess.reset(AndroidManager::runAdbCommandDetached(gdbServerArgs, &gdbProcessErr));
|
|
|
|
|
|
|
|
|
|
if (!m_gdbServerProcess) {
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Debugger process failed to start" << gdbProcessErr;
|
|
|
|
|
if (errorStr)
|
|
|
|
|
*errorStr = tr("Failed to start debugger server.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Debugger process started";
|
|
|
|
|
m_gdbServerProcess->setObjectName("AndroidDebugServerProcess");
|
|
|
|
|
|
|
|
|
|
QStringList removeForward{"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()};
|
|
|
|
|
runAdb(removeForward);
|
|
|
|
|
if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(),
|
|
|
|
|
"localfilesystem:" + gdbServerSocket})) {
|
|
|
|
|
if (errorStr)
|
|
|
|
|
*errorStr = tr("Failed to forward C++ debugging ports.");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
m_afterFinishAdbCommands.push_back(removeForward.join(' '));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::asyncStart()
|
|
|
|
|
{
|
|
|
|
|
asyncStartHelper();
|
|
|
|
|
|
2018-08-07 11:12:45 +02:00
|
|
|
m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPID, selector(),
|
2018-05-15 12:50:41 +02:00
|
|
|
m_packageName, m_isPreNougat),
|
2018-05-09 12:20:54 +02:00
|
|
|
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidRunnerWorker::asyncStop()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
if (!m_pidFinder.isFinished())
|
|
|
|
|
m_pidFinder.cancel();
|
|
|
|
|
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
forceStop();
|
|
|
|
|
|
|
|
|
|
m_jdbProcess.reset();
|
|
|
|
|
m_gdbServerProcess.reset();
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::handleJdbWaiting()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
QStringList removeForward{"forward", "--remove", "tcp:" + m_localJdbServerPort.toString()};
|
|
|
|
|
runAdb(removeForward);
|
|
|
|
|
if (!runAdb({"forward", "tcp:" + m_localJdbServerPort.toString(),
|
|
|
|
|
"jdwp:" + QString::number(m_processPID)})) {
|
2019-03-01 17:10:38 +01:00
|
|
|
emit remoteProcessFinished(tr("Failed to forward JDB debugging ports."));
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2018-05-15 12:11:54 +02:00
|
|
|
m_afterFinishAdbCommands.push_back(removeForward.join(' '));
|
2018-03-17 09:31:56 +02:00
|
|
|
|
|
|
|
|
auto jdbPath = AndroidConfigurations::currentConfig().openJDKLocation().appendPath("bin");
|
|
|
|
|
if (Utils::HostOsInfo::isWindowsHost())
|
|
|
|
|
jdbPath.appendPath("jdb.exe");
|
|
|
|
|
else
|
|
|
|
|
jdbPath.appendPath("jdb");
|
|
|
|
|
|
2018-06-18 11:49:14 +02:00
|
|
|
QStringList jdbArgs("-connect");
|
|
|
|
|
jdbArgs << QString("com.sun.jdi.SocketAttach:hostname=localhost,port=%1")
|
|
|
|
|
.arg(m_localJdbServerPort.toString());
|
|
|
|
|
qCDebug(androidRunWorkerLog) << "Starting JDB:" << jdbPath << jdbArgs.join(' ');
|
2018-05-08 11:54:32 +02:00
|
|
|
std::unique_ptr<QProcess, Deleter> jdbProcess(new QProcess, &deleter);
|
2018-03-17 09:31:56 +02:00
|
|
|
jdbProcess->setProcessChannelMode(QProcess::MergedChannels);
|
2018-06-18 11:49:14 +02:00
|
|
|
jdbProcess->start(jdbPath.toString(), jdbArgs);
|
2018-03-17 09:31:56 +02:00
|
|
|
if (!jdbProcess->waitForStarted()) {
|
2019-03-01 17:10:38 +01:00
|
|
|
emit remoteProcessFinished(tr("Failed to start JDB."));
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_jdbProcess = std::move(jdbProcess);
|
2018-06-18 11:49:14 +02:00
|
|
|
m_jdbProcess->setObjectName("JdbProcess");
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::handleJdbSettled()
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Handle JDB settled";
|
2018-03-17 09:31:56 +02:00
|
|
|
auto waitForCommand = [&]() {
|
|
|
|
|
for (int i= 0; i < 5 && m_jdbProcess->state() == QProcess::Running; ++i) {
|
|
|
|
|
m_jdbProcess->waitForReadyRead(500);
|
|
|
|
|
QByteArray lines = m_jdbProcess->readAll();
|
|
|
|
|
for (const auto &line: lines.split('\n')) {
|
|
|
|
|
auto msg = line.trimmed();
|
|
|
|
|
if (msg.startsWith(">"))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
if (waitForCommand()) {
|
|
|
|
|
m_jdbProcess->write("cont\n");
|
|
|
|
|
if (m_jdbProcess->waitForBytesWritten(5000) && waitForCommand()) {
|
|
|
|
|
m_jdbProcess->write("exit\n");
|
|
|
|
|
m_jdbProcess->waitForBytesWritten(5000);
|
|
|
|
|
if (!m_jdbProcess->waitForFinished(5000)) {
|
|
|
|
|
m_jdbProcess->terminate();
|
|
|
|
|
if (!m_jdbProcess->waitForFinished(5000)) {
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Killing JDB process";
|
2018-03-17 09:31:56 +02:00
|
|
|
m_jdbProcess->kill();
|
|
|
|
|
m_jdbProcess->waitForFinished();
|
|
|
|
|
}
|
|
|
|
|
} else if (m_jdbProcess->exitStatus() == QProcess::NormalExit && m_jdbProcess->exitCode() == 0) {
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "JDB settled";
|
2018-03-17 09:31:56 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-01 17:10:38 +01:00
|
|
|
emit remoteProcessFinished(tr("Cannot attach JDB to the running application."));
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
|
2018-05-09 12:20:54 +02:00
|
|
|
void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
|
2018-03-17 09:31:56 +02:00
|
|
|
{
|
|
|
|
|
// Don't write to m_psProc from a different thread
|
|
|
|
|
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
2018-06-18 11:49:14 +02:00
|
|
|
qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID
|
|
|
|
|
<< "to:" << pid;
|
2018-03-17 09:31:56 +02:00
|
|
|
m_processPID = pid;
|
|
|
|
|
if (pid == -1) {
|
|
|
|
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
2018-05-15 12:50:41 +02:00
|
|
|
.arg(m_packageName));
|
2018-03-17 09:31:56 +02:00
|
|
|
// App died/killed. Reset log, monitor, jdb & gdb processes.
|
|
|
|
|
m_adbLogcatProcess.reset();
|
|
|
|
|
m_psIsAlive.reset();
|
|
|
|
|
m_jdbProcess.reset();
|
|
|
|
|
m_gdbServerProcess.reset();
|
|
|
|
|
|
|
|
|
|
// Run adb commands after application quit.
|
2018-05-15 12:11:54 +02:00
|
|
|
for (const QString &entry: m_afterFinishAdbCommands)
|
2018-03-17 09:31:56 +02:00
|
|
|
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
|
|
|
|
} else {
|
|
|
|
|
// In debugging cases this will be funneled to the engine to actually start
|
|
|
|
|
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
|
|
|
|
emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID);
|
|
|
|
|
logcatReadStandardOutput();
|
|
|
|
|
QTC_ASSERT(!m_psIsAlive, /**/);
|
2018-08-07 11:12:45 +02:00
|
|
|
QStringList isAliveArgs = selector() << "shell" << pidPollingScript.arg(m_processPID);
|
|
|
|
|
m_psIsAlive.reset(AndroidManager::runAdbCommandDetached(isAliveArgs));
|
|
|
|
|
QTC_ASSERT(m_psIsAlive, return);
|
2018-06-18 11:49:14 +02:00
|
|
|
m_psIsAlive->setObjectName("IsAliveProcess");
|
2018-03-17 09:31:56 +02:00
|
|
|
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
|
2019-02-26 17:48:18 +01:00
|
|
|
connect(m_psIsAlive.get(), QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
2018-05-09 12:20:54 +02:00
|
|
|
this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1));
|
2018-03-17 09:31:56 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
} // namespace Android
|