summaryrefslogtreecommitdiffstats
path: root/src/runonphone
diff options
context:
space:
mode:
Diffstat (limited to 'src/runonphone')
-rw-r--r--src/runonphone/main.cpp277
-rw-r--r--src/runonphone/ossignalconverter.cpp121
-rw-r--r--src/runonphone/ossignalconverter.h63
-rw-r--r--src/runonphone/ossignalconverter_p.h71
-rw-r--r--src/runonphone/runonphone.pro35
-rw-r--r--src/runonphone/serenum.h56
-rw-r--r--src/runonphone/serenum_stub.cpp53
-rw-r--r--src/runonphone/serenum_unix.cpp224
-rw-r--r--src/runonphone/serenum_win.cpp105
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener.cpp224
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener.h103
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener_gui.cpp111
-rw-r--r--src/runonphone/symbianutils/bluetoothlistener_gui.h89
-rw-r--r--src/runonphone/symbianutils/callback.h160
-rw-r--r--src/runonphone/symbianutils/communicationstarter.cpp251
-rw-r--r--src/runonphone/symbianutils/communicationstarter.h160
-rw-r--r--src/runonphone/symbianutils/json.cpp490
-rw-r--r--src/runonphone/symbianutils/json.h149
-rw-r--r--src/runonphone/symbianutils/launcher.cpp1036
-rw-r--r--src/runonphone/symbianutils/launcher.h212
-rw-r--r--src/runonphone/symbianutils/symbiandevicemanager.cpp489
-rw-r--r--src/runonphone/symbianutils/symbiandevicemanager.h178
-rw-r--r--src/runonphone/symbianutils/symbianutils.pri35
-rw-r--r--src/runonphone/symbianutils/symbianutils_global.h55
-rw-r--r--src/runonphone/symbianutils/tcftrkdevice.cpp929
-rw-r--r--src/runonphone/symbianutils/tcftrkdevice.h295
-rw-r--r--src/runonphone/symbianutils/tcftrkmessage.cpp562
-rw-r--r--src/runonphone/symbianutils/tcftrkmessage.h296
-rw-r--r--src/runonphone/symbianutils/trkdevice.cpp1184
-rw-r--r--src/runonphone/symbianutils/trkdevice.h143
-rw-r--r--src/runonphone/symbianutils/trkutils.cpp603
-rw-r--r--src/runonphone/symbianutils/trkutils.h261
-rw-r--r--src/runonphone/symbianutils/trkutils_p.h62
-rw-r--r--src/runonphone/trksignalhandler.cpp368
-rw-r--r--src/runonphone/trksignalhandler.h88
35 files changed, 9538 insertions, 0 deletions
diff --git a/src/runonphone/main.cpp b/src/runonphone/main.cpp
new file mode 100644
index 000000000..528154438
--- /dev/null
+++ b/src/runonphone/main.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QTextStream>
+#include <QStringList>
+#include <QScopedPointer>
+#include <QTimer>
+#include <QFileInfo>
+#include "symbianutils/trkutils.h"
+#include "symbianutils/trkdevice.h"
+#include "symbianutils/launcher.h"
+
+#include "trksignalhandler.h"
+#include "serenum.h"
+#include "ossignalconverter.h"
+
+void printUsage(QTextStream& outstream, QString exeName)
+{
+ outstream << exeName << " [options] [program] [program arguments]" << endl
+ << "-s, --sis <local file> specify sis file to install" << endl
+ << "-p, --portname <COMx> specify COM port to use by device name" << endl
+ << "-f, --portfriendlyname <substring> specify COM port to use by friendly name" << endl
+ << "-t, --timeout <milliseconds> terminate test if timeout occurs" << endl
+ << "-v, --verbose show debugging output" << endl
+ << "-q, --quiet hide progress messages" << endl
+ << "-u, --upload <local file> upload executable file to phone" << endl
+ << "-d, --download <remote file> <local file> copy file from phone to PC after running test" << endl
+ << "--nocrashlog Don't capture call stack if test crashes" << endl
+ << "--crashlogpath <dir> Path to save crash logs (default=working dir)" << endl
+ << endl
+ << "USB COM ports can usually be autodetected, use -p or -f to force a specific port." << endl
+ << "If using System TRK, it is possible to copy the program directly to sys/bin on the phone." << endl
+ << "-s can be used with both System and Application TRK to install the program" << endl;
+}
+
+#define CHECK_PARAMETER_EXISTS if(!it.hasNext()) { printUsage(outstream, args[0]); return 1; }
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ QString serialPortName;
+ QString serialPortFriendlyName;
+ QString sisFile;
+ QString exeFile;
+ QStringList cmdLine;
+ QStringList args = QCoreApplication::arguments();
+ QTextStream outstream(stdout);
+ QTextStream errstream(stderr);
+ QString uploadLocalFile;
+ QString downloadRemoteFile;
+ QString downloadLocalFile;
+ int loglevel=1;
+ int timeout=0;
+ bool crashlog = true;
+ QString crashlogpath;
+ QListIterator<QString> it(args);
+ it.next(); //skip name of program
+ while (it.hasNext()) {
+ QString arg = it.next();
+
+ if (arg.startsWith("-")) {
+ if (arg == "--portname" || arg == "-p") {
+ CHECK_PARAMETER_EXISTS
+ serialPortName = it.next();
+ }
+ else if (arg == "--portfriendlyname" || arg == "-f") {
+ CHECK_PARAMETER_EXISTS
+ serialPortFriendlyName = it.next();
+ }
+ else if (arg == "--sis" || arg == "-s") {
+ CHECK_PARAMETER_EXISTS
+ sisFile = it.next();
+ if (!QFileInfo(sisFile).exists()) {
+ errstream << "Sis file (" << sisFile << ") doesn't exist" << endl;
+ return 1;
+ }
+ }
+ else if (arg == "--upload" || arg == "-u") {
+ CHECK_PARAMETER_EXISTS
+ uploadLocalFile = it.next();
+ if (!QFileInfo(uploadLocalFile).exists()) {
+ errstream << "Executable file (" << uploadLocalFile << ") doesn't exist" << endl;
+ return 1;
+ }
+ if (!(QFileInfo(uploadLocalFile).suffix() == "exe")) {
+ errstream << "File (" << uploadLocalFile << ") must be an executable" << endl;
+ return 1;
+ }
+ }
+ else if (arg == "--download" || arg == "-d") {
+ CHECK_PARAMETER_EXISTS
+ downloadRemoteFile = it.next();
+ CHECK_PARAMETER_EXISTS
+ downloadLocalFile = it.next();
+ }
+ else if (arg == "--timeout" || arg == "-t") {
+ CHECK_PARAMETER_EXISTS
+ bool ok;
+ timeout = it.next().toInt(&ok);
+ if (!ok) {
+ errstream << "Timeout must be specified in milliseconds" << endl;
+ return 1;
+ }
+ }
+ else if (arg == "--verbose" || arg == "-v")
+ loglevel=2;
+ else if (arg == "--quiet" || arg == "-q")
+ loglevel=0;
+ else if (arg == "--nocrashlog")
+ crashlog = false;
+ else if (arg == "--crashlogpath") {
+ CHECK_PARAMETER_EXISTS
+ crashlogpath = it.next();
+ }
+ else
+ errstream << "unknown command line option " << arg << endl;
+ } else {
+ exeFile = arg;
+ while(it.hasNext()) {
+ cmdLine.append(it.next());
+ }
+ }
+ }
+
+ if (exeFile.isEmpty() && sisFile.isEmpty() && uploadLocalFile.isEmpty() &&
+ (downloadLocalFile.isEmpty() || downloadRemoteFile.isEmpty())) {
+ printUsage(outstream, args[0]);
+ return 1;
+ }
+
+ if (!uploadLocalFile.isEmpty() && (!downloadLocalFile.isEmpty() || !downloadRemoteFile.isEmpty())) {
+ errstream << "Upload option can't be used together with download" << endl;
+ printUsage(outstream, args[0]);
+ return 1;
+ }
+
+ if (serialPortName.isEmpty()) {
+ if (loglevel > 0)
+ outstream << "Detecting serial ports" << endl;
+ foreach (const SerialPortId &id, enumerateSerialPorts(loglevel)) {
+ if (loglevel > 0)
+ outstream << "Port Name: " << id.portName << ", "
+ << "Friendly Name:" << id.friendlyName << endl;
+ if (!id.friendlyName.isEmpty()
+ && serialPortFriendlyName.isEmpty()
+ && (id.friendlyName.contains("symbian", Qt::CaseInsensitive)
+ || id.friendlyName.contains("s60", Qt::CaseInsensitive)
+ || id.friendlyName.contains("nokia", Qt::CaseInsensitive))) {
+ serialPortName = id.portName;
+ break;
+ } else if (!id.friendlyName.isEmpty()
+ && !serialPortFriendlyName.isEmpty()
+ && id.friendlyName.contains(serialPortFriendlyName)) {
+ serialPortName = id.portName;
+ break;
+ }
+ }
+ if (serialPortName.isEmpty()) {
+ errstream << "No phone found, ensure USB cable is connected or specify manually with -p" << endl;
+ return 1;
+ }
+ }
+
+ QScopedPointer<trk::Launcher> launcher;
+ launcher.reset(new trk::Launcher(trk::Launcher::ActionPingOnly));
+ QFileInfo exeInfo(exeFile);
+ QFileInfo uploadInfo(uploadLocalFile);
+ if (!sisFile.isEmpty()) {
+ launcher->addStartupActions(trk::Launcher::ActionCopyInstall);
+ launcher->setCopyFileName(sisFile, "c:\\data\\testtemp.sis");
+ launcher->setInstallFileName("c:\\data\\testtemp.sis");
+ }
+ else if (!uploadLocalFile.isEmpty() && uploadInfo.exists()) {
+ launcher->addStartupActions(trk::Launcher::ActionCopy);
+ launcher->setCopyFileName(uploadLocalFile, QString("c:\\sys\\bin\\") + uploadInfo.fileName());
+ }
+ if (!exeFile.isEmpty()) {
+ launcher->addStartupActions(trk::Launcher::ActionRun);
+ launcher->setFileName(QString("c:\\sys\\bin\\") + exeInfo.fileName());
+ launcher->setCommandLineArgs(cmdLine);
+ }
+ if (!downloadRemoteFile.isEmpty() && !downloadLocalFile.isEmpty()) {
+ launcher->addStartupActions(trk::Launcher::ActionDownload);
+ launcher->setDownloadFileName(downloadRemoteFile, downloadLocalFile);
+ }
+ if (loglevel > 0)
+ outstream << "Connecting to target via " << serialPortName << endl;
+ launcher->setTrkServerName(serialPortName);
+
+ if (loglevel > 1)
+ launcher->setVerbose(1);
+
+ TrkSignalHandler handler;
+ handler.setLogLevel(loglevel);
+ handler.setCrashLogging(crashlog);
+ handler.setCrashLogPath(crashlogpath);
+
+ QObject::connect(launcher.data(), SIGNAL(copyingStarted()), &handler, SLOT(copyingStarted()));
+ QObject::connect(launcher.data(), SIGNAL(canNotConnect(const QString &)), &handler, SLOT(canNotConnect(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotCreateFile(const QString &, const QString &)), &handler, SLOT(canNotCreateFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotWriteFile(const QString &, const QString &)), &handler, SLOT(canNotWriteFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotCloseFile(const QString &, const QString &)), &handler, SLOT(canNotCloseFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(installingStarted()), &handler, SLOT(installingStarted()));
+ QObject::connect(launcher.data(), SIGNAL(canNotInstall(const QString &, const QString &)), &handler, SLOT(canNotInstall(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(installingFinished()), &handler, SLOT(installingFinished()));
+ QObject::connect(launcher.data(), SIGNAL(startingApplication()), &handler, SLOT(startingApplication()));
+ QObject::connect(launcher.data(), SIGNAL(applicationRunning(uint)), &handler, SLOT(applicationRunning(uint)));
+ QObject::connect(launcher.data(), SIGNAL(canNotRun(const QString &)), &handler, SLOT(canNotRun(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(applicationOutputReceived(const QString &)), &handler, SLOT(applicationOutputReceived(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(copyProgress(int)), &handler, SLOT(copyProgress(int)));
+ QObject::connect(launcher.data(), SIGNAL(stateChanged(int)), &handler, SLOT(stateChanged(int)));
+ QObject::connect(launcher.data(), SIGNAL(processStopped(uint,uint,uint,QString)), &handler, SLOT(stopped(uint,uint,uint,QString)));
+ QObject::connect(launcher.data(), SIGNAL(libraryLoaded(trk::Library)), &handler, SLOT(libraryLoaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(libraryUnloaded(trk::Library)), &handler, SLOT(libraryUnloaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)), &handler, SLOT(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)));
+ QObject::connect(&handler, SIGNAL(resume(uint,uint)), launcher.data(), SLOT(resumeProcess(uint,uint)));
+ QObject::connect(&handler, SIGNAL(terminate()), launcher.data(), SLOT(terminate()));
+ QObject::connect(&handler, SIGNAL(getRegistersAndCallStack(uint,uint)), launcher.data(), SLOT(getRegistersAndCallStack(uint,uint)));
+ QObject::connect(launcher.data(), SIGNAL(finished()), &handler, SLOT(finished()));
+
+ QObject::connect(OsSignalConverter::instance(), SIGNAL(terminate()), launcher.data(), SLOT(terminate()), Qt::QueuedConnection);
+
+ QTimer timer;
+ timer.setSingleShot(true);
+ QObject::connect(&timer, SIGNAL(timeout()), &handler, SLOT(timeout()));
+ if (timeout > 0) {
+ timer.start(timeout);
+ }
+
+ QString errorMessage;
+ if (!launcher->startServer(&errorMessage)) {
+ errstream << errorMessage << endl;
+ return 1;
+ }
+
+ return a.exec();
+}
+
diff --git a/src/runonphone/ossignalconverter.cpp b/src/runonphone/ossignalconverter.cpp
new file mode 100644
index 000000000..71e705fee
--- /dev/null
+++ b/src/runonphone/ossignalconverter.cpp
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "ossignalconverter_p.h"
+#include <signal.h>
+#include <QTimer>
+#include <stdio.h>
+
+Q_GLOBAL_STATIC(OsSignalConverter, osSignalConverter);
+
+OsSignalConverter* OsSignalConverter::instance()
+{
+ return osSignalConverter();
+}
+
+OsSignalConverter::OsSignalConverter()
+: d(new OsSignalConverterPrivate(this))
+{
+};
+
+OsSignalConverter::~OsSignalConverter()
+{
+}
+
+OsSignalConverterPrivate::OsSignalConverterPrivate(OsSignalConverter* owner)
+: QObject(owner), q(owner), poller(new QTimer(this))
+{
+ trap();
+ connect(poller, SIGNAL(timeout()), this, SLOT(poll()));
+ poller->start(1000);
+}
+
+OsSignalConverterPrivate::~OsSignalConverterPrivate()
+{
+ untrap();
+}
+
+void OsSignalConverterPrivate::trap()
+{
+ signal(SIGINT, handler);
+ signal(SIGTERM, handler);
+#ifdef SIGBREAK
+ signal(SIGBREAK, handler);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, handler);
+#endif
+#ifdef SIGQUIT
+ signal(SIGQUIT, handler);
+#endif
+}
+
+void OsSignalConverterPrivate::untrap()
+{
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+#ifdef SIGBREAK
+ signal(SIGBREAK, SIG_DFL);
+#endif
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_DFL);
+#endif
+#ifdef SIGQUIT
+ signal(SIGQUIT, SIG_DFL);
+#endif
+}
+
+void OsSignalConverterPrivate::handler(int sig)
+{
+ untrap(); //allow 2nd ctrl-c to really kill us
+ terminateRequest = sig;
+}
+
+void OsSignalConverterPrivate::poll()
+{
+ if (terminateRequest) {
+ fprintf(stderr, "\n*** caught signal %d, terminating ***\n", terminateRequest);
+ poller->stop();
+ emit q->terminate();
+ }
+}
+
+sig_atomic_t OsSignalConverterPrivate::terminateRequest;
diff --git a/src/runonphone/ossignalconverter.h b/src/runonphone/ossignalconverter.h
new file mode 100644
index 000000000..7c69a4b44
--- /dev/null
+++ b/src/runonphone/ossignalconverter.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OSSIGNALCONVERTER_H
+#define OSSIGNALCONVERTER_H
+#include <QObject>
+
+class OsSignalConverterPrivate;
+
+class OsSignalConverter : public QObject
+{
+ friend class OsSignalConverterPrivate;
+ Q_OBJECT
+public:
+ static OsSignalConverter* instance();
+ OsSignalConverter();
+ ~OsSignalConverter();
+signals:
+ //emitted when this process is asked to quit, e.g. by SIGINT
+ void terminate();
+private:
+ OsSignalConverterPrivate *d;
+};
+
+#endif // OSSIGNALCONVERTER_H
diff --git a/src/runonphone/ossignalconverter_p.h b/src/runonphone/ossignalconverter_p.h
new file mode 100644
index 000000000..b7991c845
--- /dev/null
+++ b/src/runonphone/ossignalconverter_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OSSIGNALCONVERTER_P_H
+#define OSSIGNALCONVERTER_P_H
+#include "ossignalconverter.h"
+#include <signal.h>
+
+class QTimer;
+class OsSignalConverterPrivate : public QObject
+{
+ Q_OBJECT
+public:
+ OsSignalConverterPrivate(OsSignalConverter* owner);
+ ~OsSignalConverterPrivate();
+private:
+
+ static void trap();
+ static void untrap();
+ static void handler(int signal);
+
+private slots:
+
+ void poll();
+
+private:
+
+ OsSignalConverter* q;
+ static sig_atomic_t terminateRequest;
+ QTimer *poller;
+};
+
+#endif // OSSIGNALCONVERTER_P_H
diff --git a/src/runonphone/runonphone.pro b/src/runonphone/runonphone.pro
new file mode 100644
index 000000000..7ff361ccf
--- /dev/null
+++ b/src/runonphone/runonphone.pro
@@ -0,0 +1,35 @@
+TEMPLATE = app
+
+QT -= gui
+CONFIG += console
+CONFIG -= app_bundle
+
+include(symbianutils/symbianutils.pri)
+
+SOURCES += main.cpp \
+ trksignalhandler.cpp \
+ ossignalconverter.cpp
+
+HEADERS += trksignalhandler.h \
+ serenum.h \
+ ossignalconverter.h \
+ ossignalconverter_p.h
+
+DEFINES += SYMBIANUTILS_INCLUDE_PRI
+
+windows {
+ SOURCES += serenum_win.cpp
+ LIBS += -lsetupapi \
+ -luuid \
+ -ladvapi32
+}
+else:unix:!symbian {
+ SOURCES += serenum_unix.cpp
+ LIBS += -lusb
+}
+else {
+ SOURCES += serenum_stub.cpp
+}
+
+target.path=$$[QT_INSTALL_BINS]
+INSTALLS += target
diff --git a/src/runonphone/serenum.h b/src/runonphone/serenum.h
new file mode 100644
index 000000000..4ed7d4daa
--- /dev/null
+++ b/src/runonphone/serenum.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef WIN32SERENUM_H
+#define WIN32SERENUM_H
+
+#include <QString>
+#include <QList>
+
+struct SerialPortId
+{
+ QString portName;
+ QString friendlyName;
+};
+
+QList<SerialPortId> enumerateSerialPorts(int loglevel);
+
+#endif // WIN32SERENUM_H
diff --git a/src/runonphone/serenum_stub.cpp b/src/runonphone/serenum_stub.cpp
new file mode 100644
index 000000000..08b0fabd9
--- /dev/null
+++ b/src/runonphone/serenum_stub.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "serenum.h"
+#include <QByteArray>
+#include <QString>
+#include <QDebug>
+
+QList<SerialPortId> enumerateSerialPorts()
+{
+ QList<SerialPortId> list;
+ qWarning() << "enumerateSerialPorts not implemented";
+ return list;
+}
+
diff --git a/src/runonphone/serenum_unix.cpp b/src/runonphone/serenum_unix.cpp
new file mode 100644
index 000000000..8cc0b82a4
--- /dev/null
+++ b/src/runonphone/serenum_unix.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "serenum.h"
+#include <QByteArray>
+#include <QString>
+#include <QDebug>
+#include <QFileInfo>
+#include <QDir>
+
+#include <usb.h>
+
+class InterfaceInfo
+{
+public:
+ InterfaceInfo(const QString &mf, const QString &pr, int mfid, int prid);
+ QString manufacturer;
+ QString product;
+ int manufacturerid;
+ int productid;
+};
+
+InterfaceInfo::InterfaceInfo(const QString &mf, const QString &pr, int mfid, int prid) :
+ manufacturer(mf),
+ product(pr),
+ manufacturerid(mfid),
+ productid(prid)
+{
+ if(mf.isEmpty())
+ manufacturer = QString("[%1]").arg(mfid, 4, 16, QChar('0'));
+ if(pr.isEmpty())
+ product = QString("[%1]").arg(prid, 4, 16, QChar('0'));
+}
+
+QList<SerialPortId> enumerateSerialPorts(int loglevel)
+{
+ QList<QString> eligibleInterfaces;
+ QList<InterfaceInfo> eligibleInterfacesInfo;
+ QList<SerialPortId> list;
+
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+
+ for (struct usb_bus *bus = usb_get_busses(); bus; bus = bus->next) {
+ for (struct usb_device *device = bus->devices; device; device = device->next) {
+ for (int n = 0; n < device->descriptor.bNumConfigurations && device->config; ++n) {
+ struct usb_config_descriptor &usbConfig =device->config[n];
+ QList<int> usableInterfaces;
+ for (int m = 0; m < usbConfig.bNumInterfaces; ++m) {
+ for (int o = 0; o < usbConfig.interface[m].num_altsetting; ++o) {
+ struct usb_interface_descriptor &descriptor = usbConfig.interface[m].altsetting[o];
+ if (descriptor.bInterfaceClass != 2 // "Communication"
+ || descriptor.bInterfaceSubClass != 2 // Abstract (modem)
+ || descriptor.bInterfaceProtocol != 255) // Vendor Specific
+ continue;
+
+ unsigned char *buf = descriptor.extra;
+ unsigned int size = descriptor.extralen;
+ while (size >= 2 * sizeof(u_int8_t)) {
+ // for Communication devices there is a slave interface for the actual
+ // data transmission.
+ // the extra info stores that as a index for the interface
+ if (buf[0] >= 5 && buf[1] == 36 && buf[2] == 6) { // CDC Union
+ for (int i = 3; i < buf[0]; i++)
+ usableInterfaces.append((int) buf[i]);
+ }
+ size -= buf[0];
+ buf += buf[0];
+ }
+ }
+ }
+
+ if (usableInterfaces.isEmpty())
+ continue;
+
+ QString manufacturerString;
+ QString productString;
+
+ usb_dev_handle *devh = usb_open(device);
+ if (devh) {
+ QByteArray buf;
+ buf.resize(256);
+ int err = usb_get_string_simple(devh, device->descriptor.iManufacturer, buf.data(), buf.size());
+ if (err < 0) {
+ if (loglevel > 1)
+ qDebug() << " can't read manufacturer name, error:" << err;
+ } else {
+ manufacturerString = QString::fromAscii(buf);
+ if (loglevel > 1)
+ qDebug() << " manufacturer:" << manufacturerString;
+ }
+
+ buf.resize(256);
+ err = usb_get_string_simple(devh, device->descriptor.iProduct, buf.data(), buf.size());
+ if (err < 0) {
+ if (loglevel > 1)
+ qDebug() << " can't read product name, error:" << err;
+ } else {
+ productString = QString::fromAscii(buf);
+ if (loglevel > 1)
+ qDebug() << " product:" << productString;
+ }
+ usb_close(devh);
+ } else if (loglevel > 0) {
+ qDebug() << " can't open usb device";
+ }
+
+ // second loop to find the actual data interface.
+ foreach (int i, usableInterfaces) {
+#ifdef Q_OS_MAC
+ eligibleInterfaces << QString("^cu\\.usbmodem.*%1$")
+ .arg(QString("%1").arg(descriptor.bInterfaceNumber, 1, 16).toUpper());
+#else
+ // ### manufacturer and product strings are only readable as root :(
+ if (!manufacturerString.isEmpty() && !productString.isEmpty()) {
+ eligibleInterfaces << QString("usb-%1_%2-if%3")
+ .arg(manufacturerString.replace(QChar(' '), QChar('_')))
+ .arg(productString.replace(QChar(' '), QChar('_')))
+ .arg(i, 2, 16, QChar('0'));
+ } else {
+ eligibleInterfaces << QString("if%1").arg(i, 2, 16, QChar('0')); // fix!
+ }
+#endif
+ }
+ eligibleInterfacesInfo << InterfaceInfo(manufacturerString, productString, device->descriptor.idVendor, device->descriptor.idProduct);
+ }
+ }
+ }
+
+ if (loglevel > 1)
+ qDebug() << " searching for interfaces:" << eligibleInterfaces;
+
+#ifdef Q_OS_MAC
+ QDir dir("/dev/");
+ bool allowAny = false;
+#else
+ QDir dir("/dev/serial/by-id/");
+ bool allowAny = eligibleInterfaces.isEmpty();
+#endif
+ foreach (const QFileInfo &info, dir.entryInfoList(QDir::System)) {
+ if (!info.isDir()) {
+ bool usable = allowAny;
+ QString friendlyName = info.fileName();
+ foreach (const QString &iface, eligibleInterfaces) {
+ if (info.fileName().contains(QRegExp(iface))) {
+ if (loglevel > 1)
+ qDebug() << " found device file:" << info.fileName() << endl;
+#ifdef Q_OS_MAC
+ friendlyName = eligibleInterfacesInfo[eligibleInterfaces.indexOf(iface)].product;
+#endif
+ usable = true;
+ break;
+ }
+ }
+ if (!usable)
+ continue;
+
+ SerialPortId id;
+ id.friendlyName = friendlyName;
+ id.portName = info.canonicalFilePath();
+ list << id;
+ }
+ }
+
+ if (list.isEmpty() && !eligibleInterfacesInfo.isEmpty() && loglevel > 0) {
+ qDebug() << "Possible USB devices found, but without serial drivers:";
+ foreach(const InterfaceInfo &iface, eligibleInterfacesInfo) {
+ qDebug() << " Manufacturer:"
+ << iface.manufacturer
+ << "Product:"
+ << iface.product
+#ifdef Q_OS_LINUX
+ << endl
+ << " Load generic driver using:"
+ << QString("sudo modprobe usbserial vendor=0x%1 product=0x%2")
+ .arg(iface.manufacturerid, 4, 16, QChar('0'))
+ .arg(iface.productid, 4, 16, QChar('0'));
+#else
+ ;
+#endif
+ }
+ }
+ return list;
+}
+
diff --git a/src/runonphone/serenum_win.cpp b/src/runonphone/serenum_win.cpp
new file mode 100644
index 000000000..7cc9d5215
--- /dev/null
+++ b/src/runonphone/serenum_win.cpp
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "serenum.h"
+#include <QByteArray>
+#include <QString>
+#include <QDebug>
+#include <windows.h>
+#include <windef.h>
+#include <setupapi.h>
+#include <devguid.h>
+#include <winreg.h>
+#include <shlwapi.h>
+
+//{4d36e978-e325-11ce-bfc1-08002be10318}
+//DEFINE_GUID(GUID_DEVCLASS_PORTS, 0x4D36E978, 0xE325, 0x11CE, 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 );
+
+QList<SerialPortId> enumerateSerialPorts(int)
+{
+ DWORD index=0;
+ SP_DEVINFO_DATA info;
+ GUID guid = GUID_DEVCLASS_PORTS;
+ HDEVINFO infoset = SetupDiGetClassDevs(&guid, 0, 0, DIGCF_PRESENT);
+ QString valueName(16384, 0);
+ QList<SerialPortId> list;
+
+ for (index=0;;index++) {
+ ZeroMemory(&info, sizeof(SP_DEVINFO_DATA));
+ info.cbSize = sizeof(SP_DEVINFO_DATA);
+ if (!SetupDiEnumDeviceInfo(infoset, index, &info))
+ break;
+ QString friendlyName;
+ QString portName;
+ DWORD size=0;
+ SetupDiGetDeviceRegistryProperty(infoset, &info, SPDRP_FRIENDLYNAME, 0, 0, 0, &size);
+ QByteArray ba(size, 0);
+ if(SetupDiGetDeviceRegistryProperty(infoset, &info, SPDRP_FRIENDLYNAME, 0, (BYTE*)(ba.data()), size, 0)) {
+ friendlyName = QString((const QChar*)(ba.constData()), ba.size() / 2 - 1);
+ }
+ HKEY key = SetupDiOpenDevRegKey(infoset, &info, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+ if(key != INVALID_HANDLE_VALUE) {
+ //RegGetValue not supported on XP, SHRegGetValue not supported by mingw, so use the old method of enumerating all the values
+ for (DWORD dwi=0;;dwi++) {
+ DWORD vsize = valueName.size();
+ if (ERROR_SUCCESS == RegEnumValue(key, dwi, (WCHAR*)(valueName.data()), &vsize, 0, 0, 0, &size)) {
+ if (valueName.startsWith("PortName")) {
+ QByteArray ba(size, 0);
+ vsize = valueName.size();
+ if(ERROR_SUCCESS == RegEnumValue(key, dwi, (WCHAR*)(valueName.data()), &vsize, 0, 0, (BYTE*)(ba.data()), &size)) {
+ portName = QString((const QChar*)(ba.constData()), ba.size() / 2 - 1);
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ RegCloseKey(key);
+ }
+ SerialPortId id;
+ id.portName = portName;
+ id.friendlyName = friendlyName;
+ list.append(id);
+ }
+ SetupDiDestroyDeviceInfoList(infoset);
+ return list;
+}
+
diff --git a/src/runonphone/symbianutils/bluetoothlistener.cpp b/src/runonphone/symbianutils/bluetoothlistener.cpp
new file mode 100644
index 000000000..be232db04
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener.cpp
@@ -0,0 +1,224 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bluetoothlistener.h"
+#include "trkdevice.h"
+
+#include <QtCore/QDebug>
+
+#ifdef Q_OS_UNIX
+# include <unistd.h>
+# include <signal.h>
+#else
+# include <windows.h>
+#endif
+
+// Process id helpers.
+#ifdef Q_OS_WIN
+inline DWORD processId(const QProcess &p)
+{
+ if (const Q_PID processInfoStruct = p.pid())
+ return processInfoStruct->dwProcessId;
+ return 0;
+}
+#else
+inline Q_PID processId(const QProcess &p)
+{
+ return p.pid();
+}
+#endif
+
+
+enum { debug = 0 };
+
+namespace trk {
+
+struct BluetoothListenerPrivate {
+ BluetoothListenerPrivate();
+ QString device;
+ QProcess process;
+#ifdef Q_OS_WIN
+ DWORD pid;
+#else
+ Q_PID pid;
+#endif
+ bool printConsoleMessages;
+ BluetoothListener::Mode mode;
+};
+
+BluetoothListenerPrivate::BluetoothListenerPrivate() :
+ pid(0),
+ printConsoleMessages(false),
+ mode(BluetoothListener::Listen)
+{
+}
+
+BluetoothListener::BluetoothListener(QObject *parent) :
+ QObject(parent),
+ d(new BluetoothListenerPrivate)
+{
+ d->process.setProcessChannelMode(QProcess::MergedChannels);
+
+ connect(&d->process, SIGNAL(readyReadStandardError()),
+ this, SLOT(slotStdError()));
+ connect(&d->process, SIGNAL(readyReadStandardOutput()),
+ this, SLOT(slotStdOutput()));
+ connect(&d->process, SIGNAL(finished(int, QProcess::ExitStatus)),
+ this, SLOT(slotProcessFinished(int,QProcess::ExitStatus)));
+ connect(&d->process, SIGNAL(error(QProcess::ProcessError)),
+ this, SLOT(slotProcessError(QProcess::ProcessError)));
+}
+
+BluetoothListener::~BluetoothListener()
+{
+ const int trc = terminateProcess();
+ if (debug)
+ qDebug() << "~BluetoothListener: terminated" << trc;
+ delete d;
+}
+
+BluetoothListener::Mode BluetoothListener::mode() const
+{
+ return d->mode;
+}
+
+void BluetoothListener::setMode(Mode m)
+{
+ d->mode = m;
+}
+
+bool BluetoothListener::printConsoleMessages() const
+{
+ return d->printConsoleMessages;
+}
+
+void BluetoothListener::setPrintConsoleMessages(bool p)
+{
+ d->printConsoleMessages = p;
+}
+
+int BluetoothListener::terminateProcess()
+{
+ enum { TimeOutMS = 200 };
+ if (debug)
+ qDebug() << "terminateProcess" << d->process.pid() << d->process.state();
+ if (d->process.state() == QProcess::NotRunning)
+ return -1;
+ emitMessage(tr("%1: Stopping listener %2...").arg(d->device).arg(processId(d->process)));
+ // When listening, the process should terminate by itself after closing the connection
+ if (mode() == Listen && d->process.waitForFinished(TimeOutMS))
+ return 0;
+#ifdef Q_OS_UNIX
+ kill(d->process.pid(), SIGHUP); // Listens for SIGHUP
+ if (d->process.waitForFinished(TimeOutMS))
+ return 1;
+#endif
+ d->process.terminate();
+ if (d->process.waitForFinished(TimeOutMS))
+ return 2;
+ d->process.kill();
+ return 3;
+}
+
+bool BluetoothListener::start(const QString &device, QString *errorMessage)
+{
+ if (d->process.state() != QProcess::NotRunning) {
+ *errorMessage = QLatin1String("Internal error: Still running.");
+ return false;
+ }
+ d->device = device;
+ const QString binary = QLatin1String("rfcomm");
+ QStringList arguments;
+ arguments << QLatin1String("-r")
+ << (d->mode == Listen ? QLatin1String("listen") : QLatin1String("watch"))
+ << device << QString(QLatin1Char('1'));
+ if (debug)
+ qDebug() << binary << arguments;
+ emitMessage(tr("%1: Starting Bluetooth listener %2...").arg(device, binary));
+ d->pid = 0;
+ d->process.start(binary, arguments);
+ if (!d->process.waitForStarted()) {
+ *errorMessage = tr("Unable to run '%1': %2").arg(binary, d->process.errorString());
+ return false;
+ }
+ d->pid = processId(d->process); // Forgets it after crash/termination
+ emitMessage(tr("%1: Bluetooth listener running (%2).").arg(device).arg(processId(d->process)));
+ return true;
+}
+
+void BluetoothListener::slotStdOutput()
+{
+ emitMessage(QString::fromLocal8Bit(d->process.readAllStandardOutput()));
+}
+
+void BluetoothListener::emitMessage(const QString &m)
+{
+ if (d->printConsoleMessages || debug)
+ qDebug("%s\n", qPrintable(m));
+ emit message(m);
+}
+
+void BluetoothListener::slotStdError()
+{
+ emitMessage(QString::fromLocal8Bit(d->process.readAllStandardError()));
+}
+
+void BluetoothListener::slotProcessFinished(int ex, QProcess::ExitStatus state)
+{
+ switch (state) {
+ case QProcess::NormalExit:
+ emitMessage(tr("%1: Process %2 terminated with exit code %3.")
+ .arg(d->device).arg(d->pid).arg(ex));
+ break;
+ case QProcess::CrashExit:
+ emitMessage(tr("%1: Process %2 crashed.").arg(d->device).arg(d->pid));
+ break;
+ }
+ emit terminated();
+}
+
+void BluetoothListener::slotProcessError(QProcess::ProcessError error)
+{
+ emitMessage(tr("%1: Process error %2: %3")
+ .arg(d->device).arg(error).arg(d->process.errorString()));
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/bluetoothlistener.h b/src/runonphone/symbianutils/bluetoothlistener.h
new file mode 100644
index 000000000..38757275e
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BLUETOOTHLISTENER_H
+#define BLUETOOTHLISTENER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QProcess>
+
+namespace trk {
+struct BluetoothListenerPrivate;
+
+/* BluetoothListener: Starts a helper process watching connections on a
+ * Bluetooth device, Linux only:
+ * The rfcomm command is used. It process can be started in the background
+ * while connection attempts (TrkDevice::open()) are made in the foreground. */
+
+class SYMBIANUTILS_EXPORT BluetoothListener : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(BluetoothListener)
+public:
+ // The Mode property must be set before calling start().
+ enum Mode {
+ Listen, /* Terminate after client closed (read: Trk app
+ * on the phone terminated or disconnected).*/
+ Watch // Keep running, watch for next connection from client
+ };
+
+ explicit BluetoothListener(QObject *parent = 0);
+ virtual ~BluetoothListener();
+
+ Mode mode() const;
+ void setMode(Mode m);
+
+ bool start(const QString &device, QString *errorMessage);
+
+ // Print messages on the console.
+ bool printConsoleMessages() const;
+ void setPrintConsoleMessages(bool p);
+
+signals:
+ void terminated();
+ void message(const QString &);
+
+public slots:
+ void emitMessage(const QString &m); // accessed by starter
+
+private slots:
+ void slotStdOutput();
+ void slotStdError();
+ void slotProcessFinished(int, QProcess::ExitStatus);
+ void slotProcessError(QProcess::ProcessError error);
+
+private:
+ int terminateProcess();
+
+ BluetoothListenerPrivate *d;
+};
+
+} // namespace trk
+
+#endif // BLUETOOTHLISTENER_H
diff --git a/src/runonphone/symbianutils/bluetoothlistener_gui.cpp b/src/runonphone/symbianutils/bluetoothlistener_gui.cpp
new file mode 100644
index 000000000..c3f36b23a
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener_gui.cpp
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "bluetoothlistener_gui.h"
+#include "bluetoothlistener.h"
+#include "communicationstarter.h"
+
+#include <QtGui/QMessageBox>
+#include <QtGui/QPushButton>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+namespace trk {
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartCommunication(BaseCommunicationStarter &starter,
+ const QString &msgBoxTitle,
+ const QString &msgBoxText,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ errorMessage->clear();
+ // Initial connection attempt.
+ switch (starter.start()) {
+ case BaseCommunicationStarter::Started:
+ break;
+ case BaseCommunicationStarter::ConnectionSucceeded:
+ return PromptStartCommunicationConnected;
+ case BaseCommunicationStarter::StartError:
+ *errorMessage = starter.errorString();
+ return PromptStartCommunicationError;
+ }
+ // Run the starter with the event loop of a message box, have the box
+ // closed by the signals of the starter.
+ QMessageBox messageBox(QMessageBox::Information, msgBoxTitle, msgBoxText, QMessageBox::Cancel, msgBoxParent);
+ QObject::connect(&starter, SIGNAL(connected()), &messageBox, SLOT(close()));
+ QObject::connect(&starter, SIGNAL(timeout()), &messageBox, SLOT(close()));
+ messageBox.exec();
+ // Only starter.state() is reliable here to obtain the state.
+ switch (starter.state()) {
+ case AbstractBluetoothStarter::Running:
+ *errorMessage = QCoreApplication::translate("trk::promptStartCommunication", "Connection on %1 canceled.").arg(starter.device());
+ return PromptStartCommunicationCanceled;
+ case AbstractBluetoothStarter::TimedOut:
+ *errorMessage = starter.errorString();
+ return PromptStartCommunicationError;
+ case AbstractBluetoothStarter::Connected:
+ break;
+ }
+ return PromptStartCommunicationConnected;
+}
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartSerial(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for App TRK");
+ const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for App TRK to start on %1...").arg(starter.device());
+ return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
+}
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartBluetooth(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage)
+{
+ const QString title = QCoreApplication::translate("trk::promptStartCommunication", "Waiting for Bluetooth Connection");
+ const QString message = QCoreApplication::translate("trk::promptStartCommunication", "Connecting to %1...").arg(starter.device());
+ return promptStartCommunication(starter, title, message, msgBoxParent, errorMessage);
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/bluetoothlistener_gui.h b/src/runonphone/symbianutils/bluetoothlistener_gui.h
new file mode 100644
index 000000000..d0edc7a26
--- /dev/null
+++ b/src/runonphone/symbianutils/bluetoothlistener_gui.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BLUETOOTHLISTENER_GUI_H
+#define BLUETOOTHLISTENER_GUI_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QWidget;
+QT_END_NAMESPACE
+
+namespace trk {
+class BaseCommunicationStarter;
+
+/* promptStartCommunication(): Convenience functions that
+ * prompt the user to start a communication (launching or
+ * connecting TRK) using a modal message box in which they can cancel.
+ * Pass in the starter with device and parameters set up. */
+
+enum PromptStartCommunicationResult {
+ PromptStartCommunicationConnected,
+ PromptStartCommunicationCanceled,
+ PromptStartCommunicationError
+};
+
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartCommunication(BaseCommunicationStarter &starter,
+ const QString &msgBoxTitle,
+ const QString &msgBoxText,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+
+// Convenience to start a serial connection (messages prompting
+// to launch Trk).
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartSerial(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+
+// Convenience to start blue tooth connection (messages
+// prompting to connect).
+SYMBIANUTILS_EXPORT PromptStartCommunicationResult
+ promptStartBluetooth(BaseCommunicationStarter &starter,
+ QWidget *msgBoxParent,
+ QString *errorMessage);
+} // namespace trk
+
+#endif // BLUETOOTHLISTENER_GUI_H
diff --git a/src/runonphone/symbianutils/callback.h b/src/runonphone/symbianutils/callback.h
new file mode 100644
index 000000000..4ee77ea31
--- /dev/null
+++ b/src/runonphone/symbianutils/callback.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_CALLBACK_H
+#define DEBUGGER_CALLBACK_H
+
+#include "symbianutils_global.h"
+
+namespace trk {
+namespace Internal {
+
+/* Helper class for the 1-argument functor:
+ * Cloneable base class for the implementation which is
+ * invokeable with the argument. */
+template <class Argument>
+class CallbackImplBase
+{
+ Q_DISABLE_COPY(CallbackImplBase)
+public:
+ CallbackImplBase() {}
+ virtual CallbackImplBase *clone() const = 0;
+ virtual void invoke(Argument a) = 0;
+ virtual ~CallbackImplBase() {}
+};
+
+/* Helper class for the 1-argument functor: Implementation for
+ * a class instance with a member function pointer. */
+template <class Class, class Argument>
+class CallbackMemberPtrImpl : public CallbackImplBase<Argument>
+{
+public:
+ typedef void (Class::*MemberFuncPtr)(Argument);
+
+ CallbackMemberPtrImpl(Class *instance,
+ MemberFuncPtr memberFunc) :
+ m_instance(instance),
+ m_memberFunc(memberFunc) {}
+
+ virtual CallbackImplBase<Argument> *clone() const
+ {
+ return new CallbackMemberPtrImpl<Class, Argument>(m_instance, m_memberFunc);
+ }
+
+ virtual void invoke(Argument a)
+ { (m_instance->*m_memberFunc)(a); }
+private:
+ Class *m_instance;
+ MemberFuncPtr m_memberFunc;
+};
+
+} // namespace Internal
+
+/* Default-constructible, copyable 1-argument functor providing an
+ * operator()(Argument) that invokes a member function of a class:
+ * \code
+class Foo {
+public:
+ void print(const std::string &);
+};
+...
+Foo foo;
+Callback<const std::string &> f1(&foo, &Foo::print);
+f1("test");
+\endcode */
+
+template <class Argument>
+class Callback
+{
+public:
+ Callback() : m_impl(0) {}
+
+ template <class Class>
+ Callback(Class *instance, void (Class::*memberFunc)(Argument)) :
+ m_impl(new Internal::CallbackMemberPtrImpl<Class,Argument>(instance, memberFunc))
+ {}
+
+ ~Callback()
+ {
+ clean();
+ }
+
+ Callback(const Callback &rhs) :
+ m_impl(0)
+ {
+ if (rhs.m_impl)
+ m_impl = rhs.m_impl->clone();
+ }
+
+ Callback &operator=(const Callback &rhs)
+ {
+ if (this != &rhs) {
+ clean();
+ if (rhs.m_impl)
+ m_impl = rhs.m_impl->clone();
+ }
+ return *this;
+ }
+
+ bool isNull() const { return m_impl == 0; }
+ operator bool() const { return !isNull(); }
+
+ void operator()(Argument a)
+ {
+ if (m_impl)
+ m_impl->invoke(a);
+ }
+
+private:
+ void clean()
+ {
+ if (m_impl) {
+ delete m_impl;
+ m_impl = 0;
+ }
+ }
+
+ Internal::CallbackImplBase<Argument> *m_impl;
+};
+
+} // namespace trk
+
+#endif // DEBUGGER_CALLBACK_H
diff --git a/src/runonphone/symbianutils/communicationstarter.cpp b/src/runonphone/symbianutils/communicationstarter.cpp
new file mode 100644
index 000000000..602786b61
--- /dev/null
+++ b/src/runonphone/symbianutils/communicationstarter.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "communicationstarter.h"
+#include "bluetoothlistener.h"
+#include "trkdevice.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QEventLoop>
+
+namespace trk {
+
+// --------------- AbstractBluetoothStarter
+struct BaseCommunicationStarterPrivate {
+ explicit BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d);
+
+ const BaseCommunicationStarter::TrkDevicePtr trkDevice;
+ BluetoothListener *listener;
+ QTimer *timer;
+ int intervalMS;
+ int attempts;
+ int n;
+ QString errorString;
+ BaseCommunicationStarter::State state;
+};
+
+BaseCommunicationStarterPrivate::BaseCommunicationStarterPrivate(const BaseCommunicationStarter::TrkDevicePtr &d) :
+ trkDevice(d),
+ listener(0),
+ timer(0),
+ intervalMS(1000),
+ attempts(-1),
+ n(0),
+ state(BaseCommunicationStarter::TimedOut)
+{
+}
+
+BaseCommunicationStarter::BaseCommunicationStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
+ QObject(parent),
+ d(new BaseCommunicationStarterPrivate(trkDevice))
+{
+}
+
+BaseCommunicationStarter::~BaseCommunicationStarter()
+{
+ stopTimer();
+ delete d;
+}
+
+void BaseCommunicationStarter::stopTimer()
+{
+ if (d->timer && d->timer->isActive())
+ d->timer->stop();
+}
+
+bool BaseCommunicationStarter::initializeStartupResources(QString *errorMessage)
+{
+ errorMessage->clear();
+ return true;
+}
+
+BaseCommunicationStarter::StartResult BaseCommunicationStarter::start()
+{
+ if (state() == Running) {
+ d->errorString = QLatin1String("Internal error, attempt to re-start BaseCommunicationStarter.\n");
+ return StartError;
+ }
+ // Before we instantiate timers, and such, try to open the device,
+ // which should succeed if another listener is already running in
+ // 'Watch' mode
+ if (d->trkDevice->open(&(d->errorString)))
+ return ConnectionSucceeded;
+ // Pull up resources for next attempt
+ d->n = 0;
+ if (!initializeStartupResources(&(d->errorString)))
+ return StartError;
+ // Start timer
+ if (!d->timer) {
+ d->timer = new QTimer;
+ connect(d->timer, SIGNAL(timeout()), this, SLOT(slotTimer()));
+ }
+ d->timer->setInterval(d->intervalMS);
+ d->timer->setSingleShot(false);
+ d->timer->start();
+ d->state = Running;
+ return Started;
+}
+
+BaseCommunicationStarter::State BaseCommunicationStarter::state() const
+{
+ return d->state;
+}
+
+int BaseCommunicationStarter::intervalMS() const
+{
+ return d->intervalMS;
+}
+
+void BaseCommunicationStarter::setIntervalMS(int i)
+{
+ d->intervalMS = i;
+ if (d->timer)
+ d->timer->setInterval(i);
+}
+
+int BaseCommunicationStarter::attempts() const
+{
+ return d->attempts;
+}
+
+void BaseCommunicationStarter::setAttempts(int a)
+{
+ d->attempts = a;
+}
+
+QString BaseCommunicationStarter::device() const
+{
+ return d->trkDevice->port();
+}
+
+QString BaseCommunicationStarter::errorString() const
+{
+ return d->errorString;
+}
+
+void BaseCommunicationStarter::slotTimer()
+{
+ ++d->n;
+ // Check for timeout
+ if (d->attempts >= 0 && d->n >= d->attempts) {
+ stopTimer();
+ d->errorString = tr("%1: timed out after %n attempts using an interval of %2ms.", 0, d->n)
+ .arg(d->trkDevice->port()).arg(d->intervalMS);
+ d->state = TimedOut;
+ emit timeout();
+ } else {
+ // Attempt n to connect?
+ if (d->trkDevice->open(&(d->errorString))) {
+ stopTimer();
+ const QString msg = tr("%1: Connection attempt %2 succeeded.").arg(d->trkDevice->port()).arg(d->n);
+ emit message(msg);
+ d->state = Connected;
+ emit connected();
+ } else {
+ const QString msg = tr("%1: Connection attempt %2 failed: %3 (retrying)...")
+ .arg(d->trkDevice->port()).arg(d->n).arg(d->errorString);
+ emit message(msg);
+ }
+ }
+}
+
+// --------------- AbstractBluetoothStarter
+
+AbstractBluetoothStarter::AbstractBluetoothStarter(const TrkDevicePtr &trkDevice, QObject *parent) :
+ BaseCommunicationStarter(trkDevice, parent)
+{
+}
+
+bool AbstractBluetoothStarter::initializeStartupResources(QString *errorMessage)
+{
+ // Create the listener and forward messages to it.
+ BluetoothListener *listener = createListener();
+ connect(this, SIGNAL(message(QString)), listener, SLOT(emitMessage(QString)));
+ return listener->start(device(), errorMessage);
+}
+
+// -------- ConsoleBluetoothStarter
+ConsoleBluetoothStarter::ConsoleBluetoothStarter(const TrkDevicePtr &trkDevice,
+ QObject *listenerParent,
+ QObject *parent) :
+AbstractBluetoothStarter(trkDevice, parent),
+m_listenerParent(listenerParent)
+{
+}
+
+BluetoothListener *ConsoleBluetoothStarter::createListener()
+{
+ BluetoothListener *rc = new BluetoothListener(m_listenerParent);
+ rc->setMode(BluetoothListener::Listen);
+ rc->setPrintConsoleMessages(true);
+ return rc;
+}
+
+bool ConsoleBluetoothStarter::startBluetooth(const TrkDevicePtr &trkDevice,
+ QObject *listenerParent,
+ int attempts,
+ QString *errorMessage)
+{
+ // Set up a console starter to print to stdout.
+ ConsoleBluetoothStarter starter(trkDevice, listenerParent);
+ starter.setAttempts(attempts);
+ switch (starter.start()) {
+ case Started:
+ break;
+ case ConnectionSucceeded:
+ return true;
+ case StartError:
+ *errorMessage = starter.errorString();
+ return false;
+ }
+ // Run the starter with an event loop. @ToDo: Implement
+ // some asynchronous keypress read to cancel.
+ QEventLoop eventLoop;
+ connect(&starter, SIGNAL(connected()), &eventLoop, SLOT(quit()));
+ connect(&starter, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
+ eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
+ if (starter.state() != AbstractBluetoothStarter::Connected) {
+ *errorMessage = starter.errorString();
+ return false;
+ }
+ return true;
+}
+} // namespace trk
diff --git a/src/runonphone/symbianutils/communicationstarter.h b/src/runonphone/symbianutils/communicationstarter.h
new file mode 100644
index 000000000..9fec5893c
--- /dev/null
+++ b/src/runonphone/symbianutils/communicationstarter.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef COMMUNICATIONSTARTER_H
+#define COMMUNICATIONSTARTER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QObject>
+
+namespace trk {
+class TrkDevice;
+class BluetoothListener;
+struct BaseCommunicationStarterPrivate;
+
+/* BaseCommunicationStarter: A QObject that repeatedly tries to open a
+ * trk device until a connection succeeds or a timeout occurs (emitting
+ * signals), allowing to do something else in the foreground (local event loop
+ * [say QMessageBox] or some asynchronous operation). If the initial
+ * connection attempt in start() fails, the
+ * virtual initializeStartupResources() is called to initialize resources
+ * required to pull up the communication (namely Bluetooth listeners).
+ * The base class can be used as is to prompt the user to launch App TRK for a
+ * serial communication as this requires no further resource setup. */
+
+class SYMBIANUTILS_EXPORT BaseCommunicationStarter : public QObject {
+ Q_OBJECT
+ Q_DISABLE_COPY(BaseCommunicationStarter)
+public:
+ typedef QSharedPointer<TrkDevice> TrkDevicePtr;
+
+ enum State { Running, Connected, TimedOut };
+
+ explicit BaseCommunicationStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
+ virtual ~BaseCommunicationStarter();
+
+ int intervalMS() const;
+ void setIntervalMS(int i);
+
+ int attempts() const;
+ void setAttempts(int a);
+
+ QString device() const;
+
+ State state() const;
+ QString errorString() const;
+
+ enum StartResult {
+ Started, // Starter is now running.
+ ConnectionSucceeded, /* Initial connection attempt succeeded,
+ * no need to keep running. */
+ StartError // Error occurred during start.
+ };
+
+ StartResult start();
+
+signals:
+ void connected();
+ void timeout();
+ void message(const QString &);
+
+private slots:
+ void slotTimer();
+
+protected:
+ virtual bool initializeStartupResources(QString *errorMessage);
+
+private:
+ inline void stopTimer();
+
+ BaseCommunicationStarterPrivate *d;
+};
+
+/* AbstractBluetoothStarter: Repeatedly tries to open a trk Bluetooth
+ * device. Note that in case a Listener is already running mode, the
+ * connection will succeed immediately.
+ * initializeStartupResources() is implemented to fire up the listener.
+ * Introduces a new virtual createListener() that derived classes must
+ * implement as a factory function that creates and sets up the
+ * listener (mode, message connection, etc). */
+
+class SYMBIANUTILS_EXPORT AbstractBluetoothStarter : public BaseCommunicationStarter {
+ Q_OBJECT
+ Q_DISABLE_COPY(AbstractBluetoothStarter)
+public:
+
+protected:
+ explicit AbstractBluetoothStarter(const TrkDevicePtr& trkDevice, QObject *parent = 0);
+
+ // Implemented to fire up the listener.
+ virtual bool initializeStartupResources(QString *errorMessage);
+ // New virtual: Overwrite to create and parametrize the listener.
+ virtual BluetoothListener *createListener() = 0;
+};
+
+/* ConsoleBluetoothStarter: Convenience class for console processes. Creates a
+ * listener in "Listen" mode with the messages redirected to standard output. */
+
+class SYMBIANUTILS_EXPORT ConsoleBluetoothStarter : public AbstractBluetoothStarter {
+ Q_OBJECT
+ Q_DISABLE_COPY(ConsoleBluetoothStarter)
+public:
+ static bool startBluetooth(const TrkDevicePtr& trkDevice,
+ QObject *listenerParent,
+ int attempts,
+ QString *errorMessage);
+
+protected:
+ virtual BluetoothListener *createListener();
+
+private:
+ explicit ConsoleBluetoothStarter(const TrkDevicePtr& trkDevice,
+ QObject *listenerParent,
+ QObject *parent = 0);
+
+ QObject *m_listenerParent;
+};
+
+} // namespace trk
+
+#endif // COMMUNICATIONSTARTER_H
diff --git a/src/runonphone/symbianutils/json.cpp b/src/runonphone/symbianutils/json.cpp
new file mode 100644
index 000000000..755df5e1d
--- /dev/null
+++ b/src/runonphone/symbianutils/json.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "json.h"
+
+#ifdef TODO_USE_CREATOR
+#include <utils/qtcassert.h>
+#endif // TODO_USE_CREATOR
+
+#include <QtCore/QByteArray>
+#include <QtCore/QTextStream>
+#include <QtCore/QDebug>
+#include <QtCore/QStringList>
+
+#include <ctype.h>
+
+//#define DEBUG_JASON
+#ifdef DEBUG_JASON
+#define JDEBUG(s) qDebug() << s
+#else
+#define JDEBUG(s)
+#endif
+
+namespace tcftrk {
+
+static void skipSpaces(const char *&from, const char *to)
+{
+ while (from != to && isspace(*from))
+ ++from;
+}
+
+QTextStream &operator<<(QTextStream &os, const JsonValue &mi)
+{
+ return os << mi.toString();
+}
+
+void JsonValue::parsePair(const char *&from, const char *to)
+{
+ skipSpaces(from, to);
+ JDEBUG("parsePair: " << QByteArray(from, to - from));
+ m_name = parseCString(from, to);
+ skipSpaces(from, to);
+ while (from < to && *from != ':') {
+ JDEBUG("not a colon" << *from);
+ ++from;
+ }
+ ++from;
+ parseValue(from, to);
+ skipSpaces(from, to);
+}
+
+QByteArray JsonValue::parseNumber(const char *&from, const char *to)
+{
+ QByteArray result;
+ if (from < to && *from == '-') // Leading '-'.
+ result.append(*from++);
+ while (from < to && *from >= '0' && *from <= '9')
+ result.append(*from++);
+ return result;
+}
+
+QByteArray JsonValue::parseCString(const char *&from, const char *to)
+{
+ QByteArray result;
+ JDEBUG("parseCString: " << QByteArray(from, to - from));
+ if (*from != '"') {
+ qDebug() << "JSON Parse Error, double quote expected";
+ ++from; // So we don't hang
+ return QByteArray();
+ }
+ const char *ptr = from;
+ ++ptr;
+ while (ptr < to) {
+ if (*ptr == '"') {
+ ++ptr;
+ result = QByteArray(from + 1, ptr - from - 2);
+ break;
+ }
+ if (*ptr == '\\') {
+ ++ptr;
+ if (ptr == to) {
+ qDebug() << "JSON Parse Error, unterminated backslash escape";
+ from = ptr; // So we don't hang
+ return QByteArray();
+ }
+ }
+ ++ptr;
+ }
+ from = ptr;
+
+ int idx = result.indexOf('\\');
+ if (idx >= 0) {
+ char *dst = result.data() + idx;
+ const char *src = dst + 1, *end = result.data() + result.length();
+ do {
+ char c = *src++;
+ switch (c) {
+ case 'a': *dst++ = '\a'; break;
+ case 'b': *dst++ = '\b'; break;
+ case 'f': *dst++ = '\f'; break;
+ case 'n': *dst++ = '\n'; break;
+ case 'r': *dst++ = '\r'; break;
+ case 't': *dst++ = '\t'; break;
+ case 'v': *dst++ = '\v'; break;
+ case '"': *dst++ = '"'; break;
+ case '\\': *dst++ = '\\'; break;
+ default:
+ {
+ int chars = 0;
+ uchar prod = 0;
+ forever {
+ if (c < '0' || c > '7') {
+ --src;
+ break;
+ }
+ prod = prod * 8 + c - '0';
+ if (++chars == 3 || src == end)
+ break;
+ c = *src++;
+ }
+ if (!chars) {
+ qDebug() << "JSON Parse Error, unrecognized backslash escape";
+ return QByteArray();
+ }
+ *dst++ = prod;
+ }
+ }
+ while (src != end) {
+ char c = *src++;
+ if (c == '\\')
+ break;
+ *dst++ = c;
+ }
+ } while (src != end);
+ *dst = 0;
+ result.truncate(dst - result.data());
+ }
+
+ JDEBUG("parseCString, got " << result);
+ return result;
+}
+
+
+
+void JsonValue::parseValue(const char *&from, const char *to)
+{
+ JDEBUG("parseValue: " << QByteArray(from, to - from));
+ switch (*from) {
+ case '{':
+ parseObject(from, to);
+ break;
+ case 't':
+ if (to - from >= 4 && qstrncmp(from, "true", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'f':
+ if (to - from >= 5 && qstrncmp(from, "false", 5) == 0) {
+ m_data = QByteArray(from, 5);
+ from += m_data.size();
+ m_type = Boolean;
+ }
+ break;
+ case 'n':
+ if (to - from >= 4 && qstrncmp(from, "null", 4) == 0) {
+ m_data = QByteArray(from, 4);
+ from += m_data.size();
+ m_type = NullObject;
+ }
+ break;
+ case '[':
+ parseArray(from, to);
+ break;
+ case '"':
+ m_type = String;
+ m_data = parseCString(from, to);
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case '-':
+ m_type = Number;
+ m_data = parseNumber(from, to);
+ default:
+ break;
+ }
+}
+
+void JsonValue::parseObject(const char *&from, const char *to)
+{
+ JDEBUG("parseObject: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '{', /**/);
+#endif
+ ++from;
+ m_type = Object;
+ while (from < to) {
+ if (*from == '}') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parsePair(from, to);
+ if (!child.isValid())
+ return;
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::parseArray(const char *&from, const char *to)
+{
+ JDEBUG("parseArray: " << QByteArray(from, to - from));
+#ifdef TODO_USE_CREATOR
+ QTC_ASSERT(*from == '[', /**/);
+#endif
+ ++from;
+ m_type = Array;
+ while (from < to) {
+ if (*from == ']') {
+ ++from;
+ break;
+ }
+ JsonValue child;
+ child.parseValue(from, to);
+ if (child.isValid())
+ m_children += child;
+ if (*from == ',')
+ ++from;
+ }
+}
+
+void JsonValue::setStreamOutput(const QByteArray &name, const QByteArray &content)
+{
+ if (content.isEmpty())
+ return;
+ JsonValue child;
+ child.m_type = String;
+ child.m_name = name;
+ child.m_data = content;
+ m_children += child;
+ if (m_type == Invalid)
+ m_type = Object;
+}
+
+static QByteArray ind(int indent)
+{
+ return QByteArray(2 * indent, ' ');
+}
+
+void JsonValue::dumpChildren(QByteArray * str, bool multiline, int indent) const
+{
+ for (int i = 0; i < m_children.size(); ++i) {
+ if (i != 0) {
+ *str += ',';
+ if (multiline)
+ *str += '\n';
+ }
+ if (multiline)
+ *str += ind(indent);
+ *str += m_children.at(i).toString(multiline, indent);
+ }
+}
+
+class MyString : public QString {
+public:
+ ushort at(int i) const { return constData()[i].unicode(); }
+};
+
+template<class ST, typename CT>
+inline ST escapeCStringTpl(const ST &ba)
+{
+ ST ret;
+ ret.reserve(ba.length() * 2);
+ for (int i = 0; i < ba.length(); ++i) {
+ CT c = ba.at(i);
+ switch (c) {
+ case '\\': ret += "\\\\"; break;
+ case '\a': ret += "\\a"; break;
+ case '\b': ret += "\\b"; break;
+ case '\f': ret += "\\f"; break;
+ case '\n': ret += "\\n"; break;
+ case '\r': ret += "\\r"; break;
+ case '\t': ret += "\\t"; break;
+ case '\v': ret += "\\v"; break;
+ case '"': ret += "\\\""; break;
+ default:
+ if (c < 32 || c == 127) {
+ ret += '\\';
+ ret += '0' + (c >> 6);
+ ret += '0' + ((c >> 3) & 7);
+ ret += '0' + (c & 7);
+ } else {
+ ret += c;
+ }
+ }
+ }
+ return ret;
+}
+
+QString JsonValue::escapeCString(const QString &ba)
+{
+ return escapeCStringTpl<MyString, ushort>(static_cast<const MyString &>(ba));
+}
+
+QByteArray JsonValue::escapeCString(const QByteArray &ba)
+{
+ return escapeCStringTpl<QByteArray, uchar>(ba);
+}
+
+QByteArray JsonValue::toString(bool multiline, int indent) const
+{
+ QByteArray result;
+ switch (m_type) {
+ case Invalid:
+ if (multiline)
+ result += ind(indent) + "Invalid\n";
+ else
+ result += "Invalid";
+ break;
+ case String:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ result += '"' + escapeCString(m_data) + '"';
+ break;
+ case Number:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Boolean:
+ case NullObject:
+ if (!m_name.isEmpty())
+ result += '"' + m_name + "\":";
+ result += m_data;
+ break;
+ case Object:
+ if (!m_name.isEmpty())
+ result += m_name + '=';
+ if (multiline) {
+ result += "{\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "}";
+ } else {
+ result += "{";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "}";
+ }
+ break;
+ case Array:
+ if (!m_name.isEmpty())
+ result += m_name + "=";
+ if (multiline) {
+ result += "[\n";
+ dumpChildren(&result, multiline, indent + 1);
+ result += '\n' + ind(indent) + "]";
+ } else {
+ result += "[";
+ dumpChildren(&result, multiline, indent + 1);
+ result += "]";
+ }
+ break;
+ }
+ return result;
+}
+
+void JsonValue::fromString(const QByteArray &ba)
+{
+ const char *from = ba.constBegin();
+ const char *to = ba.constEnd();
+ parseValue(from, to);
+}
+
+JsonValue JsonValue::findChild(const char *name) const
+{
+ for (int i = 0; i < m_children.size(); ++i)
+ if (m_children.at(i).m_name == name)
+ return m_children.at(i);
+ return JsonValue();
+}
+
+void JsonInputStream::appendCString(const char *s)
+{
+ m_target.append('"');
+ for (const char *p = s; *p; p++) {
+ if (*p == '"' || *p == '\\')
+ m_target.append('\\');
+ m_target.append(*p);
+ }
+ m_target.append('"');
+}
+
+void JsonInputStream::appendString(const QString &in)
+{
+ if (in.isEmpty()) {
+ m_target.append("\"\"");
+ return;
+ }
+
+ const QChar doubleQuote('"');
+ const QChar backSlash('\\');
+ QString rc;
+ const int inSize = in.size();
+ rc.reserve(in.size() + 5);
+ rc.append(doubleQuote);
+ for (int i = 0; i < inSize; i++) {
+ const QChar c = in.at(i);
+ if (c == doubleQuote || c == backSlash)
+ rc.append(backSlash);
+ rc.append(c);
+ }
+ rc.append(doubleQuote);
+ m_target.append(rc.toUtf8());
+ return;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QStringList &in)
+{
+ m_target.append('[');
+ const int count = in.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendString(in.at(i));
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(const QVector<QByteArray> &ba)
+{
+ m_target.append('[');
+ const int count = ba.size();
+ for (int i = 0 ; i < count; i++) {
+ if (i)
+ m_target.append(',');
+ appendCString(ba.at(i).constData());
+ }
+ m_target.append(']');
+ return *this;
+}
+
+JsonInputStream &JsonInputStream::operator<<(bool b)
+{
+ m_target.append(b ? "true" : "false");
+ return *this;
+}
+
+} // namespace tcftrk
+
diff --git a/src/runonphone/symbianutils/json.h b/src/runonphone/symbianutils/json.h
new file mode 100644
index 000000000..a9190f812
--- /dev/null
+++ b/src/runonphone/symbianutils/json.h
@@ -0,0 +1,149 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_JSON_H
+#define SYMBIANUTILS_JSON_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+namespace tcftrk {
+
+class SYMBIANUTILS_EXPORT JsonValue
+{
+public:
+ JsonValue() : m_type(Invalid) {}
+ explicit JsonValue(const QByteArray &str) { fromString(str); }
+
+ QByteArray m_name;
+ QByteArray m_data;
+ QList<JsonValue> m_children;
+
+ enum Type {
+ Invalid,
+ String,
+ Number,
+ Boolean,
+ Object,
+ NullObject,
+ Array,
+ };
+
+ Type m_type;
+
+ inline Type type() const { return m_type; }
+ inline QByteArray name() const { return m_name; }
+ inline bool hasName(const char *name) const { return m_name == name; }
+
+ inline bool isValid() const { return m_type != Invalid; }
+ inline bool isNumber() const { return m_type == Number; }
+ inline bool isString() const { return m_type == String; }
+ inline bool isObject() const { return m_type == Object; }
+ inline bool isArray() const { return m_type == Array; }
+
+
+ inline QByteArray data() const { return m_data; }
+ inline const QList<JsonValue> &children() const { return m_children; }
+ inline int childCount() const { return m_children.size(); }
+
+ const JsonValue &childAt(int index) const { return m_children[index]; }
+ JsonValue &childAt(int index) { return m_children[index]; }
+ JsonValue findChild(const char *name) const;
+
+ QByteArray toString(bool multiline = false, int indent = 0) const;
+ void fromString(const QByteArray &str);
+ void setStreamOutput(const QByteArray &name, const QByteArray &content);
+
+private:
+ static QByteArray parseCString(const char *&from, const char *to);
+ static QByteArray parseNumber(const char *&from, const char *to);
+ static QByteArray escapeCString(const QByteArray &ba);
+ static QString escapeCString(const QString &ba);
+ void parsePair(const char *&from, const char *to);
+ void parseValue(const char *&from, const char *to);
+ void parseObject(const char *&from, const char *to);
+ void parseArray(const char *&from, const char *to);
+
+ void dumpChildren(QByteArray *str, bool multiline, int indent) const;
+};
+
+/* Thin wrapper around QByteArray for formatting JSON input. Use as in:
+ * JsonInputStream(byteArray) << '{' << "bla" << ':' << "blup" << '}';
+ * Note that strings get double quotes and JSON-escaping, characters should be
+ * used for the array/hash delimiters.
+ * */
+class SYMBIANUTILS_EXPORT JsonInputStream {
+public:
+ explicit JsonInputStream(QByteArray &a) : m_target(a) {}
+
+ JsonInputStream &operator<<(char c) { m_target.append(c); return *this; }
+ JsonInputStream &operator<<(const char *c) { appendCString(c); return *this; }
+ JsonInputStream &operator<<(const QByteArray &a) { appendCString(a.constData()); return *this; }
+ JsonInputStream &operator<<(const QString &c) { appendString(c); return *this; }
+
+ // Format as array
+ JsonInputStream &operator<<(const QStringList &c);
+
+ // Format as array
+ JsonInputStream &operator<<(const QVector<QByteArray> &ba);
+
+ JsonInputStream &operator<<(bool b);
+
+ JsonInputStream &operator<<(int i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(unsigned i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+ JsonInputStream &operator<<(quint64 i)
+ { m_target.append(QByteArray::number(i)); return *this; }
+
+private:
+ void appendString(const QString &);
+ void appendCString(const char *c);
+
+ QByteArray &m_target;
+};
+
+} // namespace tcftrk
+
+#endif // SYMBIANUTILS_JSON_H
diff --git a/src/runonphone/symbianutils/launcher.cpp b/src/runonphone/symbianutils/launcher.cpp
new file mode 100644
index 000000000..a5d3d68ae
--- /dev/null
+++ b/src/runonphone/symbianutils/launcher.cpp
@@ -0,0 +1,1036 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "launcher.h"
+#include "trkutils.h"
+#include "trkutils_p.h"
+#include "trkdevice.h"
+#include "bluetoothlistener.h"
+#include "symbiandevicemanager.h"
+
+#include <QtCore/QTimer>
+#include <QtCore/QDateTime>
+#include <QtCore/QVariant>
+#include <QtCore/QDebug>
+#include <QtCore/QQueue>
+#include <QtCore/QFile>
+#include <QtCore/QScopedPointer>
+
+#include <cstdio>
+
+namespace trk {
+
+struct CrashReportState {
+ CrashReportState();
+ void clear();
+
+ typedef uint Thread;
+ typedef QList<Thread> Threads;
+ Threads threads;
+
+ QList<uint> registers;
+ QByteArray stack;
+ uint sp;
+ uint fetchingStackPID;
+ uint fetchingStackTID;
+};
+
+CrashReportState::CrashReportState()
+{
+ clear();
+}
+
+void CrashReportState::clear()
+{
+ threads.clear();
+ stack.clear();
+ sp = fetchingStackPID = fetchingStackTID = 0;
+}
+
+struct LauncherPrivate {
+ struct CopyState {
+ QString sourceFileName;
+ QString destinationFileName;
+ uint copyFileHandle;
+ QScopedPointer<QByteArray> data;
+ qint64 position;
+ QScopedPointer<QFile> localFile;
+ };
+
+ explicit LauncherPrivate(const TrkDevicePtr &d);
+
+ TrkDevicePtr m_device;
+ QByteArray m_trkReadBuffer;
+ Launcher::State m_state;
+
+ void logMessage(const QString &msg);
+ // Debuggee state
+ Session m_session; // global-ish data (process id, target information)
+
+ CopyState m_copyState;
+ CopyState m_downloadState;
+ QString m_fileName;
+ QStringList m_commandLineArgs;
+ QString m_installFileName;
+ int m_verbose;
+ Launcher::Actions m_startupActions;
+ bool m_closeDevice;
+ CrashReportState m_crashReportState;
+};
+
+LauncherPrivate::LauncherPrivate(const TrkDevicePtr &d) :
+ m_device(d),
+ m_state(Launcher::Disconnected),
+ m_verbose(0),
+ m_closeDevice(true)
+{
+ if (m_device.isNull())
+ m_device = TrkDevicePtr(new TrkDevice);
+}
+
+Launcher::Launcher(Actions startupActions,
+ const TrkDevicePtr &dev,
+ QObject *parent) :
+ QObject(parent),
+ d(new LauncherPrivate(dev))
+{
+ d->m_startupActions = startupActions;
+ connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));
+}
+
+Launcher::~Launcher()
+{
+ // Destroyed before protocol was through: Close
+ if (d->m_closeDevice && d->m_device->isOpen())
+ d->m_device->close();
+ emit destroyed(d->m_device->port());
+ logMessage("Shutting down.\n");
+ delete d;
+}
+
+Launcher::State Launcher::state() const
+{
+ return d->m_state;
+}
+
+void Launcher::setState(State s)
+{
+ if (s != d->m_state) {
+ d->m_state = s;
+ emit stateChanged(s);
+ }
+}
+
+void Launcher::addStartupActions(trk::Launcher::Actions startupActions)
+{
+ d->m_startupActions = Actions(d->m_startupActions | startupActions);
+}
+
+void Launcher::setTrkServerName(const QString &name)
+{
+ d->m_device->setPort(name);
+}
+
+QString Launcher::trkServerName() const
+{
+ return d->m_device->port();
+}
+
+TrkDevicePtr Launcher::trkDevice() const
+{
+ return d->m_device;
+}
+
+void Launcher::setFileName(const QString &name)
+{
+ d->m_fileName = name;
+}
+
+void Launcher::setCopyFileName(const QString &srcName, const QString &dstName)
+{
+ d->m_copyState.sourceFileName = srcName;
+ d->m_copyState.destinationFileName = dstName;
+}
+
+void Launcher::setDownloadFileName(const QString &srcName, const QString &dstName)
+{
+ d->m_downloadState.sourceFileName = srcName;
+ d->m_downloadState.destinationFileName = dstName;
+}
+
+void Launcher::setInstallFileName(const QString &name)
+{
+ d->m_installFileName = name;
+}
+
+void Launcher::setCommandLineArgs(const QStringList &args)
+{
+ d->m_commandLineArgs = args;
+}
+
+void Launcher::setSerialFrame(bool b)
+{
+ d->m_device->setSerialFrame(b);
+}
+
+bool Launcher::serialFrame() const
+{
+ return d->m_device->serialFrame();
+}
+
+
+bool Launcher::closeDevice() const
+{
+ return d->m_closeDevice;
+}
+
+void Launcher::setCloseDevice(bool c)
+{
+ d->m_closeDevice = c;
+}
+
+bool Launcher::startServer(QString *errorMessage)
+{
+ errorMessage->clear();
+ d->m_crashReportState.clear();
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ str.setIntegerBase(16);
+ str << "Actions=0x" << d->m_startupActions;
+ str.setIntegerBase(10);
+ str << " Port=" << trkServerName();
+ if (!d->m_fileName.isEmpty())
+ str << " Executable=" << d->m_fileName;
+ if (!d->m_commandLineArgs.isEmpty())
+ str << " Arguments= " << d->m_commandLineArgs.join(QString(QLatin1Char(' ')));
+ if (!d->m_copyState.sourceFileName.isEmpty())
+ str << " Package/Source=" << d->m_copyState.sourceFileName;
+ if (!d->m_copyState.destinationFileName.isEmpty())
+ str << " Remote Package/Destination=" << d->m_copyState.destinationFileName;
+ if (!d->m_downloadState.sourceFileName.isEmpty())
+ str << " Source=" << d->m_downloadState.sourceFileName;
+ if (!d->m_downloadState.destinationFileName.isEmpty())
+ str << " Destination=" << d->m_downloadState.destinationFileName;
+ if (!d->m_installFileName.isEmpty())
+ str << " Install file=" << d->m_installFileName;
+ logMessage(msg);
+ }
+ if (d->m_startupActions & ActionCopy) {
+ if (d->m_copyState.sourceFileName.isEmpty()) {
+ qWarning("No local filename given for copying package.");
+ return false;
+ } else if (d->m_copyState.destinationFileName.isEmpty()) {
+ qWarning("No remote filename given for copying package.");
+ return false;
+ }
+ }
+ if (d->m_startupActions & ActionInstall && d->m_installFileName.isEmpty()) {
+ qWarning("No package name given for installing.");
+ return false;
+ }
+ if (d->m_startupActions & ActionRun && d->m_fileName.isEmpty()) {
+ qWarning("No remote executable given for running.");
+ return false;
+ }
+ if (!d->m_device->isOpen() && !d->m_device->open(errorMessage))
+ return false;
+ setState(Connecting);
+ // Set up the temporary 'waiting' state if we do not get immediate connection
+ QTimer::singleShot(1000, this, SLOT(slotWaitingForTrk()));
+ d->m_device->sendTrkInitialPing();
+ d->m_device->sendTrkMessage(TrkDisconnect); // Disconnect, as trk might be still connected
+ d->m_device->sendTrkMessage(TrkSupported, TrkCallback(this, &Launcher::handleSupportMask));
+ d->m_device->sendTrkMessage(TrkCpuType, TrkCallback(this, &Launcher::handleCpuType));
+ d->m_device->sendTrkMessage(TrkVersions, TrkCallback(this, &Launcher::handleTrkVersion));
+ if (d->m_startupActions != ActionPingOnly)
+ d->m_device->sendTrkMessage(TrkConnect, TrkCallback(this, &Launcher::handleConnect));
+ return true;
+}
+
+void Launcher::slotWaitingForTrk()
+{
+ // Set temporary state if we are still in connected state
+ if (state() == Connecting)
+ setState(WaitingForTrk);
+}
+
+void Launcher::handleConnect(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotConnect(result.errorString());
+ return;
+ }
+ setState(Connected);
+ if (d->m_startupActions & ActionCopy)
+ copyFileToRemote();
+ else if (d->m_startupActions & ActionInstall)
+ installRemotePackageSilently();
+ else if (d->m_startupActions & ActionRun)
+ startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+}
+
+void Launcher::setVerbose(int v)
+{
+ d->m_verbose = v;
+ d->m_device->setVerbose(v);
+}
+
+void Launcher::logMessage(const QString &msg)
+{
+ if (d->m_verbose)
+ qDebug() << "LAUNCHER: " << qPrintable(msg);
+}
+
+void Launcher::handleFinished()
+{
+ if (d->m_closeDevice)
+ d->m_device->close();
+ emit finished();
+}
+
+void Launcher::terminate()
+{
+ switch (state()) {
+ case DeviceDescriptionReceived:
+ case Connected:
+ if (d->m_session.pid) {
+ QByteArray ba;
+ appendShort(&ba, 0x0000, TargetByteOrder);
+ appendInt(&ba, d->m_session.pid, TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkDeleteItem, TrkCallback(this, &Launcher::handleRemoteProcessKilled), ba);
+ return;
+ }
+ if (d->m_copyState.copyFileHandle)
+ closeRemoteFile(true);
+ disconnectTrk();
+ break;
+ case Disconnected:
+ break;
+ case Connecting:
+ case WaitingForTrk:
+ setState(Disconnected);
+ handleFinished();
+ break;
+ }
+}
+
+void Launcher::handleRemoteProcessKilled(const TrkResult &result)
+{
+ Q_UNUSED(result)
+ disconnectTrk();
+}
+
+QString Launcher::msgStopped(uint pid, uint tid, uint address, const QString &why)
+{
+ return QString::fromLatin1("Process %1, thread %2 stopped at 0x%3: %4").
+ arg(pid).arg(tid).arg(address, 0, 16).
+ arg(why.isEmpty() ? QString::fromLatin1("<Unknown reason>") : why);
+}
+
+bool Launcher::parseNotifyStopped(const QByteArray &dataBA,
+ uint *pid, uint *tid, uint *address,
+ QString *why /* = 0 */)
+{
+ if (why)
+ why->clear();
+ *address = *pid = *tid = 0;
+ if (dataBA.size() < 12)
+ return false;
+ const char *data = dataBA.data();
+ *address = extractInt(data);
+ *pid = extractInt(data + 4);
+ *tid = extractInt(data + 8);
+ if (why && dataBA.size() >= 14) {
+ const unsigned short len = extractShort(data + 12);
+ if (len > 0)
+ *why = QString::fromLatin1(data + 14, len);
+ }
+ return true;
+}
+
+void Launcher::handleResult(const TrkResult &result)
+{
+ QByteArray prefix = "READ BUF: ";
+ QByteArray str = result.toString().toUtf8();
+ if (result.isDebugOutput) { // handle application output
+ QString msg;
+ if (result.multiplex == MuxTextTrace) {
+ if (result.data.length() > 8) {
+ quint64 timestamp = extractInt64(result.data) & 0x0FFFFFFFFFFFFFFFULL;
+ quint64 secs = timestamp / 1000000000;
+ quint64 ns = timestamp % 1000000000;
+ msg = QString("[%1.%2] %3").arg(secs).arg(ns).arg(QString(result.data.mid(8)));
+ logMessage("TEXT TRACE: " + msg);
+ }
+ } else {
+ logMessage("APPLICATION OUTPUT: " + stringFromArray(result.data));
+ msg = result.data;
+ }
+ msg.replace("\r\n", "\n");
+ if(!msg.endsWith('\n')) msg.append('\n');
+ emit applicationOutputReceived(msg);
+ return;
+ }
+ switch (result.code) {
+ case TrkNotifyAck:
+ break;
+ case TrkNotifyNak: { // NAK
+ logMessage(prefix + "NAK: " + str);
+ //logMessage(prefix << "TOKEN: " << result.token);
+ logMessage(prefix + "ERROR: " + errorMessage(result.data.at(0)));
+ break;
+ }
+ case TrkNotifyStopped: { // Notified Stopped
+ QString reason;
+ uint pc;
+ uint pid;
+ uint tid;
+ parseNotifyStopped(result.data, &pid, &tid, &pc, &reason);
+ logMessage(prefix + msgStopped(pid, tid, pc, reason));
+ emit(processStopped(pc, pid, tid, reason));
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyException: { // Notify Exception (obsolete)
+ logMessage(prefix + "NOTE: EXCEPTION " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyInternalError: { //
+ logMessage(prefix + "NOTE: INTERNAL ERROR: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+
+ // target->host OS notification
+ case TrkNotifyCreated: { // Notify Created
+
+ if (result.data.size() < 10)
+ break;
+ const char *data = result.data.constData();
+ const byte error = result.data.at(0);
+ Q_UNUSED(error)
+ const byte type = result.data.at(1); // type: 1 byte; for dll item, this value is 2.
+ const uint tid = extractInt(data + 6); //threadID: 4 bytes
+ Q_UNUSED(tid)
+ if (type == kDSOSDLLItem && result.data.size() >=20) {
+ const Library lib = Library(result);
+ d->m_session.libraries.push_back(lib);
+ emit libraryLoaded(lib);
+ }
+ QByteArray ba;
+ ba.append(result.data.mid(2, 8));
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+ break;
+ }
+ case TrkNotifyDeleted: { // NotifyDeleted
+ const ushort itemType = (unsigned char)result.data.at(1);
+ const uint pid = result.data.size() >= 6 ? extractShort(result.data.constData() + 2) : 0;
+ const uint tid = result.data.size() >= 10 ? extractShort(result.data.constData() + 6) : 0;
+ Q_UNUSED(tid)
+ const ushort len = result.data.size() > 12 ? extractShort(result.data.constData() + 10) : ushort(0);
+ const QString name = len ? QString::fromAscii(result.data.mid(12, len)) : QString();
+ logMessage(QString::fromLatin1("%1 %2 UNLOAD: %3").
+ arg(QString::fromAscii(prefix)).arg(itemType ? QLatin1String("LIB") : QLatin1String("PROCESS")).
+ arg(name));
+ d->m_device->sendTrkAck(result.token);
+ if (itemType == kDSOSProcessItem // process
+ && result.data.size() >= 10
+ && d->m_session.pid == extractInt(result.data.data() + 6)) {
+ if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+ }
+ else if (itemType == kDSOSDLLItem && len) {
+ // Remove libraries of process.
+ for (QList<Library>::iterator it = d->m_session.libraries.begin(); it != d->m_session.libraries.end(); ) {
+ if ((*it).pid == pid && (*it).name == name) {
+ emit libraryUnloaded(*it);
+ it = d->m_session.libraries.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ break;
+ }
+ case TrkNotifyProcessorStarted: { // NotifyProcessorStarted
+ logMessage(prefix + "NOTE: PROCESSOR STARTED: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyProcessorStandBy: { // NotifyProcessorStandby
+ logMessage(prefix + "NOTE: PROCESSOR STANDBY: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ case TrkNotifyProcessorReset: { // NotifyProcessorReset
+ logMessage(prefix + "NOTE: PROCESSOR RESET: " + str);
+ d->m_device->sendTrkAck(result.token);
+ break;
+ }
+ default: {
+ logMessage(prefix + "INVALID: " + str);
+ break;
+ }
+ }
+}
+
+QString Launcher::deviceDescription(unsigned verbose) const
+{
+ return d->m_session.deviceDescription(verbose);
+}
+
+void Launcher::handleTrkVersion(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 5) {
+ if (d->m_startupActions == ActionPingOnly) {
+ setState(Disconnected);
+ handleFinished();
+ }
+ return;
+ }
+ d->m_session.trkAppVersion.trkMajor = result.data.at(1);
+ d->m_session.trkAppVersion.trkMinor = result.data.at(2);
+ d->m_session.trkAppVersion.protocolMajor = result.data.at(3);
+ d->m_session.trkAppVersion.protocolMinor = result.data.at(4);
+ setState(DeviceDescriptionReceived);
+ const QString msg = deviceDescription();
+ emit deviceDescriptionReceived(trkServerName(), msg);
+ // Ping mode: Log & Terminate
+ if (d->m_startupActions == ActionPingOnly) {
+ qWarning("%s", qPrintable(msg));
+ setState(Disconnected);
+ handleFinished();
+ }
+}
+
+static inline QString msgCannotOpenRemoteFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open remote file '%1': %2").arg(fileName, message);
+}
+
+static inline QString msgCannotOpenLocalFile(const QString &fileName, const QString &message)
+{
+ return Launcher::tr("Cannot open '%1': %2").arg(fileName, message);
+}
+
+void Launcher::handleFileCreation(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 6) {
+ const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileName, result.errorString());
+ logMessage(msg);
+ emit canNotCreateFile(d->m_copyState.destinationFileName, msg);
+ disconnectTrk();
+ return;
+ }
+ const char *data = result.data.data();
+ d->m_copyState.copyFileHandle = extractInt(data + 2);
+ const QString localFileName = d->m_copyState.sourceFileName;
+ QFile file(localFileName);
+ d->m_copyState.position = 0;
+ if (!file.open(QIODevice::ReadOnly)) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, file.errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ return;
+ }
+ d->m_copyState.data.reset(new QByteArray(file.readAll()));
+ file.close();
+ continueCopying();
+}
+
+void Launcher::handleFileOpened(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 6) {
+ const QString msg = msgCannotOpenRemoteFile(d->m_downloadState.sourceFileName, result.errorString());
+ logMessage(msg);
+ emit canNotOpenFile(d->m_downloadState.sourceFileName, msg);
+ disconnectTrk();
+ return;
+ }
+ d->m_downloadState.position = 0;
+ const QString localFileName = d->m_downloadState.destinationFileName;
+ bool opened = false;
+ if (localFileName == QLatin1String("-")) {
+ d->m_downloadState.localFile.reset(new QFile);
+ opened = d->m_downloadState.localFile->open(stdout, QFile::WriteOnly);
+ } else {
+ d->m_downloadState.localFile.reset(new QFile(localFileName));
+ opened = d->m_downloadState.localFile->open(QFile::WriteOnly | QFile::Truncate);
+ }
+ if (!opened) {
+ const QString msg = msgCannotOpenLocalFile(localFileName, d->m_downloadState.localFile->errorString());
+ logMessage(msg);
+ emit canNotOpenLocalFile(localFileName, msg);
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ continueReading();
+}
+
+void Launcher::continueReading()
+{
+ QByteArray ba;
+ appendInt(&ba, d->m_downloadState.copyFileHandle, TargetByteOrder);
+ appendShort(&ba, 2048, TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkReadFile, TrkCallback(this, &Launcher::handleRead), ba);
+}
+
+void Launcher::handleRead(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 4) {
+ d->m_downloadState.localFile->close();
+ closeRemoteFile(true);
+ disconnectTrk();
+ } else {
+ int length = extractShort(result.data.data() + 2);
+ //TRK doesn't tell us the file length, so we need to keep reading until it returns 0 length
+ if (length > 0) {
+ d->m_downloadState.localFile->write(result.data.data() + 4, length);
+ continueReading();
+ } else {
+ closeRemoteFile(true);
+ disconnectTrk();
+ }
+ }
+}
+
+void Launcher::handleCopy(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 4) {
+ closeRemoteFile(true);
+ emit canNotWriteFile(d->m_copyState.destinationFileName, result.errorString());
+ disconnectTrk();
+ } else {
+ continueCopying(extractShort(result.data.data() + 2));
+ }
+}
+
+void Launcher::continueCopying(uint lastCopiedBlockSize)
+{
+ qint64 size = d->m_copyState.data->length();
+ d->m_copyState.position += lastCopiedBlockSize;
+ if (size == 0)
+ emit copyProgress(100);
+ else {
+ const qint64 hundred = 100;
+ const qint64 percent = qMin( (d->m_copyState.position * hundred) / size, hundred);
+ emit copyProgress(static_cast<int>(percent));
+ }
+ if (d->m_copyState.position < size) {
+ QByteArray ba;
+ appendInt(&ba, d->m_copyState.copyFileHandle, TargetByteOrder);
+ appendString(&ba, d->m_copyState.data->mid(d->m_copyState.position, 2048), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkWriteFile, TrkCallback(this, &Launcher::handleCopy), ba);
+ } else {
+ closeRemoteFile();
+ }
+}
+
+void Launcher::closeRemoteFile(bool failed)
+{
+ QByteArray ba;
+ appendInt(&ba, d->m_copyState.copyFileHandle, TargetByteOrder);
+ appendDateTime(&ba, QDateTime::currentDateTime(), TargetByteOrder);
+ d->m_device->sendTrkMessage(TrkCloseFile,
+ failed ? TrkCallback() : TrkCallback(this, &Launcher::handleFileCopied),
+ ba);
+ d->m_copyState.data.reset();
+ d->m_copyState.copyFileHandle = 0;
+ d->m_copyState.position = 0;
+}
+
+void Launcher::handleFileCopied(const TrkResult &result)
+{
+ if (result.errorCode())
+ emit canNotCloseFile(d->m_copyState.destinationFileName, result.errorString());
+ if (d->m_startupActions & ActionInstall)
+ installRemotePackageSilently();
+ else if (d->m_startupActions & ActionRun)
+ startInferiorIfNeeded();
+ else if (d->m_startupActions & ActionDownload)
+ copyFileFromRemote();
+ else
+ disconnectTrk();
+}
+
+void Launcher::handleCpuType(const TrkResult &result)
+{
+ logMessage("HANDLE CPU TYPE: " + result.toString());
+ if(result.errorCode() || result.data.size() < 7)
+ return;
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 03 00 04 00 00 04 00 00 00]
+ d->m_session.cpuMajor = result.data.at(1);
+ d->m_session.cpuMinor = result.data.at(2);
+ d->m_session.bigEndian = result.data.at(3);
+ d->m_session.defaultTypeSize = result.data.at(4);
+ d->m_session.fpTypeSize = result.data.at(5);
+ d->m_session.extended1TypeSize = result.data.at(6);
+ //d->m_session.extended2TypeSize = result.data[6];
+}
+
+void Launcher::handleCreateProcess(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotRun(result.errorString());
+ disconnectTrk();
+ return;
+ }
+ // 40 00 00]
+ //logMessage(" RESULT: " + result.toString());
+ // [80 08 00 00 00 01 B5 00 00 01 B6 78 67 40 00 00 40 00 00]
+ const char *data = result.data.data();
+ d->m_session.pid = extractInt(data + 1);
+ d->m_session.tid = extractInt(data + 5);
+ d->m_session.codeseg = extractInt(data + 9);
+ d->m_session.dataseg = extractInt(data + 13);
+ if (d->m_verbose) {
+ const QString msg = QString::fromLatin1("Process id: %1 Thread id: %2 code: 0x%3 data: 0x%4").
+ arg(d->m_session.pid).arg(d->m_session.tid).arg(d->m_session.codeseg, 0, 16).
+ arg(d->m_session.dataseg, 0 ,16);
+ logMessage(msg);
+ }
+ emit applicationRunning(d->m_session.pid);
+ //create a "library" entry for the executable which launched the process
+ Library lib;
+ lib.pid = d->m_session.pid;
+ lib.codeseg = d->m_session.codeseg;
+ lib.dataseg = d->m_session.dataseg;
+ lib.name = d->m_fileName.toUtf8();
+ d->m_session.libraries << lib;
+ emit libraryLoaded(lib);
+
+ QByteArray ba;
+ appendInt(&ba, d->m_session.pid);
+ appendInt(&ba, d->m_session.tid);
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+}
+
+void Launcher::handleWaitForFinished(const TrkResult &result)
+{
+ logMessage(" FINISHED: " + stringFromArray(result.data));
+ setState(Disconnected);
+ handleFinished();
+}
+
+void Launcher::handleSupportMask(const TrkResult &result)
+{
+ if (result.errorCode() || result.data.size() < 32)
+ return;
+ const char *data = result.data.data() + 1;
+
+ if (d->m_verbose > 1) {
+ QString str = QLatin1String("SUPPORTED: ");
+ for (int i = 0; i < 32; ++i) {
+ for (int j = 0; j < 8; ++j) {
+ if (data[i] & (1 << j)) {
+ str.append(QString::number(i * 8 + j, 16));
+ str.append(QLatin1Char(' '));
+ }
+ }
+ }
+ logMessage(str);
+ }
+}
+
+void Launcher::cleanUp()
+{
+ //
+ //---IDE------------------------------------------------------
+ // Command: 0x41 Delete Item
+ // Sub Cmd: Delete Process
+ //ProcessID: 0x0000071F (1823)
+ // [41 24 00 00 00 00 07 1F]
+ QByteArray ba(2, char(0));
+ appendInt(&ba, d->m_session.pid);
+ d->m_device->sendTrkMessage(TrkDeleteItem, TrkCallback(), ba, "Delete process");
+
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 24 00]
+
+ //---IDE------------------------------------------------------
+ // Command: 0x1C Clear Break
+ // [1C 25 00 00 00 0A 78 6A 43 40]
+
+ //---TRK------------------------------------------------------
+ // Command: 0xA1 Notify Deleted
+ // [A1 09 00 00 00 00 00 00 00 00 07 1F]
+ //---IDE------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 09 00]
+
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 25 00]
+
+ //---IDE------------------------------------------------------
+ // Command: 0x1C Clear Break
+ // [1C 26 00 00 00 0B 78 6A 43 70]
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+ // [80 26 00]
+
+
+ //---IDE------------------------------------------------------
+ // Command: 0x02 Disconnect
+ // [02 27]
+// sendTrkMessage(0x02, TrkCallback(this, &Launcher::handleDisconnect));
+ //---TRK------------------------------------------------------
+ // Command: 0x80 Acknowledge
+ // Error: 0x00
+}
+
+void Launcher::disconnectTrk()
+{
+ d->m_device->sendTrkMessage(TrkDisconnect, TrkCallback(this, &Launcher::handleWaitForFinished));
+}
+
+void Launcher::copyFileToRemote()
+{
+ emit copyingStarted();
+ QByteArray ba;
+ ba.append(char(10)); //kDSFileOpenWrite | kDSFileOpenBinary
+ appendString(&ba, d->m_copyState.destinationFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileCreation), ba);
+}
+
+void Launcher::copyFileFromRemote()
+{
+ emit copyingStarted();
+ QByteArray ba;
+ ba.append(char(9)); //kDSFileOpenRead | kDSFileOpenBinary
+ appendString(&ba, d->m_downloadState.sourceFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileOpened), ba);
+}
+
+void Launcher::installRemotePackageSilently()
+{
+ emit installingStarted();
+ QByteArray ba;
+ ba.append('C');
+ appendString(&ba, d->m_installFileName.toLocal8Bit(), TargetByteOrder, false);
+ d->m_device->sendTrkMessage(TrkInstallFile, TrkCallback(this, &Launcher::handleInstallPackageFinished), ba);
+}
+
+void Launcher::handleInstallPackageFinished(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ emit canNotInstall(d->m_installFileName, result.errorString());
+ disconnectTrk();
+ return;
+ } else {
+ emit installingFinished();
+ }
+ if (d->m_startupActions & ActionRun) {
+ startInferiorIfNeeded();
+ } else if (d->m_startupActions & ActionDownload) {
+ copyFileFromRemote();
+ } else {
+ disconnectTrk();
+ }
+}
+
+QByteArray Launcher::startProcessMessage(const QString &executable,
+ const QStringList &arguments)
+{
+ // It's not started yet
+ QByteArray ba;
+ appendShort(&ba, 0, TargetByteOrder); // create new process (kDSOSProcessItem)
+ ba.append(char(0)); // options - currently unused
+ // One string consisting of binary terminated by '\0' and arguments terminated by '\0'
+ QByteArray commandLineBa = executable.toLocal8Bit();
+ commandLineBa.append(char(0));
+ if (!arguments.isEmpty())
+ commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
+ appendString(&ba, commandLineBa, TargetByteOrder, true);
+ return ba;
+}
+
+QByteArray Launcher::readMemoryMessage(uint pid, uint tid, uint from, uint len)
+{
+ QByteArray ba;
+ ba.reserve(11);
+ ba.append(char(0x8)); // Options, FIXME: why?
+ appendShort(&ba, len);
+ appendInt(&ba, from);
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+QByteArray Launcher::readRegistersMessage(uint pid, uint tid)
+{
+ QByteArray ba;
+ ba.reserve(15);
+ ba.append(char(0)); // Register set, only 0 supported
+ appendShort(&ba, 0); //R0
+ appendShort(&ba, 16); // last register CPSR
+ appendInt(&ba, pid);
+ appendInt(&ba, tid);
+ return ba;
+}
+
+void Launcher::startInferiorIfNeeded()
+{
+ emit startingApplication();
+ if (d->m_session.pid != 0) {
+ logMessage("Process already 'started'");
+ return;
+ }
+ d->m_device->sendTrkMessage(TrkCreateItem, TrkCallback(this, &Launcher::handleCreateProcess),
+ startProcessMessage(d->m_fileName, d->m_commandLineArgs)); // Create Item
+}
+
+void Launcher::resumeProcess(uint pid, uint tid)
+{
+ QByteArray ba;
+ appendInt(&ba, pid, BigEndian);
+ appendInt(&ba, tid, BigEndian);
+ d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
+}
+
+// Acquire a device from SymbianDeviceManager, return 0 if not available.
+Launcher *Launcher::acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ const QSharedPointer<trk::TrkDevice> device = sdm->acquireDevice(serverName);
+ if (device.isNull()) {
+ *errorMessage = tr("Unable to acquire a device for port '%1'. It appears to be in use.").arg(serverName);
+ return 0;
+ }
+ // Wire release signal.
+ Launcher *rc = new Launcher(trk::Launcher::ActionPingOnly, device, parent);
+ connect(rc, SIGNAL(deviceDescriptionReceived(QString,QString)),
+ sdm, SLOT(setAdditionalInformation(QString,QString)));
+ connect(rc, SIGNAL(destroyed(QString)), sdm, SLOT(releaseDevice(QString)));
+ return rc;
+}
+
+// Preliminary release of device, disconnecting the signal.
+void Launcher::releaseToDeviceManager(Launcher *launcher)
+{
+ SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+ // Disentangle launcher and its device, remove connection from destroyed
+ launcher->setCloseDevice(false);
+ TrkDevice *device = launcher->trkDevice().data();
+ launcher->disconnect(device);
+ device->disconnect(launcher);
+ launcher->disconnect(sdm);
+ sdm->releaseDevice(launcher->trkServerName());
+}
+
+void Launcher::getRegistersAndCallStack(uint pid, uint tid)
+{
+ d->m_device->sendTrkMessage(TrkReadRegisters,
+ TrkCallback(this, &Launcher::handleReadRegisters),
+ Launcher::readRegistersMessage(pid, tid));
+ d->m_crashReportState.fetchingStackPID = pid;
+ d->m_crashReportState.fetchingStackTID = tid;
+}
+
+void Launcher::handleReadRegisters(const TrkResult &result)
+{
+ if(result.errorCode() || result.data.size() < (17*4)) {
+ terminate();
+ return;
+ }
+ const char* data = result.data.constData() + 1;
+ d->m_crashReportState.registers.clear();
+ d->m_crashReportState.stack.clear();
+ for (int i=0;i<17;i++) {
+ uint r = extractInt(data);
+ data += 4;
+ d->m_crashReportState.registers.append(r);
+ }
+ d->m_crashReportState.sp = d->m_crashReportState.registers.at(13);
+
+ const ushort len = 1024 - (d->m_crashReportState.sp % 1024); //read to 1k boundary first
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ len);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += len;
+}
+
+void Launcher::handleReadStack(const TrkResult &result)
+{
+ if (result.errorCode()) {
+ //error implies memory fault when reaching end of stack
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+
+ const uint len = extractShort(result.data.constData() + 1);
+ d->m_crashReportState.stack.append(result.data.mid(3, len));
+
+ if (d->m_crashReportState.sp - d->m_crashReportState.registers.at(13) > 0x10000) {
+ //read enough stack, stop here
+ emit registersAndCallStackReadComplete(d->m_crashReportState.registers, d->m_crashReportState.stack);
+ return;
+ }
+ //read 1k more
+ const QByteArray ba = Launcher::readMemoryMessage(d->m_crashReportState.fetchingStackPID,
+ d->m_crashReportState.fetchingStackTID,
+ d->m_crashReportState.sp,
+ 1024);
+ d->m_device->sendTrkMessage(TrkReadMemory, TrkCallback(this, &Launcher::handleReadStack), ba);
+ d->m_crashReportState.sp += 1024;
+}
+
+} // namespace trk
diff --git a/src/runonphone/symbianutils/launcher.h b/src/runonphone/symbianutils/launcher.h
new file mode 100644
index 000000000..fac65b569
--- /dev/null
+++ b/src/runonphone/symbianutils/launcher.h
@@ -0,0 +1,212 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef LAUNCHER_H
+#define LAUNCHER_H
+
+#include "trkdevice.h"
+#include "trkutils.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QSharedPointer>
+
+namespace trk {
+
+struct TrkResult;
+struct TrkMessage;
+struct LauncherPrivate;
+
+typedef QSharedPointer<TrkDevice> TrkDevicePtr;
+
+class SYMBIANUTILS_EXPORT Launcher : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(Launcher)
+public:
+ typedef void (Launcher::*TrkCallBack)(const TrkResult &);
+
+ enum Actions {
+ ActionPingOnly = 0x0,
+ ActionCopy = 0x1,
+ ActionInstall = 0x2,
+ ActionCopyInstall = ActionCopy | ActionInstall,
+ ActionRun = 0x4,
+ ActionDownload = 0x8,
+ ActionCopyRun = ActionCopy | ActionRun,
+ ActionInstallRun = ActionInstall | ActionRun,
+ ActionCopyInstallRun = ActionCopy | ActionInstall | ActionRun
+ };
+
+ enum State { Disconnected, Connecting, Connected,
+ WaitingForTrk, // This occurs only if the initial ping times out after
+ // a reasonable timeout, indicating that Trk is not
+ // running. Note that this will never happen with
+ // Bluetooth as communication immediately starts
+ // after connecting.
+ DeviceDescriptionReceived };
+
+ explicit Launcher(trk::Launcher::Actions startupActions = trk::Launcher::ActionPingOnly,
+ const TrkDevicePtr &trkDevice = TrkDevicePtr(),
+ QObject *parent = 0);
+ ~Launcher();
+
+ State state() const;
+
+ void addStartupActions(trk::Launcher::Actions startupActions);
+ void setTrkServerName(const QString &name);
+ QString trkServerName() const;
+ void setFileName(const QString &name);
+ void setCopyFileName(const QString &srcName, const QString &dstName);
+ void setDownloadFileName(const QString &srcName, const QString &dstName);
+ void setInstallFileName(const QString &name);
+ void setCommandLineArgs(const QStringList &args);
+ bool startServer(QString *errorMessage);
+ void setVerbose(int v);
+ void setSerialFrame(bool b);
+ bool serialFrame() const;
+ // Close device or leave it open
+ bool closeDevice() const;
+ void setCloseDevice(bool c);
+
+ TrkDevicePtr trkDevice() const;
+
+ // becomes valid after successful execution of ActionPingOnly
+ QString deviceDescription(unsigned verbose = 0u) const;
+
+ // Acquire a device from SymbianDeviceManager, return 0 if not available.
+ // The device will be released on destruction.
+ static Launcher *acquireFromDeviceManager(const QString &serverName,
+ QObject *parent,
+ QString *errorMessage);
+ // Preliminary release of device, disconnecting the signal.
+ static void releaseToDeviceManager(Launcher *l);
+
+ // Create Trk message to start a process.
+ static QByteArray startProcessMessage(const QString &executable,
+ const QStringList &arguments);
+ // Create Trk message to read memory
+ static QByteArray readMemoryMessage(uint pid, uint tid, uint from, uint len);
+ static QByteArray readRegistersMessage(uint pid, uint tid);
+ // Parse a TrkNotifyStopped message
+ static bool parseNotifyStopped(const QByteArray &a,
+ uint *pid, uint *tid, uint *address,
+ QString *why = 0);
+ // Helper message
+ static QString msgStopped(uint pid, uint tid, uint address, const QString &why);
+
+signals:
+ void deviceDescriptionReceived(const QString &port, const QString &description);
+ void copyingStarted();
+ void canNotConnect(const QString &errorMessage);
+ void canNotCreateFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenFile(const QString &filename, const QString &errorMessage);
+ void canNotOpenLocalFile(const QString &filename, const QString &errorMessage);
+ void canNotWriteFile(const QString &filename, const QString &errorMessage);
+ void canNotCloseFile(const QString &filename, const QString &errorMessage);
+ void installingStarted();
+ void canNotInstall(const QString &packageFilename, const QString &errorMessage);
+ void installingFinished();
+ void startingApplication();
+ void applicationRunning(uint pid);
+ void canNotRun(const QString &errorMessage);
+ void finished();
+ void applicationOutputReceived(const QString &output);
+ void copyProgress(int percent);
+ void stateChanged(int);
+ void processStopped(uint pc, uint pid, uint tid, const QString& reason);
+ void processResumed(uint pid, uint tid);
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
+ // Emitted by the destructor, for releasing devices of SymbianDeviceManager by name
+ void destroyed(const QString &serverName);
+
+public slots:
+ void terminate();
+ void resumeProcess(uint pid, uint tid);
+ //can be used to obtain traceback after a breakpoint / exception
+ void getRegistersAndCallStack(uint pid, uint tid);
+
+private slots:
+ void handleResult(const trk::TrkResult &data);
+ void slotWaitingForTrk();
+
+private:
+ // kill process and breakpoints
+ void cleanUp();
+ void disconnectTrk();
+
+ void handleRemoteProcessKilled(const TrkResult &result);
+ void handleConnect(const TrkResult &result);
+ void handleFileCreation(const TrkResult &result);
+ void handleFileOpened(const TrkResult &result);
+ void handleCopy(const TrkResult &result);
+ void handleRead(const TrkResult &result);
+ void continueCopying(uint lastCopiedBlockSize = 0);
+ void continueReading();
+ void closeRemoteFile(bool failed = false);
+ void handleFileCopied(const TrkResult &result);
+ void handleInstallPackageFinished(const TrkResult &result);
+ void handleCpuType(const TrkResult &result);
+ void handleCreateProcess(const TrkResult &result);
+ void handleWaitForFinished(const TrkResult &result);
+ void handleStop(const TrkResult &result);
+ void handleSupportMask(const TrkResult &result);
+ void handleTrkVersion(const TrkResult &result);
+ void handleReadRegisters(const TrkResult &result);
+ void handleReadStack(const TrkResult &result);
+
+ void copyFileToRemote();
+ void copyFileFromRemote();
+ void installRemotePackageSilently();
+ void startInferiorIfNeeded();
+ void handleFinished();
+
+ void logMessage(const QString &msg);
+ void setState(State s);
+
+ LauncherPrivate *d;
+};
+
+} // namespace Trk
+
+#endif // LAUNCHER_H
diff --git a/src/runonphone/symbianutils/symbiandevicemanager.cpp b/src/runonphone/symbianutils/symbiandevicemanager.cpp
new file mode 100644
index 000000000..8d65d182c
--- /dev/null
+++ b/src/runonphone/symbianutils/symbiandevicemanager.cpp
@@ -0,0 +1,489 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "symbiandevicemanager.h"
+#include "trkdevice.h"
+
+#include <QtCore/QSettings>
+#include <QtCore/QStringList>
+#include <QtCore/QFileInfo>
+#include <QtCore/QtDebug>
+#include <QtCore/QTextStream>
+#include <QtCore/QSharedData>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QSignalMapper>
+
+namespace SymbianUtils {
+
+enum { debug = 0 };
+
+static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
+static const char USBSER[] = "Services/usbser/Enum";
+
+const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
+
+// ------------- SymbianDevice
+class SymbianDeviceData : public QSharedData {
+public:
+ SymbianDeviceData();
+ ~SymbianDeviceData();
+
+ inline bool isOpen() const { return !device.isNull() && device->isOpen(); }
+ void forcedClose();
+
+ QString portName;
+ QString friendlyName;
+ QString deviceDesc;
+ QString manufacturer;
+ QString additionalInformation;
+
+ DeviceCommunicationType type;
+ QSharedPointer<trk::TrkDevice> device;
+ bool deviceAcquired;
+};
+
+SymbianDeviceData::SymbianDeviceData() :
+ type(SerialPortCommunication),
+ deviceAcquired(false)
+{
+}
+
+SymbianDeviceData::~SymbianDeviceData()
+{
+ forcedClose();
+}
+
+void SymbianDeviceData::forcedClose()
+{
+ // Close the device when unplugging. Should devices be in 'acquired' state,
+ // their owners should hit on write failures.
+ // Apart from the <shared item> destructor, also called by the devicemanager
+ // to ensure it also happens if other shared instances are still around.
+ if (isOpen()) {
+ if (deviceAcquired)
+ qWarning("Device on '%s' unplugged while an operation is in progress.",
+ qPrintable(portName));
+ device->close();
+ }
+}
+
+SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
+ m_data(data)
+{
+}
+
+SymbianDevice::SymbianDevice() :
+ m_data(new SymbianDeviceData)
+{
+}
+SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
+ m_data(rhs.m_data)
+{
+}
+
+SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
+{
+ if (this != &rhs)
+ m_data = rhs.m_data;
+ return *this;
+}
+
+SymbianDevice::~SymbianDevice()
+{
+}
+
+void SymbianDevice::forcedClose()
+{
+ m_data->forcedClose();
+}
+
+QString SymbianDevice::portName() const
+{
+ return m_data->portName;
+}
+
+QString SymbianDevice::friendlyName() const
+{
+ return m_data->friendlyName;
+}
+
+QString SymbianDevice::additionalInformation() const
+{
+ return m_data->additionalInformation;
+}
+
+void SymbianDevice::setAdditionalInformation(const QString &a)
+{
+ m_data->additionalInformation = a;
+}
+
+SymbianDevice::TrkDevicePtr SymbianDevice::acquireDevice()
+{
+ if (debug)
+ qDebug() << "SymbianDevice::acquireDevice" << m_data->portName
+ << "acquired: " << m_data->deviceAcquired << " open: " << isOpen();
+ if (isNull() || m_data->deviceAcquired)
+ return TrkDevicePtr();
+ if (m_data->device.isNull()) {
+ m_data->device = TrkDevicePtr(new trk::TrkDevice);
+ m_data->device->setPort(m_data->portName);
+ m_data->device->setSerialFrame(m_data->type == SerialPortCommunication);
+ }
+ m_data->deviceAcquired = true;
+ return m_data->device;
+}
+
+void SymbianDevice::releaseDevice(TrkDevicePtr *ptr /* = 0 */)
+{
+ if (debug)
+ qDebug() << "SymbianDevice::releaseDevice" << m_data->portName
+ << " open: " << isOpen();
+ if (m_data->deviceAcquired) {
+ if (m_data->device->isOpen())
+ m_data->device->clearWriteQueue();
+ // Release if a valid pointer was passed in.
+ if (ptr && !ptr->isNull()) {
+ ptr->data()->disconnect();
+ *ptr = TrkDevicePtr();
+ }
+ m_data->deviceAcquired = false;
+ } else {
+ qWarning("Internal error: Attempt to release device that is not acquired.");
+ }
+}
+
+QString SymbianDevice::deviceDesc() const
+{
+ return m_data->deviceDesc;
+}
+
+QString SymbianDevice::manufacturer() const
+{
+ return m_data->manufacturer;
+}
+
+DeviceCommunicationType SymbianDevice::type() const
+{
+ return m_data->type;
+}
+
+bool SymbianDevice::isNull() const
+{
+ return m_data->portName.isEmpty();
+}
+
+bool SymbianDevice::isOpen() const
+{
+ return m_data->isOpen();
+}
+
+QString SymbianDevice::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void SymbianDevice::format(QTextStream &str) const
+{
+ str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
+ << m_data->portName;
+ if (!m_data->friendlyName.isEmpty()) {
+ str << " (" << m_data->friendlyName;
+ if (!m_data->deviceDesc.isEmpty())
+ str << " / " << m_data->deviceDesc;
+ str << ')';
+ }
+ if (!m_data->manufacturer.isEmpty())
+ str << " [" << m_data->manufacturer << ']';
+}
+
+// Compare by port and friendly name
+int SymbianDevice::compare(const SymbianDevice &rhs) const
+{
+ if (const int prc = m_data->portName.compare(rhs.m_data->portName))
+ return prc;
+ if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
+ return frc;
+ return 0;
+}
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &cd)
+{
+ d.nospace() << cd.toString();
+ return d;
+}
+
+// ------------- SymbianDeviceManagerPrivate
+struct SymbianDeviceManagerPrivate {
+ SymbianDeviceManagerPrivate() : m_initialized(false), m_destroyReleaseMapper(0) {}
+
+ bool m_initialized;
+ SymbianDeviceManager::SymbianDeviceList m_devices;
+ QSignalMapper *m_destroyReleaseMapper;
+};
+
+SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
+ QObject(parent),
+ d(new SymbianDeviceManagerPrivate)
+{
+}
+
+SymbianDeviceManager::~SymbianDeviceManager()
+{
+ delete d;
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
+{
+ ensureInitialized();
+ return d->m_devices;
+}
+
+QString SymbianDeviceManager::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << d->m_devices.size() << " devices:\n";
+ const int count = d->m_devices.size();
+ for (int i = 0; i < count; i++) {
+ str << '#' << i << ' ';
+ d->m_devices.at(i).format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+int SymbianDeviceManager::findByPortName(const QString &p) const
+{
+ ensureInitialized();
+ const int count = d->m_devices.size();
+ for (int i = 0; i < count; i++)
+ if (d->m_devices.at(i).portName() == p)
+ return i;
+ return -1;
+}
+
+QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
+{
+ const int idx = findByPortName(port);
+ return idx == -1 ? QString() : d->m_devices.at(idx).friendlyName();
+}
+
+SymbianDeviceManager::TrkDevicePtr
+ SymbianDeviceManager::acquireDevice(const QString &port)
+{
+ ensureInitialized();
+ const int idx = findByPortName(port);
+ if (idx == -1) {
+ qWarning("Attempt to acquire device '%s' that does not exist.", qPrintable(port));
+ if (debug)
+ qDebug() << *this;
+ return TrkDevicePtr();
+ }
+ const TrkDevicePtr rc = d->m_devices[idx].acquireDevice();
+ if (debug)
+ qDebug() << "SymbianDeviceManager::acquireDevice" << port << " returns " << !rc.isNull();
+ return rc;
+}
+
+void SymbianDeviceManager::update()
+{
+ update(true);
+}
+
+void SymbianDeviceManager::releaseDevice(const QString &port)
+{
+ const int idx = findByPortName(port);
+ if (debug)
+ qDebug() << "SymbianDeviceManager::releaseDevice" << port << idx << sender();
+ if (idx != -1) {
+ d->m_devices[idx].releaseDevice();
+ } else {
+ qWarning("Attempt to release non-existing device %s.", qPrintable(port));
+ }
+}
+
+void SymbianDeviceManager::setAdditionalInformation(const QString &port, const QString &ai)
+{
+ const int idx = findByPortName(port);
+ if (idx != -1)
+ d->m_devices[idx].setAdditionalInformation(ai);
+}
+
+void SymbianDeviceManager::ensureInitialized() const
+{
+ if (!d->m_initialized) // Flag is set in update()
+ const_cast<SymbianDeviceManager*>(this)->update(false);
+}
+
+void SymbianDeviceManager::update(bool emitSignals)
+{
+ static int n = 0;
+ typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
+
+ if (debug)
+ qDebug(">SerialDeviceLister::update(#%d, signals=%d)\n%s", n++, int(emitSignals),
+ qPrintable(toString()));
+
+ d->m_initialized = true;
+ // Get ordered new list
+ SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
+ if (newDevices.size() > 1)
+ qStableSort(newDevices.begin(), newDevices.end());
+ if (d->m_devices == newDevices) { // Happy, nothing changed.
+ if (debug)
+ qDebug("<SerialDeviceLister::update: unchanged\n");
+ return;
+ }
+ // Merge the lists and emit the respective added/removed signals, assuming
+ // no one can plug a different device on the same port at the speed of lightning
+ if (!d->m_devices.isEmpty()) {
+ // Find deleted devices
+ for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
+ if (newDevices.contains(*oldIt)) {
+ ++oldIt;
+ } else {
+ SymbianDevice toBeDeleted = *oldIt;
+ toBeDeleted.forcedClose();
+ oldIt = d->m_devices.erase(oldIt);
+ if (emitSignals)
+ emit deviceRemoved(toBeDeleted);
+ }
+ }
+ }
+ if (!newDevices.isEmpty()) {
+ // Find new devices and insert in order
+ foreach(const SymbianDevice &newDevice, newDevices) {
+ if (!d->m_devices.contains(newDevice)) {
+ d->m_devices.append(newDevice);
+ if (emitSignals)
+ emit deviceAdded(newDevice);
+ }
+ }
+ if (d->m_devices.size() > 1)
+ qStableSort(d->m_devices.begin(), d->m_devices.end());
+ }
+ if (emitSignals)
+ emit updated();
+
+ if (debug)
+ qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
+{
+ SymbianDeviceList rc;
+#ifdef Q_OS_WIN
+ const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
+ const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
+ const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
+ for (int i = 0; i < count; ++i) {
+ QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
+ if (driver.contains(QLatin1String("JAVACOMM"))) {
+ driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
+ const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
+ if (debug > 1)
+ qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
+ << REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
+ QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+ device->type = SerialPortCommunication;
+ device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
+ device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
+ device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
+ device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
+ rc.append(SymbianDevice(device.take()));
+ }
+ }
+#endif
+ return rc;
+}
+
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
+{
+ SymbianDeviceList rc;
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+ // Bluetooth devices are created on connection. List the existing ones
+ // or at least the first one.
+ const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
+ const QString blueToothfriendlyFormat = QLatin1String("Bluetooth device (%1)");
+ for (int d = 0; d < 4; d++) {
+ QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+ device->type = BlueToothCommunication;
+ device->portName = prefix + QString::number(d);
+ if (d == 0 || QFileInfo(device->portName).exists()) {
+ device->friendlyName = blueToothfriendlyFormat.arg(device->portName);
+ rc.push_back(SymbianDevice(device.take()));
+ }
+ }
+ // New kernel versions support /dev/ttyUSB0, /dev/ttyUSB1. Trk responds
+ // on the latter (usually), try first.
+ static const char *usbTtyDevices[] = { "/dev/ttyUSB1", "/dev/ttyUSB0" };
+ const int usbTtyCount = sizeof(usbTtyDevices)/sizeof(const char *);
+ for (int d = 0; d < usbTtyCount; d++) {
+ const QString ttyUSBDevice = QLatin1String(usbTtyDevices[d]);
+ if (QFileInfo(ttyUSBDevice).exists()) {
+ SymbianDeviceData *device = new SymbianDeviceData;
+ device->type = SerialPortCommunication;
+ device->portName = ttyUSBDevice;
+ device->friendlyName = QString::fromLatin1("USB/Serial device (%1)").arg(device->portName);
+ rc.push_back(SymbianDevice(device));
+ }
+ }
+#endif
+ return rc;
+}
+
+Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
+
+SymbianDeviceManager *SymbianDeviceManager::instance()
+{
+ return symbianDeviceManager();
+}
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
+{
+ d.nospace() << sdm.toString();
+ return d;
+}
+
+} // namespace SymbianUtilsInternal
diff --git a/src/runonphone/symbianutils/symbiandevicemanager.h b/src/runonphone/symbianutils/symbiandevicemanager.h
new file mode 100644
index 000000000..b4edd7884
--- /dev/null
+++ b/src/runonphone/symbianutils/symbiandevicemanager.h
@@ -0,0 +1,178 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANDEVICEMANAGER_H
+#define SYMBIANDEVICEMANAGER_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QExplicitlySharedDataPointer>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace trk {
+ class TrkDevice;
+}
+
+namespace SymbianUtils {
+
+struct SymbianDeviceManagerPrivate;
+class SymbianDeviceData;
+
+enum DeviceCommunicationType {
+ SerialPortCommunication = 0,
+ BlueToothCommunication = 1
+};
+
+// SymbianDevice: Explicitly shared device data and a TrkDevice
+// instance that can be acquired (exclusively) for use.
+// A device removal from the manager will result in the
+// device being closed.
+class SYMBIANUTILS_EXPORT SymbianDevice {
+ explicit SymbianDevice(SymbianDeviceData *data);
+ friend class SymbianDeviceManager;
+public:
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
+
+ SymbianDevice();
+ SymbianDevice(const SymbianDevice &rhs);
+ SymbianDevice &operator=(const SymbianDevice &rhs);
+ ~SymbianDevice();
+ int compare(const SymbianDevice &rhs) const;
+
+ DeviceCommunicationType type() const;
+ bool isNull() const;
+ QString portName() const;
+ QString friendlyName() const;
+ QString additionalInformation() const;
+ void setAdditionalInformation(const QString &);
+
+ // Acquire: Mark the device as 'out' and return a shared pointer
+ // unless it is already in use by another owner. The result should not
+ // be passed on further.
+ TrkDevicePtr acquireDevice();
+ // Give back a device and mark it as 'free'.
+ void releaseDevice(TrkDevicePtr *ptr = 0);
+
+ bool isOpen() const;
+
+ // Windows only.
+ QString deviceDesc() const;
+ QString manufacturer() const;
+
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+private:
+ void forcedClose();
+
+ QExplicitlySharedDataPointer<SymbianDeviceData> m_data;
+};
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDevice &);
+
+inline bool operator==(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) == 0; }
+inline bool operator!=(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) != 0; }
+inline bool operator<(const SymbianDevice &d1, const SymbianDevice &d2)
+ { return d1.compare(d2) < 0; }
+
+/* SymbianDeviceManager: Singleton that maintains a list of Symbian devices.
+ * and emits change signals.
+ * On Windows, the update slot must be connected to a [delayed] signal
+ * emitted from an event handler listening for WM_DEVICECHANGE.
+ * Device removal will result in the device being closed. */
+class SYMBIANUTILS_EXPORT SymbianDeviceManager : public QObject
+{
+ Q_OBJECT
+public:
+ typedef QList<SymbianDevice> SymbianDeviceList;
+ typedef QSharedPointer<trk::TrkDevice> TrkDevicePtr;
+
+ static const char *linuxBlueToothDeviceRootC;
+
+ // Do not use this constructor, it is just public for Q_GLOBAL_STATIC
+ explicit SymbianDeviceManager(QObject *parent = 0);
+ virtual ~SymbianDeviceManager();
+
+ // Singleton access.
+ static SymbianDeviceManager *instance();
+
+ SymbianDeviceList devices() const;
+ QString toString() const;
+
+ // Acquire a device for use. See releaseDevice().
+ TrkDevicePtr acquireDevice(const QString &port);
+
+ int findByPortName(const QString &p) const;
+ QString friendlyNameForPort(const QString &port) const;
+
+public slots:
+ void update();
+ // Relase a device, make it available for further use.
+ void releaseDevice(const QString &port);
+ void setAdditionalInformation(const QString &port, const QString &ai);
+
+signals:
+ void deviceRemoved(const SymbianUtils::SymbianDevice &d);
+ void deviceAdded(const SymbianUtils::SymbianDevice &d);
+ void updated();
+
+private:
+ void ensureInitialized() const;
+ void update(bool emitSignals);
+ SymbianDeviceList serialPorts() const;
+ SymbianDeviceList blueToothDevices() const;
+
+ SymbianDeviceManagerPrivate *d;
+};
+
+SYMBIANUTILS_EXPORT QDebug operator<<(QDebug d, const SymbianDeviceManager &);
+
+} // namespace SymbianUtils
+
+#endif // SYMBIANDEVICEMANAGER_H
diff --git a/src/runonphone/symbianutils/symbianutils.pri b/src/runonphone/symbianutils/symbianutils.pri
new file mode 100644
index 000000000..f07e494ed
--- /dev/null
+++ b/src/runonphone/symbianutils/symbianutils.pri
@@ -0,0 +1,35 @@
+INCLUDEPATH *= $$PWD
+
+QT += network
+
+# Input
+HEADERS += $$PWD/symbianutils_global.h \
+ $$PWD/callback.h \
+ $$PWD/trkutils.h \
+ $$PWD/trkutils_p.h \
+ $$PWD/trkdevice.h \
+ $$PWD/launcher.h \
+ $$PWD/bluetoothlistener.h \
+ $$PWD/communicationstarter.h \
+ $$PWD/symbiandevicemanager.h \
+ $$PWD/tcftrkdevice.h \
+ $$PWD/tcftrkmessage.h \
+ $$PWD/json.h
+
+SOURCES += $$PWD/trkutils.cpp \
+ $$PWD/trkdevice.cpp \
+ $$PWD/launcher.cpp \
+ $$PWD/bluetoothlistener.cpp \
+ $$PWD/communicationstarter.cpp \
+ $$PWD/symbiandevicemanager.cpp \
+ $$PWD/tcftrkdevice.cpp \
+ $$PWD/tcftrkmessage.cpp \
+ $$PWD/json.cpp
+
+# Tests/trklauncher is a console application
+contains(QT, gui) {
+ HEADERS += $$PWD/bluetoothlistener_gui.h
+ SOURCES += $$PWD/bluetoothlistener_gui.cpp
+} else {
+ message(Trk: Console ...)
+}
diff --git a/src/runonphone/symbianutils/symbianutils_global.h b/src/runonphone/symbianutils/symbianutils_global.h
new file mode 100644
index 000000000..d7acabffd
--- /dev/null
+++ b/src/runonphone/symbianutils/symbianutils_global.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SYMBIANUTILS_GLOBAL_H
+#define SYMBIANUTILS_GLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(SYMBIANUTILS_BUILD_LIB)
+# define SYMBIANUTILS_EXPORT Q_DECL_EXPORT
+#elif defined(SYMBIANUTILS_BUILD_STATIC_LIB) || defined(SYMBIANUTILS_INCLUDE_PRI)
+# define SYMBIANUTILS_EXPORT
+#else
+# define SYMBIANUTILS_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif // SYMBIANUTILS_GLOBAL_H
diff --git a/src/runonphone/symbianutils/tcftrkdevice.cpp b/src/runonphone/symbianutils/tcftrkdevice.cpp
new file mode 100644
index 000000000..5b923ee90
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkdevice.cpp
@@ -0,0 +1,929 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkdevice.h"
+#include "json.h"
+
+#include <QtNetwork/QAbstractSocket>
+#include <QtCore/QDebug>
+#include <QtCore/QVector>
+#include <QtCore/QQueue>
+#include <QtCore/QTextStream>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+
+enum { debug = 0 };
+
+static const char messageTerminatorC[] = "\003\001";
+
+namespace tcftrk {
+// ------------- TcfTrkCommandError
+
+TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void TcfTrkCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+void TcfTrkCommandError::write(QTextStream &str) const
+{
+ if (timeMS) {
+ const QDateTime time(QDate(1970, 1, 1));
+ str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
+ << " '" << format << '\'';
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString TcfTrkCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty() || values.front().type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.front().children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toInt();
+ errorKeyCount++;
+ } else if (c.name() == "AltOrg") {
+ alternativeOrganization = c.data();
+ errorKeyCount++;
+ }
+ }
+ } while (false);
+ const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
+ if (!errorFound)
+ clear();
+ if (debug) {
+ qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
+ if (!values.isEmpty())
+ qDebug() << values.front().toString();
+ }
+ return errorFound;
+}
+
+// ------------ TcfTrkCommandResult
+
+TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
+ const QByteArray &r,
+ const QVector<JsonValue> &v,
+ const QVariant &ck) :
+ type(FailReply), service(s), request(r), values(v), cookie(ck)
+{
+ switch (typeChar) {
+ case 'N':
+ type = FailReply;
+ break;
+ case 'P':
+ type = ProgressReply;
+ break;
+ case 'R':
+ type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
+ break;
+ default:
+ qWarning("Unknown TCF reply type '%c'", typeChar);
+ }
+}
+
+QString TcfTrkCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ return rc;
+}
+
+QString TcfTrkCommandResult::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Command answer ";
+ switch (type) {
+ case SuccessReply:
+ str << "[success]";
+ break;
+ case CommandErrorReply:
+ str << "[command error]";
+ break;
+ case FailReply:
+ str << "[fail (NAK)]";
+ break;
+ case ProgressReply:
+ str << "[progress]";
+ break;
+ }
+ str << ", " << values.size() << " values(s) to request: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "' ";
+ if (cookie.isValid())
+ str << " cookie: " << cookie.toString();
+ str << '\n';
+ for (int i = 0, count = values.size(); i < count; i++)
+ str << '#' << i << ' ' << values.at(i).toString() << '\n';
+ if (type == CommandErrorReply)
+ str << "Error: " << errorString();
+ return rc;
+}
+
+struct TcfTrkSendQueueEntry
+{
+ typedef TcfTrkDevice::MessageType MessageType;
+
+ explicit TcfTrkSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const TcfTrkCallback &cb= TcfTrkCallback(),
+ const QVariant &ck = QVariant()) :
+ messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb) {}
+
+ MessageType messageType;
+ Services service;
+ QByteArray data;
+ int token;
+ QVariant cookie;
+ TcfTrkCallback callback;
+};
+
+struct TcfTrkDevicePrivate {
+ typedef TcfTrkDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
+
+ TcfTrkDevicePrivate();
+
+ const QByteArray m_messageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ int m_token;
+ QQueue<TcfTrkSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+};
+
+TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
+ m_messageTerminator(messageTerminatorC),
+ m_verbose(0), m_token(0)
+{
+}
+
+TcfTrkDevice::TcfTrkDevice(QObject *parent) :
+ QObject(parent), d(new TcfTrkDevicePrivate)
+{
+}
+
+TcfTrkDevice::~TcfTrkDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> TcfTrkDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
+{
+ d->m_registerNames = n;
+ if (d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ const int count = n.size();
+ str << "Registers (" << count << "): ";
+ for (int i = 0; i < count; i++)
+ str << '#' << i << '=' << n.at(i) << ' ';
+ emitLogMessage(msg);
+ }
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
+{
+ return d->m_device;
+}
+
+TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
+{
+ const IODevicePtr old = d->m_device;
+ if (!old.isNull()) {
+ old.data()->disconnect(this);
+ d->m_device = IODevicePtr();
+ }
+ d->m_readBuffer.clear();
+ d->m_token = 0;
+ d->m_sendQueue.clear();
+ return old;
+}
+
+void TcfTrkDevice::setDevice(const IODevicePtr &dp)
+{
+ if (dp.data() == d->m_device.data())
+ return;
+ if (dp.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
+ return;
+ }
+ takeDevice();
+ d->m_device = dp;
+ connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
+ if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
+ connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
+ connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
+ }
+}
+
+void TcfTrkDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void TcfTrkDevice::slotDeviceSocketStateChanged()
+{
+ if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
+ const QAbstractSocket::SocketState st = s->state();
+ switch (st) {
+ case QAbstractSocket::UnconnectedState:
+ emitLogMessage(QLatin1String("Unconnected"));
+ break;
+ case QAbstractSocket::HostLookupState:
+ emitLogMessage(QLatin1String("HostLookupState"));
+ break;
+ case QAbstractSocket::ConnectingState:
+ emitLogMessage(QLatin1String("Connecting"));
+ break;
+ case QAbstractSocket::ConnectedState:
+ emitLogMessage(QLatin1String("Connected"));
+ break;
+ case QAbstractSocket::ClosingState:
+ emitLogMessage(QLatin1String("Closing"));
+ break;
+ default:
+ emitLogMessage(QString::fromLatin1("State %1").arg(st));
+ break;
+ }
+ }
+}
+
+static inline QString debugMessage(QByteArray message, const char *prefix = 0)
+{
+ message.replace('\0', '|');
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void TcfTrkDevice::slotDeviceReadyRead()
+{
+ d->m_readBuffer += d->m_device->readAll();
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
+ if (messageEndPos == -1)
+ break;
+ const QByteArray message = d->m_readBuffer.left(messageEndPos);
+ if (debug)
+ qDebug("Read:\n%s", qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further message
+}
+
+// Split \0-terminated message into tokens, skipping the initial type character
+static inline QVector<QByteArray> splitMessage(const QByteArray &message)
+{
+ QVector<QByteArray> tokens;
+ tokens.reserve(7);
+ const int messageSize = message.size();
+ for (int pos = 2; pos < messageSize; ) {
+ const int nextPos = message.indexOf('\0', pos);
+ if (nextPos == -1)
+ break;
+ tokens.push_back(message.mid(pos, nextPos - pos));
+ pos = nextPos + 1;
+ }
+ return tokens;
+}
+
+int TcfTrkDevice::parseMessage(const QByteArray &message)
+{
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(message, "TCF ->"));
+ // Special JSON parse error message or protocol format error.
+ // The port is usually closed after receiving it.
+ // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
+ if (message.startsWith("\003\002")) {
+ QByteArray text = message.mid(2);
+ const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
+ emit error(errorMessage);
+ return 0;
+ }
+ if (message.size() < 4 || message.at(1) != '\0')
+ return 1;
+ // Split into tokens
+ const char type = message.at(0);
+ const QVector<QByteArray> tokens = splitMessage(message);
+ switch (type) {
+ case 'E':
+ return parseTcfEvent(tokens);
+ case 'R': // Command replies
+ case 'N':
+ case 'P':
+ return parseTcfCommandReply(type, tokens);
+ default:
+ emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
+ return 756;
+ }
+ return 0;
+}
+
+int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
+ // Find the corresponding entry in the written messages hash.
+ const int tokenCount = tokens.size();
+ if (tokenCount < 1)
+ return 234;
+ bool tokenOk;
+ const int token = tokens.at(0).toInt(&tokenOk);
+ if (!tokenOk)
+ return 235;
+ const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
+ if (it == d->m_writtenMessages.end()) {
+ qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ if (!it.value().callback) {
+ d->m_writtenMessages.erase(it);
+ return 0;
+ }
+ // Parse values into JSON
+ QVector<JsonValue> values;
+ values.reserve(tokenCount);
+ for (int i = 1; i < tokenCount; i++) {
+ if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
+ const JsonValue value(tokens.at(i));
+ if (value.isValid()) {
+ values.push_back(value);
+ } else {
+ qWarning("JSON parse error for reply to command token %d: #%d '%s'",
+ token, i, tokens.at(i).constData());
+ d->m_writtenMessages.erase(it);
+ return -1;
+ }
+ }
+ }
+
+ // Construct result and invoke callback, remove entry from map.
+ TcfTrkCallback callback = it.value().callback;
+ TcfTrkCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+ d->m_writtenMessages.erase(it);
+ callback(result);
+ return 0;
+}
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
+{
+ // Event: Ignore the periodical heartbeat event, answer 'Hello',
+ // emit signal for the rest
+ if (tokens.size() < 3)
+ return 433;
+ const Services service = serviceFromName(tokens.at(0).constData());
+ if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
+ return 0;
+ QVector<JsonValue> values;
+ for (int i = 2; i < tokens.size(); i++) {
+ const JsonValue value(tokens.at(i));
+ if (!value.isValid())
+ return 434;
+ values.push_back(value);
+ }
+ // Parse known events, emit signals
+ QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event.
+ if (knownEvent->type() == TcfTrkEvent::LocatorHello)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ emit tcfEvent(*knownEvent);
+ }
+ emit genericTcfEvent(service, tokens.at(1), values);
+
+ if (debug || d->m_verbose) {
+ QString msg;
+ QTextStream str(&msg);
+ if (knownEvent.isNull()) {
+ str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
+ foreach(const JsonValue &val, values)
+ str << " " << val.toString() << '\n';
+ } else {
+ str << knownEvent->toString();
+ }
+ emitLogMessage(msg);
+ }
+
+ return 0;
+}
+
+unsigned TcfTrkDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+void TcfTrkDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void TcfTrkDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool TcfTrkDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
+ return false;
+ }
+ return true;
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+
+{
+ if (!checkOpen())
+ return;
+ // Format the message
+ const int token = d->m_token++;
+ QByteArray data;
+ data.reserve(30 + commandParametersLength);
+ data.append('C');
+ data.append('\0');
+ data.append(QByteArray::number(token));
+ data.append('\0');
+ data.append(serviceName(service));
+ data.append('\0');
+ data.append(command);
+ data.append('\0');
+ if (commandParametersLength)
+ data.append(commandParameters, commandParametersLength);
+ const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack,
+ const QVariant &cookie)
+{
+ sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void TcfTrkDevice::writeMessage(QByteArray data)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (!data.endsWith('\0'))
+ data.append('\0');
+ data += d->m_messageTerminator;
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ d->m_device->write(data);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void TcfTrkDevice::checkSendQueue()
+{
+ // Fire off messages or invoke noops until a message with reply is found
+ // and an entry to writtenMessages is made.
+ while (d->m_writtenMessages.empty()) {
+ if (d->m_sendQueue.isEmpty())
+ break;
+ TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
+ switch (entry.messageType) {
+ case MessageWithReply:
+ d->m_writtenMessages.insert(entry.token, entry);
+ writeMessage(entry.data);
+ break;
+ case MessageWithoutReply:
+ writeMessage(entry.data);
+ break;
+ case NoopMessage: // Invoke the noop-callback for synchronization
+ if (entry.callback) {
+ TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
+ noopResult.cookie = entry.cookie;
+ entry.callback(noopResult);
+ }
+ break;
+ }
+ }
+}
+
+// Fix slashes
+static inline QString fixFileName(QString in)
+{
+ in.replace(QLatin1Char('/'), QLatin1Char('\\'));
+ return in;
+}
+
+// Start a process (consisting of a non-reply setSettings and start).
+void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binaryIn,
+ unsigned uid,
+ QStringList arguments,
+ QString workingDirectory,
+ bool debugControl,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ // Obtain the bin directory, expand by c:/sys/bin if missing
+ const QChar backSlash('\\');
+ int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
+ if (slashPos == -1)
+ slashPos = binaryIn.lastIndexOf(backSlash);
+ const QString sysBin = QLatin1String("c:/sys/bin");
+ const QString binaryFileName = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
+ const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
+ const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
+
+ // Fixup: Does argv[0] convention exist on Symbian?
+ arguments.push_front(binary);
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
+ << '\0' << '['
+ << binary << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << fixFileName(workingDirectory)
+ << '\0' << binary << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void TcfTrkDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries,
+ const QVariant &cookie)
+{
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
+ << '\0' << '['
+ << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries
+ << ']';
+ sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count,
+ quint64 rangeStart,
+ quint64 rangeEnd,
+ const QVariant &cookie)
+{
+ QByteArray resumeData;
+ JsonInputStream str(resumeData);
+ str << id << '\0' << int(mode) << '\0' << count;
+ switch (mode) {
+ case RM_STEP_OVER_RANGE:
+ case RM_STEP_INTO_RANGE:
+ case RM_REVERSE_STEP_OVER_RANGE:
+ case RM_REVERSE_STEP_INTO_RANGE:
+ str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
+ << ',' << "RANGE_END" << ':' << rangeEnd << '}';
+ break;
+ default:
+ break;
+ }
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendTcfTrkMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie)
+{
+ QByteArray getData;
+ JsonInputStream str(getData);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
+ << '\0' << data.toBase64();
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ // start/word size/mode. Mode should ideally be 1 (continue on error?)
+ str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
+ sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
+{
+ if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
+ return QByteArray();
+ const JsonValue &memoryV = r.values.front();
+
+ if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
+ || !memoryV.data().endsWith('='))
+ return QByteArray();
+ // Catch errors reported as hash:
+ // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
+ // Not sure what to make of it.
+ if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
+ qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
+ // decode
+ const QByteArray memory = QByteArray::fromBase64(memoryV.data());
+ if (memory.isEmpty())
+ qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
+ if (debug)
+ qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << ids;
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
+}
+
+void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
+ return;
+ }
+
+ QVector<QByteArray> ids;
+ ids.reserve(count);
+ for (unsigned i = start; i < end; i++)
+ ids.push_back(d->m_registerNames.at(i));
+ sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &id,
+ unsigned value,
+ const QVariant &cookie)
+{
+ // TODO: use "Registers" (which uses base64-encoded values)
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId << '\0' << QVector<QByteArray>(1, id)
+ << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
+ sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+} // namespace tcftrk
diff --git a/src/runonphone/symbianutils/tcftrkdevice.h b/src/runonphone/symbianutils/tcftrkdevice.h
new file mode 100644
index 000000000..d42f3f3cb
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkdevice.h
@@ -0,0 +1,295 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TCFTRKENGINE_H
+#define TCFTRKENGINE_H
+
+#include "symbianutils_global.h"
+#include "tcftrkmessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+struct TcfTrkDevicePrivate;
+struct Breakpoint;
+
+/* Command error handling in TCF:
+ * 1) 'Severe' errors (JSON format, parameter format): Trk emits a
+ * nonstandard message (\3\2 error parameters) and closes the connection.
+ * 2) Protocol errors: 'N' without error message is returned.
+ * 3) Errors in command execution: 'R' with a TCF error hash is returned
+ * (see TcfTrkCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandError {
+ TcfTrkCommandError();
+ void clear();
+ operator bool() const { return timeMS != 0; }
+ QString toString() const;
+ void write(QTextStream &str) const;
+ bool parse(const QVector<JsonValue> &values);
+
+ quint64 timeMS; // Since 1.1.1970
+ int code;
+ QByteArray format; // message
+ // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+ QByteArray alternativeOrganization;
+ int alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT TcfTrkCommandResult {
+ enum Type {
+ SuccessReply, // 'R' and no error -> all happy.
+ CommandErrorReply, // 'R' with TcfTrkCommandError received
+ ProgressReply, // 'P', progress indicator
+ FailReply // 'N' Protocol NAK, severe error
+ };
+
+ explicit TcfTrkCommandResult(Type t = SuccessReply);
+ explicit TcfTrkCommandResult(char typeChar, Services service,
+ const QByteArray &request,
+ const QVector<JsonValue> &values,
+ const QVariant &cookie);
+
+ QString toString() const;
+ QString errorString() const;
+ operator bool() const { return type == SuccessReply || type == ProgressReply; }
+
+ Type type;
+ Services service;
+ QByteArray request;
+ TcfTrkCommandError commandError;
+ QVector<JsonValue> values;
+ QVariant cookie;
+};
+
+typedef trk::Callback<const TcfTrkCommandResult &> TcfTrkCallback;
+
+/* TcfTrkDevice: TCF communication helper using an asynchronous QIODevice
+ * implementing the TCF protocol according to:
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Specification.html
+http://dev.eclipse.org/svnroot/dsdp/org.eclipse.tm.tcf/trunk/docs/TCF%20Services.html
+ * Commands can be sent along with callbacks that are passed a
+ * TcfTrkCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+*/
+
+class SYMBIANUTILS_EXPORT TcfTrkDevice : public QObject
+{
+ Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+ Q_OBJECT
+public:
+ enum MessageType { MessageWithReply,
+ MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+ NoopMessage };
+
+ typedef QSharedPointer<QIODevice> IODevicePtr;
+
+ explicit TcfTrkDevice(QObject *parent = 0);
+ virtual ~TcfTrkDevice();
+
+ unsigned verbose() const;
+
+ // Mapping of register names for indices
+ QVector<QByteArray> registerNames() const;
+ void setRegisterNames(const QVector<QByteArray>& n);
+
+ IODevicePtr device() const;
+ IODevicePtr takeDevice();
+ void setDevice(const IODevicePtr &dp);
+
+ void sendTcfTrkMessage(MessageType mt, Services service,
+ const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ void sendTcfTrkMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const TcfTrkCallback &callBack = TcfTrkCallback(),
+ const QVariant &cookie = QVariant());
+
+ // Convenience messages: Start a process
+ void sendProcessStartCommand(const TcfTrkCallback &callBack,
+ const QString &binary,
+ unsigned uid,
+ QStringList arguments = QStringList(),
+ QString workingDirectory = QString(),
+ bool debugControl = true,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Preferred over Processes:Terminate by TCF TRK.
+ void sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendProcessTerminateCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Non-standard: Remove executable from settings.
+ // Probably needs to be called after stopping. This command has no response.
+ void sendSettingsRemoveExecutableCommand(const QString &binaryIn,
+ unsigned uid,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ void sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Resume / Step (see RunControlResumeMode).
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ RunControlResumeMode mode,
+ unsigned count /* = 1, currently ignored. */,
+ quint64 rangeStart, quint64 rangeEnd,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to resume a suspended process
+ void sendRunControlResumeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
+ const Breakpoint &b,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
+ const QVector<QByteArray> &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+
+ void sendMemoryGetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie = QVariant());
+
+ void sendMemorySetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie = QVariant());
+
+ // Reply is an array of hexvalues
+ void sendRegistersGetMCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie = QVariant());
+
+ // Convenience to get a range of register "R0" .. "R<n>".
+ // Cookie will be an int containing "start".
+ void sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count);
+
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ const QByteArray &ids,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+ // Set register
+ void sendRegistersSetCommand(const TcfTrkCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ unsigned value,
+ const QVariant &cookie = QVariant());
+
+ static QByteArray parseMemoryGet(const TcfTrkCommandResult &r);
+
+signals:
+ void genericTcfEvent(int service, const QByteArray &name, const QVector<tcftrk::JsonValue> &value);
+ void tcfEvent(const tcftrk::TcfTrkEvent &knownEvent);
+
+ void logMessage(const QString &);
+ void error(const QString &);
+
+public slots:
+ void setVerbose(unsigned v);
+
+private slots:
+ void slotDeviceError();
+ void slotDeviceSocketStateChanged();
+ void slotDeviceReadyRead();
+
+private:
+ bool checkOpen();
+ void checkSendQueue();
+ void writeMessage(QByteArray data);
+ void emitLogMessage(const QString &);
+ int parseMessage(const QByteArray &);
+ int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+ int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+ TcfTrkDevicePrivate *d;
+};
+
+} // namespace tcftrk
+
+#endif // TCFTRKENGINE_H
diff --git a/src/runonphone/symbianutils/tcftrkmessage.cpp b/src/runonphone/symbianutils/tcftrkmessage.cpp
new file mode 100644
index 000000000..8d62dece2
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkmessage.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "tcftrkmessage.h"
+#include "json.h"
+
+#include <QtCore/QString>
+#include <QtCore/QTextStream>
+
+// Names matching the enum
+static const char *serviceNamesC[] =
+{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
+ "Registers", "SimpleRegisters",
+ "UnknownService"};
+
+namespace tcftrk {
+
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
+{
+ QString rc;
+ const int count = a.size();
+ for (int i = 0; i < count; i++) {
+ if (i)
+ rc += QLatin1Char(sep);
+ rc += QString::fromUtf8(a.at(i));
+ }
+ return rc;
+}
+
+static inline bool jsonToBool(const JsonValue& js)
+{
+ return js.type() == JsonValue::Boolean && js.data() == "true";
+}
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s)
+{
+ return serviceNamesC[s];
+}
+
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *n)
+{
+ const int count = sizeof(serviceNamesC)/sizeof(char *);
+ for (int i = 0; i < count; i++)
+ if (!qstrcmp(serviceNamesC[i], n))
+ return static_cast<Services>(i);
+ return UnknownService;
+}
+
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a)
+{
+ const int columns = 16;
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str.setPadChar(QLatin1Char('0'));
+ const unsigned char *start = reinterpret_cast<const unsigned char *>(a.constData());
+ const unsigned char *end = start + a.size();
+ for (const unsigned char *p = start; p < end ; ) {
+ str << "0x";
+ str.setFieldWidth(4);
+ str << (p - start);
+ str.setFieldWidth(0);
+ str << ' ';
+ QString asc;
+ int c = 0;
+ for ( ; c < columns && p < end; c++, p++) {
+ const unsigned u = *p;
+ str.setFieldWidth(2);
+ str << u;
+ str.setFieldWidth(0);
+ str << ' ';
+ switch (u) {
+ case '\n':
+ asc += QLatin1String("\\n");
+ break;
+ case '\r':
+ asc += QLatin1String("\\r");
+ break;
+ case '\t':
+ asc += QLatin1String("\\t");
+ break;
+ default:
+ if (u >= 32 && u < 128) {
+ asc += QLatin1Char(' ');
+ asc += QLatin1Char(u);
+ } else {
+ asc += QLatin1String(" .");
+ }
+ break;
+ }
+ }
+ if (const int remainder = columns - c)
+ str << QString(3 * remainder, QLatin1Char(' '));
+ str << ' ' << asc << '\n';
+ }
+ return rc;
+}
+
+// ----------- RunControlContext
+RunControlContext::RunControlContext() :
+ flags(0), resumeFlags(0)
+{
+}
+
+void RunControlContext::clear()
+{
+ flags =0;
+ resumeFlags = 0;
+ id.clear();
+ osid.clear();
+ parentId.clear();
+}
+
+RunControlContext::Type RunControlContext::typeFromTcfId(const QByteArray &id)
+{
+ // "p12" or "p12.t34"?
+ return id.contains(".t") ? Thread : Process;
+}
+
+unsigned RunControlContext::processId() const
+{
+ return processIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::threadId() const
+{
+ return threadIdFromTcdfId(id);
+}
+
+unsigned RunControlContext::processIdFromTcdfId(const QByteArray &id)
+{
+ // Cut out process id from "p12" or "p12.t34"?
+ if (!id.startsWith('p'))
+ return 0;
+ const int dotPos = id.indexOf('.');
+ const int pLen = dotPos == -1 ? id.size() : dotPos;
+ return id.mid(1, pLen - 1).toUInt();
+}
+
+unsigned RunControlContext::threadIdFromTcdfId(const QByteArray &id)
+{
+ const int tPos = id.indexOf(".t");
+ return tPos != -1 ? id.mid(tPos + 2).toUInt() : uint(0);
+}
+
+QByteArray RunControlContext::tcfId(unsigned processId, unsigned threadId /* = 0 */)
+{
+ QByteArray rc("p");
+ rc += QByteArray::number(processId);
+ if (threadId) {
+ rc += ".t";
+ rc += QByteArray::number(threadId);
+ }
+ return rc;
+}
+
+RunControlContext::Type RunControlContext::type() const
+{
+ return RunControlContext::typeFromTcfId(id);
+}
+
+bool RunControlContext::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "ID") {
+ id = c.data();
+ } else if (c.name() == "OSID") {
+ osid = c.data();
+ } else if (c.name() == "ParentID") {
+ parentId = c.data();
+ } else if (c.name() == "IsContainer") {
+ if (jsonToBool(c))
+ flags |= Container;
+ } else if (c.name() == "CanTerminate") {
+ if (jsonToBool(c))
+ flags |= CanTerminate;
+ } else if (c.name() == "CanResume") {
+ resumeFlags = c.data().toUInt();
+ } else if (c.name() == "HasState") {
+ if (jsonToBool(c))
+ flags |= HasState;
+ } else if (c.name() == "CanSuspend") {
+ if (jsonToBool(c))
+ flags |= CanSuspend;
+ }
+ }
+ return true;
+}
+
+QString RunControlContext::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+void RunControlContext::format(QTextStream &str) const
+{
+ str << " id='" << id << "' osid='" << osid
+ << "' parentId='" << parentId <<"' ";
+ if (flags & Container)
+ str << "[container] ";
+ if (flags & HasState)
+ str << "[has state] ";
+ if (flags & CanSuspend)
+ str << "[can suspend] ";
+ if (flags & CanSuspend)
+ str << "[can terminate] ";
+ str.setIntegerBase(16);
+ str << " resume_flags: 0x" << resumeFlags;
+ str.setIntegerBase(10);
+}
+
+// ------ ModuleLoadEventInfo
+ModuleLoadEventInfo::ModuleLoadEventInfo() :
+ loaded(false), codeAddress(0), dataAddress(0), requireResume(false)
+{
+}
+
+void ModuleLoadEventInfo::clear()
+{
+ loaded = requireResume = false;
+ codeAddress = dataAddress =0;
+}
+
+bool ModuleLoadEventInfo::parse(const JsonValue &val)
+{
+ clear();
+ if (val.type() != JsonValue::Object)
+ return false;
+ foreach(const JsonValue &c, val.children()) {
+ if (c.name() == "Name") {
+ name = c.data();
+ } else if (c.name() == "File") {
+ file = c.data();
+ } else if (c.name() == "CodeAddress") {
+ codeAddress = c.data().toULongLong();
+ } else if (c.name() == "DataAddress") {
+ dataAddress = c.data().toULongLong();
+ } else if (c.name() == "Loaded") {
+ loaded = jsonToBool(c);
+ } else if (c.name() == "RequireResume") {
+ requireResume =jsonToBool(c);
+ }
+ }
+ return true;
+}
+void ModuleLoadEventInfo::format(QTextStream &str) const
+{
+ str << "name='" << name << "' file='" << file << "' " <<
+ (loaded ? "[loaded] " : "[not loaded] ");
+ if (requireResume)
+ str << "[requires resume] ";
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeAddress << " data: 0x" << dataAddress;
+ str.setIntegerBase(10);
+}
+
+// ---------------------- Breakpoint
+
+// Types matching enum
+static const char *breakPointTypesC[] = {"Software", "Hardware", "Auto"};
+
+Breakpoint::Breakpoint(quint64 loc) :
+ type(Auto), enabled(true), ignoreCount(0), location(loc), size(1), thumb(true)
+{
+ if (loc)
+ id = idFromLocation(location);
+}
+
+void Breakpoint::setContextId(unsigned processId, unsigned threadId)
+{
+ contextIds = QVector<QByteArray>(1, RunControlContext::tcfId(processId, threadId));
+}
+
+QByteArray Breakpoint::idFromLocation(quint64 loc)
+{
+ return QByteArray("BP_0x") + QByteArray::number(loc, 16);
+}
+
+QString Breakpoint::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str.setIntegerBase(16);
+ str << "Breakpoint '" << id << "' " << breakPointTypesC[type] << " for contexts '"
+ << joinByteArrays(contextIds, ',') << "' at 0x" << location;
+ str.setIntegerBase(10);
+ str << " size " << size;
+ if (enabled)
+ str << " [enabled]";
+ if (thumb)
+ str << " [thumb]";
+ if (ignoreCount)
+ str << " IgnoreCount " << ignoreCount;
+ return rc;
+}
+
+JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
+{
+ if (b.contextIds.isEmpty())
+ qWarning("tcftrk::Breakpoint: No context ids specified");
+
+ str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
+ << "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
+ << "Enabled" << ':' << b.enabled << ','
+ << "IgnoreCount" << ':' << b.ignoreCount << ','
+ << "ContextIds" << ':' << b.contextIds << ','
+ << "Location" << ':' << QString::number(b.location) << ','
+ << "Size" << ':' << b.size << ','
+ << "THUMB_BREAKPOINT" << ':' << b.thumb
+ << '}';
+ return str;
+}
+
+// --- Events
+TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+{
+}
+
+TcfTrkEvent::~TcfTrkEvent()
+{
+}
+
+TcfTrkEvent::Type TcfTrkEvent::type() const
+{
+ return m_type;
+}
+
+QString TcfTrkEvent::toString() const
+{
+ return QString();
+}
+
+static const char sharedLibrarySuspendReasonC[] = "Shared Library";
+
+TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+{
+ switch (s) {
+ case LocatorService:
+ if (nameBA == "Hello" && values.size() == 1 && values.front().type() == JsonValue::Array) {
+ QStringList services;
+ foreach (const JsonValue &jv, values.front().children())
+ services.push_back(QString::fromUtf8(jv.data()));
+ return new TcfTrkLocatorHelloEvent(services);
+ }
+ break;
+ case RunControlService:
+ if (values.empty())
+ return 0;
+ // "id/PC/Reason/Data"
+ if (nameBA == "contextSuspended" && values.size() == 4) {
+ const QByteArray idBA = values.at(0).data();
+ const quint64 pc = values.at(1).data().toULongLong();
+ const QByteArray reasonBA = values.at(2).data();
+ // Module load: Special
+ if (reasonBA == sharedLibrarySuspendReasonC) {
+ ModuleLoadEventInfo info;
+ if (!info.parse(values.at(3)))
+ return 0;
+ return new TcfTrkRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+ }
+ return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+ } // "contextSuspended"
+ if (nameBA == "contextAdded")
+ return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+ if (nameBA == "contextRemoved" && values.front().type() == JsonValue::Array) {
+ QVector<QByteArray> ids;
+ foreach(const JsonValue &c, values.front().children())
+ ids.push_back(c.data());
+ return new TcfTrkRunControlContextRemovedEvent(ids);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+// -------------- TcfTrkServiceHelloEvent
+TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
+ TcfTrkEvent(LocatorHello),
+ m_services(s)
+{
+}
+
+QString TcfTrkLocatorHelloEvent::toString() const
+{
+ return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
+}
+
+// -------------- TcfTrkIdEvent
+TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
+ TcfTrkEvent(t), m_id(id)
+{
+}
+
+// ---------- TcfTrkIdsEvent
+TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
+ TcfTrkEvent(t), m_ids(ids)
+{
+}
+
+QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+{
+ return joinByteArrays(m_ids, sep);
+}
+
+// ---------------- TcfTrkRunControlContextAddedEvent
+TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
+ TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+{
+}
+
+TcfTrkRunControlContextAddedEvent
+ *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+{
+ // Parse array of contexts
+ if (values.size() < 1 || values.front().type() != JsonValue::Array)
+ return 0;
+
+ RunControlContexts contexts;
+ foreach (const JsonValue &v, values.front().children()) {
+ RunControlContext context;
+ if (context.parse(v))
+ contexts.push_back(context);
+ }
+ return new TcfTrkRunControlContextAddedEvent(contexts);
+}
+
+QString TcfTrkRunControlContextAddedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "RunControl: " << m_contexts.size() << " context(s) "
+ << (type() == RunControlContextAdded ? "added" : "removed")
+ << '\n';
+ foreach (const RunControlContext &c, m_contexts) {
+ c.format(str);
+ str << '\n';
+ }
+ return rc;
+}
+
+// --------------- TcfTrkRunControlContextRemovedEvent
+TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+ TcfTrkIdsEvent(RunControlContextRemoved, ids)
+{
+}
+
+QString TcfTrkRunControlContextRemovedEvent::toString() const
+{
+ return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
+}
+
+// --------------- TcfTrkRunControlContextSuspendedEvent
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+{
+}
+
+TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc) :
+ TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+{
+}
+
+void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+{
+ str.setIntegerBase(16);
+ str << "RunControl: '" << idString() << "' suspended at 0x"
+ << m_pc << ": '" << m_reason << "'.";
+ str.setIntegerBase(10);
+}
+
+QString TcfTrkRunControlContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ format(str);
+ return rc;
+}
+
+TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+{
+ if (m_reason == sharedLibrarySuspendReasonC)
+ return ModuleLoad;
+ if (m_reason == "Breakpoint")
+ return BreakPoint;
+ // 'Data abort exception'/'Thread has panicked' ... unfortunately somewhat unspecific.
+ if (m_reason.contains("exception") || m_reason.contains("panick"))
+ return Crash;
+ return Other;
+}
+
+TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi) :
+ TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+ m_mi(mi)
+{
+}
+
+QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ TcfTrkRunControlContextSuspendedEvent::format(str);
+ str << ' ';
+ m_mi.format(str);
+ return rc;
+}
+
+
+} // namespace tcftrk
diff --git a/src/runonphone/symbianutils/tcftrkmessage.h b/src/runonphone/symbianutils/tcftrkmessage.h
new file mode 100644
index 000000000..f2c8022e3
--- /dev/null
+++ b/src/runonphone/symbianutils/tcftrkmessage.h
@@ -0,0 +1,296 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRCFTRKMESSAGE_H
+#define TRCFTRKMESSAGE_H
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QStringList>
+#include <QtCore/QVector>
+
+QT_BEGIN_NAMESPACE
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace tcftrk {
+
+class JsonValue;
+class JsonInputStream;
+
+enum Services {
+ LocatorService,
+ RunControlService,
+ ProcessesService,
+ MemoryService,
+ SettingsService, // non-standard, trk specific
+ BreakpointsService,
+ RegistersService,
+ SimpleRegistersService, // non-standard, trk specific
+ UnknownService
+}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
+
+// Modes of RunControl/'Resume' (see EDF documentation).
+// As of 24.6.2010, RM_RESUME, RM_STEP_OVER, RM_STEP_INTO,
+// RM_STEP_OVER_RANGE, RM_STEP_INTO_RANGE are supported with
+// RANG_START/RANGE_END parameters.
+enum RunControlResumeMode {
+ RM_RESUME = 0,
+ RM_STEP_OVER = 1, RM_STEP_INTO = 2,
+ RM_STEP_OVER_LINE = 3, RM_STEP_INTO_LINE = 4,
+ RM_STEP_OUT = 5, RM_REVERSE_RESUME = 6,
+ RM_REVERSE_STEP_OVER = 7, RM_REVERSE_STEP_INTO = 8,
+ RM_REVERSE_STEP_OVER_LINE = 9, RM_REVERSE_STEP_INTO_LINE = 10,
+ RM_REVERSE_STEP_OUT = 11, RM_STEP_OVER_RANGE = 12,
+ RM_STEP_INTO_RANGE = 13, RM_REVERSE_STEP_OVER_RANGE = 14,
+ RM_REVERSE_STEP_INTO_RANGE = 15
+};
+
+SYMBIANUTILS_EXPORT const char *serviceName(Services s);
+SYMBIANUTILS_EXPORT Services serviceFromName(const char *);
+
+// Debug helpers
+SYMBIANUTILS_EXPORT QString formatData(const QByteArray &a);
+SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep = ',');
+
+// Context used in 'RunControl contextAdded' events and in reply
+// to 'Processes start'. Could be thread or process.
+struct SYMBIANUTILS_EXPORT RunControlContext {
+ enum Flags {
+ Container = 0x1, HasState = 0x2, CanSuspend = 0x4,
+ CanTerminate = 0x8
+ };
+ enum Type { Process, Thread };
+
+ RunControlContext();
+ Type type() const;
+ unsigned processId() const;
+ unsigned threadId() const;
+
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+ QString toString() const;
+
+ // Helper for converting the TCF ids ("p12" or "p12.t34")
+ static Type typeFromTcfId(const QByteArray &id);
+ static unsigned processIdFromTcdfId(const QByteArray &id);
+ static unsigned threadIdFromTcdfId(const QByteArray &id);
+ static QByteArray tcfId(unsigned processId, unsigned threadId = 0);
+
+ unsigned flags;
+ unsigned resumeFlags;
+ QByteArray id; // "p434.t699"
+ QByteArray osid; // Non-standard: Process or thread id
+ QByteArray parentId; // Parent process id of a thread.
+};
+
+// Module load information occurring with 'RunControl contextSuspended' events
+struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
+ ModuleLoadEventInfo();
+ void clear();
+ bool parse(const JsonValue &v);
+ void format(QTextStream &str) const;
+
+ QByteArray name;
+ QByteArray file;
+ bool loaded;
+ quint64 codeAddress;
+ quint64 dataAddress;
+ bool requireResume;
+};
+
+// Breakpoint as supported by TcfTrk source June 2010
+// TODO: Add watchpoints,etc once they are implemented
+struct SYMBIANUTILS_EXPORT Breakpoint {
+ enum Type { Software, Hardware, Auto };
+
+ explicit Breakpoint(quint64 loc = 0);
+ void setContextId(unsigned processId, unsigned threadId = 0);
+ QString toString() const;
+
+ static QByteArray idFromLocation(quint64 loc); // Automagically determine from location
+
+ Type type;
+ bool enabled;
+ int ignoreCount;
+ QVector<QByteArray> contextIds; // Process or thread ids.
+ QByteArray id; // Id of the breakpoint;
+ quint64 location;
+ unsigned size;
+ bool thumb;
+};
+
+SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
+
+// Event hierarchy
+class SYMBIANUTILS_EXPORT TcfTrkEvent {
+ Q_DISABLE_COPY(TcfTrkEvent)
+public:
+ enum Type { None,
+ LocatorHello,
+ RunControlContextAdded,
+ RunControlContextRemoved,
+ RunControlSuspended,
+ RunControlBreakpointSuspended,
+ RunControlModuleLoadSuspended,
+ RunControlResumed
+ };
+
+ virtual ~TcfTrkEvent();
+
+ Type type() const;
+ virtual QString toString() const;
+
+ static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+
+protected:
+ explicit TcfTrkEvent(Type type = None);
+
+private:
+ const Type m_type;
+};
+
+// ServiceHello
+class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+public:
+ explicit TcfTrkLocatorHelloEvent(const QStringList &);
+
+ const QStringList &services() { return m_services; }
+ virtual QString toString() const;
+
+private:
+ QStringList m_services;
+};
+
+// Base for events that just have one id as parameter
+// (simple suspend)
+class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+public:
+ QByteArray id() const { return m_id; }
+ QString idString() const { return QString::fromUtf8(m_id); }
+
+private:
+ const QByteArray m_id;
+};
+
+// Base for events that just have some ids as parameter
+// (context removed)
+class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+protected:
+ explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+
+public:
+ QVector<QByteArray> ids() const { return m_ids; }
+ QString joinedIdString(const char sep = ',') const;
+
+private:
+ const QVector<QByteArray> m_ids;
+};
+
+// RunControlContextAdded
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+public:
+ typedef QVector<RunControlContext> RunControlContexts;
+
+ explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+
+ const RunControlContexts &contexts() const { return m_contexts; }
+ virtual QString toString() const;
+
+ static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+
+private:
+ const RunControlContexts m_contexts;
+};
+
+// RunControlContextRemoved
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+public:
+ explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+ virtual QString toString() const;
+};
+
+// Simple RunControlContextSuspended (process/thread)
+class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+public:
+ enum Reason { BreakPoint, ModuleLoad, Crash, Other } ;
+
+ explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ virtual QString toString() const;
+
+ quint64 pc() const { return m_pc; }
+ QByteArray reasonID() const { return m_reason; }
+ Reason reason() const;
+
+protected:
+ explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+ const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc = 0);
+ void format(QTextStream &str) const;
+
+private:
+ const quint64 m_pc;
+ const QByteArray m_reason;
+};
+
+// RunControlContextSuspended due to module load
+class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+public:
+ explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ const QByteArray &reason,
+ quint64 pc,
+ const ModuleLoadEventInfo &mi);
+
+ virtual QString toString() const;
+ const ModuleLoadEventInfo &info() const { return m_mi; }
+
+private:
+ const ModuleLoadEventInfo m_mi;
+};
+
+} // namespace tcftrk
+#endif // TRCFTRKMESSAGE_H
diff --git a/src/runonphone/symbianutils/trkdevice.cpp b/src/runonphone/symbianutils/trkdevice.cpp
new file mode 100644
index 000000000..2825b5f9b
--- /dev/null
+++ b/src/runonphone/symbianutils/trkdevice.cpp
@@ -0,0 +1,1184 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "trkdevice.h"
+#include "trkutils.h"
+#include "trkutils_p.h"
+
+#include <QtCore/QString>
+#include <QtCore/QDebug>
+#include <QtCore/QQueue>
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QThread>
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QScopedPointer>
+#include <QtCore/QMetaType>
+
+#ifdef Q_OS_WIN
+# include <windows.h>
+#else
+# include <QtCore/QFile>
+
+# include <stdio.h>
+# include <sys/ioctl.h>
+# include <sys/types.h>
+# include <termios.h>
+# include <errno.h>
+# include <string.h>
+# include <unistd.h>
+/* Required headers for select() according to POSIX.1-2001 */
+# include <sys/select.h>
+/* Required headers for select() according to earlier standards:
+ #include <sys/time.h>
+ #include <sys/types.h>
+ #include <unistd.h>
+*/
+#endif
+
+#ifdef Q_OS_WIN
+
+// Format windows error from GetLastError() value:
+// TODO: Use the one provided by the utils lib.
+QString winErrorMessage(unsigned long error)
+{
+ QString rc = QString::fromLatin1("#%1: ").arg(error);
+ ushort *lpMsgBuf;
+
+ const int len = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, error, 0, (LPTSTR)&lpMsgBuf, 0, NULL);
+ if (len) {
+ rc = QString::fromUtf16(lpMsgBuf, len);
+ LocalFree(lpMsgBuf);
+ } else {
+ rc += QString::fromLatin1("<unknown error>");
+ }
+ return rc;
+}
+
+#endif
+
+enum { verboseTrk = 0 };
+
+static inline QString msgAccessingClosedDevice(const QString &msg)
+{
+ return QString::fromLatin1("Error: Attempt to access device '%1', which is closed.").arg(msg);
+}
+
+namespace trk {
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkMessage
+//
+///////////////////////////////////////////////////////////////////////
+
+/* A message to be send to TRK, triggering a callback on receipt
+ * of the answer. */
+struct TrkMessage
+{
+ explicit TrkMessage(byte code = 0u, byte token = 0u,
+ TrkCallback callback = TrkCallback());
+
+ byte code;
+ byte token;
+ QByteArray data;
+ QVariant cookie;
+ TrkCallback callback;
+};
+
+TrkMessage::TrkMessage(byte c, byte t, TrkCallback cb) :
+ code(c),
+ token(t),
+ callback(cb)
+{
+}
+
+QDebug operator<<(QDebug d, const TrkMessage &msg)
+{
+ return d << "Message: Code: " << msg.code
+ << " Token: " << msg.token << " " << msg.data.toHex();
+}
+
+} // namespace trk
+
+Q_DECLARE_METATYPE(trk::TrkMessage)
+Q_DECLARE_METATYPE(trk::TrkResult)
+
+namespace trk {
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkWriteQueue: Mixin class that manages a write queue of Trk messages.
+// pendingMessage()/notifyWriteResult() should be called from a worked/timer
+// that writes the messages. The class does not take precautions for multithreading.
+// A no-op message is simply taken off the queue. The calling class
+// can use the helper invokeNoopMessage() to trigger its callback.
+//
+///////////////////////////////////////////////////////////////////////
+
+class TrkWriteQueue
+{
+ Q_DISABLE_COPY(TrkWriteQueue)
+public:
+ explicit TrkWriteQueue();
+ void clear();
+
+ // Enqueue messages.
+ void queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie);
+ void queueTrkInitialPing();
+
+ // Call this from the device read notification with the results.
+ void slotHandleResult(const TrkResult &result, QMutex *mutex = 0);
+
+ // pendingMessage() can be called periodically in a timer to retrieve
+ // the pending messages to be sent.
+ enum PendingMessageResult {
+ NoMessage, // No message in queue.
+ PendingMessage, /* There is a queued message. The calling class
+ * can write it out and use notifyWriteResult()
+ * to notify about the result. */
+ NoopMessageDequeued // A no-op message has been dequeued. see invokeNoopMessage().
+ };
+
+ PendingMessageResult pendingMessage(TrkMessage *message);
+ // Notify the queue about the success of the write operation
+ // after taking the pendingMessage off.
+ enum WriteResult {
+ WriteOk,
+ WriteFailedDiscard, // Discard failed message
+ WriteFailedKeep, // Keep failed message
+ };
+ void notifyWriteResult(WriteResult ok);
+
+ // Helper function that invokes the callback of a no-op message
+ static void invokeNoopMessage(trk::TrkMessage);
+
+private:
+ typedef QMap<byte, TrkMessage> TokenMessageMap;
+
+ byte nextTrkWriteToken();
+
+ byte m_trkWriteToken;
+ QQueue<TrkMessage> m_trkWriteQueue;
+ TokenMessageMap m_writtenTrkMessages;
+ bool m_trkWriteBusy;
+};
+
+TrkWriteQueue::TrkWriteQueue() :
+ m_trkWriteToken(0),
+ m_trkWriteBusy(false)
+{
+}
+
+void TrkWriteQueue::clear()
+{
+ m_trkWriteToken = 0;
+ m_trkWriteBusy = false;
+ m_trkWriteQueue.clear();
+ const int discarded = m_writtenTrkMessages.size();
+ m_writtenTrkMessages.clear();
+ if (verboseTrk)
+ qDebug() << "TrkWriteQueue::clear: discarded " << discarded;
+}
+
+byte TrkWriteQueue::nextTrkWriteToken()
+{
+ ++m_trkWriteToken;
+ if (m_trkWriteToken == 0)
+ ++m_trkWriteToken;
+ if (verboseTrk)
+ qDebug() << "nextTrkWriteToken:" << m_trkWriteToken;
+ return m_trkWriteToken;
+}
+
+void TrkWriteQueue::queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ const byte token = code == TRK_WRITE_QUEUE_NOOP_CODE ?
+ byte(0) : nextTrkWriteToken();
+ TrkMessage msg(code, token, callback);
+ msg.data = data;
+ msg.cookie = cookie;
+ m_trkWriteQueue.append(msg);
+}
+
+TrkWriteQueue::PendingMessageResult TrkWriteQueue::pendingMessage(TrkMessage *message)
+{
+ // Invoked from timer, try to flush out message queue
+ if (m_trkWriteBusy || m_trkWriteQueue.isEmpty())
+ return NoMessage;
+ // Handle the noop message, just invoke CB in slot (ower thread)
+ if (m_trkWriteQueue.front().code == TRK_WRITE_QUEUE_NOOP_CODE) {
+ *message = m_trkWriteQueue.dequeue();
+ return NoopMessageDequeued;
+ }
+ // Insert into map fir answers (as reading threads might get an
+ // answer before notifyWriteResult(true)) is called.
+ *message = m_trkWriteQueue.front();
+ m_writtenTrkMessages.insert(message->token, *message);
+ m_trkWriteBusy = true;
+ return PendingMessage;
+}
+
+void TrkWriteQueue::invokeNoopMessage(trk::TrkMessage noopMessage)
+{
+ TrkResult result;
+ result.code = noopMessage.code;
+ result.token = noopMessage.token;
+ result.data = noopMessage.data;
+ result.cookie = noopMessage.cookie;
+ noopMessage.callback(result);
+}
+
+void TrkWriteQueue::notifyWriteResult(WriteResult wr)
+{
+ // On success, dequeue message and await result
+ const byte token = m_trkWriteQueue.front().token;
+ switch (wr) {
+ case WriteOk:
+ m_trkWriteQueue.dequeue();
+ break;
+ case WriteFailedKeep:
+ case WriteFailedDiscard:
+ m_writtenTrkMessages.remove(token);
+ m_trkWriteBusy = false;
+ if (wr == WriteFailedDiscard)
+ m_trkWriteQueue.dequeue();
+ break;
+ }
+}
+
+void TrkWriteQueue::slotHandleResult(const TrkResult &result, QMutex *mutex)
+{
+ // Find which request the message belongs to and invoke callback
+ // if ACK or on NAK if desired.
+ if (mutex)
+ mutex->lock();
+ m_trkWriteBusy = false;
+ const TokenMessageMap::iterator it = m_writtenTrkMessages.find(result.token);
+ if (it == m_writtenTrkMessages.end()) {
+ if (mutex)
+ mutex->unlock();
+ return;
+ }
+ TrkCallback callback = it.value().callback;
+ const QVariant cookie = it.value().cookie;
+ m_writtenTrkMessages.erase(it);
+ if (mutex)
+ mutex->unlock();
+ // Invoke callback
+ if (callback) {
+ TrkResult result1 = result;
+ result1.cookie = cookie;
+ callback(result1);
+ }
+}
+
+void TrkWriteQueue::queueTrkInitialPing()
+{
+ // Ping, reset sequence count
+ m_trkWriteToken = 0;
+ m_trkWriteQueue.append(TrkMessage(TrkPing, 0));
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// DeviceContext to be shared between threads
+//
+///////////////////////////////////////////////////////////////////////
+
+struct DeviceContext {
+ DeviceContext();
+#ifdef Q_OS_WIN
+ HANDLE device;
+ OVERLAPPED readOverlapped;
+ OVERLAPPED writeOverlapped;
+#else
+ QFile file;
+#endif
+ bool serialFrame;
+ QMutex mutex;
+};
+
+DeviceContext::DeviceContext() :
+#ifdef Q_OS_WIN
+ device(INVALID_HANDLE_VALUE),
+#endif
+ serialFrame(true)
+{
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkWriterThread: A thread operating a TrkWriteQueue.
+// with exception of the handling of the TRK_WRITE_QUEUE_NOOP_CODE
+// synchronization message. The invocation of the callback is then
+// done by the thread owning the TrkWriteQueue, while pendingMessage() is called
+// from another thread. This happens via a Qt::BlockingQueuedConnection.
+
+///////////////////////////////////////////////////////////////////////
+
+class WriterThread : public QThread
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(WriterThread)
+public:
+ explicit WriterThread(const QSharedPointer<DeviceContext> &context);
+
+ // Enqueue messages.
+ void queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie);
+ void queueTrkInitialPing();
+
+ void clearWriteQueue();
+
+ // Call this from the device read notification with the results.
+ void slotHandleResult(const TrkResult &result);
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+ void internalNoopMessageDequeued(const trk::TrkMessage&);
+
+public slots:
+ bool trkWriteRawMessage(const TrkMessage &msg);
+ void terminate();
+ void tryWrite();
+
+private slots:
+ void invokeNoopMessage(const trk::TrkMessage &);
+
+private:
+ bool write(const QByteArray &data, QString *errorMessage);
+ inline int writePendingMessage();
+
+ const QSharedPointer<DeviceContext> m_context;
+ QMutex m_dataMutex;
+ QMutex m_waitMutex;
+ QWaitCondition m_waitCondition;
+ TrkWriteQueue m_queue;
+ bool m_terminate;
+};
+
+WriterThread::WriterThread(const QSharedPointer<DeviceContext> &context) :
+ m_context(context),
+ m_terminate(false)
+{
+ static const int trkMessageMetaId = qRegisterMetaType<trk::TrkMessage>();
+ Q_UNUSED(trkMessageMetaId)
+ connect(this, SIGNAL(internalNoopMessageDequeued(trk::TrkMessage)),
+ this, SLOT(invokeNoopMessage(trk::TrkMessage)), Qt::BlockingQueuedConnection);
+}
+
+void WriterThread::run()
+{
+ while (writePendingMessage() == 0) ;
+}
+
+int WriterThread::writePendingMessage()
+{
+ enum { MaxAttempts = 100, RetryIntervalMS = 200 };
+
+ // Wait. Use a timeout in case something is already queued before we
+ // start up or some weird hanging exit condition
+ m_waitMutex.lock();
+ m_waitCondition.wait(&m_waitMutex, 100);
+ m_waitMutex.unlock();
+ if (m_terminate)
+ return 1;
+
+ // Send off message
+ m_dataMutex.lock();
+ TrkMessage message;
+ const TrkWriteQueue::PendingMessageResult pr = m_queue.pendingMessage(&message);
+ m_dataMutex.unlock();
+
+ switch (pr) {
+ case TrkWriteQueue::NoMessage:
+ break;
+ case TrkWriteQueue::PendingMessage: {
+ //qDebug() << "Write pending message " << message;
+ // Untested: try to re-send a few times
+ bool success = false;
+ for (int r = 0; !success && (r < MaxAttempts); r++) {
+ success = trkWriteRawMessage(message);
+ if (!success) {
+ emit error(QString::fromLatin1("Write failure, attempt %1 of %2.").arg(r).arg(int(MaxAttempts)));
+ if (m_terminate)
+ return 1;
+ QThread::msleep(RetryIntervalMS);
+ }
+ }
+ // Notify queue. If still failed, give up.
+ m_dataMutex.lock();
+ m_queue.notifyWriteResult(success ? TrkWriteQueue::WriteOk : TrkWriteQueue::WriteFailedDiscard);
+ m_dataMutex.unlock();
+ }
+ break;
+ case TrkWriteQueue::NoopMessageDequeued:
+ // Sync with thread that owns us via a blocking signal
+ if (verboseTrk)
+ qDebug() << "Noop message dequeued" << message;
+ emit internalNoopMessageDequeued(message);
+ break;
+ } // switch
+ return 0;
+}
+
+void WriterThread::invokeNoopMessage(const trk::TrkMessage &msg)
+{
+ TrkWriteQueue::invokeNoopMessage(msg);
+}
+
+void WriterThread::terminate()
+{
+ m_terminate = true;
+ m_waitCondition.wakeAll();
+ wait();
+ m_terminate = false;
+ m_queue.clear();
+}
+
+#ifdef Q_OS_WIN
+
+static inline QString msgTerminated(int size)
+{
+ return QString::fromLatin1("Terminated with %1 bytes pending.").arg(size);
+}
+
+// Interruptible synchronous write function.
+static inline bool overlappedSyncWrite(HANDLE file,
+ const bool &terminateFlag,
+ const char *data,
+ DWORD size, DWORD *charsWritten,
+ OVERLAPPED *overlapped,
+ QString *errorMessage)
+{
+ if (WriteFile(file, data, size, charsWritten, overlapped))
+ return true;
+ const DWORD writeError = GetLastError();
+ if (writeError != ERROR_IO_PENDING) {
+ *errorMessage = QString::fromLatin1("WriteFile failed: %1").arg(winErrorMessage(writeError));
+ return false;
+ }
+ // Wait for written or thread terminated
+ const DWORD timeoutMS = 200;
+ const unsigned maxAttempts = 20;
+ DWORD wr = WaitForSingleObject(overlapped->hEvent, timeoutMS);
+ for (unsigned n = 0; wr == WAIT_TIMEOUT && n < maxAttempts && !terminateFlag;
+ wr = WaitForSingleObject(overlapped->hEvent, timeoutMS), n++);
+ if (terminateFlag) {
+ *errorMessage = msgTerminated(size);
+ return false;
+ }
+ switch (wr) {
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_TIMEOUT:
+ *errorMessage = QString::fromLatin1("Write timed out.");
+ return false;
+ default:
+ *errorMessage = QString::fromLatin1("Error while waiting for WriteFile results: %1").arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+ if (!GetOverlappedResult(file, overlapped, charsWritten, TRUE)) {
+ *errorMessage = QString::fromLatin1("Error writing %1 bytes: %2").arg(size).arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool WriterThread::write(const QByteArray &data, QString *errorMessage)
+{
+ if (verboseTrk)
+ qDebug() << "Write raw data: " << stringFromArray(data).toLatin1();
+ QMutexLocker locker(&m_context->mutex);
+#ifdef Q_OS_WIN
+ DWORD charsWritten;
+ if (!overlappedSyncWrite(m_context->device, m_terminate, data.data(), data.size(), &charsWritten, &m_context->writeOverlapped, errorMessage)) {
+ return false;
+ }
+ FlushFileBuffers(m_context->device);
+ return true;
+#else
+ if (m_context->file.write(data) == -1 || !m_context->file.flush()) {
+ *errorMessage = QString::fromLatin1("Cannot write: %1").arg(m_context->file.errorString());
+ return false;
+ }
+ return true;
+#endif
+}
+
+bool WriterThread::trkWriteRawMessage(const TrkMessage &msg)
+{
+ const QByteArray ba = frameMessage(msg.code, msg.token, msg.data, m_context->serialFrame);
+ QString errorMessage;
+ const bool rc = write(ba, &errorMessage);
+ if (!rc) {
+ qWarning("%s\n", qPrintable(errorMessage));
+ emit error(errorMessage);
+ }
+ return rc;
+}
+
+void WriterThread::tryWrite()
+{
+ m_waitCondition.wakeAll();
+}
+
+void WriterThread::queueTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ m_dataMutex.lock();
+ m_queue.queueTrkMessage(code, callback, data, cookie);
+ m_dataMutex.unlock();
+ tryWrite();
+}
+
+void WriterThread::clearWriteQueue()
+{
+ m_dataMutex.lock();
+ m_queue.clear();
+ m_dataMutex.unlock();
+}
+
+void WriterThread::queueTrkInitialPing()
+{
+ m_dataMutex.lock();
+ m_queue.queueTrkInitialPing();
+ m_dataMutex.unlock();
+ tryWrite();
+}
+
+// Call this from the device read notification with the results.
+void WriterThread::slotHandleResult(const TrkResult &result)
+{
+ m_queue.slotHandleResult(result, &m_dataMutex);
+ tryWrite(); // Have messages been enqueued in-between?
+}
+
+
+///////////////////////////////////////////////////////////////////////
+//
+// ReaderThreadBase: Base class for a thread that reads data from
+// the device, decodes the messages and emit signals for the messages.
+// A Qt::BlockingQueuedConnection should be used for the message signal
+// to ensure messages are processed in the correct sequence.
+//
+///////////////////////////////////////////////////////////////////////
+
+class ReaderThreadBase : public QThread
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(ReaderThreadBase)
+public:
+
+ int bytesPending() const { return m_trkReadBuffer.size(); }
+
+signals:
+ void messageReceived(const trk::TrkResult &result, const QByteArray &rawData);
+
+protected:
+ explicit ReaderThreadBase(const QSharedPointer<DeviceContext> &context);
+ void processData(const QByteArray &a);
+ void processData(char c);
+
+ const QSharedPointer<DeviceContext> m_context;
+
+private:
+ void readMessages();
+
+ QByteArray m_trkReadBuffer;
+ bool linkEstablishmentMode;
+};
+
+ReaderThreadBase::ReaderThreadBase(const QSharedPointer<DeviceContext> &context) :
+ m_context(context), linkEstablishmentMode(true)
+{
+ static const int trkResultMetaId = qRegisterMetaType<trk::TrkResult>();
+ Q_UNUSED(trkResultMetaId)
+}
+
+void ReaderThreadBase::processData(const QByteArray &a)
+{
+ m_trkReadBuffer += a;
+ readMessages();
+}
+
+void ReaderThreadBase::processData(char c)
+{
+ m_trkReadBuffer += c;
+ if (m_trkReadBuffer.size() > 1)
+ readMessages();
+}
+
+void ReaderThreadBase::readMessages()
+{
+ TrkResult r;
+ QByteArray rawData;
+ while (extractResult(&m_trkReadBuffer, m_context->serialFrame, &r, linkEstablishmentMode, &rawData)) {
+ emit messageReceived(r, rawData);
+ }
+}
+
+#ifdef Q_OS_WIN
+///////////////////////////////////////////////////////////////////////
+//
+// WinReaderThread: A thread reading from the device using Windows API.
+// Waits on an overlapped I/O handle and an event that tells the thread to
+// terminate.
+//
+///////////////////////////////////////////////////////////////////////
+
+class WinReaderThread : public ReaderThreadBase
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(WinReaderThread)
+public:
+ explicit WinReaderThread(const QSharedPointer<DeviceContext> &context);
+ ~WinReaderThread();
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+
+public slots:
+ void terminate();
+
+private:
+ enum Handles { FileHandle, TerminateEventHandle, HandleCount };
+
+ inline int tryRead();
+
+ HANDLE m_handles[HandleCount];
+};
+
+WinReaderThread::WinReaderThread(const QSharedPointer<DeviceContext> &context) :
+ ReaderThreadBase(context)
+{
+ m_handles[FileHandle] = NULL;
+ m_handles[TerminateEventHandle] = CreateEvent(NULL, FALSE, FALSE, NULL);
+}
+
+WinReaderThread::~WinReaderThread()
+{
+ CloseHandle(m_handles[TerminateEventHandle]);
+}
+
+// Return 0 to continue or error code
+int WinReaderThread::tryRead()
+{
+ enum { BufSize = 1024 };
+ char buffer[BufSize];
+ // Check if there are already bytes waiting. If not, wait for first byte
+ COMSTAT comStat;
+ if (!ClearCommError(m_context->device, NULL, &comStat)){
+ emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError())));
+ return -7;
+ }
+ const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize)));
+ // Trigger read
+ DWORD bytesRead = 0;
+ if (ReadFile(m_context->device, &buffer, bytesToRead, &bytesRead, &m_context->readOverlapped)) {
+ if (bytesRead == 1) {
+ processData(buffer[0]);
+ } else {
+ processData(QByteArray(buffer, bytesRead));
+ }
+ return 0;
+ }
+ const DWORD readError = GetLastError();
+ if (readError != ERROR_IO_PENDING) {
+ emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError)));
+ return -1;
+ }
+ // Wait for either termination or data
+ const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE);
+ if (wr == WAIT_FAILED) {
+ emit error(QString::fromLatin1("Wait failed: %1").arg(winErrorMessage(GetLastError())));
+ return -2;
+ }
+ if (wr - WAIT_OBJECT_0 == TerminateEventHandle) {
+ return 1; // Terminate
+ }
+ // Check data
+ if (!GetOverlappedResult(m_context->device, &m_context->readOverlapped, &bytesRead, true)) {
+ emit error(QString::fromLatin1("GetOverlappedResult failed: %1").arg(winErrorMessage(GetLastError())));
+ return -3;
+ }
+ if (bytesRead == 1) {
+ processData(buffer[0]);
+ } else {
+ processData(QByteArray(buffer, bytesRead));
+ }
+ return 0;
+}
+
+void WinReaderThread::run()
+{
+ m_handles[FileHandle] = m_context->readOverlapped.hEvent;
+ while ( tryRead() == 0) ;
+}
+
+void WinReaderThread::terminate()
+{
+ SetEvent(m_handles[TerminateEventHandle]);
+ wait();
+}
+
+typedef WinReaderThread ReaderThread;
+
+#else
+
+///////////////////////////////////////////////////////////////////////
+//
+// UnixReaderThread: A thread reading from the device.
+// Uses select() to wait and a special ioctl() to find out the number
+// of bytes queued. For clean termination, the self-pipe trick is used.
+// The class maintains a pipe, on whose read end the select waits besides
+// the device file handle. To terminate, a byte is written to the pipe.
+//
+///////////////////////////////////////////////////////////////////////
+
+static inline QString msgUnixCallFailedErrno(const char *func, int errorNumber)
+{
+ return QString::fromLatin1("Call to %1() failed: %2").arg(QLatin1String(func), QString::fromLocal8Bit(strerror(errorNumber)));
+}
+
+class UnixReaderThread : public ReaderThreadBase {
+ Q_OBJECT
+ Q_DISABLE_COPY(UnixReaderThread)
+public:
+ explicit UnixReaderThread(const QSharedPointer<DeviceContext> &context);
+ ~UnixReaderThread();
+
+ virtual void run();
+
+signals:
+ void error(const QString &);
+
+public slots:
+ void terminate();
+
+private:
+ inline int tryRead();
+
+ int m_terminatePipeFileDescriptors[2];
+};
+
+UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) :
+ ReaderThreadBase(context)
+{
+ m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1;
+ // Set up pipes for termination. Should not fail
+ if (pipe(m_terminatePipeFileDescriptors) < 0)
+ qWarning("%s\n", qPrintable(msgUnixCallFailedErrno("pipe", errno)));
+}
+
+UnixReaderThread::~UnixReaderThread()
+{
+ close(m_terminatePipeFileDescriptors[0]);
+ close(m_terminatePipeFileDescriptors[1]);
+}
+
+int UnixReaderThread::tryRead()
+{
+ fd_set readSet, tempReadSet, tempExceptionSet;
+ struct timeval timeOut;
+ const int fileDescriptor = m_context->file.handle();
+ FD_ZERO(&readSet);
+ FD_SET(fileDescriptor, &readSet);
+ FD_SET(m_terminatePipeFileDescriptors[0], &readSet);
+ const int maxFileDescriptor = qMax(m_terminatePipeFileDescriptors[0], fileDescriptor);
+ int result = 0;
+ do {
+ memcpy(&tempReadSet, &readSet, sizeof(fd_set));
+ memcpy(&tempExceptionSet, &readSet, sizeof(fd_set));
+ timeOut.tv_sec = 1;
+ timeOut.tv_usec = 0;
+ result = select(maxFileDescriptor + 1, &tempReadSet, NULL, &tempExceptionSet, &timeOut);
+ } while ( result < 0 && errno == EINTR );
+ // Timeout?
+ if (result == 0)
+ return 0;
+ // Something wrong?
+ if (result < 0) {
+ emit error(msgUnixCallFailedErrno("select", errno));
+ return -1;
+ }
+ // Did the exception set trigger on the device?
+ if (FD_ISSET(fileDescriptor,&tempExceptionSet)) {
+ emit error(QLatin1String("An Exception occurred on the device."));
+ return -2;
+ }
+ // Check termination pipe.
+ if (FD_ISSET(m_terminatePipeFileDescriptors[0], &tempReadSet)
+ || FD_ISSET(m_terminatePipeFileDescriptors[0], &tempExceptionSet))
+ return 1;
+
+ // determine number of pending bytes and read
+ int numBytes;
+ if (ioctl(fileDescriptor, FIONREAD, &numBytes) < 0) {
+ emit error(msgUnixCallFailedErrno("ioctl", errno));
+ return -1;
+ }
+ m_context->mutex.lock();
+ const QByteArray data = m_context->file.read(numBytes);
+ m_context->mutex.unlock();
+ processData(data);
+ return 0;
+}
+
+void UnixReaderThread::run()
+{
+ // Read loop
+ while (tryRead() == 0)
+ ;
+}
+
+void UnixReaderThread::terminate()
+{
+ // Trigger select() by writing to the pipe
+ char c = 0;
+ const int written = write(m_terminatePipeFileDescriptors[1], &c, 1);
+ Q_UNUSED(written)
+ wait();
+}
+
+typedef UnixReaderThread ReaderThread;
+
+#endif
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevicePrivate
+//
+///////////////////////////////////////////////////////////////////////
+
+struct TrkDevicePrivate
+{
+ TrkDevicePrivate();
+
+ QSharedPointer<DeviceContext> deviceContext;
+ QScopedPointer<WriterThread> writerThread;
+ QScopedPointer<ReaderThread> readerThread;
+
+ QByteArray trkReadBuffer;
+ int verbose;
+ QString errorString;
+ QString port;
+};
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevice
+//
+///////////////////////////////////////////////////////////////////////
+
+TrkDevicePrivate::TrkDevicePrivate() :
+ deviceContext(new DeviceContext),
+ verbose(0)
+{
+}
+
+///////////////////////////////////////////////////////////////////////
+//
+// TrkDevice
+//
+///////////////////////////////////////////////////////////////////////
+
+TrkDevice::TrkDevice(QObject *parent) :
+ QObject(parent),
+ d(new TrkDevicePrivate)
+{}
+
+TrkDevice::~TrkDevice()
+{
+ close();
+ delete d;
+}
+
+bool TrkDevice::open(QString *errorMessage)
+{
+ if (d->verbose || verboseTrk)
+ qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame();
+ if (isOpen())
+ return true;
+ if (d->port.isEmpty()) {
+ *errorMessage = QLatin1String("Internal error: No port set on TrkDevice");
+ return false;
+ }
+#ifdef Q_OS_WIN
+ const QString fullPort = QLatin1String("\\\\.\\") + d->port;
+ d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING|FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if (INVALID_HANDLE_VALUE == d->deviceContext->device) {
+ *errorMessage = QString::fromLatin1("Could not open device '%1': %2").arg(port(), winErrorMessage(GetLastError()));
+ return false;
+ }
+ memset(&d->deviceContext->readOverlapped, 0, sizeof(OVERLAPPED));
+ d->deviceContext->readOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ memset(&d->deviceContext->writeOverlapped, 0, sizeof(OVERLAPPED));
+ d->deviceContext->writeOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (d->deviceContext->readOverlapped.hEvent == NULL || d->deviceContext->writeOverlapped.hEvent == NULL) {
+ *errorMessage = QString::fromLatin1("Failed to create events: %1").arg(winErrorMessage(GetLastError()));
+ return false;
+ }
+#else
+ d->deviceContext->file.setFileName(d->port);
+ if (!d->deviceContext->file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
+ *errorMessage = QString::fromLatin1("Cannot open %1: %2").arg(d->port, d->deviceContext->file.errorString());
+ return false;
+ }
+
+ struct termios termInfo;
+ if (tcgetattr(d->deviceContext->file.handle(), &termInfo) < 0) {
+ *errorMessage = QString::fromLatin1("Unable to retrieve terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
+ return false;
+ }
+ // Turn off terminal echo as not get messages back, among other things
+ termInfo.c_cflag |= CREAD|CLOCAL;
+ termInfo.c_lflag &= (~(ICANON|ECHO|ECHOE|ECHOK|ECHONL|ISIG));
+ termInfo.c_iflag &= (~(INPCK|IGNPAR|PARMRK|ISTRIP|ICRNL|IXANY));
+ termInfo.c_oflag &= (~OPOST);
+ termInfo.c_cc[VMIN] = 0;
+ termInfo.c_cc[VINTR] = _POSIX_VDISABLE;
+ termInfo.c_cc[VQUIT] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSTART] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSTOP] = _POSIX_VDISABLE;
+ termInfo.c_cc[VSUSP] = _POSIX_VDISABLE;
+ if (tcsetattr(d->deviceContext->file.handle(), TCSAFLUSH, &termInfo) < 0) {
+ *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno)));
+ return false;
+ }
+#endif
+ d->readerThread.reset(new ReaderThread(d->deviceContext));
+ connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
+ Qt::QueuedConnection);
+ connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)),
+ this, SLOT(slotMessageReceived(trk::TrkResult,QByteArray)),
+ Qt::QueuedConnection);
+ d->readerThread->start();
+
+ d->writerThread.reset(new WriterThread(d->deviceContext));
+ connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)),
+ Qt::QueuedConnection);
+ d->writerThread->start();
+
+ if (d->verbose || verboseTrk)
+ qDebug() << "Opened" << d->port << d->readerThread.data() << d->writerThread.data();
+ return true;
+}
+
+void TrkDevice::close()
+{
+ if (verboseTrk)
+ qDebug() << "close" << d->port << " is open: " << isOpen()
+ << " read pending " << (d->readerThread.isNull() ? 0 : d->readerThread->bytesPending())
+ << sender();
+ if (!isOpen())
+ return;
+ if (d->readerThread)
+ d->readerThread->terminate();
+ if (d->writerThread)
+ d->writerThread->terminate();
+#ifdef Q_OS_WIN
+ CloseHandle(d->deviceContext->device);
+ d->deviceContext->device = INVALID_HANDLE_VALUE;
+ CloseHandle(d->deviceContext->readOverlapped.hEvent);
+ CloseHandle(d->deviceContext->writeOverlapped.hEvent);
+ d->deviceContext->readOverlapped.hEvent = d->deviceContext->writeOverlapped.hEvent = NULL;
+#else
+ d->deviceContext->file.close();
+#endif
+
+ if (d->verbose)
+ emitLogMessage("Close");
+}
+
+bool TrkDevice::isOpen() const
+{
+#ifdef Q_OS_WIN
+ return d->deviceContext->device != INVALID_HANDLE_VALUE;
+#else
+ return d->deviceContext->file.isOpen();
+#endif
+}
+
+QString TrkDevice::port() const
+{
+ return d->port;
+}
+
+void TrkDevice::setPort(const QString &p)
+{
+ if (verboseTrk)
+ qDebug() << "setPort" << p;
+ d->port = p;
+}
+
+QString TrkDevice::errorString() const
+{
+ return d->errorString;
+}
+
+bool TrkDevice::serialFrame() const
+{
+ return d->deviceContext->serialFrame;
+}
+
+void TrkDevice::setSerialFrame(bool f)
+{
+ if (verboseTrk)
+ qDebug() << "setSerialFrame" << f;
+ d->deviceContext->serialFrame = f;
+}
+
+int TrkDevice::verbose() const
+{
+ return d->verbose;
+}
+
+void TrkDevice::setVerbose(int b)
+{
+ d->verbose = b;
+}
+
+void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData)
+{
+ if (isOpen()) { // Might receive bytes after closing due to queued connections.
+ d->writerThread->slotHandleResult(result);
+ if (d->verbose > 1)
+ qDebug() << "Received: " << result.toString();
+ emit messageReceived(result);
+ if (!rawData.isEmpty())
+ emit rawDataReceived(rawData);
+ }
+}
+
+void TrkDevice::emitError(const QString &s)
+{
+ d->errorString = s;
+ qWarning("%s\n", qPrintable(s));
+ emit error(s);
+}
+
+void TrkDevice::clearWriteQueue()
+{
+ if (isOpen())
+ d->writerThread->clearWriteQueue();
+}
+
+void TrkDevice::sendTrkMessage(byte code, TrkCallback callback,
+ const QByteArray &data, const QVariant &cookie)
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
+ if (!d->writerThread.isNull()) {
+ if (d->verbose > 1) {
+ QByteArray msg = "Sending: 0x";
+ msg += QByteArray::number(code, 16);
+ msg += ": ";
+ msg += stringFromArray(data).toLatin1();
+ if (cookie.isValid())
+ msg += " Cookie: " + cookie.toString().toLatin1();
+ qDebug("%s", msg.data());
+ }
+ d->writerThread->queueTrkMessage(code, callback, data, cookie);
+ }
+}
+
+void TrkDevice::sendTrkInitialPing()
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return;
+ }
+ if (!d->writerThread.isNull())
+ d->writerThread->queueTrkInitialPing();
+}
+
+bool TrkDevice::sendTrkAck(byte token)
+{
+ if (!isOpen()) {
+ emitError(msgAccessingClosedDevice(d->port));
+ return false;
+ }
+ if (d->writerThread.isNull())
+ return false;
+ // The acknowledgement must not be queued!
+ TrkMessage msg(0x80, token);
+ msg.token = token;
+ msg.data.append('\0');
+ if (verboseTrk)
+ qDebug() << "Write synchroneous message: " << msg;
+ return d->writerThread->trkWriteRawMessage(msg);
+ // 01 90 00 07 7e 80 01 00 7d 5e 7e
+}
+
+void TrkDevice::emitLogMessage(const QString &msg)
+{
+ if (d->verbose)
+ qDebug("%s\n", qPrintable(msg));
+ emit logMessage(msg);
+}
+
+} // namespace trk
+
+#include "trkdevice.moc"
diff --git a/src/runonphone/symbianutils/trkdevice.h b/src/runonphone/symbianutils/trkdevice.h
new file mode 100644
index 000000000..bb54be520
--- /dev/null
+++ b/src/runonphone/symbianutils/trkdevice.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRKDEVICE_H
+#define TRKDEVICE_H
+
+#include "symbianutils_global.h"
+#include "callback.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QByteArray>
+#include <QtCore/QSharedPointer>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+QT_END_NAMESPACE
+
+namespace trk {
+
+struct TrkResult;
+struct TrkMessage;
+struct TrkDevicePrivate;
+
+/* TrkDevice: Implements a Windows COM or Linux device for
+ * Trk communications. Provides synchronous write and asynchronous
+ * read operation.
+ * The serialFrames property specifies whether packets are encapsulated in
+ * "0x90 <length>" frames, which is currently the case for serial ports.
+ * Contains a write message queue allowing
+ * for queueing messages with a notification callback. If the message receives
+ * an ACK, the callback is invoked.
+ * The special message TRK_WRITE_QUEUE_NOOP_CODE code can be used for synchronization.
+ * The respective message will not be sent, the callback is just invoked.
+ * Note that calling open/close in quick succession can cause crashes
+ * due to the use of queused signals. */
+
+enum { TRK_WRITE_QUEUE_NOOP_CODE = 0x7f };
+
+typedef trk::Callback<const TrkResult &> TrkCallback;
+
+class SYMBIANUTILS_EXPORT TrkDevice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame)
+ Q_PROPERTY(bool verbose READ verbose WRITE setVerbose)
+ Q_PROPERTY(QString port READ port WRITE setPort)
+public:
+ explicit TrkDevice(QObject *parent = 0);
+ virtual ~TrkDevice();
+
+ bool open(QString *errorMessage);
+ bool isOpen() const;
+
+ QString port() const;
+ void setPort(const QString &p);
+
+ QString errorString() const;
+
+ bool serialFrame() const;
+ void setSerialFrame(bool f);
+
+ int verbose() const;
+ void setVerbose(int b);
+
+ // Enqueue a message with a notification callback.
+ void sendTrkMessage(unsigned char code,
+ TrkCallback callBack = TrkCallback(),
+ const QByteArray &data = QByteArray(),
+ const QVariant &cookie = QVariant());
+
+ // Enqeue an initial ping
+ void sendTrkInitialPing();
+
+ // Send an Ack synchronously, bypassing the queue
+ bool sendTrkAck(unsigned char token);
+
+public slots:
+ void clearWriteQueue();
+
+signals:
+ void messageReceived(const trk::TrkResult &result);
+ // Emitted with the contents of messages enclosed in 07e, not for log output
+ void rawDataReceived(const QByteArray &data);
+ void error(const QString &msg);
+ void logMessage(const QString &msg);
+
+private slots:
+ void slotMessageReceived(const trk::TrkResult &result, const QByteArray &a);
+
+protected slots:
+ void emitError(const QString &msg);
+ void emitLogMessage(const QString &msg);
+
+public slots:
+ void close();
+
+private:
+ void readMessages();
+ TrkDevicePrivate *d;
+};
+
+} // namespace trk
+
+#endif // TRKDEVICE_H
diff --git a/src/runonphone/symbianutils/trkutils.cpp b/src/runonphone/symbianutils/trkutils.cpp
new file mode 100644
index 000000000..168f53960
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils.cpp
@@ -0,0 +1,603 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "trkutils.h"
+#include <ctype.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QDate>
+#include <QtCore/QDateTime>
+#include <QtCore/QTime>
+
+#define logMessage(s) do { qDebug() << "TRKCLIENT: " << s; } while (0)
+
+namespace trk {
+
+Library::Library() : codeseg(0), dataseg(0), pid(0)
+{
+}
+
+Library::Library(const TrkResult &result) : codeseg(0), dataseg(0), pid(0)
+{
+ if (result.data.size() < 20) {
+ qWarning("Invalid trk creation notification received.");
+ return;
+ }
+
+ const char *data = result.data.constData();
+ pid = extractInt(data + 2);
+ codeseg = extractInt(data + 10);
+ dataseg = extractInt(data + 14);
+ const uint len = extractShort(data + 18);
+ name = result.data.mid(20, len);
+}
+
+TrkAppVersion::TrkAppVersion()
+{
+ reset();
+}
+
+void TrkAppVersion::reset()
+{
+ trkMajor = trkMinor= protocolMajor = protocolMinor = 0;
+}
+
+Session::Session()
+{
+ reset();
+}
+
+void Session::reset()
+{
+ cpuMajor = 0;
+ cpuMinor = 0;
+ bigEndian = 0;
+ defaultTypeSize = 0;
+ fpTypeSize = 0;
+ extended1TypeSize = 0;
+ extended2TypeSize = 0;
+ pid = 0;
+ mainTid = 0;
+ tid = 0;
+ codeseg = 0;
+ dataseg = 0;
+
+ libraries.clear();
+ trkAppVersion.reset();
+}
+
+static QString formatCpu(int major, int minor)
+{
+ //: CPU description of an S60 device
+ //: %1 major verison, %2 minor version
+ //: %3 real name of major verison, %4 real name of minor version
+ const QString str = QCoreApplication::translate("trk::Session", "CPU: v%1.%2%3%4");
+ QString majorStr;
+ QString minorStr;
+ switch (major) {
+ case 0x04:
+ majorStr = " ARM";
+ break;
+ }
+ switch (minor) {
+ case 0x00:
+ minorStr = " 920T";
+ break;
+ }
+ return str.arg(major).arg(minor).arg(majorStr).arg(minorStr);
+ }
+
+QString formatTrkVersion(const TrkAppVersion &version)
+{
+ QString str = QCoreApplication::translate("trk::Session",
+ "App TRK: v%1.%2 TRK protocol: v%3.%4");
+ str = str.arg(version.trkMajor).arg(version.trkMinor);
+ return str.arg(version.protocolMajor).arg(version.protocolMinor);
+}
+
+QString Session::deviceDescription(unsigned verbose) const
+{
+ if (!cpuMajor)
+ return QString();
+
+ //: s60description
+ //: description of an S60 device
+ //: %1 CPU description, %2 endianness
+ //: %3 default type size (if any), %4 float size (if any)
+ //: %5 TRK version
+ QString msg = QCoreApplication::translate("trk::Session", "%1, %2%3%4, %5");
+ QString endianness = bigEndian
+ ? QCoreApplication::translate("trk::Session", "big endian")
+ : QCoreApplication::translate("trk::Session", "little endian");
+ msg = msg.arg(formatCpu(cpuMajor, cpuMinor)).arg(endianness);
+ //: The separator in a list of strings
+ QString defaultTypeSizeStr;
+ QString fpTypeSizeStr;
+ if (verbose && defaultTypeSize)
+ //: will be inserted into s60description
+ defaultTypeSizeStr = QCoreApplication::translate("trk::Session", ", type size: %1").arg(defaultTypeSize);
+ if (verbose && fpTypeSize)
+ //: will be inserted into s60description
+ fpTypeSizeStr = QCoreApplication::translate("trk::Session", ", float size: %1").arg(fpTypeSize);
+ msg = msg.arg(defaultTypeSizeStr).arg(fpTypeSizeStr);
+ return msg.arg(formatTrkVersion(trkAppVersion));
+}
+
+QByteArray Session::gdbLibraryList() const
+{
+ const int count = libraries.size();
+ QByteArray response = "l<library-list>";
+ for (int i = 0; i != count; ++i) {
+ const trk::Library &lib = libraries.at(i);
+ response += "<library name=\"";
+ response += lib.name;
+ response += "\">";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.codeseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "<section address=\"0x";
+ response += trk::hexNumber(lib.dataseg);
+ response += "\"/>";
+ response += "</library>";
+ }
+ response += "</library-list>";
+ return response;
+}
+
+QByteArray Session::gdbQsDllInfo(int start, int count) const
+{
+ // Happens with gdb 6.4.50.20060226-cvs / CodeSourcery.
+ // Never made it into FSF gdb that got qXfer:libraries:read instead.
+ // http://sourceware.org/ml/gdb/2007-05/msg00038.html
+ // Name=hexname,TextSeg=textaddr[,DataSeg=dataaddr]
+ const int libraryCount = libraries.size();
+ const int end = count < 0 ? libraryCount : qMin(libraryCount, start + count);
+ QByteArray response(1, end == libraryCount ? 'l' : 'm');
+ for (int i = start; i < end; ++i) {
+ if (i != start)
+ response += ';';
+ const Library &lib = libraries.at(i);
+ response += "Name=";
+ response += lib.name.toHex();
+ response += ",TextSeg=";
+ response += hexNumber(lib.codeseg);
+ response += ",DataSeg=";
+ response += hexNumber(lib.dataseg);
+ }
+ return response;
+}
+
+QString Session::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ str << "Session: " << deviceDescription(false) << '\n'
+ << "pid: " << pid << "main thread: " << mainTid
+ << " current thread: " << tid << ' ';
+ str.setIntegerBase(16);
+ str << " code: 0x" << codeseg << " data: 0x" << dataseg << '\n';
+ if (const int libCount = libraries.size()) {
+ str << "Libraries:\n";
+ for (int i = 0; i < libCount; i++)
+ str << " #" << i << ' ' << libraries.at(i).name
+ << " code: 0x" << libraries.at(i).codeseg
+ << " data: 0x" << libraries.at(i).dataseg << '\n';
+ }
+ if (const int moduleCount = modules.size()) {
+ str << "Modules:\n";
+ for (int i = 0; i < moduleCount; i++)
+ str << " #" << i << ' ' << modules.at(i) << '\n';
+ }
+ str.setIntegerBase(10);
+ if (!addressToBP.isEmpty()) {
+ typedef QHash<uint, uint>::const_iterator BP_ConstIterator;
+ str << "Breakpoints:\n";
+ const BP_ConstIterator cend = addressToBP.constEnd();
+ for (BP_ConstIterator it = addressToBP.constBegin(); it != cend; ++it) {
+ str.setIntegerBase(16);
+ str << " 0x" << it.key();
+ str.setIntegerBase(10);
+ str << ' ' << it.value() << '\n';
+ }
+ }
+
+ return rc;
+}
+
+// --------------
+
+QByteArray decode7d(const QByteArray &ba)
+{
+ QByteArray res;
+ res.reserve(ba.size());
+ for (int i = 0; i < ba.size(); ++i) {
+ byte c = byte(ba.at(i));
+ if (c == 0x7d) {
+ ++i;
+ c = 0x20 ^ byte(ba.at(i));
+ }
+ res.append(c);
+ }
+ return res;
+}
+
+QByteArray encode7d(const QByteArray &ba)
+{
+ QByteArray res;
+ res.reserve(ba.size() + 2);
+ for (int i = 0; i < ba.size(); ++i) {
+ byte c = byte(ba.at(i));
+ if (c == 0x7e || c == 0x7d) {
+ res.append(0x7d);
+ res.append(0x20 ^ c);
+ } else {
+ res.append(c);
+ }
+ }
+ return res;
+}
+
+// FIXME: Use the QByteArray based version below?
+static inline QString stringFromByte(byte c)
+{
+ return QString::fromLatin1("%1").arg(c, 2, 16, QChar('0'));
+}
+
+SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen)
+{
+ QString str;
+ QString ascii;
+ const int size = maxLen == -1 ? ba.size() : qMin(ba.size(), maxLen);
+ for (int i = 0; i < size; ++i) {
+ const int c = byte(ba.at(i));
+ str += QString::fromAscii("%1 ").arg(c, 2, 16, QChar('0'));
+ ascii += QChar(c).isPrint() ? QChar(c) : QChar('.');
+ }
+ if (size != ba.size()) {
+ str += QLatin1String("...");
+ ascii += QLatin1String("...");
+ }
+ return str + QLatin1String(" ") + ascii;
+}
+
+SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits)
+{
+ QByteArray ba = QByteArray::number(n, 16);
+ if (digits == 0 || ba.size() == digits)
+ return ba;
+ return QByteArray(digits - ba.size(), '0') + ba;
+}
+
+SYMBIANUTILS_EXPORT QByteArray hexxNumber(uint n, int digits)
+{
+ return "0x" + hexNumber(n, digits);
+}
+
+TrkResult::TrkResult() :
+ code(0),
+ token(0),
+ isDebugOutput(false)
+{
+}
+
+void TrkResult::clear()
+{
+ code = token= 0;
+ isDebugOutput = false;
+ data.clear();
+ cookie = QVariant();
+}
+
+QString TrkResult::toString() const
+{
+ QString res = stringFromByte(code);
+ res += QLatin1String(" [");
+ res += stringFromByte(token);
+ res += QLatin1Char(']');
+ res += QLatin1Char(' ');
+ res += stringFromArray(data);
+ return res;
+}
+
+QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame)
+{
+ byte s = command + token;
+ for (int i = 0; i != data.size(); ++i)
+ s += data.at(i);
+ byte checksum = 255 - (s & 0xff);
+ //int x = s + ~s;
+ //logMessage("check: " << s << checksum << x;
+
+ QByteArray response;
+ response.reserve(data.size() + 3);
+ response.append(char(command));
+ response.append(char(token));
+ response.append(data);
+ response.append(char(checksum));
+
+ QByteArray encodedData = encode7d(response);
+
+ QByteArray ba;
+ ba.reserve(encodedData.size() + 6);
+ if (serialFrame) {
+ ba.append(char(0x01));
+ ba.append(char(0x90));
+ const ushort encodedSize = encodedData.size() + 2; // 2 x 0x7e
+ appendShort(&ba, encodedSize, BigEndian);
+ }
+ ba.append(char(0x7e));
+ ba.append(encodedData);
+ ba.append(char(0x7e));
+
+ return ba;
+}
+
+/* returns 0 if array doesn't represent a result,
+otherwise returns the length of the result data */
+ushort isValidTrkResult(const QByteArray &buffer, bool serialFrame, ushort& mux)
+{
+ if (serialFrame) {
+ // Serial protocol with length info
+ if (buffer.length() < 4)
+ return 0;
+ mux = extractShort(buffer.data());
+ const ushort len = extractShort(buffer.data() + 2);
+ return (buffer.size() >= len + 4) ? len : ushort(0);
+ }
+ // Frameless protocol without length info
+ const char delimiter = char(0x7e);
+ const int firstDelimiterPos = buffer.indexOf(delimiter);
+ // Regular message delimited by 0x7e..0x7e
+ if (firstDelimiterPos == 0) {
+ mux = MuxTrk;
+ const int endPos = buffer.indexOf(delimiter, firstDelimiterPos + 1);
+ return endPos != -1 ? endPos + 1 - firstDelimiterPos : 0;
+ }
+ // Some ASCII log message up to first delimiter or all
+ return firstDelimiterPos != -1 ? firstDelimiterPos : buffer.size();
+}
+
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *result, bool &linkEstablishmentMode, QByteArray *rawData)
+{
+ result->clear();
+ if(rawData)
+ rawData->clear();
+ ushort len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ // handle receiving application output, which is not a regular command
+ const int delimiterPos = serialFrame ? 4 : 0;
+ if (linkEstablishmentMode) {
+ //when "hot connecting" a device, we can receive partial frames.
+ //this code resyncs by discarding data until a TRK frame is found
+ while (buffer->length() > delimiterPos
+ && result->multiplex != MuxTextTrace
+ && !(result->multiplex == MuxTrk && buffer->at(delimiterPos) == 0x7e)) {
+ buffer->remove(0,1);
+ len = isValidTrkResult(*buffer, serialFrame, result->multiplex);
+ }
+ }
+ if (!len)
+ return false;
+ if (buffer->at(delimiterPos) != 0x7e) {
+ result->isDebugOutput = true;
+ result->data = buffer->mid(delimiterPos, len);
+ buffer->remove(0, delimiterPos + len);
+ return true;
+ }
+ // FIXME: what happens if the length contains 0xfe?
+ // Assume for now that it passes unencoded!
+ const QByteArray data = decode7d(buffer->mid(delimiterPos + 1, len - 2));
+ if(rawData)
+ *rawData = data;
+ buffer->remove(0, delimiterPos + len);
+
+ byte sum = 0;
+ for (int i = 0; i < data.size(); ++i) // 3 = 2 * 0xfe + sum
+ sum += byte(data.at(i));
+ if (sum != 0xff)
+ logMessage("*** CHECKSUM ERROR: " << byte(sum));
+
+ result->code = data.at(0);
+ result->token = data.at(1);
+ result->data = data.mid(2, data.size() - 3);
+ //logMessage(" REST BUF: " << stringFromArray(*buffer));
+ //logMessage(" CURR DATA: " << stringFromArray(data));
+ //QByteArray prefix = "READ BUF: ";
+ //logMessage((prefix + "HEADER: " + stringFromArray(header).toLatin1()).data());
+ linkEstablishmentMode = false; //have received a good TRK packet, therefore in sync
+ return true;
+}
+
+SYMBIANUTILS_EXPORT ushort extractShort(const char *data)
+{
+ return byte(data[0]) * 256 + byte(data[1]);
+}
+
+SYMBIANUTILS_EXPORT uint extractInt(const char *data)
+{
+ uint res = byte(data[0]);
+ res *= 256; res += byte(data[1]);
+ res *= 256; res += byte(data[2]);
+ res *= 256; res += byte(data[3]);
+ return res;
+}
+
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data)
+{
+ quint64 res = byte(data[0]);
+ res <<= 8; res += byte(data[1]);
+ res <<= 8; res += byte(data[2]);
+ res <<= 8; res += byte(data[3]);
+ res <<= 8; res += byte(data[4]);
+ res <<= 8; res += byte(data[5]);
+ res <<= 8; res += byte(data[6]);
+ res <<= 8; res += byte(data[7]);
+ return res;
+}
+
+SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba)
+{
+ QString res;
+ char buf[10];
+ for (int i = 0, n = ba.size(); i != n; ++i) {
+ const byte c = ba.at(i);
+ if (isprint(c)) {
+ res += c;
+ } else {
+ qsnprintf(buf, sizeof(buf) - 1, "\\%x", int(c));
+ res += buf;
+ }
+ }
+ return res;
+}
+
+SYMBIANUTILS_EXPORT void appendShort(QByteArray *ba, ushort s, Endianness endian)
+{
+ if (endian == BigEndian) {
+ ba->append(s / 256);
+ ba->append(s % 256);
+ } else {
+ ba->append(s % 256);
+ ba->append(s / 256);
+ }
+}
+
+SYMBIANUTILS_EXPORT void appendInt(QByteArray *ba, uint i, Endianness endian)
+{
+ const uchar b3 = i % 256; i /= 256;
+ const uchar b2 = i % 256; i /= 256;
+ const uchar b1 = i % 256; i /= 256;
+ const uchar b0 = i;
+ ba->reserve(ba->size() + 4);
+ if (endian == BigEndian) {
+ ba->append(b0);
+ ba->append(b1);
+ ba->append(b2);
+ ba->append(b3);
+ } else {
+ ba->append(b3);
+ ba->append(b2);
+ ba->append(b1);
+ ba->append(b0);
+ }
+}
+
+void appendString(QByteArray *ba, const QByteArray &str, Endianness endian, bool appendNullTerminator)
+{
+ const int fullSize = str.size() + (appendNullTerminator ? 1 : 0);
+ appendShort(ba, fullSize, endian); // count the terminating \0
+ ba->append(str);
+ if (appendNullTerminator)
+ ba->append('\0');
+}
+
+void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness endian)
+{
+ // convert the QDateTime to UTC and append its representation to QByteArray
+ // format is the same as in FAT file system
+ dateTime = dateTime.toUTC();
+ const QTime utcTime = dateTime.time();
+ const QDate utcDate = dateTime.date();
+ uint fatDateTime = (utcTime.hour() << 11 | utcTime.minute() << 5 | utcTime.second()/2) << 16;
+ fatDateTime |= (utcDate.year()-1980) << 9 | utcDate.month() << 5 | utcDate.day();
+ appendInt(ba, fatDateTime, endian);
+}
+
+QByteArray errorMessage(byte code)
+{
+ switch (code) {
+ case 0x00: return "No error";
+ case 0x01: return "Generic error in CWDS message";
+ case 0x02: return "Unexpected packet size in send msg";
+ case 0x03: return "Internal error occurred in CWDS";
+ case 0x04: return "Escape followed by frame flag";
+ case 0x05: return "Bad FCS in packet";
+ case 0x06: return "Packet too long";
+ case 0x07: return "Sequence ID not expected (gap in sequence)";
+
+ case 0x10: return "Command not supported";
+ case 0x11: return "Command param out of range";
+ case 0x12: return "An option was not supported";
+ case 0x13: return "Read/write to invalid memory";
+ case 0x14: return "Read/write invalid registers";
+ case 0x15: return "Exception occurred in CWDS";
+ case 0x16: return "Targeted system or thread is running";
+ case 0x17: return "Breakpoint resources (HW or SW) exhausted";
+ case 0x18: return "Requested breakpoint conflicts with existing one";
+
+ case 0x20: return "General OS-related error";
+ case 0x21: return "Request specified invalid process";
+ case 0x22: return "Request specified invalid thread";
+ }
+ return "Unknown error";
+}
+
+uint swapEndian(uint in)
+{
+ return (in>>24) | ((in<<8) & 0x00FF0000) | ((in>>8) & 0x0000FF00) | (in<<24);
+}
+
+int TrkResult::errorCode() const
+{
+ // NAK means always error, else data sized 1 with a non-null element
+ const bool isNAK = code == 0xff;
+ if (data.size() != 1 && !isNAK)
+ return 0;
+ if (const int errorCode = data.at(0))
+ return errorCode;
+ return isNAK ? 0xff : 0;
+}
+
+QString TrkResult::errorString() const
+{
+ // NAK means always error, else data sized 1 with a non-null element
+ if (code == 0xff)
+ return "NAK";
+ if (data.size() < 1)
+ return "Unknown error packet";
+ return errorMessage(data.at(0));
+}
+
+} // namespace trk
+
diff --git a/src/runonphone/symbianutils/trkutils.h b/src/runonphone/symbianutils/trkutils.h
new file mode 100644
index 000000000..c22df865a
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils.h
@@ -0,0 +1,261 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_TRK_UTILS
+#define DEBUGGER_TRK_UTILS
+
+#include "symbianutils_global.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+QT_END_NAMESPACE
+
+namespace trk {
+
+typedef unsigned char byte;
+struct TrkResult;
+
+enum Command {
+ //meta commands
+ TrkPing = 0x00,
+ TrkConnect = 0x01,
+ TrkDisconnect = 0x02,
+ TrkReset = 0x03,
+ TrkVersions = 0x04,
+ TrkSupported = 0x05,
+ TrkCpuType = 0x06,
+ TrkConfigTransport = 0x07,
+ TrkVersions2 = 0x08,
+ TrkHostVersions = 0x09,
+
+ //state commands
+ TrkReadMemory = 0x10,
+ TrkWriteMemory = 0x11,
+ TrkReadRegisters = 0x12,
+ TrkWriteRegisters = 0x13,
+ TrkFillMemory = 0x14,
+ TrkCopyMemory = 0x15,
+ TrkFlushCache = 0x16,
+
+ //execution commands
+ TrkContinue = 0x18,
+ TrkStep = 0x19,
+ TrkStop = 0x1a,
+ TrkSetBreak = 0x1b,
+ TrkClearBreak = 0x1c,
+ TrkDownload = 0x1d,
+ TrkModifyBreakThread = 0x1e,
+
+ //host -> target IO management
+ TrkNotifyFileInput = 0x20,
+ TrkBlockFileIo = 0x21,
+
+ //host -> target os commands
+ TrkCreateItem = 0x40,
+ TrkDeleteItem = 0x41,
+ TrkReadInfo = 0x42,
+ TrkWriteInfo = 0x43,
+
+ TrkWriteFile = 0x48,
+ TrkReadFile = 0x49,
+ TrkOpenFile = 0x4a,
+ TrkCloseFile = 0x4b,
+ TrkPositionFile = 0x4c,
+ TrkInstallFile = 0x4d,
+ TrkInstallFile2 = 0x4e,
+
+ TrkPhoneSwVersion = 0x4f,
+ TrkPhoneName = 0x50,
+ TrkVersions3 = 0x51,
+
+ //replies
+ TrkNotifyAck = 0x80,
+ TrkNotifyNak = 0xff,
+
+ //target -> host notification
+ TrkNotifyStopped = 0x90,
+ TrkNotifyException = 0x91,
+ TrkNotifyInternalError = 0x92,
+ TrkNotifyStopped2 = 0x94,
+
+ //target -> host OS notification
+ TrkNotifyCreated = 0xa0,
+ TrkNotifyDeleted = 0xa1,
+ TrkNotifyProcessorStarted = 0xa2,
+ TrkNotifyProcessorStandBy = 0xa6,
+ TrkNotifyProcessorReset = 0xa7,
+
+ //target -> host support commands (these are defined but not implemented in TRK)
+ TrkDSWriteFile = 0xd0,
+ TrkDSReadFile = 0xd1,
+ TrkDSOpenFile = 0xd2,
+ TrkDSCloseFile = 0xd3,
+ TrkDSPositionFile = 0xd4
+};
+
+enum DSOSItemTypes {
+ kDSOSProcessItem = 0x0000,
+ kDSOSThreadItem = 0x0001,
+ kDSOSDLLItem = 0x0002,
+ kDSOSAppItem = 0x0003,
+ kDSOSMemBlockItem = 0x0004,
+ kDSOSProcAttachItem = 0x0005,
+ kDSOSThreadAttachItem = 0x0006,
+ kDSOSProcAttach2Item = 0x0007,
+ kDSOSProcRunItem = 0x0008,
+ /* 0x0009 - 0x00ff reserved for general expansion */
+ /* 0x0100 - 0xffff available for target-specific use */
+};
+
+enum SerialMultiplexor {
+ MuxRaw = 0,
+ MuxTextTrace = 0x0102,
+ MuxTrk = 0x0190
+};
+
+inline byte extractByte(const char *data) { return *data; }
+SYMBIANUTILS_EXPORT ushort extractShort(const char *data);
+SYMBIANUTILS_EXPORT uint extractInt(const char *data);
+SYMBIANUTILS_EXPORT quint64 extractInt64(const char *data);
+
+SYMBIANUTILS_EXPORT QString quoteUnprintableLatin1(const QByteArray &ba);
+
+// produces "xx xx xx "
+SYMBIANUTILS_EXPORT QString stringFromArray(const QByteArray &ba, int maxLen = - 1);
+
+enum Endianness
+{
+ LittleEndian,
+ BigEndian,
+ TargetByteOrder = BigEndian,
+};
+
+SYMBIANUTILS_EXPORT void appendShort(QByteArray *ba, ushort s, Endianness = TargetByteOrder);
+SYMBIANUTILS_EXPORT void appendInt(QByteArray *ba, uint i, Endianness = TargetByteOrder);
+SYMBIANUTILS_EXPORT void appendString(QByteArray *ba, const QByteArray &str, Endianness = TargetByteOrder, bool appendNullTerminator = true);
+
+struct SYMBIANUTILS_EXPORT Library
+{
+ Library();
+ explicit Library(const TrkResult &r);
+
+ QByteArray name;
+ uint codeseg;
+ uint dataseg;
+ //library addresses are valid for a given process (depending on memory model, they might be loaded at the same address in all processes or not)
+ uint pid;
+};
+
+struct SYMBIANUTILS_EXPORT TrkAppVersion
+{
+ TrkAppVersion();
+ void reset();
+
+ int trkMajor;
+ int trkMinor;
+ int protocolMajor;
+ int protocolMinor;
+};
+
+struct SYMBIANUTILS_EXPORT Session
+{
+ Session();
+ void reset();
+ QString deviceDescription(unsigned verbose) const;
+ QString toString() const;
+ // Answer to qXfer::libraries
+ QByteArray gdbLibraryList() const;
+ // Answer to qsDllInfo, can be sent chunk-wise.
+ QByteArray gdbQsDllInfo(int start = 0, int count = -1) const;
+
+ // Trk feedback
+ byte cpuMajor;
+ byte cpuMinor;
+ byte bigEndian;
+ byte defaultTypeSize;
+ byte fpTypeSize;
+ byte extended1TypeSize;
+ byte extended2TypeSize;
+ TrkAppVersion trkAppVersion;
+ uint pid;
+ uint mainTid;
+ uint tid;
+ uint codeseg;
+ uint dataseg;
+ QHash<uint, uint> addressToBP;
+
+ typedef QList<Library> Libraries;
+ Libraries libraries;
+
+ // Gdb request
+ QStringList modules;
+};
+
+struct SYMBIANUTILS_EXPORT TrkResult
+{
+ TrkResult();
+ void clear();
+ QString toString() const;
+ // 0 for no error.
+ int errorCode() const;
+ QString errorString() const;
+
+ ushort multiplex;
+ byte code;
+ byte token;
+ QByteArray data;
+ QVariant cookie;
+ bool isDebugOutput;
+};
+
+SYMBIANUTILS_EXPORT QByteArray errorMessage(byte code);
+SYMBIANUTILS_EXPORT QByteArray hexNumber(uint n, int digits = 0);
+SYMBIANUTILS_EXPORT QByteArray hexxNumber(uint n, int digits = 0); // prepends '0x', too
+SYMBIANUTILS_EXPORT uint swapEndian(uint in);
+
+} // namespace trk
+
+#endif // DEBUGGER_TRK_UTILS
diff --git a/src/runonphone/symbianutils/trkutils_p.h b/src/runonphone/symbianutils/trkutils_p.h
new file mode 100644
index 000000000..df9504c5e
--- /dev/null
+++ b/src/runonphone/symbianutils/trkutils_p.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEBUGGER_TRK_PRIVATE_UTILS
+#define DEBUGGER_TRK_PRIVATE_UTILS
+
+#include "trkutils.h"
+#include "symbianutils_global.h"
+
+QT_BEGIN_NAMESPACE
+class QDateTime;
+QT_END_NAMESPACE
+
+namespace trk {
+
+void appendDateTime(QByteArray *ba, QDateTime dateTime, Endianness = TargetByteOrder);
+// returns a QByteArray containing optionally
+// the serial frame [0x01 0x90 <len>] and 0x7e encoded7d(ba) 0x7e
+QByteArray frameMessage(byte command, byte token, const QByteArray &data, bool serialFrame);
+bool extractResult(QByteArray *buffer, bool serialFrame, TrkResult *r, bool& linkEstablishmentMode, QByteArray *rawData = 0);
+
+} // namespace trk
+
+#endif // DEBUGGER_TRK_PRIVATE_UTILS
diff --git a/src/runonphone/trksignalhandler.cpp b/src/runonphone/trksignalhandler.cpp
new file mode 100644
index 000000000..a2b8f6f27
--- /dev/null
+++ b/src/runonphone/trksignalhandler.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QDebug>
+#include <QCoreApplication>
+#include <QObject>
+#include <QFile>
+#include <QDir>
+#include "trksignalhandler.h"
+#include "trkutils.h"
+
+class CrashState
+{
+public:
+ uint pid;
+ uint tid;
+ QString crashReason;
+ uint crashPC;
+};
+
+class TrkSignalHandlerPrivate
+{
+ friend class TrkSignalHandler;
+public:
+ TrkSignalHandlerPrivate();
+ ~TrkSignalHandlerPrivate();
+private:
+ QTextStream out;
+ QTextStream err;
+ int loglevel;
+ int lastpercent;
+ QList<trk::Library> libraries;
+ QFile crashlogtextfile;
+ QFile crashstackfile;
+ QList<CrashState> queuedCrashes;
+ QList<int> dyingThreads;
+ QString crashlogPath;
+ bool crashlog;
+ bool terminateNeeded;
+};
+
+void TrkSignalHandler::copyingStarted()
+{
+ if (d->loglevel > 0)
+ d->out << "Copying..." << endl;
+}
+
+void TrkSignalHandler::canNotConnect(const QString &errorMessage)
+{
+ d->err << "Cannot connect - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::canNotCreateFile(const QString &filename, const QString &errorMessage)
+{
+ d->err << "Cannot create file (" << filename << ") - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::canNotWriteFile(const QString &filename, const QString &errorMessage)
+{
+ d->err << "Cannot write file (" << filename << ") - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::canNotCloseFile(const QString &filename, const QString &errorMessage)
+{
+ d->err << "Cannot close file (" << filename << ") - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::installingStarted()
+{
+ if (d->loglevel > 0)
+ d->out << "Installing..." << endl;
+}
+
+void TrkSignalHandler::canNotInstall(const QString &packageFilename, const QString &errorMessage)
+{
+ d->err << "Cannot install file (" << packageFilename << ") - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::installingFinished()
+{
+ if (d->loglevel > 0)
+ d->out << "Installing finished" << endl;
+}
+
+void TrkSignalHandler::startingApplication()
+{
+ if (d->loglevel > 0)
+ d->out << "Starting app..." << endl;
+}
+
+void TrkSignalHandler::applicationRunning(uint pid)
+{
+ Q_UNUSED(pid)
+ if (d->loglevel > 0)
+ d->out << "Running..." << endl;
+}
+
+void TrkSignalHandler::canNotRun(const QString &errorMessage)
+{
+ d->err << "Cannot run - " << errorMessage << endl;
+}
+
+void TrkSignalHandler::finished()
+{
+ if (d->loglevel > 0)
+ d->out << "Done." << endl;
+ QCoreApplication::quit();
+}
+
+void TrkSignalHandler::applicationOutputReceived(const QString &output)
+{
+ d->out << output << flush;
+}
+
+void TrkSignalHandler::copyProgress(int percent)
+{
+ if (d->loglevel > 0) {
+ if (d->lastpercent == 0)
+ d->out << "[ ]\r[" << flush;
+ while (percent > d->lastpercent) {
+ d->out << QLatin1Char('#');
+ d->lastpercent+=2; //because typical console is 80 chars wide
+ }
+ d->out.flush();
+ if (percent==100)
+ d->out << endl;
+ }
+}
+
+void TrkSignalHandler::stateChanged(int state)
+{
+ if (d->loglevel > 1)
+ d->out << "State" << state << endl;
+}
+
+void TrkSignalHandler::setLogLevel(int level)
+{
+ d->loglevel = level;
+}
+
+void TrkSignalHandler::setCrashLogging(bool enabled)
+{
+ d->crashlog = enabled;
+}
+
+void TrkSignalHandler::setCrashLogPath(QString path)
+{
+ d->crashlogPath = path;
+}
+
+bool lessThanCodeBase(const trk::Library& cs1, const trk::Library& cs2)
+{
+ return cs1.codeseg < cs2.codeseg;
+}
+
+void TrkSignalHandler::stopped(uint pc, uint pid, uint tid, const QString& reason)
+{
+ d->err << "STOPPED: pc=" << hex << pc << " pid=" << pid
+ << " tid=" << tid << dec << " - " << reason << endl;
+
+ if (d->crashlog) {
+ CrashState cs;
+ cs.pid = pid;
+ cs.tid = tid;
+ cs.crashPC = pc;
+ cs.crashReason = reason;
+
+ if (d->dyingThreads.contains(tid)) {
+ if(d->queuedCrashes.isEmpty())
+ emit terminate();
+ else
+ d->terminateNeeded = true;
+ } else {
+ d->queuedCrashes.append(cs);
+ d->dyingThreads.append(tid);
+
+ if (d->queuedCrashes.count() == 1) {
+ d->err << "Fetching registers and stack..." << endl;
+ emit getRegistersAndCallStack(pid, tid);
+ }
+ }
+ }
+ else
+ emit terminate();
+}
+
+void TrkSignalHandler::registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack)
+{
+ CrashState cs = d->queuedCrashes.first();
+ QDir dir(d->crashlogPath);
+ d->crashlogtextfile.setFileName(dir.filePath(QString("d_exc_%1.txt").arg(cs.tid)));
+ d->crashstackfile.setFileName(dir.filePath(QString("d_exc_%1.stk").arg(cs.tid)));
+ d->crashlogtextfile.open(QIODevice::WriteOnly);
+ QTextStream crashlog(&d->crashlogtextfile);
+
+ crashlog << "-----------------------------------------------------------------------------" << endl;
+ crashlog << "EKA2 USER CRASH LOG" << endl;
+ crashlog << "Thread Name: " << QString("ProcessID-%1::ThreadID-%2").arg(cs.pid).arg(cs.tid) << endl;
+ crashlog << "Thread ID: " << cs.tid << endl;
+ //this is wrong, but TRK doesn't make stack limit available so we lie
+ crashlog << QString("User Stack %1-%2").arg(registers.at(13), 8, 16, QChar('0')).arg(registers.at(13) + stack.size(), 8, 16, QChar('0')) << endl;
+ //this is also wrong, but TRK doesn't give all information for exceptions
+ crashlog << QString("Panic: PC=%1 ").arg(cs.crashPC, 8, 16, QChar('0')) << cs.crashReason << endl;
+ crashlog << endl;
+ crashlog << "USER REGISTERS:" << endl;
+ crashlog << QString("CPSR=%1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
+ for (int i=0;i<16;i+=4) {
+ crashlog << QString("r%1=%2 %3 %4 %5")
+ .arg(i, 2, 10, QChar('0'))
+ .arg(registers.at(i), 8, 16, QChar('0'))
+ .arg(registers.at(i+1), 8, 16, QChar('0'))
+ .arg(registers.at(i+2), 8, 16, QChar('0'))
+ .arg(registers.at(i+3), 8, 16, QChar('0')) << endl;
+ }
+ crashlog << endl;
+
+ //emit info for post mortem debug
+ qSort(d->libraries.begin(), d->libraries.end(), lessThanCodeBase);
+ d->err << "Code Segments:" << endl;
+ crashlog << "CODE SEGMENTS:" << endl;
+ for(int i=0; i<d->libraries.count(); i++) {
+ const trk::Library& seg = d->libraries.at(i);
+ if(seg.pid != cs.pid)
+ continue;
+ if (d->loglevel > 1) {
+ d->err << QString("Code: %1 Data: %2 Name: ")
+ .arg(seg.codeseg, 8, 16, QChar('0'))
+ .arg(seg.dataseg, 8, 16, QChar('0'))
+ << seg.name << endl;
+ }
+
+ //produce fake code segment end addresses since we don't get the real ones from TRK
+ uint end;
+ if (i+1 < d->libraries.count())
+ end = d->libraries.at(i+1).codeseg - 1;
+ else
+ end = 0xFFFFFFFF;
+
+ crashlog << QString("%1-%2 ")
+ .arg(seg.codeseg, 8, 16, QChar('0'))
+ .arg(end, 8, 16, QChar('0'))
+ << seg.name << endl;
+ }
+
+ d->crashlogtextfile.close();
+
+ if (d->loglevel > 1) {
+ d->err << "Registers:" << endl;
+ for (int i=0;i<16;i++) {
+ d->err << QString("R%1: %2 ").arg(i, 2, 10, QChar('0')).arg(registers.at(i), 8, 16, QChar('0'));
+ if (i % 4 == 3)
+ d->err << endl;
+ }
+ d->err << QString("CPSR: %1").arg(registers.at(16), 8, 16, QChar('0')) << endl;
+
+ d->err << "Stack:" << endl;
+ uint sp = registers.at(13);
+ for(int i=0; i<stack.size(); i+=16, sp+=16) {
+ d->err << QString("%1: ").arg(sp, 8, 16, QChar('0'));
+ d->err << trk::stringFromArray(stack.mid(i,16));
+ d->err << endl;
+ }
+ }
+ d->crashstackfile.open(QIODevice::WriteOnly);
+ d->crashstackfile.write(stack);
+ d->crashstackfile.close();
+
+ if (d->loglevel > 0)
+ d->err << "Crash logs saved to " << d->crashlogtextfile.fileName() << " & " << d->crashstackfile.fileName() << endl;
+
+ // resume the thread to allow Symbian OS to handle the panic normally.
+ // terminate when a non main thread is suspended reboots the phone (TRK bug)
+ emit resume(cs.pid, cs.tid);
+
+ //fetch next crashed thread
+ d->queuedCrashes.removeFirst();
+ if (d->queuedCrashes.count()) {
+ cs = d->queuedCrashes.first();
+ d->err << "Fetching registers and stack..." << endl;
+ emit getRegistersAndCallStack(cs.pid, cs.tid);
+ }
+ else if (d->terminateNeeded)
+ emit terminate();
+
+}
+
+void TrkSignalHandler::libraryLoaded(const trk::Library &lib)
+{
+ d->libraries << lib;
+}
+
+void TrkSignalHandler::libraryUnloaded(const trk::Library &lib)
+{
+ for (QList<trk::Library>::iterator i = d->libraries.begin(); i != d->libraries.end(); i++) {
+ if((*i).name == lib.name && (*i).pid == lib.pid)
+ i = d->libraries.erase(i);
+ }
+}
+
+void TrkSignalHandler::timeout()
+{
+ d->err << "FAILED: stopping test due to timeout" << endl;
+ emit terminate();
+}
+
+TrkSignalHandlerPrivate::TrkSignalHandlerPrivate()
+ : out(stdout),
+ err(stderr),
+ loglevel(0),
+ lastpercent(0),
+ terminateNeeded(false)
+{
+
+}
+
+TrkSignalHandlerPrivate::~TrkSignalHandlerPrivate()
+{
+ out.flush();
+ err.flush();
+}
+
+TrkSignalHandler::TrkSignalHandler()
+ : d(new TrkSignalHandlerPrivate())
+{
+}
+
+TrkSignalHandler::~TrkSignalHandler()
+{
+ delete d;
+}
diff --git a/src/runonphone/trksignalhandler.h b/src/runonphone/trksignalhandler.h
new file mode 100644
index 000000000..f8331800c
--- /dev/null
+++ b/src/runonphone/trksignalhandler.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** 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, 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef TRKSIGNALHANDLER_H
+#define TRKSIGNALHANDLER_H
+#include <QObject>
+#include <QString>
+#include "symbianutils/trkutils.h"
+
+class TrkSignalHandlerPrivate;
+class TrkSignalHandler : public QObject
+{
+ Q_OBJECT
+public slots:
+ void copyingStarted();
+ void canNotConnect(const QString &errorMessage);
+ void canNotCreateFile(const QString &filename, const QString &errorMessage);
+ void canNotWriteFile(const QString &filename, const QString &errorMessage);
+ void canNotCloseFile(const QString &filename, const QString &errorMessage);
+ void installingStarted();
+ void canNotInstall(const QString &packageFilename, const QString &errorMessage);
+ void installingFinished();
+ void startingApplication();
+ void applicationRunning(uint pid);
+ void canNotRun(const QString &errorMessage);
+ void finished();
+ void applicationOutputReceived(const QString &output);
+ void copyProgress(int percent);
+ void stateChanged(int);
+ void stopped(uint pc, uint pid, uint tid, const QString& reason);
+ void timeout();
+ void libraryLoaded(const trk::Library &lib);
+ void libraryUnloaded(const trk::Library &lib);
+ void registersAndCallStackReadComplete(const QList<uint>& registers, const QByteArray& stack);
+signals:
+ void resume(uint pid, uint tid);
+ void stop(uint pid, uint tid);
+ void terminate();
+ void getRegistersAndCallStack(uint pid, uint tid);
+public:
+ TrkSignalHandler();
+ ~TrkSignalHandler();
+ void setLogLevel(int);
+ void setCrashLogging(bool);
+ void setCrashLogPath(QString);
+private:
+ TrkSignalHandlerPrivate *d;
+};
+
+#endif // TRKSIGNALHANDLER_H