2012-04-18 20:30:57 +03:00
|
|
|
/**************************************************************************
|
|
|
|
|
**
|
2014-01-07 13:27:11 +01:00
|
|
|
** Copyright (c) 2014 BogDan Vatra <bog_dan_ro@yahoo.com>
|
2012-10-02 09:12:39 +02:00
|
|
|
** Contact: http://www.qt-project.org/legal
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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 Digia. For licensing terms and
|
|
|
|
|
** conditions see http://qt.digia.com/licensing. For further information
|
|
|
|
|
** use the contact form at http://qt.digia.com/contact-us.
|
2012-04-18 20:30:57 +03:00
|
|
|
**
|
|
|
|
|
** GNU Lesser General Public License Usage
|
2012-10-02 09:12:39 +02:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
|
** General Public License version 2.1 as published by the Free Software
|
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
|
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
|
**
|
|
|
|
|
** In addition, as a special exception, Digia gives you certain additional
|
|
|
|
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
2012-04-18 20:30:57 +03:00
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
#include "androidrunner.h"
|
|
|
|
|
|
2013-09-17 18:24:57 +02:00
|
|
|
#include "androiddeployqtstep.h"
|
2012-04-18 20:30:57 +03:00
|
|
|
#include "androidconfigurations.h"
|
|
|
|
|
#include "androidglobal.h"
|
|
|
|
|
#include "androidrunconfiguration.h"
|
2012-04-24 15:49:09 +02:00
|
|
|
#include "androidmanager.h"
|
|
|
|
|
|
2013-03-27 13:03:15 +01:00
|
|
|
#include <debugger/debuggerrunconfigurationaspect.h>
|
2012-04-24 15:49:09 +02:00
|
|
|
#include <projectexplorer/target.h>
|
2014-08-07 10:10:32 +03:00
|
|
|
#include <qtsupport/qtkitinformation.h>
|
2013-02-27 15:12:36 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2014-08-12 09:06:16 +02:00
|
|
|
#include <QDir>
|
2012-04-18 20:30:57 +03:00
|
|
|
#include <QTime>
|
|
|
|
|
#include <QtConcurrentRun>
|
2013-02-27 15:12:36 +01:00
|
|
|
#include <QTemporaryFile>
|
2013-04-18 10:01:05 +02:00
|
|
|
#include <QTcpServer>
|
2012-04-18 20:30:57 +03:00
|
|
|
|
|
|
|
|
namespace Android {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
typedef QLatin1String _;
|
|
|
|
|
|
2013-05-03 12:41:58 +02:00
|
|
|
AndroidRunner::AndroidRunner(QObject *parent,
|
|
|
|
|
AndroidRunConfiguration *runConfig,
|
|
|
|
|
ProjectExplorer::RunMode runMode)
|
2012-04-18 20:30:57 +03:00
|
|
|
: QThread(parent)
|
|
|
|
|
{
|
2014-02-12 12:24:56 +01:00
|
|
|
m_tries = 0;
|
2013-03-27 13:03:15 +01:00
|
|
|
Debugger::DebuggerRunConfigurationAspect *aspect
|
|
|
|
|
= runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
|
2013-05-03 12:41:58 +02:00
|
|
|
const bool debuggingMode = runMode == ProjectExplorer::DebugRunMode;
|
2013-03-26 17:03:57 +01:00
|
|
|
m_useCppDebugger = debuggingMode && aspect->useCppDebugger();
|
|
|
|
|
m_useQmlDebugger = debuggingMode && aspect->useQmlDebugger();
|
2013-02-27 15:12:36 +01:00
|
|
|
QString channel = runConfig->remoteChannel();
|
|
|
|
|
QTC_CHECK(channel.startsWith(QLatin1Char(':')));
|
|
|
|
|
m_localGdbServerPort = channel.mid(1).toUShort();
|
|
|
|
|
QTC_CHECK(m_localGdbServerPort);
|
2013-05-03 12:41:58 +02:00
|
|
|
m_useQmlProfiler = runMode == ProjectExplorer::QmlProfilerRunMode;
|
|
|
|
|
if (m_useQmlDebugger || m_useQmlProfiler) {
|
2013-04-18 10:01:05 +02:00
|
|
|
QTcpServer server;
|
|
|
|
|
QTC_ASSERT(server.listen(QHostAddress::LocalHost)
|
|
|
|
|
|| server.listen(QHostAddress::LocalHostIPv6),
|
|
|
|
|
qDebug() << tr("No free ports available on host for QML debugging."));
|
|
|
|
|
m_qmlPort = server.serverPort();
|
|
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
ProjectExplorer::Target *target = runConfig->target();
|
2013-09-17 18:24:57 +02:00
|
|
|
m_useLocalQtLibs = AndroidManager::useLocalLibs(target);
|
2013-05-22 17:24:15 +02:00
|
|
|
if (m_useLocalQtLibs) {
|
2013-09-17 18:24:57 +02:00
|
|
|
int deviceApiLevel = AndroidManager::minimumSDK(target);
|
|
|
|
|
m_localLibs = AndroidManager::loadLocalLibs(target, deviceApiLevel);
|
|
|
|
|
m_localJars = AndroidManager::loadLocalJars(target, deviceApiLevel);
|
|
|
|
|
m_localJarsInitClasses = AndroidManager::loadLocalJarsInitClasses(target, deviceApiLevel);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
2012-04-24 15:49:09 +02:00
|
|
|
m_intentName = AndroidManager::intentName(target);
|
2012-04-18 20:30:57 +03:00
|
|
|
m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));
|
2013-09-17 18:24:57 +02:00
|
|
|
|
|
|
|
|
m_deviceSerialNumber = AndroidManager::deviceSerialNumber(target);
|
2012-04-18 20:30:57 +03:00
|
|
|
m_processPID = -1;
|
2013-12-16 20:19:07 +01:00
|
|
|
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
2013-02-27 15:12:36 +01:00
|
|
|
m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber);
|
|
|
|
|
|
|
|
|
|
QString packageDir = _("/data/data/") + m_packageName;
|
|
|
|
|
m_pingFile = packageDir + _("/debug-ping");
|
|
|
|
|
m_pongFile = _("/data/local/tmp/qt/debug-pong-") + m_packageName;
|
|
|
|
|
m_gdbserverSocket = packageDir + _("/debug-socket");
|
2014-08-07 10:10:32 +03:00
|
|
|
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target->kit());
|
|
|
|
|
if (version && version->qtVersion() >= QtSupport::QtVersionNumber(5, 4, 0))
|
2014-08-14 16:07:33 +03:00
|
|
|
m_gdbserverPath = packageDir + _("/lib/libgdbserver.so");
|
|
|
|
|
else
|
|
|
|
|
m_gdbserverPath = packageDir + _("/lib/gdbserver");
|
|
|
|
|
|
2014-08-07 10:10:32 +03:00
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
m_gdbserverCommand = m_gdbserverPath + _(" --multi +") + m_gdbserverSocket;
|
|
|
|
|
// Detect busybox, as we need to pass -w to ps to get wide output.
|
|
|
|
|
QProcess psProc;
|
|
|
|
|
psProc.start(m_adb, selector() << _("shell") << _("readlink") << _("$(which ps)"));
|
|
|
|
|
psProc.waitForFinished();
|
|
|
|
|
QByteArray which = psProc.readAll();
|
|
|
|
|
m_isBusyBox = which.startsWith("busybox");
|
|
|
|
|
|
2013-06-14 15:49:38 +02:00
|
|
|
m_checkPIDTimer.setInterval(1000);
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
|
2013-02-27 15:12:36 +01:00
|
|
|
connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()), SLOT(logcatReadStandardError()));
|
|
|
|
|
connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AndroidRunner::~AndroidRunner()
|
|
|
|
|
{
|
2013-02-27 15:12:36 +01:00
|
|
|
//stop();
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
static int extractPidFromChunk(const QByteArray &chunk, int from)
|
|
|
|
|
{
|
|
|
|
|
int pos1 = chunk.indexOf(' ', from);
|
|
|
|
|
if (pos1 == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
while (chunk[pos1] == ' ')
|
|
|
|
|
++pos1;
|
|
|
|
|
int pos3 = chunk.indexOf(' ', pos1);
|
|
|
|
|
int pid = chunk.mid(pos1, pos3 - pos1).toInt();
|
|
|
|
|
return pid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int extractPid(const QString &exeName, const QByteArray &psOutput)
|
|
|
|
|
{
|
|
|
|
|
const QByteArray needle = exeName.toUtf8() + '\r';
|
|
|
|
|
const int to = psOutput.indexOf(needle);
|
|
|
|
|
if (to == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
const int from = psOutput.lastIndexOf('\n', to);
|
|
|
|
|
if (from == -1)
|
|
|
|
|
return -1;
|
|
|
|
|
return extractPidFromChunk(psOutput, from);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QByteArray AndroidRunner::runPs()
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
|
|
|
|
QProcess psProc;
|
2013-02-27 15:12:36 +01:00
|
|
|
QStringList args = m_selector;
|
|
|
|
|
args << _("shell") << _("ps");
|
|
|
|
|
if (m_isBusyBox)
|
|
|
|
|
args << _("-w");
|
|
|
|
|
|
|
|
|
|
psProc.start(m_adb, args);
|
|
|
|
|
psProc.waitForFinished();
|
|
|
|
|
return psProc.readAll();
|
|
|
|
|
}
|
2012-12-10 23:49:46 +00:00
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
void AndroidRunner::checkPID()
|
|
|
|
|
{
|
|
|
|
|
QByteArray psOut = runPs();
|
|
|
|
|
m_processPID = extractPid(m_packageName, psOut);
|
2014-02-12 12:24:56 +01:00
|
|
|
|
2013-06-14 15:49:38 +02:00
|
|
|
if (m_processPID == -1) {
|
2014-02-12 12:24:56 +01:00
|
|
|
if (m_wasStarted) {
|
|
|
|
|
m_wasStarted = false;
|
|
|
|
|
m_checkPIDTimer.stop();
|
2014-02-28 14:37:46 +01:00
|
|
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.").arg(m_packageName));
|
2014-02-12 12:24:56 +01:00
|
|
|
} else {
|
|
|
|
|
if (++m_tries > 3)
|
2014-03-06 14:35:30 +01:00
|
|
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("Unable to start \"%1\".").arg(m_packageName));
|
2014-02-12 12:24:56 +01:00
|
|
|
}
|
|
|
|
|
} else if (!m_wasStarted){
|
|
|
|
|
if (m_useCppDebugger) {
|
|
|
|
|
// This will be funneled to the engine to actually start and attach
|
|
|
|
|
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
|
|
|
|
QByteArray serverChannel = ':' + QByteArray::number(m_localGdbServerPort);
|
|
|
|
|
emit remoteServerRunning(serverChannel, m_processPID);
|
|
|
|
|
} else if (m_useQmlDebugger) {
|
|
|
|
|
// This will be funneled to the engine to actually start and attach
|
|
|
|
|
// gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
|
|
|
|
QByteArray serverChannel = QByteArray::number(m_qmlPort);
|
|
|
|
|
emit remoteServerRunning(serverChannel, m_processPID);
|
|
|
|
|
} else if (m_useQmlProfiler) {
|
|
|
|
|
emit remoteProcessStarted(m_qmlPort);
|
|
|
|
|
} else {
|
|
|
|
|
// Start without debugging.
|
|
|
|
|
emit remoteProcessStarted(-1, -1);
|
|
|
|
|
}
|
|
|
|
|
m_wasStarted = true;
|
2014-03-13 16:21:36 +01:00
|
|
|
logcatReadStandardOutput();
|
2013-06-14 15:49:38 +02:00
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
}
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
void AndroidRunner::forceStop()
|
|
|
|
|
{
|
|
|
|
|
QProcess proc;
|
2013-10-30 15:39:05 +01:00
|
|
|
proc.start(m_adb, selector() << _("shell") << _("am") << _("force-stop")
|
|
|
|
|
<< m_packageName);
|
2013-02-27 15:12:36 +01:00
|
|
|
proc.waitForFinished();
|
2012-04-18 20:30:57 +03:00
|
|
|
|
2013-10-30 15:39:05 +01:00
|
|
|
// try killing it via kill -9
|
2013-02-27 15:12:36 +01:00
|
|
|
const QByteArray out = runPs();
|
|
|
|
|
int from = 0;
|
|
|
|
|
while (1) {
|
|
|
|
|
const int to = out.indexOf('\n', from);
|
|
|
|
|
if (to == -1)
|
|
|
|
|
break;
|
|
|
|
|
QString line = QString::fromUtf8(out.data() + from, to - from - 1);
|
|
|
|
|
if (line.endsWith(m_packageName) || line.endsWith(m_gdbserverPath)) {
|
|
|
|
|
int pid = extractPidFromChunk(out, from);
|
|
|
|
|
adbKill(pid);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
from = to + 1;
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidRunner::start()
|
|
|
|
|
{
|
2013-02-27 15:12:36 +01:00
|
|
|
m_adbLogcatProcess.start(m_adb, selector() << _("logcat"));
|
|
|
|
|
QtConcurrent::run(this, &AndroidRunner::asyncStart);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidRunner::asyncStart()
|
|
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
2013-02-27 15:12:36 +01:00
|
|
|
forceStop();
|
|
|
|
|
|
2012-06-24 19:32:45 -07:00
|
|
|
if (m_useCppDebugger) {
|
2013-02-27 15:12:36 +01:00
|
|
|
// Remove pong file.
|
|
|
|
|
QProcess adb;
|
|
|
|
|
adb.start(m_adb, selector() << _("shell") << _("rm") << m_pongFile);
|
|
|
|
|
adb.waitForFinished();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QStringList args = selector();
|
|
|
|
|
args << _("shell") << _("am") << _("start") << _("-n") << m_intentName;
|
|
|
|
|
|
|
|
|
|
if (m_useCppDebugger) {
|
|
|
|
|
QProcess adb;
|
|
|
|
|
adb.start(m_adb, selector() << _("forward")
|
|
|
|
|
<< QString::fromLatin1("tcp:%1").arg(m_localGdbServerPort)
|
|
|
|
|
<< _("localfilesystem:") + m_gdbserverSocket);
|
|
|
|
|
if (!adb.waitForStarted()) {
|
|
|
|
|
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports. Reason: %1.").arg(adb.errorString()));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2014-06-23 14:13:37 +02:00
|
|
|
if (!adb.waitForFinished(10000)) {
|
2012-09-28 16:13:16 +02:00
|
|
|
emit remoteProcessFinished(tr("Failed to forward C++ debugging ports."));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
|
|
|
|
|
args << _("-e") << _("debug_ping") << _("true");
|
|
|
|
|
args << _("-e") << _("ping_file") << m_pingFile;
|
|
|
|
|
args << _("-e") << _("pong_file") << m_pongFile;
|
|
|
|
|
args << _("-e") << _("gdbserver_command") << m_gdbserverCommand;
|
|
|
|
|
args << _("-e") << _("gdbserver_socket") << m_gdbserverSocket;
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
2013-05-03 12:41:58 +02:00
|
|
|
|
|
|
|
|
if (m_useQmlDebugger || m_useQmlProfiler) {
|
2013-02-27 15:12:36 +01:00
|
|
|
// currently forward to same port on device and host
|
2013-04-18 10:01:05 +02:00
|
|
|
const QString port = QString::fromLatin1("tcp:%1").arg(m_qmlPort);
|
2013-02-27 15:12:36 +01:00
|
|
|
QProcess adb;
|
|
|
|
|
adb.start(m_adb, selector() << _("forward") << port << port);
|
|
|
|
|
if (!adb.waitForStarted()) {
|
|
|
|
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports. Reason: %1.").arg(adb.errorString()));
|
2012-06-24 19:32:45 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
if (!adb.waitForFinished()) {
|
2012-09-28 16:13:16 +02:00
|
|
|
emit remoteProcessFinished(tr("Failed to forward QML debugging ports."));
|
2012-06-24 19:32:45 -07:00
|
|
|
return;
|
|
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
args << _("-e") << _("qml_debug") << _("true");
|
2013-04-18 10:01:05 +02:00
|
|
|
args << _("-e") << _("qmljsdebugger") << QString::fromLatin1("port:%1,block").arg(m_qmlPort);
|
2012-06-24 19:32:45 -07:00
|
|
|
}
|
2012-04-18 20:30:57 +03:00
|
|
|
if (m_useLocalQtLibs) {
|
2013-02-27 15:12:36 +01:00
|
|
|
args << _("-e") << _("use_local_qt_libs") << _("true");
|
|
|
|
|
args << _("-e") << _("libs_prefix") << _("/data/local/tmp/qt/");
|
|
|
|
|
args << _("-e") << _("load_local_libs") << m_localLibs;
|
|
|
|
|
args << _("-e") << _("load_local_jars") << m_localJars;
|
2013-01-06 16:23:08 +02:00
|
|
|
if (!m_localJarsInitClasses.isEmpty())
|
2013-02-27 15:12:36 +01:00
|
|
|
args << _("-e") << _("static_init_classes") << m_localJarsInitClasses;
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
QProcess adb;
|
|
|
|
|
adb.start(m_adb, args);
|
|
|
|
|
if (!adb.waitForStarted()) {
|
|
|
|
|
emit remoteProcessFinished(tr("Failed to start the activity. Reason: %1.").arg(adb.errorString()));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2014-06-23 14:13:37 +02:00
|
|
|
if (!adb.waitForFinished(10000)) {
|
2013-02-27 15:12:36 +01:00
|
|
|
adb.terminate();
|
2014-04-17 14:09:47 +02:00
|
|
|
emit remoteProcessFinished(tr("Unable to start \"%1\".").arg(m_packageName));
|
2012-04-18 20:30:57 +03:00
|
|
|
return;
|
|
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
|
2013-04-18 10:01:05 +02:00
|
|
|
if (m_useCppDebugger) {
|
2013-02-27 15:12:36 +01:00
|
|
|
|
|
|
|
|
// Handling ping.
|
|
|
|
|
for (int i = 0; ; ++i) {
|
2014-08-12 09:06:16 +02:00
|
|
|
QTemporaryFile tmp(QDir::tempPath() + _("/pingpong"));
|
2013-02-27 15:12:36 +01:00
|
|
|
tmp.open();
|
|
|
|
|
tmp.close();
|
|
|
|
|
|
|
|
|
|
QProcess process;
|
|
|
|
|
process.start(m_adb, selector() << _("pull") << m_pingFile << tmp.fileName());
|
|
|
|
|
process.waitForFinished();
|
|
|
|
|
|
|
|
|
|
QFile res(tmp.fileName());
|
|
|
|
|
const bool doBreak = res.size();
|
|
|
|
|
res.remove();
|
|
|
|
|
if (doBreak)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (i == 20) {
|
2014-04-17 14:09:47 +02:00
|
|
|
emit remoteProcessFinished(tr("Unable to start \"%1\".").arg(m_packageName));
|
2013-02-27 15:12:36 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
qDebug() << "WAITING FOR " << tmp.fileName();
|
|
|
|
|
QThread::msleep(500);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
|
2014-02-12 12:24:56 +01:00
|
|
|
m_tries = 0;
|
|
|
|
|
m_wasStarted = false;
|
2013-06-14 15:49:38 +02:00
|
|
|
QMetaObject::invokeMethod(&m_checkPIDTimer, "start");
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2013-04-18 10:01:05 +02:00
|
|
|
void AndroidRunner::handleRemoteDebuggerRunning()
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
2013-04-18 10:01:05 +02:00
|
|
|
if (m_useCppDebugger) {
|
2014-08-12 09:06:16 +02:00
|
|
|
QTemporaryFile tmp(QDir::tempPath() + _("/pingpong"));
|
2013-04-18 10:01:05 +02:00
|
|
|
tmp.open();
|
2013-02-27 15:12:36 +01:00
|
|
|
|
2013-04-18 10:01:05 +02:00
|
|
|
QProcess process;
|
|
|
|
|
process.start(m_adb, selector() << _("push") << tmp.fileName() << m_pongFile);
|
|
|
|
|
process.waitForFinished();
|
2013-02-27 15:12:36 +01:00
|
|
|
|
2013-04-18 10:01:05 +02:00
|
|
|
QTC_CHECK(m_processPID != -1);
|
|
|
|
|
}
|
|
|
|
|
emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2012-12-18 14:25:07 +02:00
|
|
|
void AndroidRunner::stop()
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
|
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
m_checkPIDTimer.stop();
|
2014-02-12 12:24:56 +01:00
|
|
|
m_tries = 0;
|
2013-02-27 15:12:36 +01:00
|
|
|
if (m_processPID != -1) {
|
2013-10-30 15:39:05 +01:00
|
|
|
forceStop();
|
2014-02-28 14:37:46 +01:00
|
|
|
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" terminated.").arg(m_packageName));
|
2012-12-18 14:25:07 +02:00
|
|
|
}
|
2013-02-27 15:12:36 +01:00
|
|
|
//QObject::disconnect(&m_adbLogcatProcess, 0, this, 0);
|
2012-12-18 14:25:07 +02:00
|
|
|
m_adbLogcatProcess.kill();
|
2013-02-27 15:12:36 +01:00
|
|
|
m_adbLogcatProcess.waitForFinished();
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
2014-03-13 17:33:47 +01:00
|
|
|
void AndroidRunner::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
2014-03-13 17:33:47 +01:00
|
|
|
QList<QByteArray> lines = text.split('\n');
|
2014-02-13 12:55:19 +01:00
|
|
|
// lines always contains at least one item
|
2014-03-13 17:33:47 +01:00
|
|
|
lines[0].prepend(buffer);
|
2014-02-13 12:55:19 +01:00
|
|
|
if (!lines.last().endsWith('\n')) {
|
|
|
|
|
// incomplete line
|
2014-03-13 17:33:47 +01:00
|
|
|
buffer = lines.last();
|
2014-02-13 12:55:19 +01:00
|
|
|
lines.removeLast();
|
|
|
|
|
} else {
|
2014-03-13 17:33:47 +01:00
|
|
|
buffer.clear();
|
2014-02-13 12:55:19 +01:00
|
|
|
}
|
|
|
|
|
|
2012-09-21 13:54:38 +02:00
|
|
|
QByteArray pid(QString::fromLatin1("%1):").arg(m_processPID).toLatin1());
|
2014-02-13 12:55:19 +01:00
|
|
|
foreach (QByteArray line, lines) {
|
2012-04-18 20:30:57 +03:00
|
|
|
if (!line.contains(pid))
|
|
|
|
|
continue;
|
2012-06-24 19:32:45 -07:00
|
|
|
if (line.endsWith('\r'))
|
|
|
|
|
line.chop(1);
|
|
|
|
|
line.append('\n');
|
2014-03-14 11:44:31 +01:00
|
|
|
if (onlyError || line.startsWith("F/")
|
|
|
|
|
|| line.startsWith("E/")
|
2013-10-30 13:37:10 +01:00
|
|
|
|| line.startsWith("D/Qt")
|
|
|
|
|
|| line.startsWith("W/"))
|
2012-04-18 20:30:57 +03:00
|
|
|
emit remoteErrorOutput(line);
|
|
|
|
|
else
|
|
|
|
|
emit remoteOutput(line);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-13 17:33:47 +01:00
|
|
|
void AndroidRunner::logcatReadStandardError()
|
|
|
|
|
{
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
logcatProcess(m_adbLogcatProcess.readAllStandardError(), m_stderrBuffer, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidRunner::logcatReadStandardOutput()
|
|
|
|
|
{
|
|
|
|
|
if (m_processPID != -1)
|
|
|
|
|
logcatProcess(m_adbLogcatProcess.readAllStandardOutput(), m_stdoutBuffer, false);
|
|
|
|
|
}
|
|
|
|
|
|
2013-02-27 15:12:36 +01:00
|
|
|
void AndroidRunner::adbKill(qint64 pid)
|
2012-04-18 20:30:57 +03:00
|
|
|
{
|
2013-02-27 15:12:36 +01:00
|
|
|
{
|
|
|
|
|
QProcess process;
|
|
|
|
|
process.start(m_adb, selector() << _("shell")
|
|
|
|
|
<< _("kill") << QLatin1String("-9") << QString::number(pid));
|
|
|
|
|
process.waitForFinished();
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
QProcess process;
|
|
|
|
|
process.start(m_adb, selector() << _("shell")
|
|
|
|
|
<< _("run-as") << m_packageName
|
|
|
|
|
<< _("kill") << QLatin1String("-9") << QString::number(pid));
|
|
|
|
|
process.waitForFinished();
|
|
|
|
|
}
|
2012-04-18 20:30:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString AndroidRunner::displayName() const
|
|
|
|
|
{
|
|
|
|
|
return m_packageName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
2013-10-16 11:02:37 +02:00
|
|
|
} // namespace Android
|