Android: Remove multiple code paths to run adb

Refactor the code to use adb from AndroidManager, do cleanup
and improve logging

Change-Id: I77b682d37c9328e6aa978eaf05b3b5c131907f09
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Vikas Pachdha
2018-08-07 11:12:45 +02:00
parent d905131351
commit 8c95ab7ac8
4 changed files with 174 additions and 141 deletions

View File

@@ -79,18 +79,6 @@ namespace {
Q_LOGGING_CATEGORY(androidManagerLog, "qtc.android.androidManager") Q_LOGGING_CATEGORY(androidManagerLog, "qtc.android.androidManager")
bool runCommand(const QString &executable, const QStringList &args,
QString *output = nullptr, int timeoutS = 30)
{
Utils::SynchronousProcess cmdProc;
cmdProc.setTimeoutS(timeoutS);
qCDebug(androidManagerLog) << executable << args.join(' ');
Utils::SynchronousProcessResponse response = cmdProc.runBlocking(executable, args);
if (output)
*output = response.allOutput();
return response.result == Utils::SynchronousProcessResponse::Finished;
}
QString parseAaptOutput(const QString &output, const QString &regEx) { QString parseAaptOutput(const QString &output, const QString &regEx) {
const QRegularExpression regRx(regEx, const QRegularExpression regRx(regEx,
QRegularExpression::CaseInsensitiveOption | QRegularExpression::CaseInsensitiveOption |
@@ -162,9 +150,7 @@ bool AndroidManager::packageInstalled(const QString &deviceSerial,
return false; return false;
QStringList args = AndroidDeviceInfo::adbSelector(deviceSerial); QStringList args = AndroidDeviceInfo::adbSelector(deviceSerial);
args << "shell" << "pm" << "list" << "packages"; args << "shell" << "pm" << "list" << "packages";
QString output; QStringList lines = runAdbCommand(args).stdOut().split(QRegularExpression("[\\n\\r]"),
runAdbCommand(args, &output);
QStringList lines = output.split(QRegularExpression("[\\n\\r]"),
QString::SkipEmptyParts); QString::SkipEmptyParts);
for (const QString &line : lines) { for (const QString &line : lines) {
// Don't want to confuse com.abc.xyz with com.abc.xyz.def so check with // Don't want to confuse com.abc.xyz with com.abc.xyz.def so check with
@@ -180,24 +166,24 @@ void AndroidManager::apkInfo(const Utils::FileName &apkPath,
QVersionNumber *version, QVersionNumber *version,
QString *activityPath) QString *activityPath)
{ {
QString output; SdkToolResult result;
runAaptCommand({"dump", "badging", apkPath.toString()}, &output); result = runAaptCommand({"dump", "badging", apkPath.toString()});
QString packageStr; QString packageStr;
if (activityPath) { if (activityPath) {
packageStr = parseAaptOutput(output, packageNameRegEx); packageStr = parseAaptOutput(result.stdOut(), packageNameRegEx);
QString path = parseAaptOutput(output, activityRegEx); QString path = parseAaptOutput(result.stdOut(), activityRegEx);
if (!packageStr.isEmpty() && !path.isEmpty()) if (!packageStr.isEmpty() && !path.isEmpty())
*activityPath = packageStr + '/' + path; *activityPath = packageStr + '/' + path;
} }
if (packageName) { if (packageName) {
*packageName = activityPath ? packageStr : *packageName = activityPath ? packageStr :
parseAaptOutput(output, packageNameRegEx); parseAaptOutput(result.stdOut(), packageNameRegEx);
} }
if (version) { if (version) {
QString versionStr = parseAaptOutput(output, apkVersionRegEx); QString versionStr = parseAaptOutput(result.stdOut(), apkVersionRegEx);
*version = QVersionNumber::fromString(versionStr); *version = QVersionNumber::fromString(versionStr);
} }
} }
@@ -477,8 +463,11 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
} }
QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
arguments << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt"); arguments << "shell" << "rm" << "-r" << "/data/local/tmp/qt";
runAdbCommandDetached(arguments);
QString error;
if (!runAdbCommandDetached(arguments, &error))
Core::MessageManager::write(tr("Cleaning Qt libraries on device failed.\n%1").arg(error));
} }
void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath) void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath)
@@ -499,8 +488,10 @@ void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const Q
} }
QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber);
arguments << QLatin1String("install") << QLatin1String("-r ") << packagePath; arguments << "install" << "-r " << packagePath;
runAdbCommandDetached(arguments); QString error;
if (!runAdbCommandDetached(arguments, &error, true))
Core::MessageManager::write(tr("Android package installation failed.\n%1").arg(error));
} }
bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd) bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd)
@@ -683,27 +674,57 @@ int AndroidManager::findApiLevel(const Utils::FileName &platformPath)
return apiLevel; return apiLevel;
} }
void AndroidManager::runAdbCommandDetached(const QStringList &args) QProcess *AndroidManager::runAdbCommandDetached(const QStringList &args, QString *err,
bool deleteOnFinish)
{ {
auto process = new QProcess(); std::unique_ptr<QProcess> p(new QProcess);
connect(process, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
process, &QObject::deleteLater);
const QString adb = AndroidConfigurations::currentConfig().adbToolPath().toString(); const QString adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
qCDebug(androidManagerLog) << adb << args.join(' '); qCDebug(androidManagerLog) << "Running command:" << adb << args.join(' ');
process->start(adb, args); p->start(adb, args);
if (!process->waitForStarted(500) && process->state() != QProcess::Running) if (p->waitForStarted(500) && p->state() == QProcess::Running) {
delete process; if (deleteOnFinish) {
connect(p.get(), static_cast<void (QProcess::*)(int)>(&QProcess::finished),
p.get(), &QObject::deleteLater);
}
return p.release();
}
QString errorStr = QString::fromUtf8(p->readAllStandardError());
qCDebug(androidManagerLog) << "Running command failed" << adb << args.join(' ') << errorStr;
if (err)
*err = errorStr;
return nullptr;
} }
bool AndroidManager::runAdbCommand(const QStringList &args, QString *output) SdkToolResult AndroidManager::runCommand(const QString &executable, const QStringList &args,
const QByteArray &writeData, int timeoutS)
{ {
return runCommand(AndroidConfigurations::currentConfig().adbToolPath().toString(), Android::SdkToolResult cmdResult;
args, output); Utils::SynchronousProcess cmdProc;
cmdProc.setTimeoutS(timeoutS);
qCDebug(androidManagerLog) << "Running command: " << executable << args.join(' ');
Utils::SynchronousProcessResponse response = cmdProc.run(executable, args, writeData);
cmdResult.m_stdOut = response.stdOut().trimmed();
cmdResult.m_stdErr = response.stdErr().trimmed();
cmdResult.m_success = response.result == Utils::SynchronousProcessResponse::Finished;
qCDebug(androidManagerLog) << "Running command finshed:" << executable << args.join(' ')
<< "Success:" << cmdResult.m_success
<< "Output:" << response.allRawOutput();
if (!cmdResult.success())
cmdResult.m_exitMessage = response.exitMessage(executable, timeoutS);
return cmdResult;
} }
bool AndroidManager::runAaptCommand(const QStringList &args, QString *output) SdkToolResult AndroidManager::runAdbCommand(const QStringList &args,
const QByteArray &writeData, int timeoutS)
{ {
return runCommand(AndroidConfigurations::currentConfig().aaptToolPath().toString(), return runCommand(AndroidConfigurations::currentConfig().adbToolPath().toString(), args,
args, output); writeData, timeoutS);
}
SdkToolResult AndroidManager::runAaptCommand(const QStringList &args, int timeoutS)
{
return runCommand(AndroidConfigurations::currentConfig().aaptToolPath().toString(), args, {},
timeoutS);
} }
} // namespace Android } // namespace Android

View File

@@ -31,6 +31,10 @@
#include <QObject> #include <QObject>
#include <QVersionNumber> #include <QVersionNumber>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
namespace ProjectExplorer { namespace ProjectExplorer {
class Kit; class Kit;
class Target; class Target;
@@ -42,6 +46,22 @@ namespace Android {
class AndroidQtSupport; class AndroidQtSupport;
class SdkToolResult {
public:
SdkToolResult() = default;
bool success() const { return m_success; }
const QString &stdOut() { return m_stdOut; }
const QString &stdErr() { return m_stdErr; }
const QString &exitMessage() { return m_exitMessage; }
private:
bool m_success = false;
QString m_stdOut;
QString m_stdErr;
QString m_exitMessage;
friend class AndroidManager;
};
class ANDROID_EXPORT AndroidManager : public QObject class ANDROID_EXPORT AndroidManager : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -94,9 +114,15 @@ public:
static bool updateGradleProperties(ProjectExplorer::Target *target); static bool updateGradleProperties(ProjectExplorer::Target *target);
static int findApiLevel(const Utils::FileName &platformPath); static int findApiLevel(const Utils::FileName &platformPath);
static void runAdbCommandDetached(const QStringList &args); static QProcess *runAdbCommandDetached(const QStringList &args, QString *err = nullptr,
static bool runAdbCommand(const QStringList &args, QString *output = nullptr); bool deleteOnFinish = false);
static bool runAaptCommand(const QStringList &args, QString *output = nullptr); static SdkToolResult runAdbCommand(const QStringList &args, const QByteArray &writeData = {},
int timeoutS = 30);
static SdkToolResult runAaptCommand(const QStringList &args, int timeoutS = 30);
private:
static SdkToolResult runCommand(const QString &executable, const QStringList &args,
const QByteArray &writeData = {}, int timeoutS = 30);
}; };
} // namespace Android } // namespace Android

View File

@@ -94,14 +94,14 @@ static bool isTimedOut(const chrono::high_resolution_clock::time_point &start,
return timedOut; return timedOut;
} }
static qint64 extractPID(const QByteArray &output, const QString &packageName) static qint64 extractPID(const QString &output, const QString &packageName)
{ {
qint64 pid = -1; qint64 pid = -1;
foreach (auto tuple, output.split('\n')) { foreach (auto tuple, output.split('\n')) {
tuple = tuple.simplified(); tuple = tuple.simplified();
if (!tuple.isEmpty()) { if (!tuple.isEmpty()) {
auto parts = tuple.split(':'); auto parts = tuple.split(':');
QString commandName = QString::fromLocal8Bit(parts.first()); QString commandName = parts.first();
if (parts.length() == 2 && commandName == packageName) { if (parts.length() == 2 && commandName == packageName) {
pid = parts.last().toLongLong(); pid = parts.last().toLongLong();
break; break;
@@ -111,9 +111,8 @@ static qint64 extractPID(const QByteArray &output, const QString &packageName)
return pid; return pid;
} }
static void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath, static void findProcessPID(QFutureInterface<qint64> &fi, QStringList selector,
QStringList selector, const QString &packageName, const QString &packageName, bool preNougat)
bool preNougat)
{ {
qCDebug(androidRunWorkerLog) << "Finding PID. PreNougat:" << preNougat; qCDebug(androidRunWorkerLog) << "Finding PID. PreNougat:" << preNougat;
if (packageName.isEmpty()) if (packageName.isEmpty())
@@ -127,12 +126,12 @@ static void findProcessPID(QFutureInterface<qint64> &fi, const QString &adbPath,
do { do {
QThread::msleep(200); QThread::msleep(200);
const QByteArray out = Utils::SynchronousProcess().runBlocking(adbPath, selector).allRawOutput(); SdkToolResult result = AndroidManager::runAdbCommand(selector);
if (preNougat) { if (preNougat) {
processPID = extractPID(out, packageName); processPID = extractPID(result.stdOut(), packageName);
} else { } else {
if (!out.isEmpty()) if (!result.stdOut().isEmpty())
processPID = out.trimmed().toLongLong(); processPID = result.stdOut().trimmed().toLongLong();
} }
} while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled());
@@ -188,7 +187,6 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa
m_qmlServer.setPort(server.serverPort()); m_qmlServer.setPort(server.serverPort());
qCDebug(androidRunWorkerLog) << "QML server:" << m_qmlServer.toDisplayString(); qCDebug(androidRunWorkerLog) << "QML server:" << m_qmlServer.toDisplayString();
} }
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
m_localJdbServerPort = Utils::Port(5038); m_localJdbServerPort = Utils::Port(5038);
QTC_CHECK(m_localJdbServerPort.isValid()); QTC_CHECK(m_localJdbServerPort.isValid());
@@ -236,40 +234,16 @@ AndroidRunnerWorker::~AndroidRunnerWorker()
m_pidFinder.cancel(); m_pidFinder.cancel();
} }
bool AndroidRunnerWorker::adbShellAmNeedsQuotes() bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *stdOut,
{ const QByteArray &writeData)
// Between Android SDK Tools version 24.3.1 and 24.3.4 the quoting
// needs for the 'adb shell am start ...' parameters changed.
// Run a test to find out on what side of the fence we live.
// The command will fail with a complaint about the "--dummy"
// option on newer SDKs, and with "No intent supplied" on older ones.
// In case the test itself fails assume a new SDK.
Utils::SynchronousProcess adb;
adb.setTimeoutS(10);
Utils::SynchronousProcessResponse response
= adb.run(m_adb, selector() << "shell" << "am" << "start"
<< "-e" << "dummy" << "dummy --dummy");
if (response.result == Utils::SynchronousProcessResponse::StartFailed
|| response.result != Utils::SynchronousProcessResponse::Finished)
return true;
const QString output = response.allOutput();
const bool oldSdk = output.contains("Error: No intent supplied");
return !oldSdk;
}
bool AndroidRunnerWorker::runAdb(const QStringList &args, int timeoutS, const QByteArray &writeData)
{ {
QStringList adbArgs = selector() + args; QStringList adbArgs = selector() + args;
qCDebug(androidRunWorkerLog) << "ADB command: " << m_adb << adbArgs.join(' '); SdkToolResult result = AndroidManager::runAdbCommand(adbArgs, writeData);
Utils::SynchronousProcess adb; if (!result.success())
adb.setTimeoutS(timeoutS); emit remoteErrorOutput(result.exitMessage() + "\n" + result.stdErr());
Utils::SynchronousProcessResponse response = adb.run(m_adb, adbArgs, writeData); if (stdOut)
m_lastRunAdbError = response.exitMessage(m_adb, timeoutS); *stdOut = result.stdOut();
m_lastRunAdbRawOutput = response.allRawOutput(); return result.success();
bool success = response.result == Utils::SynchronousProcessResponse::Finished;
qCDebug(androidRunWorkerLog) << "ADB command result:" << success << response.allRawOutput();
return success;
} }
bool AndroidRunnerWorker::uploadFile(const QString &from, const QString &to, const QString &flags) bool AndroidRunnerWorker::uploadFile(const QString &from, const QString &to, const QString &flags)
@@ -278,7 +252,8 @@ bool AndroidRunnerWorker::uploadFile(const QString &from, const QString &to, con
if (!f.open(QIODevice::ReadOnly)) if (!f.open(QIODevice::ReadOnly))
return false; return false;
runAdb({"shell", "run-as", m_packageName, "rm", to}); runAdb({"shell", "run-as", m_packageName, "rm", to});
auto res = runAdb({"shell", "run-as", m_packageName, "sh", "-c", QString("'cat > %1'").arg(to)}, 60, f.readAll()); auto res = runAdb({"shell", "run-as", m_packageName, "sh", "-c", QString("'cat > %1'").arg(to)},
nullptr, f.readAll());
if (!res) if (!res)
return false; return false;
return runAdb({"shell", "run-as", m_packageName, "chmod", flags, to}); return runAdb({"shell", "run-as", m_packageName, "chmod", flags, to});
@@ -297,17 +272,14 @@ QStringList AndroidRunnerWorker::selector() const
void AndroidRunnerWorker::forceStop() void AndroidRunnerWorker::forceStop()
{ {
runAdb({"shell", "am", "force-stop", m_packageName}, 30); runAdb({"shell", "am", "force-stop", m_packageName});
// try killing it via kill -9 // try killing it via kill -9
const QByteArray out = Utils::SynchronousProcess() QString out;
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScriptPreNougat) runAdb({"shell", pidScriptPreNougat}, &out);
.allRawOutput();
qint64 pid = extractPID(out.simplified(), m_packageName); qint64 pid = extractPID(out.simplified(), m_packageName);
if (pid != -1) { if (pid != -1)
adbKill(pid); adbKill(pid);
}
} }
void AndroidRunnerWorker::logcatReadStandardError() void AndroidRunnerWorker::logcatReadStandardError()
@@ -395,17 +367,18 @@ void AndroidRunnerWorker::asyncStartHelper()
{ {
forceStop(); forceStop();
// Start the logcat process before app starts.
std::unique_ptr<QProcess, Deleter> logcatProcess(new QProcess, deleter);
connect(logcatProcess.get(), &QProcess::readyReadStandardOutput,
this, &AndroidRunnerWorker::logcatReadStandardOutput);
connect(logcatProcess.get(), &QProcess::readyReadStandardError,
this, &AndroidRunnerWorker::logcatReadStandardError);
// Its assumed that the device or avd returned by selector() is online. // Its assumed that the device or avd returned by selector() is online.
logcatProcess->start(m_adb, selector() << "logcat"); // Start the logcat process before app starts.
QTC_ASSERT(!m_adbLogcatProcess, /**/); QTC_ASSERT(!m_adbLogcatProcess, /**/);
m_adbLogcatProcess = std::move(logcatProcess); m_adbLogcatProcess.reset(AndroidManager::runAdbCommandDetached(selector() << "logcat"));
if (m_adbLogcatProcess) {
m_adbLogcatProcess->setObjectName("AdbLogcatProcess"); m_adbLogcatProcess->setObjectName("AdbLogcatProcess");
connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardOutput,
this, &AndroidRunnerWorker::logcatReadStandardOutput);
connect(m_adbLogcatProcess.get(), &QProcess::readyReadStandardError,
this, &AndroidRunnerWorker::logcatReadStandardError);
}
for (const QString &entry : m_beforeStartAdbCommands) for (const QString &entry : m_beforeStartAdbCommands)
runAdb(entry.split(' ', QString::SkipEmptyParts)); runAdb(entry.split(' ', QString::SkipEmptyParts));
@@ -415,42 +388,27 @@ void AndroidRunnerWorker::asyncStartHelper()
if (m_useCppDebugger) { if (m_useCppDebugger) {
args << "-D"; args << "-D";
// run-as <package-name> pwd fails on API 22 so route the pwd through shell. // run-as <package-name> pwd fails on API 22 so route the pwd through shell.
if (!runAdb({"shell", "run-as", m_packageName, "/system/bin/sh", "-c", "pwd"})) { QString packageDir;
emit remoteProcessFinished(tr("Failed to get process path. Reason: %1.").arg(m_lastRunAdbError)); if (!runAdb({"shell", "run-as", m_packageName, "/system/bin/sh", "-c", "pwd"},
&packageDir)) {
emit remoteProcessFinished(tr("Failed to find application directory."));
return; return;
} }
QString packageDir = QString::fromUtf8(m_lastRunAdbRawOutput.trimmed());
// 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
// e.g. on Android 8 with NDK 10e // e.g. on Android 8 with NDK 10e
runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir}); runAdb({"shell", "run-as", m_packageName, "chmod", "a+x", packageDir.trimmed()});
if (m_gdbserverPath.isEmpty() || !uploadFile(m_gdbserverPath, "gdbserver")) { if (m_gdbserverPath.isEmpty() || !uploadFile(m_gdbserverPath, "gdbserver")) {
emit remoteProcessFinished(tr("Can not find/copy C++ debug server.")); emit remoteProcessFinished(tr("Can not find/copy C++ debug server."));
return; return;
} }
QString gdbServerSocket = packageDir + "/debug-socket"; QString debuggerServerErr;
runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"}); if (!startDebuggerServer(packageDir, &debuggerServerErr)) {
runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket}); emit remoteProcessFinished(debuggerServerErr);
std::unique_ptr<QProcess, Deleter> gdbServerProcess(new QProcess, deleter);
gdbServerProcess->start(m_adb, selector() << "shell" << "run-as"
<< m_packageName << "./gdbserver"
<< "--multi" << "+" + gdbServerSocket);
if (!gdbServerProcess->waitForStarted()) {
emit remoteProcessFinished(tr("Failed to start C++ debugger."));
return; return;
} }
m_gdbServerProcess = std::move(gdbServerProcess);
m_gdbServerProcess->setObjectName("GdbServerProcess");
QStringList removeForward{"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()};
runAdb(removeForward);
if (!runAdb({"forward", "tcp:" + m_localGdbServerPort.toString(),
"localfilesystem:" + gdbServerSocket})) {
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(m_lastRunAdbError));
return;
}
m_afterFinishAdbCommands.push_back(removeForward.join(' '));
} }
if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) { if (m_qmlDebugServices != QmlDebug::NoQmlDebugServices) {
@@ -459,8 +417,7 @@ void AndroidRunnerWorker::asyncStartHelper()
QStringList removeForward{{"forward", "--remove", port}}; QStringList removeForward{{"forward", "--remove", port}};
runAdb(removeForward); runAdb(removeForward);
if (!runAdb({"forward", port, port})) { if (!runAdb({"forward", port, port})) {
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.") emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
.arg(m_lastRunAdbError) + "\n" + m_lastRunAdbRawOutput);
return; return;
} }
m_afterFinishAdbCommands.push_back(removeForward.join(' ')); m_afterFinishAdbCommands.push_back(removeForward.join(' '));
@@ -487,17 +444,49 @@ void AndroidRunnerWorker::asyncStartHelper()
} }
if (!runAdb(args)) { if (!runAdb(args)) {
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.") emit remoteProcessFinished(tr("Failed to start the activity"));
.arg(m_lastRunAdbError));
return; return;
} }
} }
bool AndroidRunnerWorker::startDebuggerServer(QString packageDir, QString *errorStr)
{
QString gdbServerSocket = packageDir + "/debug-socket";
runAdb({"shell", "run-as", m_packageName, "killall", "gdbserver"});
runAdb({"shell", "run-as", m_packageName, "rm", gdbServerSocket});
QString gdbProcessErr;
QStringList gdbServerArgs = selector();
gdbServerArgs << "shell" << "run-as" << m_packageName << "./gdbserver" << "--multi"
<< "+" + gdbServerSocket;
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;
}
void AndroidRunnerWorker::asyncStart() void AndroidRunnerWorker::asyncStart()
{ {
asyncStartHelper(); asyncStartHelper();
m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPID, m_adb, selector(), m_pidFinder = Utils::onResultReady(Utils::runAsync(findProcessPID, selector(),
m_packageName, m_isPreNougat), m_packageName, m_isPreNougat),
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1)); bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
} }
@@ -520,7 +509,7 @@ void AndroidRunnerWorker::handleJdbWaiting()
runAdb(removeForward); runAdb(removeForward);
if (!runAdb({"forward", "tcp:" + m_localJdbServerPort.toString(), if (!runAdb({"forward", "tcp:" + m_localJdbServerPort.toString(),
"jdwp:" + QString::number(m_processPID)})) { "jdwp:" + QString::number(m_processPID)})) {
emit remoteProcessFinished(tr("Failed to forward jdb debugging ports. Reason: %1.").arg(m_lastRunAdbError)); emit remoteProcessFinished(tr("Failed to forward jdb debugging ports."));
return; return;
} }
m_afterFinishAdbCommands.push_back(removeForward.join(' ')); m_afterFinishAdbCommands.push_back(removeForward.join(' '));
@@ -579,8 +568,7 @@ void AndroidRunnerWorker::handleJdbSettled()
} }
} }
} }
emit remoteProcessFinished(tr("Cannot attach jdb to the running application. Reason: %1.") emit remoteProcessFinished(tr("Cannot attach jdb to the running application"));
.arg(m_lastRunAdbError));
} }
void AndroidRunnerWorker::onProcessIdChanged(qint64 pid) void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
@@ -608,13 +596,13 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID); emit remoteProcessStarted(m_localGdbServerPort, m_qmlServer, m_processPID);
logcatReadStandardOutput(); logcatReadStandardOutput();
QTC_ASSERT(!m_psIsAlive, /**/); QTC_ASSERT(!m_psIsAlive, /**/);
m_psIsAlive.reset(new QProcess); QStringList isAliveArgs = selector() << "shell" << pidPollingScript.arg(m_processPID);
m_psIsAlive.reset(AndroidManager::runAdbCommandDetached(isAliveArgs));
QTC_ASSERT(m_psIsAlive, return);
m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setObjectName("IsAliveProcess");
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
connect(m_psIsAlive.get(), static_cast<void(QProcess::*)(int)>(&QProcess::finished), connect(m_psIsAlive.get(), static_cast<void(QProcess::*)(int)>(&QProcess::finished),
this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1)); this, bind(&AndroidRunnerWorker::onProcessIdChanged, this, -1));
m_psIsAlive->start(m_adb, selector() << QStringLiteral("shell")
<< pidPollingScript.arg(m_processPID));
} }
} }

View File

@@ -46,9 +46,9 @@ class AndroidRunnerWorker : public QObject
public: public:
AndroidRunnerWorker(ProjectExplorer::RunWorker *runner, const QString &packageName); AndroidRunnerWorker(ProjectExplorer::RunWorker *runner, const QString &packageName);
~AndroidRunnerWorker() override; ~AndroidRunnerWorker() override;
bool adbShellAmNeedsQuotes();
bool runAdb(const QStringList &args, int timeoutS = 10, const QByteArray &writeData = {});
bool uploadFile(const QString &from, const QString &to, const QString &flags = QString("+x")); bool uploadFile(const QString &from, const QString &to, const QString &flags = QString("+x"));
bool runAdb(const QStringList &args, QString *stdOut = nullptr, const QByteArray &writeData = {});
void adbKill(qint64 pid); void adbKill(qint64 pid);
QStringList selector() const; QStringList selector() const;
void forceStop(); void forceStop();
@@ -73,6 +73,7 @@ signals:
protected: protected:
void asyncStartHelper(); void asyncStartHelper();
bool startDebuggerServer(QString packageDir, QString *errorStr = nullptr);
enum class JDBState { enum class JDBState {
Idle, Idle,
@@ -88,7 +89,6 @@ protected:
QString m_intentName; QString m_intentName;
QStringList m_beforeStartAdbCommands; QStringList m_beforeStartAdbCommands;
QStringList m_afterFinishAdbCommands; QStringList m_afterFinishAdbCommands;
QString m_adb;
QStringList m_amStartExtraArgs; QStringList m_amStartExtraArgs;
qint64 m_processPID = -1; qint64 m_processPID = -1;
std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess; std::unique_ptr<QProcess, Deleter> m_adbLogcatProcess;
@@ -101,8 +101,6 @@ protected:
QmlDebug::QmlDebugServicesPreset m_qmlDebugServices; QmlDebug::QmlDebugServicesPreset m_qmlDebugServices;
Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket. Utils::Port m_localGdbServerPort; // Local end of forwarded debug socket.
QUrl m_qmlServer; QUrl m_qmlServer;
QByteArray m_lastRunAdbRawOutput;
QString m_lastRunAdbError;
JDBState m_jdbState = JDBState::Idle; JDBState m_jdbState = JDBState::Idle;
Utils::Port m_localJdbServerPort; Utils::Port m_localJdbServerPort;
std::unique_ptr<QProcess, Deleter> m_gdbServerProcess; std::unique_ptr<QProcess, Deleter> m_gdbServerProcess;