forked from qt-creator/qt-creator
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:
@@ -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 ®Ex) {
|
QString parseAaptOutput(const QString &output, const QString ®Ex) {
|
||||||
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
|
||||||
|
@@ -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
|
||||||
|
@@ -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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user