tools/runonphone/symbianutils/launcher.cpp
changeset 30 5dc02b23752f
parent 18 2f34d5167611
child 33 3e2da88830cd
--- a/tools/runonphone/symbianutils/launcher.cpp	Wed Jun 23 19:07:03 2010 +0300
+++ b/tools/runonphone/symbianutils/launcher.cpp	Tue Jul 06 15:10:48 2010 +0300
@@ -44,6 +44,7 @@
 #include "trkutils_p.h"
 #include "trkdevice.h"
 #include "bluetoothlistener.h"
+#include "symbiandevicemanager.h"
 
 #include <QtCore/QTimer>
 #include <QtCore/QDateTime>
@@ -53,6 +54,8 @@
 #include <QtCore/QFile>
 #include <QtCore/QScopedPointer>
 
+#include <cstdio>
+
 namespace trk {
 
 struct LauncherPrivate {
@@ -61,7 +64,8 @@
         QString destinationFileName;
         uint copyFileHandle;
         QScopedPointer<QByteArray> data;
-        int position;
+        qint64 position;
+        QScopedPointer<QFile> localFile;
     };
 
     explicit LauncherPrivate(const TrkDevicePtr &d);
@@ -75,6 +79,7 @@
     Session m_session; // global-ish data (process id, target information)
 
     CopyState m_copyState;
+    CopyState m_downloadState;
     QString m_fileName;
     QStringList m_commandLineArgs;
     QString m_installFileName;
@@ -100,12 +105,15 @@
     d(new LauncherPrivate(dev))
 {
     d->m_startupActions = startupActions;
-    connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));    
-    connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close()));
+    connect(d->m_device.data(), SIGNAL(messageReceived(trk::TrkResult)), this, SLOT(handleResult(trk::TrkResult)));
 }
 
 Launcher::~Launcher()
 {
+    // Destroyed before protocol was through: Close
+    if (d->m_closeDevice && d->m_device->isOpen())
+        d->m_device->close();
+    emit destroyed(d->m_device->port());
     logMessage("Shutting down.\n");
     delete d;
 }
@@ -154,6 +162,12 @@
     d->m_copyState.destinationFileName = dstName;
 }
 
+void Launcher::setDownloadFileName(const QString &srcName, const QString &dstName)
+{
+    d->m_downloadState.sourceFileName = srcName;
+    d->m_downloadState.destinationFileName = dstName;
+}
+
 void Launcher::setInstallFileName(const QString &name)
 {
     d->m_installFileName = name;
@@ -189,10 +203,26 @@
 {
     errorMessage->clear();
     if (d->m_verbose) {
-        const QString msg = QString::fromLatin1("Port=%1 Executable=%2 Arguments=%3 Package=%4 Remote Package=%5 Install file=%6")
-                            .arg(trkServerName(), d->m_fileName,
-                                 d->m_commandLineArgs.join(QString(QLatin1Char(' '))),
-                                 d->m_copyState.sourceFileName, d->m_copyState.destinationFileName, d->m_installFileName);
+        QString msg;
+        QTextStream str(&msg);
+        str.setIntegerBase(16);
+        str << "Actions=0x" << d->m_startupActions;
+        str.setIntegerBase(10);
+        str << " Port=" << trkServerName();
+        if (!d->m_fileName.isEmpty())
+            str << " Executable=" << d->m_fileName;
+        if (!d->m_commandLineArgs.isEmpty())
+            str << " Arguments= " << d->m_commandLineArgs.join(QString(QLatin1Char(' ')));
+        if (!d->m_copyState.sourceFileName.isEmpty())
+            str << " Package/Source=" << d->m_copyState.sourceFileName;
+        if (!d->m_copyState.destinationFileName.isEmpty())
+            str << " Remote Package/Destination=" << d->m_copyState.destinationFileName;
+        if (!d->m_downloadState.sourceFileName.isEmpty())
+            str << " Source=" << d->m_downloadState.sourceFileName;
+        if (!d->m_downloadState.destinationFileName.isEmpty())
+            str << " Destination=" << d->m_downloadState.destinationFileName;
+        if (!d->m_installFileName.isEmpty())
+            str << " Install file=" << d->m_installFileName;
         logMessage(msg);
     }
     if (d->m_startupActions & ActionCopy) {
@@ -214,11 +244,6 @@
     }
     if (!d->m_device->isOpen() && !d->m_device->open(errorMessage))
         return false;
-    if (d->m_closeDevice) {
-        connect(this, SIGNAL(finished()), d->m_device.data(), SLOT(close()));
-    } else {
-        disconnect(this, SIGNAL(finished()), d->m_device.data(), 0);
-    }
     setState(Connecting);
     // Set up the temporary 'waiting' state if we do not get immediate connection
     QTimer::singleShot(1000, this, SLOT(slotWaitingForTrk()));
@@ -252,6 +277,8 @@
         installRemotePackageSilently();
     else if (d->m_startupActions & ActionRun)
         startInferiorIfNeeded();
+    else if (d->m_startupActions & ActionDownload)
+        copyFileFromRemote();
 }
 
 void Launcher::setVerbose(int v)
@@ -266,6 +293,13 @@
         qDebug() << "LAUNCHER: " << qPrintable(msg);
 }
 
+void Launcher::handleFinished()
+{
+    if (d->m_closeDevice)
+        d->m_device->close();
+    emit finished();
+}
+
 void Launcher::terminate()
 {
     switch (state()) {
@@ -287,7 +321,7 @@
     case Connecting:
     case WaitingForTrk:
         setState(Disconnected);
-        emit finished();
+        handleFinished();
         break;
     }
 }
@@ -331,8 +365,22 @@
     QByteArray prefix = "READ BUF:                                       ";
     QByteArray str = result.toString().toUtf8();
     if (result.isDebugOutput) { // handle application output
-        logMessage("APPLICATION OUTPUT: " + result.data);
-        emit applicationOutputReceived(result.data);
+        QString msg;
+        if (result.multiplex == MuxTextTrace) {
+            if (result.data.length() > 8) {
+            quint64 timestamp = extractInt64(result.data) & 0x0FFFFFFFFFFFFFFFULL;
+            quint64 secs = timestamp / 1000000000;
+            quint64 ns = timestamp % 1000000000;
+            msg = QString("[%1.%2] %3").arg(secs).arg(ns).arg(QString(result.data.mid(8)));
+            logMessage("TEXT TRACE: " + msg);
+            }
+        } else {
+            logMessage("APPLICATION OUTPUT: " + result.data);
+            msg = result.data;
+        }
+        msg.replace("\r\n", "\n");
+        if(!msg.endsWith('\n')) msg.append('\n');
+        emit applicationOutputReceived(msg);
         return;
     }
     switch (result.code) {
@@ -410,7 +458,7 @@
             if (itemType == 0 // process
                 && result.data.size() >= 10
                 && d->m_session.pid == extractInt(result.data.data() + 6)) {
-                disconnectTrk();
+                    copyFileFromRemote();
             }
             break;
         }
@@ -446,7 +494,7 @@
     if (result.errorCode() || result.data.size() < 5) {
         if (d->m_startupActions == ActionPingOnly) {
             setState(Disconnected);
-            emit finished();
+            handleFinished();
         }
         return;
     }
@@ -455,31 +503,109 @@
     d->m_session.trkAppVersion.protocolMajor = result.data.at(3);
     d->m_session.trkAppVersion.protocolMinor = result.data.at(4);
     setState(DeviceDescriptionReceived);
+    const QString msg = deviceDescription();
+    emit deviceDescriptionReceived(trkServerName(), msg);
     // Ping mode: Log & Terminate
     if (d->m_startupActions == ActionPingOnly) {
-        qWarning("%s", qPrintable(deviceDescription()));
+        qWarning("%s", qPrintable(msg));
         setState(Disconnected);
-        emit finished();
+        handleFinished();
     }
 }
 
+static inline QString msgCannotOpenRemoteFile(const QString &fileName, const QString &message)
+{
+    return Launcher::tr("Cannot open remote file '%1': %2").arg(fileName, message);
+}
+
+static inline QString msgCannotOpenLocalFile(const QString &fileName, const QString &message)
+{
+    return Launcher::tr("Cannot open '%1': %2").arg(fileName, message);
+}
+
 void Launcher::handleFileCreation(const TrkResult &result)
 {
     if (result.errorCode() || result.data.size() < 6) {
-        emit canNotCreateFile(d->m_copyState.destinationFileName, result.errorString());
+        const QString msg = msgCannotOpenRemoteFile(d->m_copyState.destinationFileName, result.errorString());
+        logMessage(msg);
+        emit canNotCreateFile(d->m_copyState.destinationFileName, msg);
         disconnectTrk();
         return;
     }
     const char *data = result.data.data();
     d->m_copyState.copyFileHandle = extractInt(data + 2);
-    QFile file(d->m_copyState.sourceFileName);
-    file.open(QIODevice::ReadOnly);
+    const QString localFileName = d->m_copyState.sourceFileName;
+    QFile file(localFileName);
+    d->m_copyState.position = 0;
+    if (!file.open(QIODevice::ReadOnly)) {
+        const QString msg = msgCannotOpenLocalFile(localFileName, file.errorString());
+        logMessage(msg);
+        emit canNotOpenLocalFile(localFileName, msg);
+        closeRemoteFile(true);
+        disconnectTrk();
+        return;
+    }
     d->m_copyState.data.reset(new QByteArray(file.readAll()));
-    d->m_copyState.position = 0;
     file.close();
     continueCopying();
 }
 
+void Launcher::handleFileOpened(const TrkResult &result)
+{
+    if (result.errorCode() || result.data.size() < 6) {
+        const QString msg = msgCannotOpenRemoteFile(d->m_downloadState.sourceFileName, result.errorString());
+        logMessage(msg);
+        emit canNotOpenFile(d->m_downloadState.sourceFileName, msg);
+        disconnectTrk();
+        return;
+    }
+    d->m_downloadState.position = 0;
+    const QString localFileName = d->m_downloadState.destinationFileName;
+    bool opened = false;
+    if (localFileName == QLatin1String("-")) {
+        d->m_downloadState.localFile.reset(new QFile);
+        opened = d->m_downloadState.localFile->open(stdout, QFile::WriteOnly);
+    } else {
+        d->m_downloadState.localFile.reset(new QFile(localFileName));
+        opened = d->m_downloadState.localFile->open(QFile::WriteOnly | QFile::Truncate);
+    }
+    if (!opened) {
+        const QString msg = msgCannotOpenLocalFile(localFileName, d->m_downloadState.localFile->errorString());
+        logMessage(msg);
+        emit canNotOpenLocalFile(localFileName, msg);
+        closeRemoteFile(true);
+        disconnectTrk();
+    }
+    continueReading();
+}
+
+void Launcher::continueReading()
+{
+    QByteArray ba;
+    appendInt(&ba, d->m_downloadState.copyFileHandle, TargetByteOrder);
+    appendShort(&ba, 2048, TargetByteOrder);
+    d->m_device->sendTrkMessage(TrkReadFile, TrkCallback(this, &Launcher::handleRead), ba);
+}
+
+void Launcher::handleRead(const TrkResult &result)
+{
+    if (result.errorCode() || result.data.size() < 4) {
+        d->m_downloadState.localFile->close();
+        closeRemoteFile(true);
+        disconnectTrk();
+    } else {
+        int length = extractShort(result.data.data() + 2);
+        //TRK doesn't tell us the file length, so we need to keep reading until it returns 0 length
+        if (length > 0) {
+            d->m_downloadState.localFile->write(result.data.data() + 4, length);
+            continueReading();
+        } else {
+            closeRemoteFile(true);
+            disconnectTrk();
+        }
+    }
+}
+
 void Launcher::handleCopy(const TrkResult &result)
 {
     if (result.errorCode() || result.data.size() < 4) {
@@ -493,13 +619,14 @@
 
 void Launcher::continueCopying(uint lastCopiedBlockSize)
 {
-    int size = d->m_copyState.data->length();
+    qint64 size = d->m_copyState.data->length();
     d->m_copyState.position += lastCopiedBlockSize;
     if (size == 0)
         emit copyProgress(100);
     else {
-        int percent = qMin((d->m_copyState.position*100)/size, 100);
-        emit copyProgress(percent);
+        const qint64 hundred = 100;
+        const qint64 percent = qMin( (d->m_copyState.position * hundred) / size, hundred);
+        emit copyProgress(static_cast<int>(percent));
     }
     if (d->m_copyState.position < size) {
         QByteArray ba;
@@ -532,6 +659,8 @@
         installRemotePackageSilently();
     else if (d->m_startupActions & ActionRun)
         startInferiorIfNeeded();
+    else if (d->m_startupActions & ActionDownload)
+        copyFileFromRemote();
     else
         disconnectTrk();
 }
@@ -586,7 +715,7 @@
 {
     logMessage("   FINISHED: " + stringFromArray(result.data));
     setState(Disconnected);
-    emit finished();
+    handleFinished();
 }
 
 void Launcher::handleSupportMask(const TrkResult &result)
@@ -595,17 +724,18 @@
         return;
     const char *data = result.data.data() + 1;
 
-    QString str = QLatin1String("SUPPORTED: ");
-    for (int i = 0; i < 32; ++i) {
-        //str.append("  [" + formatByte(data[i]) + "]: ");
-        for (int j = 0; j < 8; ++j) {
-            if (data[i] & (1 << j)) {
-                str.append(QString::number(i * 8 + j, 16));
-                str.append(QLatin1Char(' '));
+    if (d->m_verbose > 1) {
+        QString str = QLatin1String("SUPPORTED: ");
+        for (int i = 0; i < 32; ++i) {
+            for (int j = 0; j < 8; ++j) {
+                if (data[i] & (1 << j)) {
+                    str.append(QString::number(i * 8 + j, 16));
+                    str.append(QLatin1Char(' '));
+                }
             }
         }
+        logMessage(str);
     }
-    logMessage(str);
 }
 
 void Launcher::cleanUp()
@@ -669,11 +799,20 @@
 {
     emit copyingStarted();
     QByteArray ba;
-    ba.append(char(10));
+    ba.append(char(10)); //kDSFileOpenWrite | kDSFileOpenBinary
     appendString(&ba, d->m_copyState.destinationFileName.toLocal8Bit(), TargetByteOrder, false);
     d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileCreation), ba);
 }
 
+void Launcher::copyFileFromRemote()
+{
+    emit copyingStarted();
+    QByteArray ba;
+    ba.append(char(9)); //kDSFileOpenRead | kDSFileOpenBinary
+    appendString(&ba, d->m_downloadState.sourceFileName.toLocal8Bit(), TargetByteOrder, false);
+    d->m_device->sendTrkMessage(TrkOpenFile, TrkCallback(this, &Launcher::handleFileOpened), ba);
+}
+
 void Launcher::installRemotePackageSilently()
 {
     emit installingStarted();
@@ -694,6 +833,8 @@
     }
     if (d->m_startupActions & ActionRun) {
         startInferiorIfNeeded();
+    } else if (d->m_startupActions & ActionDownload) {
+        copyFileFromRemote();
     } else {
         disconnectTrk();
     }
@@ -704,18 +845,14 @@
 {
     // It's not started yet
     QByteArray ba;
-    appendShort(&ba, 0, TargetByteOrder); // create new process
+    appendShort(&ba, 0, TargetByteOrder); // create new process (kDSOSProcessItem)
     ba.append(char(0)); // options - currently unused
-    if(arguments.isEmpty()) {
-        appendString(&ba, executable.toLocal8Bit(), TargetByteOrder);
-        return ba;
-    }
-    // Append full command line as one string (leading length information).
-    QByteArray commandLineBa;
-    commandLineBa.append(executable.toLocal8Bit());
-    commandLineBa.append('\0');
-    commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
-    appendString(&ba, commandLineBa, TargetByteOrder);
+    // One string consisting of binary terminated by '\0' and arguments terminated by '\0'
+    QByteArray commandLineBa = executable.toLocal8Bit();
+    commandLineBa.append(char(0));
+    if (!arguments.isEmpty())
+        commandLineBa.append(arguments.join(QString(QLatin1Char(' '))).toLocal8Bit());
+    appendString(&ba, commandLineBa, TargetByteOrder, true);
     return ba;
 }
 
@@ -737,4 +874,37 @@
     appendInt(&ba, tid, BigEndian);
     d->m_device->sendTrkMessage(TrkContinue, TrkCallback(), ba, "CONTINUE");
 }
+
+// Acquire a device from SymbianDeviceManager, return 0 if not available.
+Launcher *Launcher::acquireFromDeviceManager(const QString &serverName,
+                                             QObject *parent,
+                                             QString *errorMessage)
+{
+    SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+    const QSharedPointer<trk::TrkDevice> device = sdm->acquireDevice(serverName);
+    if (device.isNull()) {
+        *errorMessage = tr("Unable to acquire a device for port '%1'. It appears to be in use.").arg(serverName);
+        return 0;
+    }
+    // Wire release signal.
+    Launcher *rc = new Launcher(trk::Launcher::ActionPingOnly, device, parent);
+    connect(rc, SIGNAL(deviceDescriptionReceived(QString,QString)),
+            sdm, SLOT(setAdditionalInformation(QString,QString)));
+    connect(rc, SIGNAL(destroyed(QString)), sdm, SLOT(releaseDevice(QString)));
+    return rc;
+}
+
+// Preliminary release of device, disconnecting the signal.
+void Launcher::releaseToDeviceManager(Launcher *launcher)
+{
+    SymbianUtils::SymbianDeviceManager *sdm = SymbianUtils::SymbianDeviceManager::instance();
+    // Disentangle launcher and its device, remove connection from destroyed
+    launcher->setCloseDevice(false);
+    TrkDevice *device = launcher->trkDevice().data();
+    launcher->disconnect(device);
+    device->disconnect(launcher);
+    launcher->disconnect(sdm);
+    sdm->releaseDevice(launcher->trkServerName());
+}
+
 } // namespace trk