summaryrefslogtreecommitdiffstats
path: root/doc/src/snippets/declarative/parallelanimation.qml
diff options
context:
space:
mode:
Diffstat (limited to 'doc/src/snippets/declarative/parallelanimation.qml')
-rw-r--r--doc/src/snippets/declarative/parallelanimation.qml2
1 files changed, 1 insertions, 1 deletions
diff --git a/doc/src/snippets/declarative/parallelanimation.qml b/doc/src/snippets/declarative/parallelanimation.qml
index 0badc03..32487ef 100644
--- a/doc/src/snippets/declarative/parallelanimation.qml
+++ b/doc/src/snippets/declarative/parallelanimation.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
=v4.8.3&id=bb070cae0cde7a83d519582e5872908f7eb2b51b'>tools/linguist/linguist/main.cpp58
-rw-r--r--tools/macdeployqt/shared/shared.cpp128
-rw-r--r--tools/macdeployqt/shared/shared.h5
-rw-r--r--tools/runonphone/codasignalhandler.cpp579
-rw-r--r--tools/runonphone/codasignalhandler.h118
-rw-r--r--tools/runonphone/main.cpp192
-rw-r--r--tools/runonphone/runonphone.pro12
-rw-r--r--tools/runonphone/symbianutils/codadevice.cpp1501
-rw-r--r--tools/runonphone/symbianutils/codadevice.h447
-rw-r--r--tools/runonphone/symbianutils/codamessage.cpp (renamed from tools/runonphone/symbianutils/tcftrkmessage.cpp)143
-rw-r--r--tools/runonphone/symbianutils/codamessage.h (renamed from tools/runonphone/symbianutils/tcftrkmessage.h)99
-rw-r--r--tools/runonphone/symbianutils/json.cpp87
-rw-r--r--tools/runonphone/symbianutils/json.h8
-rw-r--r--tools/runonphone/symbianutils/launcher.cpp202
-rw-r--r--tools/runonphone/symbianutils/launcher.h29
-rw-r--r--tools/runonphone/symbianutils/symbiandevicemanager.cpp290
-rw-r--r--tools/runonphone/symbianutils/symbiandevicemanager.h78
-rw-r--r--tools/runonphone/symbianutils/symbianutils.pri18
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.cpp929
-rw-r--r--tools/runonphone/symbianutils/tcftrkdevice.h295
-rw-r--r--tools/runonphone/symbianutils/trkdevice.cpp6
-rw-r--r--tools/runonphone/symbianutils/trkutils.h4
-rw-r--r--tools/runonphone/symbianutils/virtualserialdevice.cpp89
-rw-r--r--tools/runonphone/symbianutils/virtualserialdevice.h127
-rw-r--r--tools/runonphone/symbianutils/virtualserialdevice_posix.cpp344
-rw-r--r--tools/runonphone/symbianutils/virtualserialdevice_win.cpp369
-rw-r--r--tools/runonphone/trksignalhandler.cpp8
-rw-r--r--tools/runonphone/trksignalhandler.h4
29 files changed, 4687 insertions, 1542 deletions
diff --git a/tools/linguist/linguist/Info_mac.plist b/tools/linguist/linguist/Info_mac.plist
index b11f493..b37a43b 100644
--- a/tools/linguist/linguist/Info_mac.plist
+++ b/tools/linguist/linguist/Info_mac.plist
@@ -14,5 +14,65 @@
<string>????</string>
<key>CFBundleExecutable</key>
<string>Linguist</string>
+
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>CFBundleTypeIconFile</key>
+ <string>linguist.icns</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>qph</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Qt Linguist 'Phrase Book'</string>
+ <key>LSHandlerRank</key>
+ <string>Default</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>CFBundleTypeIconFile</key>
+ <string>linguist.icns</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>ts</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Qt Translation Source</string>
+ <key>LSHandlerRank</key>
+ <string>Default</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>CFBundleTypeIconFile</key>
+ <string>linguist.icns</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>po</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>GNU Gettext Localization File</string>
+ <key>LSHandlerRank</key>
+ <string>Default</string>
+ </dict>
+ <dict>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>CFBundleTypeIconFile</key>
+ <string>linguist.icns</string>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>xlf</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>XLIFF Localization File</string>
+ <key>LSHandlerRank</key>
+ <string>Default</string>
+ </dict>
+ </array>
</dict>
</plist>
diff --git a/tools/linguist/linguist/main.cpp b/tools/linguist/linguist/main.cpp
index a137f36..ba45613 100644
--- a/tools/linguist/linguist/main.cpp
+++ b/tools/linguist/linguist/main.cpp
@@ -54,8 +54,54 @@
#include <QtGui/QPixmap>
#include <QtGui/QSplashScreen>
+#ifdef Q_WS_MAC
+#include <QtCore/QUrl>
+#include <QtGui/QFileOpenEvent>
+#endif // Q_WS_MAC
+
QT_USE_NAMESPACE
+#ifdef Q_WS_MAC
+class ApplicationEventFilter : public QObject
+{
+ Q_OBJECT
+
+public:
+ ApplicationEventFilter()
+ : m_mainWindow(0)
+ {
+ }
+
+ void setMainWindow(MainWindow *mw)
+ {
+ m_mainWindow = mw;
+ if (!m_filesToOpen.isEmpty() && m_mainWindow) {
+ m_mainWindow->openFiles(m_filesToOpen);
+ m_filesToOpen.clear();
+ }
+ }
+
+protected:
+ bool eventFilter(QObject *object, QEvent *event)
+ {
+ if (object == qApp && event->type() == QEvent::FileOpen) {
+ QFileOpenEvent *e = static_cast<QFileOpenEvent*>(event);
+ QString file = e->url().toLocalFile();
+ if (!m_mainWindow)
+ m_filesToOpen << file;
+ else
+ m_mainWindow->openFiles(QStringList() << file);
+ return true;
+ }
+ return QObject::eventFilter(object, event);
+ }
+
+private:
+ MainWindow *m_mainWindow;
+ QStringList m_filesToOpen;
+};
+#endif // Q_WS_MAC
+
int main(int argc, char **argv)
{
Q_INIT_RESOURCE(linguist);
@@ -63,6 +109,11 @@ int main(int argc, char **argv)
QApplication app(argc, argv);
QApplication::setOverrideCursor(Qt::WaitCursor);
+#ifdef Q_WS_MAC
+ ApplicationEventFilter eventFilter;
+ app.installEventFilter(&eventFilter);
+#endif // Q_WS_MAC
+
QStringList files;
QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
QStringList args = app.arguments();
@@ -111,6 +162,9 @@ int main(int argc, char **argv)
splash->show();
MainWindow mw;
+#ifdef Q_WS_MAC
+ eventFilter.setMainWindow(&mw);
+#endif // Q_WS_MAC
mw.show();
splash->finish(&mw);
QApplication::restoreOverrideCursor();
@@ -119,3 +173,7 @@ int main(int argc, char **argv)
return app.exec();
}
+
+#ifdef Q_WS_MAC
+#include "main.moc"
+#endif // Q_WS_MAC
diff --git a/tools/macdeployqt/shared/shared.cpp b/tools/macdeployqt/shared/shared.cpp
index d2ceedd..156f1de 100644
--- a/tools/macdeployqt/shared/shared.cpp
+++ b/tools/macdeployqt/shared/shared.cpp
@@ -72,7 +72,7 @@ QDebug operator<<(QDebug debug, const FrameworkInfo &info)
debug << "Install name" << info.installName << "\n";
debug << "Deployed install name" << info.deployedInstallName << "\n";
debug << "Source file Path" << info.sourceFilePath << "\n";
- debug << "Deployed Directory (relative to bundle)" << info.destinationDirectory << "\n";
+ debug << "Destination Directory (relative to bundle)" << info.destinationDirectory << "\n";
return debug;
}
@@ -84,6 +84,7 @@ inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info)
{
debug << "Application bundle path" << info.path << "\n";
debug << "Binary path" << info.binaryPath << "\n";
+ debug << "Additional libraries" << info.libraryPaths << "\n";
return debug;
}
@@ -111,7 +112,7 @@ FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs)
// Don't deploy system libraries.
if (trimmed.startsWith("/System/Library/") ||
(trimmed.startsWith("/usr/lib/") && trimmed.contains("libQt") == false) // exception for libQtuitools and libQtlucene
- || trimmed.startsWith("@executable_path"))
+ || trimmed.startsWith("@executable_path") || trimmed.startsWith("@loader_path") || trimmed.startsWith("@rpath"))
return info;
enum State {QtPath, FrameworkName, DylibName, Version, End};
@@ -208,6 +209,21 @@ QString findAppBinary(const QString &appBundlePath)
return QString();
}
+QStringList findAppLibraries(const QString &appBundlePath)
+{
+ QStringList result;
+ // dylibs
+ QDirIterator iter(appBundlePath, QStringList() << QString::fromLatin1("*.dylib"),
+ QDir::Files, QDirIterator::Subdirectories);
+
+ while (iter.hasNext()) {
+ iter.next();
+ result << iter.fileInfo().filePath();
+ }
+ return result;
+}
+
+
QList<FrameworkInfo> getQtFrameworks(const QStringList &otoolLines, bool useDebugLibs)
{
QList<FrameworkInfo> libraries;
@@ -238,11 +254,26 @@ QList<FrameworkInfo> getQtFrameworks(const QString &path, bool useDebugLibs)
QStringList outputLines = output.split("\n");
outputLines.removeFirst(); // remove line containing the binary path
if (path.contains(".framework") || path.contains(".dylib"))
- outputLines.removeFirst(); // frameworks and dylibs lists themselves as a dependency.
+ outputLines.removeFirst(); // frameworks and dylibs print install name of themselves first.
return getQtFrameworks(outputLines, useDebugLibs);
}
+QList<FrameworkInfo> getQtFrameworksForPaths(const QStringList &paths, bool useDebugLibs)
+{
+ QList<FrameworkInfo> result;
+ QSet<QString> existing;
+ foreach (const QString &path, paths) {
+ foreach (const FrameworkInfo &info, getQtFrameworks(path, useDebugLibs)) {
+ if (!existing.contains(info.frameworkPath)) { // avoid duplicates
+ existing.insert(info.frameworkPath);
+ result << info;
+ }
+ }
+ }
+ return result;
+}
+
// copies everything _inside_ sourcePath to destinationPath
void recursiveCopy(const QString &sourcePath, const QString &destinationPath)
{
@@ -264,32 +295,51 @@ void recursiveCopy(const QString &sourcePath, const QString &destinationPath)
QString copyFramework(const FrameworkInfo &framework, const QString path)
{
QString from = framework.sourceFilePath;
- QString toDir = path + "/" + framework.destinationDirectory;
- QString to = toDir + "/" + framework.binaryName;
- if (QFile::exists(from) == false) {
+ if (!QFile::exists(from)) {
LogError() << "no file at" << from;
return QString();
}
+ QFileInfo fromDirInfo(framework.frameworkPath + QLatin1Char('/')
+ + framework.binaryDirectory);
+ bool fromDirIsSymLink = fromDirInfo.isSymLink();
+ QString unresolvedToDir = path + "/" + framework.destinationDirectory;
+ QString resolvedToDir;
+ QString relativeLinkTarget; // will contain the link from Current to e.g. 4 in the Versions directory
+ if (fromDirIsSymLink) {
+ // handle the case where framework is referenced with Versions/Current
+ // which is a symbolic link, so copy to target and recreate as symbolic link
+ relativeLinkTarget = QDir(fromDirInfo.canonicalPath())
+ .relativeFilePath(QFileInfo(fromDirInfo.symLinkTarget()).canonicalFilePath());
+ resolvedToDir = QFileInfo(unresolvedToDir).path() + QLatin1Char('/') + relativeLinkTarget;
+ } else {
+ resolvedToDir = unresolvedToDir;
+ }
+
+ QString to = resolvedToDir + "/" + framework.binaryName;
+ // create the (non-symlink) dir
QDir dir;
- if (dir.mkpath(toDir) == false) {
+ if (!dir.mkpath(resolvedToDir)) {
LogError() << "could not create destination directory" << to;
return QString();
}
+ if (!QFile::exists(to)) { // copy the binary and resources if that wasn't done before
+ copyFilePrintStatus(from, to);
- if (QFile::exists(to)) {
- return QString();
+ const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
+ const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources";
+ recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
}
- copyFilePrintStatus(from, to);
-
- const QString resourcesSourcePath = framework.frameworkPath + "/Resources";
- const QString resourcesDestianationPath = path + "/Contents/Frameworks/" + framework.frameworkName + "/Resources";
- recursiveCopy(resourcesSourcePath, resourcesDestianationPath);
-
+ // create the Versions/Current symlink dir if necessary
+ if (fromDirIsSymLink) {
+ QFile::link(relativeLinkTarget, unresolvedToDir);
+ LogNormal() << " linked:" << unresolvedToDir;
+ LogNormal() << " to" << resolvedToDir << "(" << relativeLinkTarget << ")";
+ }
return to;
}
@@ -312,13 +362,14 @@ void changeIdentification(const QString &id, const QString &binaryPath)
runInstallNameTool(QStringList() << "-id" << id << binaryPath);
}
-void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath)
+void changeInstallName(const QString &oldName, const QString &newName, const QStringList &binaryPaths)
{
LogDebug() << "Using install_name_tool:";
- LogDebug() << " in" << binaryPath;
+ LogDebug() << " in" << binaryPaths;
LogDebug() << " change reference" << oldName;
LogDebug() << " to" << newName;
- runInstallNameTool(QStringList() << "-change" << oldName << newName << binaryPath);
+ foreach (const QString &path, binaryPaths)
+ runInstallNameTool(QStringList() << "-change" << oldName << newName << path);
}
void runStrip(const QString &binaryPath)
@@ -345,23 +396,23 @@ void runStrip(const QString &binaryPath)
a list of actually deployed frameworks.
*/
DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
- const QString &bundlePath, const QString &binaryPath, bool useDebugLibs)
+ const QString &bundlePath, const QStringList &binaryPaths, bool useDebugLibs)
{
LogNormal();
- LogNormal() << "Deploying Qt frameworks found inside:" << binaryPath;
+ LogNormal() << "Deploying Qt frameworks found inside:" << binaryPaths;
QStringList copiedFrameworks;
- DeploymentInfo deploymenInfo;
+ DeploymentInfo deploymentInfo;
while (frameworks.isEmpty() == false) {
const FrameworkInfo framework = frameworks.takeFirst();
copiedFrameworks.append(framework.frameworkName);
// Get the qt path from one of the Qt frameworks;
- if (deploymenInfo.qtPath.isNull() && framework.frameworkName.contains("Qt")
+ if (deploymentInfo.qtPath.isNull() && framework.frameworkName.contains("Qt")
&& framework.frameworkDirectory.contains("/lib"))
{
- deploymenInfo.qtPath = framework.frameworkDirectory;
- deploymenInfo.qtPath.chop(5); // remove "/lib/"
+ deploymentInfo.qtPath = framework.frameworkDirectory;
+ deploymentInfo.qtPath.chop(5); // remove "/lib/"
}
if (framework.installName.startsWith("/@executable_path/")) {
@@ -370,7 +421,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
}
// Install_name_tool the new id into the binary
- changeInstallName(framework.installName, framework.deployedInstallName, binaryPath);
+ changeInstallName(framework.installName, framework.deployedInstallName, binaryPaths);
// Copy farmework to app bundle.
const QString deployedBinaryPath = copyFramework(framework, bundlePath);
@@ -386,7 +437,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
QList<FrameworkInfo> dependencies = getQtFrameworks(deployedBinaryPath, useDebugLibs);
foreach (FrameworkInfo dependency, dependencies) {
- changeInstallName(dependency.installName, dependency.deployedInstallName, deployedBinaryPath);
+ changeInstallName(dependency.installName, dependency.deployedInstallName, QStringList() << deployedBinaryPath);
// Deploy framework if necessary.
if (copiedFrameworks.contains(dependency.frameworkName) == false && frameworks.contains(dependency) == false) {
@@ -394,8 +445,8 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks,
}
}
}
- deploymenInfo.deployedFrameworks = copiedFrameworks;
- return deploymenInfo;
+ deploymentInfo.deployedFrameworks = copiedFrameworks;
+ return deploymentInfo;
}
DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLibs)
@@ -403,7 +454,9 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLib
ApplicationBundleInfo applicationBundle;
applicationBundle.path = appBundlePath;
applicationBundle.binaryPath = findAppBinary(appBundlePath);
- QList<FrameworkInfo> frameworks = getQtFrameworks(applicationBundle.binaryPath, useDebugLibs);
+ applicationBundle.libraryPaths = findAppLibraries(appBundlePath);
+ QStringList allBinaryPaths = QStringList() << applicationBundle.binaryPath << applicationBundle.libraryPaths;
+ QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(allBinaryPaths, useDebugLibs);
if (frameworks.isEmpty()) {
LogWarning();
LogWarning() << "Could not find any external Qt frameworks to deploy in" << appBundlePath;
@@ -411,7 +464,7 @@ DeploymentInfo deployQtFrameworks(const QString &appBundlePath, bool useDebugLib
LogWarning() << "If so, you will need to rebuild" << appBundlePath << "before trying again.";
return DeploymentInfo();
} else {
- return deployQtFrameworks(frameworks, applicationBundle.path, applicationBundle.binaryPath, useDebugLibs);
+ return deployQtFrameworks(frameworks, applicationBundle.path, allBinaryPaths, useDebugLibs);
}
}
@@ -477,11 +530,11 @@ void deployPlugins(const ApplicationBundleInfo &appBundleInfo, const QString &pl
if (pluginName.contains("libphonon_qt7")) {
changeInstallName("/System/Library/Frameworks/CoreVideo.framework/Versions/A/CoreVideo",
"/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore",
- destinationPath);
+ QStringList() << destinationPath);
}
QList<FrameworkInfo> frameworks = getQtFrameworks(destinationPath, useDebugLibs);
- deployQtFrameworks(frameworks, appBundleInfo.path, destinationPath, useDebugLibs);
+ deployQtFrameworks(frameworks, appBundleInfo.path, QStringList() << destinationPath, useDebugLibs);
}
} // foreach plugins
@@ -527,9 +580,9 @@ void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo,
}
-void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &absoluteQtPath)
+void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QStringList &binaryPaths, const QString &absoluteQtPath)
{
- LogNormal() << "Changing" << appBinaryPath << "to link against";
+ LogNormal() << "Changing" << binaryPaths << "to link against";
LogNormal() << "Qt in" << absoluteQtPath;
QString finalQtPath = absoluteQtPath;
@@ -539,21 +592,22 @@ void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &ap
foreach (FrameworkInfo framework, frameworks) {
const QString oldBinaryId = framework.installName;
const QString newBinaryId = finalQtPath + framework.frameworkName + framework.binaryPath;
- changeInstallName(oldBinaryId, newBinaryId, appBinaryPath);
+ changeInstallName(oldBinaryId, newBinaryId, binaryPaths);
}
}
void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs)
{
const QString appBinaryPath = findAppBinary(appPath);
- const QList<FrameworkInfo> frameworks = getQtFrameworks(appBinaryPath, useDebugLibs);
+ const QStringList libraryPaths = findAppLibraries(appPath);
+ const QList<FrameworkInfo> frameworks = getQtFrameworksForPaths(QStringList() << appBinaryPath << libraryPaths, useDebugLibs);
if (frameworks.isEmpty()) {
LogWarning();
LogWarning() << "Could not find any _external_ Qt frameworks to change in" << appPath;
return;
} else {
const QString absoluteQtPath = QDir(qtPath).absolutePath();
- changeQtFrameworks(frameworks, appBinaryPath, absoluteQtPath);
+ changeQtFrameworks(frameworks, QStringList() << appBinaryPath << libraryPaths, absoluteQtPath);
}
}
diff --git a/tools/macdeployqt/shared/shared.h b/tools/macdeployqt/shared/shared.h
index 5eec3cf..ab9ada2 100644
--- a/tools/macdeployqt/shared/shared.h
+++ b/tools/macdeployqt/shared/shared.h
@@ -77,6 +77,7 @@ class ApplicationBundleInfo
public:
QString path;
QString binaryPath;
+ QStringList libraryPaths;
};
class DeploymentInfo
@@ -91,7 +92,7 @@ public:
inline QDebug operator<<(QDebug debug, const ApplicationBundleInfo &info);
void changeQtFrameworks(const QString appPath, const QString &qtPath, bool useDebugLibs);
-void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QString &appBinaryPath, const QString &qtPath);
+void changeQtFrameworks(const QList<FrameworkInfo> frameworks, const QStringList &binaryPaths, const QString &qtPath);
FrameworkInfo parseOtoolLibraryLine(const QString &line, bool useDebugLibs);
QString findAppBinary(const QString &appBundlePath);
@@ -103,7 +104,7 @@ DeploymentInfo deployQtFrameworks(QList<FrameworkInfo> frameworks, const QString
void createQtConf(const QString &appBundlePath);
void deployPlugins(const QString &appBundlePath, DeploymentInfo deploymentInfo, bool useDebugLibs);
void changeIdentification(const QString &id, const QString &binaryPath);
-void changeInstallName(const QString &oldName, const QString &newName, const QString &binaryPath);
+void changeInstallName(const QString &oldName, const QString &newName, const QStringList &binaryPaths);
QString findAppBinary(const QString &appBundlePath);
void createDiskImage(const QString &appBundlePath);
diff --git a/tools/runonphone/codasignalhandler.cpp b/tools/runonphone/codasignalhandler.cpp
new file mode 100644
index 0000000..0d086b5
--- /dev/null
+++ b/tools/runonphone/codasignalhandler.cpp
@@ -0,0 +1,579 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QDir>
+#include <QFile>
+#include <QObject>
+#include <QTimer>
+#include "codasignalhandler.h"
+
+static const quint64 DEFAULT_CHUNK_SIZE = 40000;
+
+class CodaSignalHandlerPrivate
+{
+ friend class CodaSignalHandler;
+public:
+ CodaSignalHandlerPrivate();
+ ~CodaSignalHandlerPrivate();
+private:
+ SymbianUtils::CodaDevicePtr codaDevice;
+ QEventLoop *eventLoop;
+ QTextStream out;
+ QTextStream err;
+ int loglevel;
+ int timeout;
+ int result;
+ CodaAction action;
+ QString copySrcFileName;
+ QString copyDstFileName;
+ QString dlSrcFileName;
+ QString dlDstFileName;
+ QString appFileName;
+ QString commandLineArgs;
+ QString serialPortName;
+ QString appID;
+ QByteArray remoteFileHandle;
+ QScopedPointer<QFile> localFile;
+ QScopedPointer<QFile> remoteFile;
+ quint64 putChunkSize;
+ quint64 putLastChunkSize;
+ quint64 remoteBytesLeft;
+ quint64 remoteFileSize;
+ bool putWriteOk;
+ bool connected;
+ bool debugSessionControl;
+};
+
+CodaSignalHandlerPrivate::CodaSignalHandlerPrivate()
+ : eventLoop(0),
+ out(stdout),
+ err(stderr),
+ loglevel(0),
+ timeout(0),
+ result(0),
+ action(ActionPingOnly),
+ putChunkSize(DEFAULT_CHUNK_SIZE),
+ putLastChunkSize(0),
+ remoteBytesLeft(0),
+ remoteFileSize(0),
+ putWriteOk(false),
+ connected(false),
+ debugSessionControl(false)
+{
+}
+
+CodaSignalHandlerPrivate::~CodaSignalHandlerPrivate()
+{
+ delete eventLoop;
+ out.flush();
+ err.flush();
+}
+
+void CodaSignalHandler::error(const QString &errorMessage)
+{
+ reportError(tr("CODA error: %1").arg(errorMessage));
+}
+
+void CodaSignalHandler::logMessage(const QString &logMessage)
+{
+ reportMessage(tr("CODA log: %1").arg(logMessage));
+}
+
+void CodaSignalHandler::serialPong(const QString &codaVersion)
+{
+ reportMessage(tr("CODA version: %1").arg(codaVersion));
+}
+
+void CodaSignalHandler::tcfEvent(const Coda::CodaEvent &event)
+{
+ reportMessage(tr("CODA event: Type: %1 Message: %2").arg(event.type()).arg(event.toString()));
+
+ switch (event.type()) {
+ case Coda::CodaEvent::LocatorHello:
+ handleConnected(event);
+ break;
+ case Coda::CodaEvent::ProcessExitedEvent:
+ handleAppExited(event);
+ break;
+ default:
+ reportMessage(tr("CODA unhandled event: Type: %1 Message: %2").arg(event.type()).arg(event.toString()));
+ break;
+ }
+}
+
+void CodaSignalHandler::terminate()
+{
+ if (d->codaDevice) {
+ disconnect(d->codaDevice.data(), 0, this, 0);
+ SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice);
+ }
+}
+
+void CodaSignalHandler::finished()
+{
+ if (d->eventLoop)
+ d->eventLoop->exit();
+}
+
+void CodaSignalHandler::timeout()
+{
+ reportError(tr("Unable to connect to CODA device. Operation timed out."));
+}
+
+int CodaSignalHandler::run()
+{
+ d->codaDevice = SymbianUtils::SymbianDeviceManager::instance()->getCodaDevice(d->serialPortName);
+ bool ok = d->codaDevice && d->codaDevice->device()->isOpen();
+ if (!ok) {
+ QString deviceError = "No such port";
+ if (d->codaDevice)
+ deviceError = d->codaDevice->device()->errorString();
+ reportError(tr("Could not open serial device: ").arg(deviceError));
+ SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice);
+ return 1;
+ }
+
+ if (d->loglevel > 1) {
+ d->codaDevice->setVerbose(1);
+ }
+
+ connect(d->codaDevice.data(), SIGNAL(error(const QString &)), this, SLOT(error(const QString &)));
+ connect(d->codaDevice.data(), SIGNAL(logMessage(const QString &)), this, SLOT(logMessage(const QString &)));
+ connect(d->codaDevice.data(), SIGNAL(serialPong(const QString &)), this, SLOT(serialPong(const QString &)));
+ connect(d->codaDevice.data(), SIGNAL(tcfEvent(const Coda::CodaEvent &)), this, SLOT(tcfEvent(const Coda::CodaEvent &)));
+ connect(this, SIGNAL(done()), this, SLOT(finished()));
+
+ d->codaDevice->sendSerialPing(false);
+ if (d->timeout > 0)
+ QTimer::singleShot(d->timeout, this, SLOT(timeout()));
+ d->eventLoop = new QEventLoop();
+ d->eventLoop->exec();
+ int result = d->result;
+ reportMessage(tr("Done."));
+ disconnect(d->codaDevice.data(), 0, this, 0);
+ SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice);
+
+ QCoreApplication::quit();
+ return result;
+}
+
+void CodaSignalHandler::setActionType(CodaAction action)
+{
+ d->action = CodaAction(d->action | action);
+}
+
+void CodaSignalHandler::setAppFileName(const QString &fileName)
+{
+ d->appFileName = fileName;
+}
+
+void CodaSignalHandler::setCodaDevice(SymbianUtils::CodaDevicePtr &codaDevice)
+{
+ d->codaDevice = codaDevice;
+}
+
+void CodaSignalHandler::setCommandLineArgs(const QString &args)
+{
+ d->commandLineArgs = args;
+}
+
+void CodaSignalHandler::setCopyFileName(const QString &srcName, const QString &dstName)
+{
+ d->copySrcFileName = srcName;
+ d->copyDstFileName = dstName;
+}
+
+void CodaSignalHandler::setDownloadFileName(const QString &srcName, const QString &dstName)
+{
+ d->dlSrcFileName = srcName;
+ d->dlDstFileName = dstName;
+}
+
+void CodaSignalHandler::setLogLevel(int level)
+{
+ d->loglevel = level;
+}
+
+void CodaSignalHandler::setSerialPortName(const QString &serialPortName)
+{
+ d->serialPortName = serialPortName;
+}
+
+void CodaSignalHandler::setTimeout(const int msec)
+{
+ d->timeout = msec;
+}
+
+void CodaSignalHandler::closeFile()
+{
+ d->remoteFile.reset();
+ if (!d->codaDevice) {
+ emit done();
+ return;
+ }
+
+ d->codaDevice->sendFileSystemCloseCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemClose),
+ d->remoteFileHandle);
+}
+
+void CodaSignalHandler::handleConnected(const Coda::CodaEvent &event)
+{
+ if (d->connected)
+ return;
+
+ const Coda::CodaLocatorHelloEvent &hEvent = static_cast<const Coda::CodaLocatorHelloEvent &>(event);
+ QStringList services = hEvent.services();
+ if (services.contains("DebugSessionControl")) {
+ d->debugSessionControl = true;
+ }
+ d->connected = true;
+ handleActions();
+}
+
+void CodaSignalHandler::handleActions()
+{
+ if (d->action & ActionCopy) {
+ initFileSending();
+ } else if (d->action & ActionInstall) {
+ initFileInstallation();
+ } else if (d->action & ActionRun) {
+ initAppRunning();
+ } else if (d->action & ActionDownload) {
+ initFileDownloading();
+ } else {
+ emit done();
+ }
+}
+
+void CodaSignalHandler::handleAppExited(const Coda::CodaEvent &event)
+{
+ const Coda::CodaProcessExitedEvent &pEvent = static_cast<const Coda::CodaProcessExitedEvent &>(event);
+ QString id = pEvent.idString();
+ if (!id.compare(d->appID, Qt::CaseInsensitive)) {
+ d->codaDevice->sendDebugSessionControlSessionEndCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleDebugSessionControlEnd));
+ d->action = static_cast<CodaAction>(d->action & ~ActionRun);
+ handleActions();
+ }
+}
+
+void CodaSignalHandler::handleAppRunning(const Coda::CodaCommandResult &result)
+{
+ if (result.type == Coda::CodaCommandResult::SuccessReply) {
+ reportMessage(tr("Running..."));
+
+ Coda::JsonValue value = result.values.at(0);
+ readAppId(value);
+ } else {
+ reportError(tr("Launch failed: %1").arg(result.toString()));
+ }
+}
+
+void CodaSignalHandler::handleDebugSessionControlEnd(const Coda::CodaCommandResult &result)
+{
+ if (result.type == Coda::CodaCommandResult::SuccessReply) {
+ // nothing to do
+ }
+}
+
+void CodaSignalHandler::handleDebugSessionControlStart(const Coda::CodaCommandResult &result)
+{
+ if (result.type == Coda::CodaCommandResult::SuccessReply) {
+ d->codaDevice->sendRunProcessCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleAppRunning),
+ d->appFileName.toAscii(),
+ d->commandLineArgs.split(' '));
+ reportMessage(tr("Launching %1...").arg(QFileInfo(d->appFileName).fileName()));
+ } else {
+ reportError(tr("Failed to start CODA debug session control."));
+ }
+}
+
+void CodaSignalHandler::handleFileSystemClose(const Coda::CodaCommandResult &result)
+{
+ if (result.type == Coda::CodaCommandResult::SuccessReply) {
+ reportMessage(tr("File closed."));
+ if (d->action & ActionCopy) {
+ d->action = static_cast<CodaAction>(d->action & ~ActionCopy);
+ } else if (d->action & ActionDownload) {
+ d->action = static_cast<CodaAction>(d->action & ~ActionDownload);
+ }
+ handleActions();
+ } else {
+ reportError(tr("Failed to close the remote file: %1").arg(result.toString()));
+ }
+}
+
+void CodaSignalHandler::handleFileSystemOpen(const Coda::CodaCommandResult &result)
+{
+ if (result.type != Coda::CodaCommandResult::SuccessReply) {
+ reportError(tr("Could not open remote file: %1").arg(result.errorString()));
+ return;
+ }
+
+ if (result.values.size() < 1 || result.values.at(0).data().isEmpty()) {
+ reportError(tr("Internal error: No filehandle obtained"));
+ return;
+ }
+
+ if (d->action & ActionCopy) {
+ d->remoteFileHandle = result.values.at(0).data();
+ d->remoteFile.reset(new QFile(d->copySrcFileName));
+ if (!d->remoteFile->open(QIODevice::ReadOnly)) { // Should not fail, was checked before
+ reportError(tr("Could not open local file %1").arg(d->copySrcFileName));
+ return;
+ }
+ putSendNextChunk();
+ } else if (d->action & ActionDownload) {
+ d->remoteFileHandle = result.values.at(0).data();
+ d->localFile.reset(new QFile(d->dlDstFileName));
+ // remove any existing file with the same name
+ if (d->localFile->exists() && !d->localFile->remove()) {
+ reportError(tr("Could not create host file: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString()));
+ return;
+ }
+ // open local file in write-only mode
+ if (!d->localFile->open(QFile::WriteOnly)) {
+ reportError(tr("Could not open host file for writing: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString()));
+ return;
+ }
+ d->codaDevice->sendFileSystemFstatCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemStart),
+ d->remoteFileHandle);
+ }
+}
+
+void CodaSignalHandler::handleFileSystemRead(const Coda::CodaCommandResult &result)
+{
+ if (result.type != Coda::CodaCommandResult::SuccessReply || result.values.size() != 2) {
+ reportError(tr("Could not read remote file: %1").arg(result.errorString()));
+ return;
+ }
+
+ QByteArray data = QByteArray::fromBase64(result.values.at(0).data());
+ bool eof = result.values.at(1).toVariant().toBool();
+ qint64 written = d->localFile->write(data);
+ if (written < 0) {
+ reportError(tr("Could not write data to host file: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString()));
+ return;
+ }
+
+ d->remoteBytesLeft -= written;
+ if (!eof && d->remoteBytesLeft > 0) {
+ readNextChunk();
+ }
+ else {
+ d->localFile->flush();
+ d->localFile->close();
+ closeFile();
+ }
+}
+
+void CodaSignalHandler::handleFileSystemStart(const Coda::CodaCommandResult &result)
+{
+ if (result.type != Coda::CodaCommandResult::SuccessReply) {
+ reportError(tr("Could not open remote file: %1").arg(result.errorString()));
+ return;
+ }
+
+ if (result.values.size() < 1 || result.values.at(0).children().isEmpty()) {
+ reportError(tr("Could not get file attributes"));
+ return;
+ }
+
+ Coda::JsonValue val = result.values.at(0).findChild("Size");
+ d->remoteFileSize = val.isValid() ? val.data().toLong() : -1L;
+ if (d->remoteFileSize < 0) {
+ reportError(tr("Could not get file size"));
+ return;
+ }
+
+ d->remoteBytesLeft = d->remoteFileSize;
+ readNextChunk();
+}
+
+void CodaSignalHandler::handleFileSystemWrite(const Coda::CodaCommandResult &result)
+{
+ // Close remote file even if copy fails
+ d->putWriteOk = result;
+ if (!d->putWriteOk) {
+ QString fileName = QFileInfo(d->copyDstFileName).fileName();
+ reportError(tr("Could not write to file %1 on device: %2").arg(fileName).arg(result.errorString()));
+ }
+
+ if (!d->putWriteOk || d->putLastChunkSize < d->putChunkSize) {
+ closeFile();
+ } else {
+ putSendNextChunk();
+ }
+}
+
+void CodaSignalHandler::handleSymbianInstall(const Coda::CodaCommandResult &result)
+{
+ if (result.type == Coda::CodaCommandResult::SuccessReply) {
+ reportMessage(tr("Installation has finished."));
+ d->action = static_cast<CodaAction>(d->action & ~ActionInstall);
+ handleActions();
+ } else {
+ reportError(tr("Installation failed: %1").arg(result.errorString()));
+ }
+}
+
+void CodaSignalHandler::initAppRunning()
+{
+ if (!d->codaDevice || d->appFileName.isEmpty()) {
+ emit done();
+ return;
+ }
+
+ if (!d->debugSessionControl) {
+ reportError(tr("CODA DebugSessionControl service not found, please update to CODA v4.0.23 or later."));
+ }
+
+ d->codaDevice->sendDebugSessionControlSessionStartCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleDebugSessionControlStart));
+}
+
+void CodaSignalHandler::initFileDownloading()
+{
+ if (!d->codaDevice || d->dlSrcFileName.isEmpty()) {
+ emit done();
+ return;
+ }
+
+ d->codaDevice->sendFileSystemOpenCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemOpen),
+ d->dlSrcFileName.toAscii(), Coda::CodaDevice::FileSystem_TCF_O_READ);
+ reportMessage(tr("Downloading %1...").arg(QFileInfo(d->dlSrcFileName).fileName()));
+}
+
+void CodaSignalHandler::initFileInstallation()
+{
+ if (!d->codaDevice || d->copyDstFileName.isEmpty()) {
+ emit done();
+ return;
+ }
+
+ QString installationDrive = "C";
+ d->codaDevice->sendSymbianInstallSilentInstallCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleSymbianInstall),
+ d->copyDstFileName.toAscii(),
+ installationDrive.toAscii());
+ reportMessage(tr("Installing package \"%1\" on drive %2...").arg(QFileInfo(d->copyDstFileName).fileName(), installationDrive));
+}
+
+void CodaSignalHandler::initFileSending()
+{
+ if (!d->codaDevice || d->copySrcFileName.isEmpty()) {
+ emit done();
+ return;
+ }
+
+ const unsigned flags =
+ Coda::CodaDevice::FileSystem_TCF_O_WRITE
+ |Coda::CodaDevice::FileSystem_TCF_O_CREAT
+ |Coda::CodaDevice::FileSystem_TCF_O_TRUNC;
+ d->putWriteOk = false;
+ d->codaDevice->sendFileSystemOpenCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemOpen),
+ d->copyDstFileName.toAscii(), flags);
+ reportMessage(tr("Copying %1...").arg(QFileInfo(d->copyDstFileName).fileName()));
+}
+
+void CodaSignalHandler::putSendNextChunk()
+{
+ if (!d->codaDevice || !d->remoteFile) {
+ emit done();
+ return;
+ }
+
+ // Read and send off next chunk
+ const quint64 pos = d->remoteFile->pos();
+ const QByteArray data = d->remoteFile->read(d->putChunkSize);
+ if (data.isEmpty()) {
+ d->putWriteOk = true;
+ closeFile();
+ } else {
+ d->putLastChunkSize = data.size();
+ d->codaDevice->sendFileSystemWriteCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemWrite),
+ d->remoteFileHandle, data, unsigned(pos));
+ }
+}
+
+void CodaSignalHandler::readNextChunk()
+{
+ const quint64 pos = d->remoteFileSize - d->remoteBytesLeft;
+ const quint64 size = qMin(d->remoteBytesLeft, DEFAULT_CHUNK_SIZE);
+ d->codaDevice->sendFileSystemReadCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemRead),
+ d->remoteFileHandle, pos, size);
+}
+
+void CodaSignalHandler::readAppId(Coda::JsonValue value)
+{
+ if (value.isObject()) {
+ Coda::JsonValue idValue = value.findChild("ID");
+ if (idValue.isString()) {
+ d->appID = idValue.data();
+ return;
+ }
+ }
+
+ reportError(tr("Could not get process ID of %1.").arg(QFileInfo(d->appFileName).fileName()));
+}
+
+void CodaSignalHandler::reportError(const QString &message)
+{
+ d->err << message << endl;
+ d->result = 1;
+ emit done();
+}
+
+void CodaSignalHandler::reportMessage(const QString &message)
+{
+ if (d->loglevel > 0)
+ d->out << message << endl;
+}
+
+CodaSignalHandler::CodaSignalHandler()
+ : d(new CodaSignalHandlerPrivate())
+{
+}
+
+CodaSignalHandler::~CodaSignalHandler()
+{
+ delete d;
+}
+
diff --git a/tools/runonphone/codasignalhandler.h b/tools/runonphone/codasignalhandler.h
new file mode 100644
index 0000000..a978bfa
--- /dev/null
+++ b/tools/runonphone/codasignalhandler.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CODASIGNALHANDLER_H
+#define CODASIGNALHANDLER_H
+
+#include "symbianutils/codamessage.h"
+#include "symbianutils/symbiandevicemanager.h"
+
+#include "symbianutils/codadevice.h"
+
+enum CodaAction {
+ ActionPingOnly = 0x0,
+ ActionCopy = 0x1,
+ ActionInstall = 0x2,
+ ActionCopyInstall = ActionCopy | ActionInstall,
+ ActionRun = 0x4,
+ ActionDownload = 0x8,
+ ActionCopyRun = ActionCopy | ActionRun,
+ ActionInstallRun = ActionInstall | ActionRun,
+ ActionCopyInstallRun = ActionCopy | ActionInstall | ActionRun
+};
+
+class CodaSignalHandlerPrivate;
+class CodaSignalHandler : public QObject
+{
+ Q_OBJECT
+public slots:
+ void error(const QString &errorMessage);
+ void logMessage(const QString &logMessage);
+ void serialPong(const QString &codaVersion);
+ void tcfEvent(const Coda::CodaEvent &event);
+ void terminate();
+private slots:
+ void finished();
+ void timeout();
+signals:
+ void done();
+public:
+ CodaSignalHandler();
+ ~CodaSignalHandler();
+ void init();
+ int run();
+ void setActionType(CodaAction action);
+ void setAppFileName(const QString &fileName);
+ void setCodaDevice(SymbianUtils::CodaDevicePtr &codeDevice);
+ void setCommandLineArgs(const QString &args);
+ void setCopyFileName(const QString &srcName, const QString &dstName);
+ void setDownloadFileName(const QString &srcName, const QString &dstName);
+ void setLogLevel(int level);
+ void setSerialPortName(const QString &serialPortName);
+ void setTimeout(const int msec);
+private:
+ void closeFile();
+ void handleConnected(const Coda::CodaEvent &event);
+ void handleActions();
+ void handleAppExited(const Coda::CodaEvent &event);
+ void handleAppRunning(const Coda::CodaCommandResult &result);
+ void handleDebugSessionControlEnd(const Coda::CodaCommandResult &result);
+ void handleDebugSessionControlStart(const Coda::CodaCommandResult &result);
+ void handleFileSystemClose(const Coda::CodaCommandResult &result);
+ void handleFileSystemOpen(const Coda::CodaCommandResult &result);
+ void handleFileSystemRead(const Coda::CodaCommandResult &result);
+ void handleFileSystemStart(const Coda::CodaCommandResult &result);
+ void handleFileSystemWrite(const Coda::CodaCommandResult &result);
+ void handleSymbianInstall(const Coda::CodaCommandResult &result);
+ void initAppRunning();
+ void initFileDownloading();
+ void initFileInstallation();
+ void initFileSending();
+ void putSendNextChunk();
+ void readNextChunk();
+ void readAppId(Coda::JsonValue value);
+ void reportError(const QString &message);
+ void reportMessage(const QString &message);
+
+ CodaSignalHandlerPrivate *d;
+};
+
+#endif // CODESIGNALHANDLER_H
diff --git a/tools/runonphone/main.cpp b/tools/runonphone/main.cpp
index d749106..868a2a8 100644
--- a/tools/runonphone/main.cpp
+++ b/tools/runonphone/main.cpp
@@ -45,10 +45,13 @@
#include <QScopedPointer>
#include <QTimer>
#include <QFileInfo>
+#include "symbianutils/codadevice.h"
#include "symbianutils/trkutils.h"
#include "symbianutils/trkdevice.h"
#include "symbianutils/launcher.h"
+#include "symbianutils/symbiandevicemanager.h"
+#include "codasignalhandler.h"
#include "trksignalhandler.h"
#include "serenum.h"
#include "ossignalconverter.h"
@@ -62,14 +65,16 @@ void printUsage(QTextStream& outstream, QString exeName)
<< "-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> <remote file> upload file to phone" << 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
+ << "--coda Use CODA instead of TRK (default agent)" << endl
<< endl
<< "USB COM ports can usually be autodetected, use -p or -f to force a specific port." << endl
+ << "TRK is the default debugging agent, use --coda option when using CODA instead of TRK." << 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;
+ << "-s can be used with both System and Application TRK/CODA to install the program" << endl;
}
#define CHECK_PARAMETER_EXISTS if(!it.hasNext()) { printUsage(outstream, args[0]); return 1; }
@@ -86,12 +91,12 @@ int main(int argc, char *argv[])
QTextStream outstream(stdout);
QTextStream errstream(stderr);
QString uploadLocalFile;
- QString uploadRemoteFile;
QString downloadRemoteFile;
QString downloadLocalFile;
int loglevel=1;
int timeout=0;
bool crashlog = true;
+ bool coda = false;
QString crashlogpath;
QListIterator<QString> it(args);
it.next(); //skip name of program
@@ -122,14 +127,21 @@ int main(int argc, char *argv[])
errstream << "Executable file (" << uploadLocalFile << ") doesn't exist" << endl;
return 1;
}
- CHECK_PARAMETER_EXISTS
- uploadRemoteFile = it.next();
+ 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();
+ QFileInfo downloadInfo(downloadLocalFile);
+ if (downloadInfo.exists() && !downloadInfo.isFile()) {
+ errstream << downloadLocalFile << " is not a file." << endl;
+ return 1;
+ }
}
else if (arg == "--timeout" || arg == "-t") {
CHECK_PARAMETER_EXISTS
@@ -140,6 +152,8 @@ int main(int argc, char *argv[])
return 1;
}
}
+ else if (arg == "--coda")
+ coda = true;
else if (arg == "--verbose" || arg == "-v")
loglevel=2;
else if (arg == "--quiet" || arg == "-q")
@@ -160,8 +174,7 @@ int main(int argc, char *argv[])
}
}
- if (exeFile.isEmpty() && sisFile.isEmpty() &&
- (uploadLocalFile.isEmpty() || uploadRemoteFile.isEmpty()) &&
+ if (exeFile.isEmpty() && sisFile.isEmpty() && uploadLocalFile.isEmpty() &&
(downloadLocalFile.isEmpty() || downloadRemoteFile.isEmpty())) {
printUsage(outstream, args[0]);
return 1;
@@ -200,76 +213,117 @@ int main(int argc, char *argv[])
}
}
+ CodaSignalHandler codaHandler;
QScopedPointer<trk::Launcher> launcher;
- launcher.reset(new trk::Launcher(trk::Launcher::ActionPingOnly));
- QFileInfo exeInfo(exeFile);
+ TrkSignalHandler trkHandler;
+ QFileInfo info(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, uploadRemoteFile);
- }
- 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);
+ if (coda) {
+ codaHandler.setSerialPortName(serialPortName);
+ codaHandler.setLogLevel(loglevel);
- TrkSignalHandler handler;
- handler.setLogLevel(loglevel);
- handler.setCrashLogging(crashlog);
- handler.setCrashLogPath(crashlogpath);
+ if (!sisFile.isEmpty()) {
+ codaHandler.setActionType(ActionCopyInstall);
+ QString dstName = "c:\\data\\testtemp.sis";
+ codaHandler.setCopyFileName(sisFile, dstName);
+ }
+ else if (!uploadLocalFile.isEmpty() && uploadInfo.exists()) {
+ codaHandler.setActionType(ActionCopy);
+ QString dstName = QString("c:\\sys\\bin\\") + uploadInfo.fileName();
+ codaHandler.setCopyFileName(uploadLocalFile, dstName);
+ }
+ if (!exeFile.isEmpty()) {
+ codaHandler.setActionType(ActionRun);
+ codaHandler.setAppFileName(QString("c:\\sys\\bin\\") + info.fileName());
+ codaHandler.setCommandLineArgs(cmdLine.join(QLatin1String(", ")));
+ }
+ if (!downloadRemoteFile.isEmpty() && !downloadLocalFile.isEmpty()) {
+ codaHandler.setActionType(ActionDownload);
+ codaHandler.setDownloadFileName(downloadRemoteFile, downloadLocalFile);
+ }
- 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()));
+ if (loglevel > 0)
+ outstream << "Connecting to target via " << serialPortName << endl;
- QObject::connect(OsSignalConverter::instance(), SIGNAL(terminate()), launcher.data(), SLOT(terminate()), Qt::QueuedConnection);
+ if (timeout > 0)
+ codaHandler.setTimeout(timeout);
- QTimer timer;
- timer.setSingleShot(true);
- QObject::connect(&timer, SIGNAL(timeout()), &handler, SLOT(timeout()));
- if (timeout > 0) {
- timer.start(timeout);
- }
+ QObject::connect(OsSignalConverter::instance(), SIGNAL(terminate()), &codaHandler, SLOT(terminate()), Qt::QueuedConnection);
+ return codaHandler.run();
- QString errorMessage;
- if (!launcher->startServer(&errorMessage)) {
- errstream << errorMessage << endl;
- return 1;
+ } else {
+ launcher.reset(new trk::Launcher(trk::Launcher::ActionPingOnly));
+ if (!sisFile.isEmpty()) {
+ launcher->addStartupActions(trk::Launcher::ActionCopyInstall);
+ QStringList srcName(sisFile);
+ QStringList dstName("c:\\data\\testtemp.sis");
+ launcher->setCopyFileNames(srcName, dstName);
+ launcher->setInstallFileNames(dstName);
+ }
+ else if (!uploadLocalFile.isEmpty() && uploadInfo.exists()) {
+ launcher->addStartupActions(trk::Launcher::ActionCopy);
+ QStringList srcName(uploadLocalFile);
+ QStringList dstName(QString("c:\\sys\\bin\\") + uploadInfo.fileName());
+ launcher->setCopyFileNames(srcName, dstName);
+ }
+ if (!exeFile.isEmpty()) {
+ launcher->addStartupActions(trk::Launcher::ActionRun);
+ launcher->setFileName(QString("c:\\sys\\bin\\") + info.fileName());
+ launcher->setCommandLineArgs(cmdLine.join(QLatin1String(" ")));
+ }
+ 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);
+
+ trkHandler.setLogLevel(loglevel);
+ trkHandler.setCrashLogging(crashlog);
+ trkHandler.setCrashLogPath(crashlogpath);
+
+ QObject::connect(launcher.data(), SIGNAL(copyingStarted(const QString &)), &trkHandler, SLOT(copyingStarted(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotConnect(const QString &)), &trkHandler, SLOT(canNotConnect(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotCreateFile(const QString &, const QString &)), &trkHandler, SLOT(canNotCreateFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotWriteFile(const QString &, const QString &)), &trkHandler, SLOT(canNotWriteFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotCloseFile(const QString &, const QString &)), &trkHandler, SLOT(canNotCloseFile(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(installingStarted(const QString &)), &trkHandler, SLOT(installingStarted(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(canNotInstall(const QString &, const QString &)), &trkHandler, SLOT(canNotInstall(const QString &, const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(installingFinished()), &trkHandler, SLOT(installingFinished()));
+ QObject::connect(launcher.data(), SIGNAL(startingApplication()), &trkHandler, SLOT(startingApplication()));
+ QObject::connect(launcher.data(), SIGNAL(applicationRunning(uint)), &trkHandler, SLOT(applicationRunning(uint)));
+ QObject::connect(launcher.data(), SIGNAL(canNotRun(const QString &)), &trkHandler, SLOT(canNotRun(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(applicationOutputReceived(const QString &)), &trkHandler, SLOT(applicationOutputReceived(const QString &)));
+ QObject::connect(launcher.data(), SIGNAL(copyProgress(int)), &trkHandler, SLOT(copyProgress(int)));
+ QObject::connect(launcher.data(), SIGNAL(stateChanged(int)), &trkHandler, SLOT(stateChanged(int)));
+ QObject::connect(launcher.data(), SIGNAL(processStopped(uint,uint,uint,QString)), &trkHandler, SLOT(stopped(uint,uint,uint,QString)));
+ QObject::connect(launcher.data(), SIGNAL(libraryLoaded(trk::Library)), &trkHandler, SLOT(libraryLoaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(libraryUnloaded(trk::Library)), &trkHandler, SLOT(libraryUnloaded(trk::Library)));
+ QObject::connect(launcher.data(), SIGNAL(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)), &trkHandler, SLOT(registersAndCallStackReadComplete(const QList<uint> &,const QByteArray &)));
+ QObject::connect(&trkHandler, SIGNAL(resume(uint,uint)), launcher.data(), SLOT(resumeProcess(uint,uint)));
+ QObject::connect(&trkHandler, SIGNAL(terminate()), launcher.data(), SLOT(terminate()));
+ QObject::connect(&trkHandler, SIGNAL(getRegistersAndCallStack(uint,uint)), launcher.data(), SLOT(getRegistersAndCallStack(uint,uint)));
+ QObject::connect(launcher.data(), SIGNAL(finished()), &trkHandler, SLOT(finished()));
+
+ QObject::connect(OsSignalConverter::instance(), SIGNAL(terminate()), launcher.data(), SLOT(terminate()), Qt::QueuedConnection);
+
+ QTimer timer;
+ timer.setSingleShot(true);
+ QObject::connect(&timer, SIGNAL(timeout()), &trkHandler, 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/tools/runonphone/runonphone.pro b/tools/runonphone/runonphone.pro
index 7ff361c..d006a05 100644
--- a/tools/runonphone/runonphone.pro
+++ b/tools/runonphone/runonphone.pro
@@ -1,27 +1,29 @@
TEMPLATE = app
QT -= gui
-CONFIG += console
+CONFIG += console static
CONFIG -= app_bundle
include(symbianutils/symbianutils.pri)
SOURCES += main.cpp \
trksignalhandler.cpp \
- ossignalconverter.cpp
+ ossignalconverter.cpp \
+ codasignalhandler.cpp
HEADERS += trksignalhandler.h \
serenum.h \
ossignalconverter.h \
- ossignalconverter_p.h
+ ossignalconverter_p.h \
+ codasignalhandler.h
DEFINES += SYMBIANUTILS_INCLUDE_PRI
windows {
SOURCES += serenum_win.cpp
LIBS += -lsetupapi \
- -luuid \
- -ladvapi32
+ -luuid \
+ -ladvapi32
}
else:unix:!symbian {
SOURCES += serenum_unix.cpp
diff --git a/tools/runonphone/symbianutils/codadevice.cpp b/tools/runonphone/symbianutils/codadevice.cpp
new file mode 100644
index 0000000..751f84e
--- /dev/null
+++ b/tools/runonphone/symbianutils/codadevice.cpp
@@ -0,0 +1,1501 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "codadevice.h"
+#include "json.h"
+#include "trkutils.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 tcpMessageTerminatorC[] = "\003\001";
+
+// Serial Ping: 0xfc,0x1f
+static const char serialPingC[] = "\xfc\x1f";
+// Serial Pong: 0xfc,0xf1, followed by version info
+static const char serialPongC[] = "\xfc\xf1";
+
+static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
+
+/* Serial messages > (1K - 2) have to chunked in order to pass the USB
+ * router as '0xfe char(chunkCount - 1) data' ... '0x0 char(chunkCount - 2) data'
+ * ... '0x0 0x0 last-data' */
+static const unsigned serialChunkLength = 0x400; // 1K max USB router
+static const int maxSerialMessageLength = 0x10000; // given chunking scheme
+
+static const char validProtocolIdStart = (char)0x90;
+static const char validProtocolIdEnd = (char)0x95;
+static const char codaProtocolId = (char)0x92;
+static const unsigned char serialChunkingStart = 0xfe;
+static const unsigned char serialChunkingContinuation = 0x0;
+enum { SerialChunkHeaderSize = 2 };
+
+// Create USB router frame
+static inline void encodeSerialFrame(const QByteArray &data, QByteArray *target, char protocolId)
+{
+ target->append(char(0x01));
+ target->append(protocolId);
+ appendShort(target, ushort(data.size()), trk::BigEndian);
+ target->append(data);
+}
+
+// Split in chunks of 1K according to CODA protocol chunking
+static inline QByteArray encodeUsbSerialMessage(const QByteArray &dataIn)
+{
+ // Reserve 2 header bytes
+ static const int chunkSize = serialChunkLength - SerialChunkHeaderSize;
+ const int size = dataIn.size();
+ QByteArray frame;
+ // Do we need to split?
+ if (size < chunkSize) { // Nope, all happy.
+ frame.reserve(size + 4);
+ encodeSerialFrame(dataIn, &frame, codaProtocolId);
+ return frame;
+ }
+ // Split.
+ unsigned chunkCount = size / chunkSize;
+ if (size % chunkSize)
+ chunkCount++;
+ if (debug)
+ qDebug("Serial: Splitting message of %d bytes into %u chunks of %d", size, chunkCount, chunkSize);
+
+ frame.reserve((4 + serialChunkLength) * chunkCount);
+ int pos = 0;
+ for (unsigned c = chunkCount - 1; pos < size ; c--) {
+ QByteArray chunk; // chunk with long message start/continuation code
+ chunk.reserve(serialChunkLength);
+ chunk.append(pos ? serialChunkingContinuation : serialChunkingStart);
+ chunk.append(char(static_cast<unsigned char>(c))); // Avoid any signedness issues.
+ const int chunkEnd = qMin(pos + chunkSize, size);
+ chunk.append(dataIn.mid(pos, chunkEnd - pos));
+ encodeSerialFrame(chunk, &frame, codaProtocolId);
+ pos = chunkEnd;
+ }
+ if (debug > 1)
+ qDebug("Serial chunked:\n%s", qPrintable(Coda::formatData(frame)));
+ return frame;
+}
+
+namespace Coda {
+// ------------- CodaCommandError
+
+CodaCommandError::CodaCommandError() : timeMS(0), code(0), alternativeCode(0)
+{
+}
+
+void CodaCommandError::clear()
+{
+ timeMS = 0;
+ code = alternativeCode = 0;
+ format.clear();
+ alternativeOrganization.clear();
+}
+
+QDateTime CodaCommandResult::tcfTimeToQDateTime(quint64 tcfTimeMS)
+{
+ const QDateTime time(QDate(1970, 1, 1));
+ return time.addMSecs(tcfTimeMS);
+}
+
+void CodaCommandError::write(QTextStream &str) const
+{
+ if (isError()) {
+ if (debug && timeMS)
+ str << CodaCommandResult::tcfTimeToQDateTime(timeMS).toString(Qt::ISODate) << ": ";
+ str << "'" << format << '\'' //for symbian the format is the real error message
+ << " Code: " << code;
+ if (!alternativeOrganization.isEmpty())
+ str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
+ } else{
+ str << "<No error>";
+ }
+}
+
+QString CodaCommandError::toString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+ write(str);
+ return rc;
+}
+
+bool CodaCommandError::isError() const
+{
+ return timeMS != 0 || code != 0 || !format.isEmpty() || alternativeCode != 0;
+}
+
+/* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
+bool CodaCommandError::parse(const QVector<JsonValue> &values)
+{
+ // Parse an arbitrary hash (that could as well be a command response)
+ // and check for error elements. It looks like sometimes errors are appended
+ // to other values.
+ unsigned errorKeyCount = 0;
+ clear();
+ do {
+ if (values.isEmpty())
+ break;
+ // Errors are mostly appended, except for FileSystem::open, in which case
+ // a string "null" file handle (sic!) follows the error.
+ const int last = values.size() - 1;
+ const int checkIndex = last == 1 && values.at(last).data() == "null" ?
+ last - 1 : last;
+ if (values.at(checkIndex).type() != JsonValue::Object)
+ break;
+ foreach (const JsonValue &c, values.at(checkIndex).children()) {
+ if (c.name() == "Time") {
+ timeMS = c.data().toULongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Code") {
+ code = c.data().toLongLong();
+ errorKeyCount++;
+ } else if (c.name() == "Format") {
+ format = c.data();
+ errorKeyCount++;
+ } else if (c.name() == "AltCode") {
+ alternativeCode = c.data().toULongLong();
+ 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("CodaCommandError::parse: Found error %d (%u): ", errorFound, errorKeyCount);
+ if (!values.isEmpty())
+ qDebug() << values.back().toString();
+ }
+ return errorFound;
+}
+
+// ------------ CodaCommandResult
+
+CodaCommandResult::CodaCommandResult(Type t) :
+ type(t), service(LocatorService)
+{
+}
+
+CodaCommandResult::CodaCommandResult(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 CodaCommandResult::errorString() const
+{
+ QString rc;
+ QTextStream str(&rc);
+
+ switch (type) {
+ case SuccessReply:
+ case ProgressReply:
+ str << "<No error>";
+ return rc;
+ case FailReply:
+ str << "NAK";
+ return rc;
+ case CommandErrorReply:
+ commandError.write(str);
+ break;
+ }
+ if (debug) {
+ // Append the failed command for reference
+ str << " (Command was: '";
+ QByteArray printableRequest = request;
+ printableRequest.replace('\0', '|');
+ str << printableRequest << "')";
+ }
+ return rc;
+}
+
+QString CodaCommandResult::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;
+}
+
+CodaStatResponse::CodaStatResponse() : size(0)
+{
+}
+
+struct CodaSendQueueEntry
+{
+ typedef CodaDevice::MessageType MessageType;
+
+ explicit CodaSendQueueEntry(MessageType mt,
+ int tok,
+ Services s,
+ const QByteArray &d,
+ const CodaCallback &cb= CodaCallback(),
+ 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;
+ CodaCallback callback;
+ unsigned specialHandling;
+};
+
+struct CodaDevicePrivate {
+ typedef CodaDevice::IODevicePtr IODevicePtr;
+ typedef QHash<int, CodaSendQueueEntry> TokenWrittenMessageMap;
+
+ CodaDevicePrivate();
+
+ const QByteArray m_tcpMessageTerminator;
+
+ IODevicePtr m_device;
+ unsigned m_verbose;
+ QByteArray m_readBuffer;
+ QByteArray m_serialBuffer; // for chunked messages
+ int m_token;
+ QQueue<CodaSendQueueEntry> m_sendQueue;
+ TokenWrittenMessageMap m_writtenMessages;
+ QVector<QByteArray> m_registerNames;
+ QVector<QByteArray> m_fakeGetMRegisterValues;
+ bool m_serialFrame;
+ bool m_serialPingOnly;
+};
+
+CodaDevicePrivate::CodaDevicePrivate() :
+ m_tcpMessageTerminator(tcpMessageTerminatorC),
+ m_verbose(0), m_token(0), m_serialFrame(false), m_serialPingOnly(false)
+{
+}
+
+CodaDevice::CodaDevice(QObject *parent) :
+ QObject(parent), d(new CodaDevicePrivate)
+{
+ if (debug) setVerbose(true);
+}
+
+CodaDevice::~CodaDevice()
+{
+ delete d;
+}
+
+QVector<QByteArray> CodaDevice::registerNames() const
+{
+ return d->m_registerNames;
+}
+
+void CodaDevice::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);
+ }
+}
+
+CodaDevice::IODevicePtr CodaDevice::device() const
+{
+ return d->m_device;
+}
+
+CodaDevice::IODevicePtr CodaDevice::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 CodaDevice::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 CodaDevice::slotDeviceError()
+{
+ const QString message = d->m_device->errorString();
+ emitLogMessage(message);
+ emit error(message);
+}
+
+void CodaDevice::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)
+{
+ const bool isBinary = !message.isEmpty() && message.at(0) < 0;
+ if (isBinary) {
+ message = message.toHex(); // Some serial special message
+ } else {
+ message.replace('\0', '|');
+ }
+ const QString messageS = QString::fromLatin1(message);
+ return prefix ?
+ (QLatin1String(prefix) + messageS) : messageS;
+}
+
+void CodaDevice::slotDeviceReadyRead()
+{
+ const QByteArray newData = d->m_device->readAll();
+ d->m_readBuffer += newData;
+ if (debug)
+ qDebug("ReadBuffer: %s", qPrintable(trk::stringFromArray(newData)));
+ if (d->m_serialFrame) {
+ deviceReadyReadSerial();
+ } else {
+ deviceReadyReadTcp();
+ }
+}
+
+// Find a serial header in input stream '0x1', '0x92', 'lenH', 'lenL'
+// and return message position and size.
+QPair<int, int> CodaDevice::findSerialHeader(QByteArray &in)
+{
+ static const char header1 = 0x1;
+ // Header should in theory always be at beginning of
+ // buffer. Warn if there are bogus data in-between.
+
+ while (in.size() >= 4) {
+ if (in.at(0) == header1 && in.at(1) == codaProtocolId) {
+ // Good packet
+ const int length = trk::extractShort(in.constData() + 2);
+ return QPair<int, int>(4, length);
+ } else if (in.at(0) == header1 && in.at(1) >= validProtocolIdStart && in.at(1) <= validProtocolIdEnd) {
+ // We recognise it but it's not a TCF message - emit it for any interested party to handle
+ const int length = trk::extractShort(in.constData() + 2);
+ if (4 + length <= in.size()) {
+ // We have all the data
+ QByteArray data(in.mid(4, length));
+ emit unknownEvent(in.at(1), data);
+ in.remove(0, 4+length);
+ // and continue
+ } else {
+ // If we don't have all this packet, there can't be any data following it, so return now
+ // and wait for more data
+ return QPair<int, int>(-1, -1);
+ }
+ } else {
+ // Bad data - log it, remove it, and go round again
+ int nextHeader = in.indexOf(header1, 1);
+ QByteArray bad = in.mid(0, nextHeader);
+ qWarning("Bogus data received on serial line: %s\n"
+ "Frame Header at: %d", qPrintable(trk::stringFromArray(bad)), nextHeader);
+ in.remove(0, bad.length());
+ // and continue
+ }
+ }
+ return QPair<int, int>(-1, -1); // No more data, or not enough for a complete header
+}
+
+void CodaDevice::deviceReadyReadSerial()
+{
+ do {
+ // Extract message (pos,len)
+ const QPair<int, int> messagePos = findSerialHeader(d->m_readBuffer);
+ if (messagePos.first < 0)
+ break;
+ // Do we have the complete message?
+ const int messageEnd = messagePos.first + messagePos.second;
+ if (messageEnd > d->m_readBuffer.size())
+ break;
+ processSerialMessage(d->m_readBuffer.mid(messagePos.first, messagePos.second));
+ d->m_readBuffer.remove(0, messageEnd);
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further messages
+}
+
+void CodaDevice::processSerialMessage(const QByteArray &message)
+{
+ if (debug > 1)
+ qDebug("Serial message: %s",qPrintable(trk::stringFromArray(message)));
+ if (message.isEmpty())
+ return;
+ // Is thing a ping/pong response
+ const int size = message.size();
+ if (message.startsWith(serialPongC)) {
+ const QString version = QString::fromLatin1(message.mid(sizeof(serialPongC) - 1));
+ emitLogMessage(QString::fromLatin1("Serial connection from '%1'").arg(version));
+ emit serialPong(version);
+ // Answer with locator.
+ if (!d->m_serialPingOnly)
+ writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
+ return;
+ }
+ // Check for long message (see top, '0xfe #number, data' or '0x0 #number, data')
+ // TODO: This is currently untested.
+ const unsigned char *dataU = reinterpret_cast<const unsigned char *>(message.constData());
+ const bool isLongMessageStart = size > SerialChunkHeaderSize
+ && *dataU == serialChunkingStart;
+ const bool isLongMessageContinuation = size > SerialChunkHeaderSize
+ && *dataU == serialChunkingContinuation;
+ if (isLongMessageStart || isLongMessageContinuation) {
+ const unsigned chunkNumber = *++dataU;
+ if (isLongMessageStart) { // Start new buffer
+ d->m_serialBuffer.clear();
+ d->m_serialBuffer.reserve( (chunkNumber + 1) * serialChunkLength);
+ }
+ d->m_serialBuffer.append(message.mid(SerialChunkHeaderSize, size - SerialChunkHeaderSize));
+ // Last chunk? - Process
+ if (!chunkNumber) {
+ processMessage(d->m_serialBuffer);
+ d->m_serialBuffer.clear();
+ d->m_serialBuffer.squeeze();
+ }
+ } else {
+ processMessage(message); // Normal, unchunked message
+ }
+}
+
+void CodaDevice::deviceReadyReadTcp()
+{
+ // Take complete message off front of readbuffer.
+ do {
+ const int messageEndPos = d->m_readBuffer.indexOf(d->m_tcpMessageTerminator);
+ if (messageEndPos == -1)
+ break;
+ if (messageEndPos == 0) {
+ // TCF TRK 4.0.5 emits empty messages on errors.
+ emitLogMessage(QString::fromLatin1("An empty TCF TRK message has been received."));
+ } else {
+ processMessage(d->m_readBuffer.left(messageEndPos));
+ }
+ d->m_readBuffer.remove(0, messageEndPos + d->m_tcpMessageTerminator.size());
+ } while (!d->m_readBuffer.isEmpty());
+ checkSendQueue(); // Send off further messages
+}
+
+void CodaDevice::processMessage(const QByteArray &message)
+{
+ if (debug)
+ qDebug("Read %d bytes:\n%s", message.size(), qPrintable(formatData(message)));
+ if (const int errorCode = parseMessage(message)) {
+ emitLogMessage(QString::fromLatin1("Parse error %1 : %2").
+ arg(errorCode).arg(debugMessage(message)));
+ if (debug)
+ qDebug("Parse error %d for %d bytes:\n%s", errorCode,
+ message.size(), qPrintable(formatData(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 CodaDevice::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 CodaDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
+{
+ typedef CodaDevicePrivate::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("CodaDevice: Internal error: token %d not found for '%s'",
+ token, qPrintable(joinByteArrays(tokens)));
+ return 236;
+ }
+ // No callback: remove entry from map, happy
+ const unsigned specialHandling = it.value().specialHandling;
+ if (!it.value().callback && specialHandling == 0u) {
+ 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.
+ CodaCommandResult result(type, it.value().service, it.value().data,
+ values, it.value().cookie);
+
+ if (it.value().callback)
+ it.value().callback(result);
+ d->m_writtenMessages.erase(it);
+ return 0;
+}
+
+int CodaDevice::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<CodaEvent> knownEvent(CodaEvent::parseEvent(service, tokens.at(1), values));
+ if (!knownEvent.isNull()) {
+ // Answer hello event (WLAN)
+ if (knownEvent->type() == CodaEvent::LocatorHello)
+ if (!d->m_serialFrame)
+ 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 CodaDevice::verbose() const
+{
+ return d->m_verbose;
+}
+
+bool CodaDevice::serialFrame() const
+{
+ return d->m_serialFrame;
+}
+
+void CodaDevice::setSerialFrame(bool s)
+{
+ d->m_serialFrame = s;
+}
+
+void CodaDevice::setVerbose(unsigned v)
+{
+ d->m_verbose = v;
+}
+
+void CodaDevice::emitLogMessage(const QString &m)
+{
+ if (debug)
+ qWarning("%s", qPrintable(m));
+ emit logMessage(m);
+}
+
+bool CodaDevice::checkOpen()
+{
+ if (d->m_device.isNull()) {
+ emitLogMessage(QLatin1String("Internal error: No device set on CodaDevice."));
+ return false;
+ }
+ if (!d->m_device->isOpen()) {
+ emitLogMessage(QLatin1String("Internal error: Device not open in CodaDevice."));
+ return false;
+ }
+ return true;
+}
+
+void CodaDevice::sendSerialPing(bool pingOnly)
+{
+ if (!checkOpen())
+ return;
+
+ d->m_serialPingOnly = pingOnly;
+ setSerialFrame(true);
+ writeMessage(QByteArray(serialPingC, qstrlen(serialPingC)), false);
+ if (d->m_verbose)
+ emitLogMessage(QLatin1String("Ping..."));
+}
+
+void CodaDevice::sendCodaMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, // may contain '\0'
+ int commandParametersLength,
+ const CodaCallback &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 CodaSendQueueEntry entry(mt, token, service, data, callBack, cookie);
+ d->m_sendQueue.enqueue(entry);
+ checkSendQueue();
+}
+
+void CodaDevice::sendCodaMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
+ callBack, cookie);
+}
+
+// Enclose in message frame and write.
+void CodaDevice::writeMessage(QByteArray data, bool ensureTerminating0)
+{
+ if (!checkOpen())
+ return;
+
+ if (d->m_serialFrame && data.size() > maxSerialMessageLength) {
+ qCritical("Attempt to send large message (%d bytes) exceeding the "
+ "limit of %d bytes over serial channel. Skipping.",
+ data.size(), maxSerialMessageLength);
+ return;
+ }
+
+ if (d->m_verbose)
+ emitLogMessage(debugMessage(data, "TCF <-"));
+
+ // Ensure \0-termination which easily gets lost in QByteArray CT.
+ if (ensureTerminating0 && !data.endsWith('\0'))
+ data.append('\0');
+ if (d->m_serialFrame) {
+ data = encodeUsbSerialMessage(data);
+ } else {
+ data += d->m_tcpMessageTerminator;
+ }
+
+ if (debug > 1)
+ qDebug("Writing:\n%s", qPrintable(formatData(data)));
+
+ int result = d->m_device->write(data);
+ if (result < data.length())
+ qWarning("Failed to write all data! result=%d", result);
+ if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
+ as->flush();
+}
+
+void CodaDevice::writeCustomData(char protocolId, const QByteArray &data)
+{
+ if (!checkOpen())
+ return;
+
+ if (!d->m_serialFrame) {
+ qWarning("Ignoring request to send data to non-serial CodaDevice");
+ return;
+ }
+ if (data.length() > 0xFFFF) {
+ qWarning("Ignoring request to send too large packet, of size %d", data.length());
+ return;
+ }
+ QByteArray framedData;
+ encodeSerialFrame(data, &framedData, protocolId);
+ device()->write(framedData);
+}
+
+void CodaDevice::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;
+ CodaSendQueueEntry 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) {
+ CodaCommandResult noopResult(CodaCommandResult::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 CodaDevice::sendProcessStartCommand(const CodaCallback &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);
+
+ if (workingDirectory.isEmpty())
+ workingDirectory = sysBin;
+
+ // Format settings with empty dummy parameter
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ',' << "logUserTraces" << ']'
+ << '\0' << '['
+ << binaryFileName << ','
+ << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
+ << additionalLibraries << ',' << true
+ << ']';
+ sendCodaMessage(
+#if 1
+ MessageWithReply, // TCF TRK 4.0.5 onwards
+#else
+ MessageWithoutReply, // TCF TRK 4.0.2
+#endif
+ SettingsService, "set", setData);
+
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << "" //We don't really know the drive of the working dir
+ << '\0' << binaryFileName << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << debugControl;
+ sendCodaMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void CodaDevice::sendRunProcessCommand(const CodaCallback &callBack,
+ const QString &processName,
+ QStringList arguments,
+ const QVariant &cookie)
+{
+ QByteArray startData;
+ JsonInputStream startStr(startData);
+ startStr << "" //We don't really know the drive of the working dir
+ << '\0' << processName << '\0' << arguments << '\0'
+ << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
+ << false; // Don't attach debugger
+ sendCodaMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
+}
+
+void CodaDevice::sendSettingsEnableLogCommand()
+{
+
+ QByteArray setData;
+ JsonInputStream setStr(setData);
+ setStr << "" << '\0'
+ << '[' << "logUserTraces" << ']'
+ << '\0' << '['
+ << true
+ << ']';
+ sendCodaMessage(
+#if 1
+ MessageWithReply, // TCF TRK 4.0.5 onwards
+#else
+ MessageWithoutReply, // TCF TRK 4.0.2
+#endif
+ SettingsService, "set", setData);
+}
+
+void CodaDevice::sendProcessTerminateCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlTerminateCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
+}
+
+// Non-standard: Remove executable from settings
+void CodaDevice::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
+ << ']';
+ sendCodaMessage(MessageWithoutReply, SettingsService, "set", setData, CodaCallback(), cookie);
+}
+
+void CodaDevice::sendRunControlResumeCommand(const CodaCallback &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;
+ }
+ sendCodaMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlSuspendCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << id;
+ sendCodaMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
+}
+
+void CodaDevice::sendRunControlResumeCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
+}
+
+void CodaDevice::sendBreakpointsAddCommand(const CodaCallback &callBack,
+ const Breakpoint &bp,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << bp;
+ sendCodaMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
+}
+
+void CodaDevice::sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie)
+{
+ sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
+}
+
+void CodaDevice::sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendCodaMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
+}
+
+void CodaDevice::sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie)
+{
+ sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
+}
+
+void CodaDevice::sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &ids,
+ bool enable,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << ids;
+ sendCodaMessage(MessageWithReply, BreakpointsService,
+ enable ? "enable" : "disable",
+ data, callBack, cookie);
+}
+
+void CodaDevice::sendMemorySetCommand(const CodaCallback &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();
+ sendCodaMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
+}
+
+void CodaDevice::sendMemoryGetCommand(const CodaCallback &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;
+ sendCodaMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
+}
+
+QByteArray CodaDevice::parseMemoryGet(const CodaCommandResult &r)
+{
+ if (r.type != CodaCommandResult::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("CodaDevice::parseMemoryGet(): 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("CodaDevice::parseMemoryGet: received %d bytes", memory.size());
+ return memory;
+}
+
+// Parse register children (array of names)
+QVector<QByteArray> CodaDevice::parseRegisterGetChildren(const CodaCommandResult &r)
+{
+ QVector<QByteArray> rc;
+ if (!r || r.values.size() < 1 || r.values.front().type() != JsonValue::Array)
+ return rc;
+ const JsonValue &front = r.values.front();
+ rc.reserve(front.childCount());
+ foreach(const JsonValue &v, front.children())
+ rc.push_back(v.data());
+ return rc;
+}
+
+CodaStatResponse CodaDevice::parseStat(const CodaCommandResult &r)
+{
+ CodaStatResponse rc;
+ if (!r || r.values.size() < 1 || r.values.front().type() != JsonValue::Object)
+ return rc;
+ foreach(const JsonValue &v, r.values.front().children()) {
+ if (v.name() == "Size") {
+ rc.size = v.data().toULongLong();
+ } else if (v.name() == "ATime") {
+ if (const quint64 atime = v.data().toULongLong())
+ rc.accessTime = CodaCommandResult::tcfTimeToQDateTime(atime);
+ } else if (v.name() == "MTime") {
+ if (const quint64 mtime = v.data().toULongLong())
+ rc.modTime = CodaCommandResult::tcfTimeToQDateTime(mtime);
+ }
+ }
+ return rc;
+}
+
+void CodaDevice::sendRegistersGetChildrenCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << contextId;
+ sendCodaMessage(MessageWithReply, RegistersService, "getChildren", data, callBack, cookie);
+}
+
+// Format id of register get request (needs contextId containing process and thread)
+static inline QByteArray registerId(const QByteArray &contextId, QByteArray id)
+{
+ QByteArray completeId = contextId;
+ if (!completeId.isEmpty())
+ completeId.append('.');
+ completeId.append(id);
+ return completeId;
+}
+
+// Format parameters of register get request
+static inline QByteArray registerGetData(const QByteArray &contextId, QByteArray id)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << registerId(contextId, id);
+ return data;
+}
+
+void CodaDevice::sendRegistersGetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray id,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, RegistersService, "get",
+ registerGetData(contextId, id), callBack, cookie);
+}
+
+void CodaDevice::sendRegistersGetMCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ const QVector<QByteArray> &ids,
+ const QVariant &cookie)
+{
+ // Format the register ids as a JSON list
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ const int count = ids.size();
+ for (int r = 0; r < count; r++) {
+ if (r)
+ str << ',';
+ // TODO: When 8-byte floating-point registers are supported, query for register length based on register id
+ str << '[' << registerId(contextId, ids.at(r)) << ',' << '0' << ',' << '4' << ']';
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, RegistersService, "getm", data, callBack, cookie);
+}
+
+void CodaDevice::sendRegistersGetMRangeCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count)
+{
+ const unsigned end = start + count;
+ if (end > (unsigned)d->m_registerNames.size()) {
+ qWarning("CodaDevice: 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 CodaDevice::sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray id,
+ const QByteArray &value,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ if (!contextId.isEmpty()) {
+ id.prepend('.');
+ id.prepend(contextId);
+ }
+ str << id << '\0' << value.toBase64();
+ sendCodaMessage(MessageWithReply, RegistersService, "set", data, callBack, cookie);
+}
+
+// Set register
+void CodaDevice::sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ const QByteArray &value,
+ const QVariant &cookie)
+{
+ if (registerNumber >= (unsigned)d->m_registerNames.size()) {
+ qWarning("CodaDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
+ return;
+ }
+ sendRegistersSetCommand(callBack, contextId,
+ d->m_registerNames[registerNumber],
+ value, cookie);
+}
+
+static const char outputListenerIDC[] = "ProgramOutputConsoleLogger";
+
+void CodaDevice::sendLoggingAddListenerCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << outputListenerIDC;
+ sendCodaMessage(MessageWithReply, LoggingService, "addListener", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianUninstallCommand(const Coda::CodaCallback &callBack,
+ const quint32 package,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ QString string = QString::number(package, 16);
+ str << string;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "uninstall", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetThreadsCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getThreads", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataFindProcessesCommand(const CodaCallback &callBack,
+ const QByteArray &processName,
+ const QByteArray &uid,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << processName << '\0' << uid;
+ sendCodaMessage(MessageWithReply, SymbianOSData, "findRunningProcesses", data, callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetQtVersionCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getQtVersion", QByteArray(), callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetRomInfoCommand(const CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getRomInfo", QByteArray(), callBack, cookie);
+}
+
+void CodaDevice::sendSymbianOsDataGetHalInfoCommand(const CodaCallback &callBack,
+ const QStringList &keys,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ for (int i = 0; i < keys.count(); ++i) {
+ if (i)
+ str << ',';
+ str << keys[i];
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, SymbianOSData, "getHalInfo", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemOpenCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &name,
+ unsigned flags,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << name << '\0' << flags << '\0' << '{' << '}';
+ sendCodaMessage(MessageWithReply, FileSystemService, "open", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemFstatCommand(const CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle;
+ sendCodaMessage(MessageWithReply, FileSystemService, "fstat", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemReadCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ unsigned int offset,
+ unsigned int size,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle << '\0' << offset << '\0' << size;
+ sendCodaMessage(MessageWithReply, FileSystemService, "read", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemWriteCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ const QByteArray &dataIn,
+ unsigned offset,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle << '\0' << offset << '\0' << dataIn.toBase64();
+ sendCodaMessage(MessageWithReply, FileSystemService, "write", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendFileSystemCloseCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << handle;
+ sendCodaMessage(MessageWithReply, FileSystemService, "close", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallSilentInstallCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &file,
+ const QByteArray &targetDrive,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << file << '\0' << targetDrive;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "install", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallUIInstallCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &file,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << file;
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "installWithUI", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendSymbianInstallGetPackageInfoCommand(const Coda::CodaCallback &callBack,
+ const QList<quint32> &packages,
+ const QVariant &cookie)
+{
+ QByteArray data;
+ JsonInputStream str(data);
+ str << '[';
+ for (int i = 0; i < packages.count(); ++i) {
+ if (i)
+ str << ',';
+ QString pkgString;
+ pkgString.setNum(packages[i], 16);
+ str << pkgString;
+ }
+ str << ']';
+ sendCodaMessage(MessageWithReply, SymbianInstallService, "getPackageInfo", data, callBack, cookie);
+}
+
+void Coda::CodaDevice::sendDebugSessionControlSessionStartCommand(const Coda::CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, DebugSessionControl, "sessionStart", QByteArray(), callBack, cookie);
+}
+
+void Coda::CodaDevice::sendDebugSessionControlSessionEndCommand(const Coda::CodaCallback &callBack,
+ const QVariant &cookie)
+{
+ sendCodaMessage(MessageWithReply, DebugSessionControl, "sessionEnd ", QByteArray(), callBack, cookie);
+}
+
+} // namespace Coda
diff --git a/tools/runonphone/symbianutils/codadevice.h b/tools/runonphone/symbianutils/codadevice.h
new file mode 100644
index 0000000..253e8b2
--- /dev/null
+++ b/tools/runonphone/symbianutils/codadevice.h
@@ -0,0 +1,447 @@
+/****************************************************************************
+**
+** 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$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CODAENGINE_H
+#define CODAENGINE_H
+
+#include "symbianutils_global.h"
+#include "codamessage.h"
+#include "callback.h"
+#include "json.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVector>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+#include <QtCore/QDateTime>
+
+QT_BEGIN_NAMESPACE
+class QIODevice;
+class QTextStream;
+QT_END_NAMESPACE
+
+namespace Coda {
+
+struct CodaDevicePrivate;
+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 CodaCommandError). */
+
+/* Error code return in 'R' reply to command
+ * (see top of 'Services' documentation). */
+struct SYMBIANUTILS_EXPORT CodaCommandError {
+ CodaCommandError();
+ void clear();
+ bool isError() const;
+ operator bool() const { return isError(); }
+ QString toString() const;
+ void write(QTextStream &str) const;
+ bool parse(const QVector<JsonValue> &values);
+
+ quint64 timeMS; // Since 1.1.1970
+ qint64 code;
+ QByteArray format; // message
+ // 'Alternative' meaning, like altOrg="POSIX"/altCode=<some errno>
+ QByteArray alternativeOrganization;
+ qint64 alternativeCode;
+};
+
+/* Answer to a Tcf command passed to the callback. */
+struct SYMBIANUTILS_EXPORT CodaCommandResult {
+ enum Type
+ {
+ SuccessReply, // 'R' and no error -> all happy.
+ CommandErrorReply, // 'R' with CodaCommandError received
+ ProgressReply, // 'P', progress indicator
+ FailReply // 'N' Protocol NAK, severe error
+ };
+
+ explicit CodaCommandResult(Type t = SuccessReply);
+ explicit CodaCommandResult(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; }
+
+ static QDateTime tcfTimeToQDateTime(quint64 tcfTimeMS);
+
+ Type type;
+ Services service;
+ QByteArray request;
+ CodaCommandError commandError;
+ QVector<JsonValue> values;
+ QVariant cookie;
+};
+
+// Response to stat/fstat
+struct SYMBIANUTILS_EXPORT CodaStatResponse
+{
+ CodaStatResponse();
+
+ quint64 size;
+ QDateTime modTime;
+ QDateTime accessTime;
+};
+
+typedef trk::Callback<const CodaCommandResult &> CodaCallback;
+
+/* CodaDevice: 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
+ * CodaCommandResult and an opaque QVariant cookie. In addition, events are emitted.
+ *
+ * CODA notes:
+ * - Commands are accepted only after receiving the Locator Hello event
+ * - Serial communication initiation sequence:
+ * Send serial ping from host sendSerialPing() -> receive pong response with
+ * version information -> Send Locator Hello Event -> Receive Locator Hello Event
+ * -> Commands are accepted.
+ * - WLAN communication initiation sequence:
+ * Receive Locator Hello Event from CODA -> Commands are accepted.
+ */
+
+class SYMBIANUTILS_EXPORT CodaDevice : public QObject
+{
+ Q_PROPERTY(unsigned verbose READ verbose WRITE setVerbose)
+ Q_PROPERTY(bool serialFrame READ serialFrame WRITE setSerialFrame)
+ Q_OBJECT
+public:
+ // Flags for FileSystem:open
+ enum FileSystemOpenFlags
+ {
+ FileSystem_TCF_O_READ = 0x00000001,
+ FileSystem_TCF_O_WRITE = 0x00000002,
+ FileSystem_TCF_O_APPEND = 0x00000004,
+ FileSystem_TCF_O_CREAT = 0x00000008,
+ FileSystem_TCF_O_TRUNC = 0x00000010,
+ FileSystem_TCF_O_EXCL = 0x00000020
+ };
+
+ enum MessageType
+ {
+ MessageWithReply,
+ MessageWithoutReply, /* Non-standard: "Settings:set" command does not reply */
+ NoopMessage
+ };
+
+ typedef QSharedPointer<QIODevice> IODevicePtr;
+
+ explicit CodaDevice(QObject *parent = 0);
+ virtual ~CodaDevice();
+
+ unsigned verbose() const;
+ bool serialFrame() const;
+ void setSerialFrame(bool);
+
+ // Mapping of register names to indices for multi-requests.
+ // Register names can be retrieved via 'Registers:getChildren' (requires
+ // context id to be stripped).
+ QVector<QByteArray> registerNames() const;
+ void setRegisterNames(const QVector<QByteArray>& n);
+
+ IODevicePtr device() const;
+ IODevicePtr takeDevice();
+ void setDevice(const IODevicePtr &dp);
+
+ // Serial Only: Initiate communication. Will emit serialPong() signal with version.
+ void sendSerialPing(bool pingOnly = false);
+
+ // Send with parameters from string (which may contain '\0').
+ void sendCodaMessage(MessageType mt, Services service, const char *command,
+ const char *commandParameters, int commandParametersLength,
+ const CodaCallback &callBack = CodaCallback(),
+ const QVariant &cookie = QVariant());
+
+ void sendCodaMessage(MessageType mt, Services service, const char *command,
+ const QByteArray &commandParameters,
+ const CodaCallback &callBack = CodaCallback(),
+ const QVariant &cookie = QVariant());
+
+ // Convenience messages: Start a process
+ void sendProcessStartCommand(const CodaCallback &callBack,
+ const QString &binary,
+ unsigned uid,
+ QStringList arguments = QStringList(),
+ QString workingDirectory = QString(),
+ bool debugControl = true,
+ const QStringList &additionalLibraries = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Just launch a process, don't attempt to attach the debugger to it
+ void sendRunProcessCommand(const CodaCallback &callBack,
+ const QString &processName,
+ QStringList arguments = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // Preferred over Processes:Terminate by TCF TRK.
+ void sendRunControlTerminateCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendProcessTerminateCommand(const CodaCallback &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 CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ // Resume / Step (see RunControlResumeMode).
+ void sendRunControlResumeCommand(const CodaCallback &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 CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsAddCommand(const CodaCallback &callBack,
+ const Breakpoint &b,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsRemoveCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &id,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QByteArray &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+ void sendBreakpointsEnableCommand(const CodaCallback &callBack,
+ const QVector<QByteArray> &id,
+ bool enable,
+ const QVariant &cookie = QVariant());
+
+
+ void sendMemoryGetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, quint64 size,
+ const QVariant &cookie = QVariant());
+
+ void sendMemorySetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ quint64 start, const QByteArray& data,
+ const QVariant &cookie = QVariant());
+
+ // Get register names (children of context).
+ // It is possible to recurse from thread id down to single registers.
+ void sendRegistersGetChildrenCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ const QVariant &cookie = QVariant());
+
+ // Register get
+ void sendRegistersGetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray id,
+ const QVariant &cookie);
+
+ void sendRegistersGetMCommand(const CodaCallback &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 CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned start, unsigned count);
+
+ // Set register
+ void sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ QByteArray ids,
+ const QByteArray &value, // binary value
+ const QVariant &cookie = QVariant());
+ // Set register
+ void sendRegistersSetCommand(const CodaCallback &callBack,
+ const QByteArray &contextId,
+ unsigned registerNumber,
+ const QByteArray &value, // binary value
+ const QVariant &cookie = QVariant());
+
+ // File System
+ void sendFileSystemOpenCommand(const CodaCallback &callBack,
+ const QByteArray &name,
+ unsigned flags = FileSystem_TCF_O_READ,
+ const QVariant &cookie = QVariant());
+
+ void sendFileSystemFstatCommand(const CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie = QVariant());
+
+ void sendFileSystemReadCommand(const Coda::CodaCallback &callBack,
+ const QByteArray &handle,
+ unsigned int offset,
+ unsigned int size,
+ const QVariant &cookie = QVariant());
+
+ void sendFileSystemWriteCommand(const CodaCallback &callBack,
+ const QByteArray &handle,
+ const QByteArray &data,
+ unsigned offset = 0,
+ const QVariant &cookie = QVariant());
+
+ void sendFileSystemCloseCommand(const CodaCallback &callBack,
+ const QByteArray &handle,
+ const QVariant &cookie = QVariant());
+
+ // Symbian Install
+ void sendSymbianInstallSilentInstallCommand(const CodaCallback &callBack,
+ const QByteArray &file,
+ const QByteArray &targetDrive,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianInstallUIInstallCommand(const CodaCallback &callBack,
+ const QByteArray &file,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianInstallGetPackageInfoCommand(const Coda::CodaCallback &callBack,
+ const QList<quint32> &packages,
+ const QVariant &cookie = QVariant());
+
+ void sendLoggingAddListenerCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianUninstallCommand(const Coda::CodaCallback &callBack,
+ const quint32 package,
+ const QVariant &cookie = QVariant());
+
+ // SymbianOs Data
+ void sendSymbianOsDataGetThreadsCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianOsDataFindProcessesCommand(const CodaCallback &callBack,
+ const QByteArray &processName,
+ const QByteArray &uid,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianOsDataGetQtVersionCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianOsDataGetRomInfoCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ void sendSymbianOsDataGetHalInfoCommand(const CodaCallback &callBack,
+ const QStringList &keys = QStringList(),
+ const QVariant &cookie = QVariant());
+
+ // DebugSessionControl
+ void sendDebugSessionControlSessionStartCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ void sendDebugSessionControlSessionEndCommand(const CodaCallback &callBack,
+ const QVariant &cookie = QVariant());
+
+ // Settings
+ void sendSettingsEnableLogCommand();
+
+ void writeCustomData(char protocolId, const QByteArray &aData);
+
+ static QByteArray parseMemoryGet(const CodaCommandResult &r);
+ static QVector<QByteArray> parseRegisterGetChildren(const CodaCommandResult &r);
+ static CodaStatResponse parseStat(const CodaCommandResult &r);
+
+signals:
+ void genericTcfEvent(int service, const QByteArray &name, const QVector<JsonValue> &value);
+ void tcfEvent(const Coda::CodaEvent &knownEvent);
+ void unknownEvent(uchar protocolId, const QByteArray& data);
+ void serialPong(const QString &codaVersion);
+
+ void logMessage(const QString &);
+ void error(const QString &);
+
+public slots:
+ void setVerbose(unsigned v);
+
+private slots:
+ void slotDeviceError();
+ void slotDeviceSocketStateChanged();
+ void slotDeviceReadyRead();
+
+private:
+ void deviceReadyReadSerial();
+ void deviceReadyReadTcp();
+
+ bool checkOpen();
+ void checkSendQueue();
+ void writeMessage(QByteArray data, bool ensureTerminating0 = true);
+ void emitLogMessage(const QString &);
+ inline int parseMessage(const QByteArray &);
+ void processMessage(const QByteArray &message);
+ inline void processSerialMessage(const QByteArray &message);
+ int parseTcfCommandReply(char type, const QVector<QByteArray> &tokens);
+ int parseTcfEvent(const QVector<QByteArray> &tokens);
+
+private:
+ QPair<int, int> findSerialHeader(QByteArray &in);
+ CodaDevicePrivate *d;
+};
+
+} // namespace Coda
+
+#endif // CODAENGINE_H
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.cpp b/tools/runonphone/symbianutils/codamessage.cpp
index 9e9c16c..13fbcf2 100644
--- a/tools/runonphone/symbianutils/tcftrkmessage.cpp
+++ b/tools/runonphone/symbianutils/codamessage.cpp
@@ -39,7 +39,7 @@
**
****************************************************************************/
-#include "tcftrkmessage.h"
+#include "codamessage.h"
#include "json.h"
#include <QtCore/QString>
@@ -48,10 +48,11 @@
// Names matching the enum
static const char *serviceNamesC[] =
{ "Locator", "RunControl", "Processes", "Memory", "Settings", "Breakpoints",
- "Registers", "SimpleRegisters",
+ "Registers", "Logging", "FileSystem", "SymbianInstall", "SymbianOSData",
+ "DebugSessionControl",
"UnknownService"};
-namespace tcftrk {
+namespace Coda {
SYMBIANUTILS_EXPORT QString joinByteArrays(const QVector<QByteArray> &a, char sep)
{
@@ -341,7 +342,7 @@ QString Breakpoint::toString() const
JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
{
if (b.contextIds.isEmpty())
- qWarning("tcftrk::Breakpoint: No context ids specified");
+ qWarning("Coda::Breakpoint: No context ids specified");
str << '{' << "ID" << ':' << QString::fromUtf8(b.id) << ','
<< "BreakpointType" << ':' << breakPointTypesC[b.type] << ','
@@ -356,27 +357,27 @@ JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b)
}
// --- Events
-TcfTrkEvent::TcfTrkEvent(Type type) : m_type(type)
+CodaEvent::CodaEvent(Type type) : m_type(type)
{
}
-TcfTrkEvent::~TcfTrkEvent()
+CodaEvent::~CodaEvent()
{
}
-TcfTrkEvent::Type TcfTrkEvent::type() const
+CodaEvent::Type CodaEvent::type() const
{
return m_type;
}
-QString TcfTrkEvent::toString() const
+QString CodaEvent::toString() const
{
return QString();
}
static const char sharedLibrarySuspendReasonC[] = "Shared Library";
-TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
+CodaEvent *CodaEvent::parseEvent(Services s, const QByteArray &nameBA, const QVector<JsonValue> &values)
{
switch (s) {
case LocatorService:
@@ -384,7 +385,7 @@ TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const
QStringList services;
foreach (const JsonValue &jv, values.front().children())
services.push_back(QString::fromUtf8(jv.data()));
- return new TcfTrkLocatorHelloEvent(services);
+ return new CodaLocatorHelloEvent(services);
}
break;
case RunControlService:
@@ -395,67 +396,96 @@ TcfTrkEvent *TcfTrkEvent::parseEvent(Services s, const QByteArray &nameBA, const
const QByteArray idBA = values.at(0).data();
const quint64 pc = values.at(1).data().toULongLong();
const QByteArray reasonBA = values.at(2).data();
+ QByteArray messageBA;
// 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 CodaRunControlModuleLoadContextSuspendedEvent(idBA, reasonBA, pc, info);
+ } else {
+ // hash containing a 'message'-key with a verbose crash message.
+ if (values.at(3).type() == JsonValue::Object && values.at(3).childCount()
+ && values.at(3).children().at(0).type() == JsonValue::String)
+ messageBA = values.at(3).children().at(0).data();
}
- return new TcfTrkRunControlContextSuspendedEvent(idBA, reasonBA, pc);
+ return new CodaRunControlContextSuspendedEvent(idBA, reasonBA, messageBA, pc);
} // "contextSuspended"
if (nameBA == "contextAdded")
- return TcfTrkRunControlContextAddedEvent::parseEvent(values);
+ return CodaRunControlContextAddedEvent::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);
+ return new CodaRunControlContextRemovedEvent(ids);
}
break;
+ case LoggingService:
+ if ((nameBA == "writeln" || nameBA == "write" /*not yet used*/) && values.size() >= 2)
+ return new CodaLoggingWriteEvent(values.at(0).data(), values.at(1).data());
+ break;
+ case ProcessesService:
+ if (nameBA == "exited" && values.size() >= 2)
+ return new CodaProcessExitedEvent(values.at(0).data());
+ break;
default:
break;
}
return 0;
}
-// -------------- TcfTrkServiceHelloEvent
-TcfTrkLocatorHelloEvent::TcfTrkLocatorHelloEvent(const QStringList &s) :
- TcfTrkEvent(LocatorHello),
+// -------------- CodaServiceHelloEvent
+CodaLocatorHelloEvent::CodaLocatorHelloEvent(const QStringList &s) :
+ CodaEvent(LocatorHello),
m_services(s)
{
}
-QString TcfTrkLocatorHelloEvent::toString() const
+QString CodaLocatorHelloEvent::toString() const
{
return QLatin1String("ServiceHello: ") + m_services.join(QLatin1String(", "));
}
-// -------------- TcfTrkIdEvent
-TcfTrkIdEvent::TcfTrkIdEvent(Type t, const QByteArray &id) :
- TcfTrkEvent(t), m_id(id)
+// -------------- Logging event
+
+CodaLoggingWriteEvent::CodaLoggingWriteEvent(const QByteArray &console, const QByteArray &message) :
+ CodaEvent(LoggingWriteEvent), m_console(console), m_message(message)
+{
+}
+
+QString CodaLoggingWriteEvent::toString() const
+{
+ QByteArray msgBA = m_console;
+ msgBA += ": ";
+ msgBA += m_message;
+ return QString::fromUtf8(msgBA);
+}
+
+// -------------- CodaIdEvent
+CodaIdEvent::CodaIdEvent(Type t, const QByteArray &id) :
+ CodaEvent(t), m_id(id)
{
}
-// ---------- TcfTrkIdsEvent
-TcfTrkIdsEvent::TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids) :
- TcfTrkEvent(t), m_ids(ids)
+// ---------- CodaIdsEvent
+CodaIdsEvent::CodaIdsEvent(Type t, const QVector<QByteArray> &ids) :
+ CodaEvent(t), m_ids(ids)
{
}
-QString TcfTrkIdsEvent::joinedIdString(const char sep) const
+QString CodaIdsEvent::joinedIdString(const char sep) const
{
return joinByteArrays(m_ids, sep);
}
-// ---------------- TcfTrkRunControlContextAddedEvent
-TcfTrkRunControlContextAddedEvent::TcfTrkRunControlContextAddedEvent(const RunControlContexts &c) :
- TcfTrkEvent(RunControlContextAdded), m_contexts(c)
+// ---------------- CodaRunControlContextAddedEvent
+CodaRunControlContextAddedEvent::CodaRunControlContextAddedEvent(const RunControlContexts &c) :
+ CodaEvent(RunControlContextAdded), m_contexts(c)
{
}
-TcfTrkRunControlContextAddedEvent
- *TcfTrkRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
+CodaRunControlContextAddedEvent
+ *CodaRunControlContextAddedEvent::parseEvent(const QVector<JsonValue> &values)
{
// Parse array of contexts
if (values.size() < 1 || values.front().type() != JsonValue::Array)
@@ -467,10 +497,10 @@ TcfTrkRunControlContextAddedEvent
if (context.parse(v))
contexts.push_back(context);
}
- return new TcfTrkRunControlContextAddedEvent(contexts);
+ return new CodaRunControlContextAddedEvent(contexts);
}
-QString TcfTrkRunControlContextAddedEvent::toString() const
+QString CodaRunControlContextAddedEvent::toString() const
{
QString rc;
QTextStream str(&rc);
@@ -484,42 +514,45 @@ QString TcfTrkRunControlContextAddedEvent::toString() const
return rc;
}
-// --------------- TcfTrkRunControlContextRemovedEvent
-TcfTrkRunControlContextRemovedEvent::TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
- TcfTrkIdsEvent(RunControlContextRemoved, ids)
+// --------------- CodaRunControlContextRemovedEvent
+CodaRunControlContextRemovedEvent::CodaRunControlContextRemovedEvent(const QVector<QByteArray> &ids) :
+ CodaIdsEvent(RunControlContextRemoved, ids)
{
}
-QString TcfTrkRunControlContextRemovedEvent::toString() const
+QString CodaRunControlContextRemovedEvent::toString() const
{
return QLatin1String("RunControl: Removed contexts '") + joinedIdString() + ("'.");
}
-// --------------- TcfTrkRunControlContextSuspendedEvent
-TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+// --------------- CodaRunControlContextSuspendedEvent
+CodaRunControlContextSuspendedEvent::CodaRunControlContextSuspendedEvent(const QByteArray &id,
const QByteArray &reason,
+ const QByteArray &message,
quint64 pc) :
- TcfTrkIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason)
+ CodaIdEvent(RunControlSuspended, id), m_pc(pc), m_reason(reason), m_message(message)
{
}
-TcfTrkRunControlContextSuspendedEvent::TcfTrkRunControlContextSuspendedEvent(Type t,
+CodaRunControlContextSuspendedEvent::CodaRunControlContextSuspendedEvent(Type t,
const QByteArray &id,
const QByteArray &reason,
quint64 pc) :
- TcfTrkIdEvent(t, id), m_pc(pc), m_reason(reason)
+ CodaIdEvent(t, id), m_pc(pc), m_reason(reason)
{
}
-void TcfTrkRunControlContextSuspendedEvent::format(QTextStream &str) const
+void CodaRunControlContextSuspendedEvent::format(QTextStream &str) const
{
str.setIntegerBase(16);
str << "RunControl: '" << idString() << "' suspended at 0x"
<< m_pc << ": '" << m_reason << "'.";
str.setIntegerBase(10);
+ if (!m_message.isEmpty())
+ str << " (" <<m_message << ')';
}
-QString TcfTrkRunControlContextSuspendedEvent::toString() const
+QString CodaRunControlContextSuspendedEvent::toString() const
{
QString rc;
QTextStream str(&rc);
@@ -527,36 +560,46 @@ QString TcfTrkRunControlContextSuspendedEvent::toString() const
return rc;
}
-TcfTrkRunControlContextSuspendedEvent::Reason TcfTrkRunControlContextSuspendedEvent::reason() const
+CodaRunControlContextSuspendedEvent::Reason CodaRunControlContextSuspendedEvent::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"))
+ if (m_reason.contains("Exception") || m_reason.contains("panick"))
return Crash;
return Other;
}
-TcfTrkRunControlModuleLoadContextSuspendedEvent::TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+CodaRunControlModuleLoadContextSuspendedEvent::CodaRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
const QByteArray &reason,
quint64 pc,
const ModuleLoadEventInfo &mi) :
- TcfTrkRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
+ CodaRunControlContextSuspendedEvent(RunControlModuleLoadSuspended, id, reason, pc),
m_mi(mi)
{
}
-QString TcfTrkRunControlModuleLoadContextSuspendedEvent::toString() const
+QString CodaRunControlModuleLoadContextSuspendedEvent::toString() const
{
QString rc;
QTextStream str(&rc);
- TcfTrkRunControlContextSuspendedEvent::format(str);
+ CodaRunControlContextSuspendedEvent::format(str);
str << ' ';
m_mi.format(str);
return rc;
}
+// -------------- CodaIdEvent
+CodaProcessExitedEvent::CodaProcessExitedEvent(const QByteArray &id) :
+ CodaEvent(ProcessExitedEvent), m_id(id)
+{
+}
+
+QString CodaProcessExitedEvent::toString() const
+{
+ return QString("Process \"%1\" exited").arg(idString());
+}
-} // namespace tcftrk
+} // namespace Coda
diff --git a/tools/runonphone/symbianutils/tcftrkmessage.h b/tools/runonphone/symbianutils/codamessage.h
index cee8584..68347cb 100644
--- a/tools/runonphone/symbianutils/tcftrkmessage.h
+++ b/tools/runonphone/symbianutils/codamessage.h
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#ifndef TRCFTRKMESSAGE_H
-#define TRCFTRKMESSAGE_H
+#ifndef CODAMESSAGE_H
+#define CODAMESSAGE_H
#include "symbianutils_global.h"
@@ -51,7 +51,7 @@ QT_BEGIN_NAMESPACE
class QTextStream;
QT_END_NAMESPACE
-namespace tcftrk {
+namespace Coda {
class JsonValue;
class JsonInputStream;
@@ -61,10 +61,14 @@ enum Services {
RunControlService,
ProcessesService,
MemoryService,
- SettingsService, // non-standard, trk specific
+ SettingsService, // non-standard, CODA specific
BreakpointsService,
RegistersService,
- SimpleRegistersService, // non-standard, trk specific
+ LoggingService, // non-standard, CODA specific
+ FileSystemService,
+ SymbianInstallService, // non-standard, CODA specific
+ SymbianOSData, // non-standard, CODA specific
+ DebugSessionControl, // non-standard, CODA specific
UnknownService
}; // Note: Check string array 'serviceNamesC' of same size when modifying this.
@@ -138,7 +142,7 @@ struct SYMBIANUTILS_EXPORT ModuleLoadEventInfo {
bool requireResume;
};
-// Breakpoint as supported by TcfTrk source June 2010
+// Breakpoint as supported by Coda source June 2010
// TODO: Add watchpoints,etc once they are implemented
struct SYMBIANUTILS_EXPORT Breakpoint {
enum Type { Software, Hardware, Auto };
@@ -162,8 +166,8 @@ struct SYMBIANUTILS_EXPORT Breakpoint {
SYMBIANUTILS_EXPORT JsonInputStream &operator<<(JsonInputStream &str, const Breakpoint &b);
// Event hierarchy
-class SYMBIANUTILS_EXPORT TcfTrkEvent {
- Q_DISABLE_COPY(TcfTrkEvent)
+class SYMBIANUTILS_EXPORT CodaEvent {
+ Q_DISABLE_COPY(CodaEvent)
public:
enum Type { None,
LocatorHello,
@@ -172,40 +176,57 @@ public:
RunControlSuspended,
RunControlBreakpointSuspended,
RunControlModuleLoadSuspended,
- RunControlResumed
+ RunControlResumed,
+ LoggingWriteEvent, // Non-standard
+ ProcessExitedEvent // Non-standard
};
- virtual ~TcfTrkEvent();
+ virtual ~CodaEvent();
Type type() const;
virtual QString toString() const;
- static TcfTrkEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
+ static CodaEvent *parseEvent(Services s, const QByteArray &name, const QVector<JsonValue> &val);
protected:
- explicit TcfTrkEvent(Type type = None);
+ explicit CodaEvent(Type type = None);
private:
const Type m_type;
};
// ServiceHello
-class SYMBIANUTILS_EXPORT TcfTrkLocatorHelloEvent : public TcfTrkEvent {
+class SYMBIANUTILS_EXPORT CodaLocatorHelloEvent : public CodaEvent {
public:
- explicit TcfTrkLocatorHelloEvent(const QStringList &);
+ explicit CodaLocatorHelloEvent(const QStringList &);
- const QStringList &services() { return m_services; }
+ const QStringList &services() const { return m_services; }
virtual QString toString() const;
private:
QStringList m_services;
};
+// Logging event (non-standard, CODA specific)
+class SYMBIANUTILS_EXPORT CodaLoggingWriteEvent : public CodaEvent {
+public:
+ explicit CodaLoggingWriteEvent(const QByteArray &console, const QByteArray &message);
+
+ QByteArray message() const { return m_message; }
+ QByteArray console() const { return m_console; }
+
+ virtual QString toString() const;
+
+private:
+ const QByteArray m_console;
+ const QByteArray m_message;
+};
+
// Base for events that just have one id as parameter
// (simple suspend)
-class SYMBIANUTILS_EXPORT TcfTrkIdEvent : public TcfTrkEvent {
+class SYMBIANUTILS_EXPORT CodaIdEvent : public CodaEvent {
protected:
- explicit TcfTrkIdEvent(Type t, const QByteArray &id);
+ explicit CodaIdEvent(Type t, const QByteArray &id);
public:
QByteArray id() const { return m_id; }
QString idString() const { return QString::fromUtf8(m_id); }
@@ -216,9 +237,9 @@ private:
// Base for events that just have some ids as parameter
// (context removed)
-class SYMBIANUTILS_EXPORT TcfTrkIdsEvent : public TcfTrkEvent {
+class SYMBIANUTILS_EXPORT CodaIdsEvent : public CodaEvent {
protected:
- explicit TcfTrkIdsEvent(Type t, const QVector<QByteArray> &ids);
+ explicit CodaIdsEvent(Type t, const QVector<QByteArray> &ids);
public:
QVector<QByteArray> ids() const { return m_ids; }
@@ -229,44 +250,46 @@ private:
};
// RunControlContextAdded
-class SYMBIANUTILS_EXPORT TcfTrkRunControlContextAddedEvent : public TcfTrkEvent {
+class SYMBIANUTILS_EXPORT CodaRunControlContextAddedEvent : public CodaEvent {
public:
typedef QVector<RunControlContext> RunControlContexts;
- explicit TcfTrkRunControlContextAddedEvent(const RunControlContexts &c);
+ explicit CodaRunControlContextAddedEvent(const RunControlContexts &c);
const RunControlContexts &contexts() const { return m_contexts; }
virtual QString toString() const;
- static TcfTrkRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
+ static CodaRunControlContextAddedEvent *parseEvent(const QVector<JsonValue> &val);
private:
const RunControlContexts m_contexts;
};
// RunControlContextRemoved
-class SYMBIANUTILS_EXPORT TcfTrkRunControlContextRemovedEvent : public TcfTrkIdsEvent {
+class SYMBIANUTILS_EXPORT CodaRunControlContextRemovedEvent : public CodaIdsEvent {
public:
- explicit TcfTrkRunControlContextRemovedEvent(const QVector<QByteArray> &id);
+ explicit CodaRunControlContextRemovedEvent(const QVector<QByteArray> &id);
virtual QString toString() const;
};
// Simple RunControlContextSuspended (process/thread)
-class SYMBIANUTILS_EXPORT TcfTrkRunControlContextSuspendedEvent : public TcfTrkIdEvent {
+class SYMBIANUTILS_EXPORT CodaRunControlContextSuspendedEvent : public CodaIdEvent {
public:
enum Reason { BreakPoint, ModuleLoad, Crash, Other } ;
- explicit TcfTrkRunControlContextSuspendedEvent(const QByteArray &id,
+ explicit CodaRunControlContextSuspendedEvent(const QByteArray &id,
const QByteArray &reason,
+ const QByteArray &message,
quint64 pc = 0);
virtual QString toString() const;
quint64 pc() const { return m_pc; }
QByteArray reasonID() const { return m_reason; }
Reason reason() const;
+ QByteArray message() const { return m_message; }
protected:
- explicit TcfTrkRunControlContextSuspendedEvent(Type t,
+ explicit CodaRunControlContextSuspendedEvent(Type t,
const QByteArray &id,
const QByteArray &reason,
quint64 pc = 0);
@@ -275,12 +298,13 @@ protected:
private:
const quint64 m_pc;
const QByteArray m_reason;
+ const QByteArray m_message;
};
// RunControlContextSuspended due to module load
-class SYMBIANUTILS_EXPORT TcfTrkRunControlModuleLoadContextSuspendedEvent : public TcfTrkRunControlContextSuspendedEvent {
+class SYMBIANUTILS_EXPORT CodaRunControlModuleLoadContextSuspendedEvent : public CodaRunControlContextSuspendedEvent {
public:
- explicit TcfTrkRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
+ explicit CodaRunControlModuleLoadContextSuspendedEvent(const QByteArray &id,
const QByteArray &reason,
quint64 pc,
const ModuleLoadEventInfo &mi);
@@ -292,5 +316,18 @@ private:
const ModuleLoadEventInfo m_mi;
};
-} // namespace tcftrk
-#endif // TRCFTRKMESSAGE_H
+// Process exited event
+class SYMBIANUTILS_EXPORT CodaProcessExitedEvent : public CodaEvent {
+public:
+ explicit CodaProcessExitedEvent(const QByteArray &id);
+
+ QByteArray id() const { return m_id; }
+ QString idString() const { return QString::fromUtf8(m_id); }
+ virtual QString toString() const;
+
+private:
+ const QByteArray m_id;
+};
+
+} // namespace Coda
+#endif // CODAMESSAGE_H
diff --git a/tools/runonphone/symbianutils/json.cpp b/tools/runonphone/symbianutils/json.cpp
index a2d53ce..93f9395 100644
--- a/tools/runonphone/symbianutils/json.cpp
+++ b/tools/runonphone/symbianutils/json.cpp
@@ -49,6 +49,7 @@
#include <QtCore/QTextStream>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
+#include <QtCore/QVariant>
#include <ctype.h>
@@ -59,7 +60,7 @@
#define JDEBUG(s)
#endif
-namespace tcftrk {
+namespace Coda {
static void skipSpaces(const char *&from, const char *to)
{
@@ -100,6 +101,7 @@ QByteArray JsonValue::parseNumber(const char *&from, const char *to)
QByteArray JsonValue::parseCString(const char *&from, const char *to)
{
QByteArray result;
+ const char * const fromSaved = from;
JDEBUG("parseCString: " << QByteArray(from, to - from));
if (*from != '"') {
qDebug() << "JSON Parse Error, double quote expected";
@@ -117,7 +119,8 @@ QByteArray JsonValue::parseCString(const char *&from, const char *to)
if (*ptr == '\\') {
++ptr;
if (ptr == to) {
- qDebug() << "JSON Parse Error, unterminated backslash escape";
+ qWarning("JSON Parse Error, unterminated backslash escape in '%s'",
+ QByteArray(fromSaved, to - fromSaved).constData());
from = ptr; // So we don't hang
return QByteArray();
}
@@ -142,8 +145,24 @@ QByteArray JsonValue::parseCString(const char *&from, const char *to)
case 'v': *dst++ = '\v'; break;
case '"': *dst++ = '"'; break;
case '\\': *dst++ = '\\'; break;
- default:
- {
+ case 'u': { // 4 digit hex escape as in '\u000a'
+ if (end - src < 4) {
+ qWarning("JSON Parse Error, too few hex digits in \\u-escape in '%s' obtained from '%s'",
+ result.constData(), QByteArray(fromSaved, to - fromSaved).constData());
+ return QByteArray();
+ }
+ bool ok;
+ const uchar prod = QByteArray(src, 4).toUInt(&ok, 16);
+ if (!ok) {
+ qWarning("JSON Parse Error, invalid hex digits in \\u-escape in '%s' obtained from '%s'",
+ result.constData(), QByteArray(fromSaved, to - fromSaved).constData());
+ return QByteArray();
+ }
+ *dst++ = prod;
+ src += 4;
+ }
+ break;
+ default: { // Up to 3 decimal digits: Not sure if this is supported in JSON?
int chars = 0;
uchar prod = 0;
forever {
@@ -157,7 +176,8 @@ QByteArray JsonValue::parseCString(const char *&from, const char *to)
c = *src++;
}
if (!chars) {
- qDebug() << "JSON Parse Error, unrecognized backslash escape";
+ qWarning("JSON Parse Error, unrecognized backslash escape in string '%s' obtained from '%s'",
+ result.constData(), QByteArray(fromSaved, to - fromSaved).constData());
return QByteArray();
}
*dst++ = prod;
@@ -360,7 +380,7 @@ QByteArray JsonValue::toString(bool multiline, int indent) const
break;
case String:
if (!m_name.isEmpty())
- result += m_name + "=";
+ result += m_name + '=';
result += '"' + escapeCString(m_data) + '"';
break;
case Number:
@@ -380,30 +400,69 @@ QByteArray JsonValue::toString(bool multiline, int indent) const
if (multiline) {
result += "{\n";
dumpChildren(&result, multiline, indent + 1);
- result += '\n' + ind(indent) + "}";
+ result += '\n' + ind(indent) + '}';
} else {
- result += "{";
+ result += '{';
dumpChildren(&result, multiline, indent + 1);
- result += "}";
+ result += '}';
}
break;
case Array:
if (!m_name.isEmpty())
- result += m_name + "=";
+ result += m_name + '=';
if (multiline) {
result += "[\n";
dumpChildren(&result, multiline, indent + 1);
- result += '\n' + ind(indent) + "]";
+ result += '\n' + ind(indent) + ']';
} else {
- result += "[";
+ result += '[';
dumpChildren(&result, multiline, indent + 1);
- result += "]";
+ result += ']';
}
break;
}
return result;
}
+
+QVariant JsonValue::toVariant() const
+{
+ switch (m_type) {
+ case String:
+ return QString(m_data);
+ case Number: {
+ bool ok;
+ qint64 val = QString(m_data).toLongLong(&ok);
+ if (ok)
+ return val;
+ return QVariant();
+ }
+ case Object: {
+ QHash<QString, QVariant> hash;
+ for (int i = 0; i < m_children.size(); ++i) {
+ QString name(m_children[i].name());
+ QVariant val = m_children[i].toVariant();
+ hash.insert(name, val);
+ }
+ return hash;
+ }
+ case Array: {
+ QList<QVariant> list;
+ for (int i = 0; i < m_children.size(); ++i) {
+ list.append(m_children[i].toVariant());
+ }
+ return list;
+ }
+ case Boolean:
+ return data() == QByteArray("true");
+ case Invalid:
+ case NullObject:
+ default:
+ return QVariant();
+ }
+}
+
+
void JsonValue::fromString(const QByteArray &ba)
{
const char *from = ba.constBegin();
@@ -486,5 +545,5 @@ JsonInputStream &JsonInputStream::operator<<(bool b)
return *this;
}
-} // namespace tcftrk
+} // namespace Coda
diff --git a/tools/runonphone/symbianutils/json.h b/tools/runonphone/symbianutils/json.h