summaryrefslogtreecommitdiff
path: root/src/plugins/android/androidrunner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/android/androidrunner.cpp')
-rw-r--r--src/plugins/android/androidrunner.cpp281
1 files changed, 281 insertions, 0 deletions
diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp
new file mode 100644
index 0000000000..4b30bee178
--- /dev/null
+++ b/src/plugins/android/androidrunner.cpp
@@ -0,0 +1,281 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2012 BogDan Vatra <bog_dan_ro@yahoo.com>
+**
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**************************************************************************/
+
+#include "androidrunner.h"
+
+#include "androiddeploystep.h"
+#include "androidconfigurations.h"
+#include "androidglobal.h"
+#include "androidrunconfiguration.h"
+#include "androidtarget.h"
+
+#include <QTime>
+#include <QtConcurrentRun>
+
+namespace Android {
+namespace Internal {
+
+AndroidRunner::AndroidRunner(QObject *parent,
+ AndroidRunConfiguration *runConfig, bool debugging)
+ : QThread(parent)
+{
+ m_remoteChannel = runConfig->remoteChannel();
+ AndroidTarget * at = runConfig->androidTarget();
+ AndroidDeployStep * ds = runConfig->deployStep();
+ if ((m_useLocalQtLibs = ds->useLocalQtLibs())) {
+ m_localLibs = at->loadLocalLibs(ds->deviceAPILevel());
+ m_localJars = at->loadLocalJars(ds->deviceAPILevel());
+ }
+ m_intentName = at->intentName();
+ m_debugingMode = debugging;
+ m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/')));
+ m_deviceSerialNumber = ds->deviceSerialNumber();
+ m_processPID = -1;
+ m_gdbserverPID = -1;
+ connect(&m_checkPIDTimer, SIGNAL(timeout()), SLOT(checkPID()));
+ connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardOutput()), SLOT(logcatReadStandardOutput()));
+ connect(&m_adbLogcatProcess, SIGNAL(readyReadStandardError()) , SLOT(logcatReadStandardError()));
+}
+
+AndroidRunner::~AndroidRunner()
+{
+ stop();
+}
+
+void AndroidRunner::checkPID()
+{
+ QProcess psProc;
+ psProc.start(AndroidConfigurations::instance().adbToolPath(),
+ QStringList() << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("shell") << QLatin1String("ps"));
+ if (!psProc.waitForFinished(-1)) {
+ psProc.terminate();
+ return;
+ }
+ qint64 pid = -1;
+ QList<QByteArray> procs = psProc.readAll().split('\n');
+ foreach (const QByteArray &proc, procs) {
+ if (proc.trimmed().endsWith(m_packageName.toAscii())) {
+ QRegExp rx(QLatin1String("(\\d+)"));
+ if (rx.indexIn(QLatin1String(proc), proc.indexOf(' ')) > 0) {
+ pid = rx.cap(1).toLongLong();
+ break;
+ }
+ }
+ }
+
+ if (-1 != m_processPID && pid == -1) {
+ m_processPID = -1;
+ emit remoteProcessFinished(tr("\n\n'%1' died").arg(m_packageName));
+ return;
+ }
+ m_processPID = pid;
+ if (!m_debugingMode)
+ return;
+
+ m_gdbserverPID = -1;
+ foreach (const QByteArray &proc, procs) {
+ if (proc.trimmed().endsWith("gdbserver")) {
+ QRegExp rx(QLatin1String("(\\d+)"));
+ if (rx.indexIn(QLatin1String(proc), proc.indexOf(' ')) > 0) {
+ m_gdbserverPID = rx.cap(1).toLongLong();
+ break;
+ }
+ }
+ }
+}
+
+void AndroidRunner::killPID()
+{
+ checkPID(); //updates m_processPID and m_gdbserverPID
+ for (int tries = 0; tries < 10 && (m_processPID != -1 || m_gdbserverPID != -1); ++tries) {
+ if (m_processPID != -1) {
+ adbKill(m_processPID, m_deviceSerialNumber, 2000);
+ adbKill(m_processPID, m_deviceSerialNumber, 2000, m_packageName);
+ }
+
+ if (m_gdbserverPID != -1) {
+ adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000);
+ adbKill(m_gdbserverPID, m_deviceSerialNumber, 2000, m_packageName);
+ }
+ checkPID();
+ }
+}
+
+void AndroidRunner::start()
+{
+ QtConcurrent::run(this,&AndroidRunner::asyncStart);
+}
+
+void AndroidRunner::asyncStart()
+{
+ QMutexLocker locker(&m_mutex);
+ m_processPID = -1;
+ killPID(); // kill any process with this name
+ QString extraParams;
+ QProcess adbStarProc;
+ if (m_debugingMode) {
+ QStringList arguments;
+ arguments << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("forward") << QString::fromLatin1("tcp%1").arg(m_remoteChannel)
+ << QString::fromLatin1("localfilesystem:/data/data/%1/debug-socket").arg(m_packageName);
+ adbStarProc.start(AndroidConfigurations::instance().adbToolPath(), arguments);
+ if (!adbStarProc.waitForStarted()) {
+ emit remoteProcessFinished(tr("Failed to forward debugging ports. Reason: $1").arg(adbStarProc.errorString()));
+ return;
+ }
+ if (!adbStarProc.waitForFinished(-1)) {
+ emit remoteProcessFinished(tr("Failed to forward debugging ports"));
+ return;
+ }
+ extraParams = QLatin1String("-e native_debug true -e gdbserver_socket +debug-socket");
+ }
+
+ if (m_useLocalQtLibs) {
+ extraParams += QLatin1String(" -e use_local_qt_libs true");
+ extraParams += QLatin1String(" -e libs_prefix /data/local/qt/");
+ extraParams += QLatin1String(" -e load_local_libs ") + m_localLibs;
+ extraParams += QLatin1String(" -e load_local_jars ") + m_localJars;
+ }
+
+ extraParams = extraParams.trimmed();
+ QStringList arguments;
+ arguments << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("shell") << QLatin1String("am")
+ << QLatin1String("start") << QLatin1String("-n") << m_intentName;
+
+ if (extraParams.length())
+ arguments << extraParams.split(QLatin1Char(' '));
+
+ adbStarProc.start(AndroidConfigurations::instance().adbToolPath(), arguments);
+ if (!adbStarProc.waitForStarted()) {
+ emit remoteProcessFinished(tr("Failed to start the activity. Reason: $1").arg(adbStarProc.errorString()));
+ return;
+ }
+ if (!adbStarProc.waitForFinished(-1)) {
+ adbStarProc.terminate();
+ emit remoteProcessFinished(tr("Unable to start '%1'").arg(m_packageName));
+ return;
+ }
+ QTime startTime = QTime::currentTime();
+ while (m_processPID == -1 && startTime.secsTo(QTime::currentTime()) < 10) { // wait up to 10 seconds for application to start
+ checkPID();
+ }
+ if (m_processPID == -1) {
+ emit remoteProcessFinished(tr("Can't find %1 process").arg(m_packageName));
+ return;
+ }
+
+ if (m_debugingMode) {
+ startTime = QTime::currentTime();
+ while (m_gdbserverPID == -1 && startTime.secsTo(QTime::currentTime()) < 25) { // wait up to 25 seconds to connect
+ checkPID();
+ }
+ msleep(200); // give gdbserver more time to start
+ }
+
+ QMetaObject::invokeMethod(this, "startLogcat", Qt::QueuedConnection);
+}
+
+void AndroidRunner::startLogcat()
+{
+ m_checkPIDTimer.start(1000); // check if the application is alive every 1 seconds
+ m_adbLogcatProcess.start(AndroidConfigurations::instance().adbToolPath(),
+ QStringList() << QLatin1String("-s") << m_deviceSerialNumber
+ << QLatin1String("logcat"));
+ emit remoteProcessStarted(5039);
+}
+
+void AndroidRunner::stop()
+{
+ QMutexLocker locker(&m_mutex);
+ m_adbLogcatProcess.terminate();
+ m_adbLogcatProcess.waitForFinished(-1);
+ m_checkPIDTimer.stop();
+ if (m_processPID == -1)
+ return; // don't emit another signal
+ QtConcurrent::run(this, &AndroidRunner::asyncStop);
+}
+void AndroidRunner::asyncStop()
+{
+ killPID();
+ emit remoteProcessFinished(tr("\n\n'%1' killed").arg(m_packageName));
+}
+
+void AndroidRunner::logcatReadStandardError()
+{
+ emit remoteErrorOutput(m_adbLogcatProcess.readAllStandardError());
+}
+
+void AndroidRunner::logcatReadStandardOutput()
+{
+ m_logcat += m_adbLogcatProcess.readAllStandardOutput();
+ bool keepLastLine = m_logcat.endsWith('\n');
+ QByteArray line;
+ QByteArray pid(QString::fromLatin1("%1):").arg(m_processPID).toAscii());
+ foreach (line, m_logcat.split('\n')) {
+ if (!line.contains(pid))
+ continue;
+ if (line.startsWith("E/"))
+ emit remoteErrorOutput(line);
+ else
+ emit remoteOutput(line);
+
+ }
+ if (keepLastLine)
+ m_logcat = line;
+}
+
+void AndroidRunner::adbKill(qint64 pid, const QString &device, int timeout, const QString &runAsPackageName)
+{
+ QProcess process;
+ QStringList arguments;
+
+ arguments << QLatin1String("-s") << device;
+ arguments << QLatin1String("shell");
+ if (runAsPackageName.size())
+ arguments << QLatin1String("run-as") << runAsPackageName;
+ arguments << QLatin1String("kill") << QLatin1String("-9");
+ arguments << QString::number(pid);
+
+ process.start(AndroidConfigurations::instance().adbToolPath(), arguments);
+ if (!process.waitForFinished(timeout))
+ process.terminate();
+}
+
+QString AndroidRunner::displayName() const
+{
+ return m_packageName;
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager