s60deploystep.cpp 32.7 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
7
8
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
con's avatar
con committed
9
** No Commercial Usage
10
**
con's avatar
con committed
11
12
13
14
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
15
16
17
18
19
20
21
22
23
24
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
con's avatar
con committed
25
26
27
28
29
30
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
31
32
33
**
**************************************************************************/

Tobias Hunger's avatar
Tobias Hunger committed
34
#include "s60deploystep.h"
35
36

#include "qt4buildconfiguration.h"
37
#include "s60deployconfiguration.h"
38
39
#include "s60devicerunconfiguration.h"
#include "s60runconfigbluetoothstarter.h"
40
#include "codadevice.h"
41
#include "trkruncontrol.h"
42
#include "codaruncontrol.h"
43
44

#include <coreplugin/icore.h>
Tobias Hunger's avatar
Tobias Hunger committed
45
#include <projectexplorer/buildsteplist.h>
46
#include <projectexplorer/target.h>
Tobias Hunger's avatar
Tobias Hunger committed
47
#include <projectexplorer/projectexplorerconstants.h>
48
49
#include <qt4projectmanagerconstants.h>

50
51
#include <symbianutils/launcher.h>
#include <symbianutils/symbiandevicemanager.h>
52
53
#include <utils/qtcassert.h>

54
55
56
57
58
59
60
#include <QtGui/QMessageBox>
#include <QtGui/QMainWindow>

#include <QtCore/QTimer>
#include <QtCore/QDateTime>
#include <QtCore/QDir>
#include <QtCore/QEventLoop>
61
62
63
64
#include <QtCore/QFile>
#include <QtCore/QFileInfo>

#include <QtNetwork/QTcpSocket>
65

66
67
68
using namespace ProjectExplorer;
using namespace Qt4ProjectManager::Internal;

69
enum { debug = 0 };
70

71
static const quint64  DEFAULT_CHUNK_SIZE = 40000;
72

73
namespace {
74
const char * const S60_DEPLOY_STEP_ID = "Qt4ProjectManager.S60DeployStep";
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
}

static inline bool ensureDeleteFile(const QString &fileName, QString *errorMessage)
{
    QFile file(fileName);
    if (file.exists() && !file.remove()) {
        *errorMessage = S60DeployStep::tr("Unable to remove existing file '%1': %2").arg(fileName, file.errorString());
        return false;
    }
    return true;
}

static inline bool renameFile(const QString &sourceName, const QString &targetName,
                              QString *errorMessage)
{
    if (sourceName == targetName)
        return true;
    if (!ensureDeleteFile(targetName, errorMessage))
        return false;
    QFile source(sourceName);
    if (!source.rename(targetName)) {
        *errorMessage = S60DeployStep::tr("Unable to rename file '%1' to '%2': %3")
97
                .arg(sourceName, targetName, source.errorString());
98
99
100
101
102
103
104
        return false;
    }
    return true;
}

// #pragma mark -- S60DeployStep

Tobias Hunger's avatar
Tobias Hunger committed
105
S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc,
106
                             S60DeployStep *bs):
107
108
109
110
111
112
113
114
    BuildStep(bc, bs), m_timer(0),
    m_releaseDeviceAfterLauncherFinish(bs->m_releaseDeviceAfterLauncherFinish),
    m_handleDeviceRemoval(bs->m_handleDeviceRemoval),
    m_launcher(0),
    m_eventLoop(0),
    m_state(StateUninit),
    m_putWriteOk(false),
    m_putLastChunkSize(0),
Friedemann Kleint's avatar
Friedemann Kleint committed
115
    m_putChunkSize(DEFAULT_CHUNK_SIZE),
116
117
    m_currentFileIndex(0),
    m_channel(bs->m_channel),
118
119
    m_deployCanceled(false),
    m_copyProgress(0)
120
{
121
    ctor();
122
123
}

Tobias Hunger's avatar
Tobias Hunger committed
124
S60DeployStep::S60DeployStep(ProjectExplorer::BuildStepList *bc):
125
126
127
128
129
130
131
132
    BuildStep(bc, QLatin1String(S60_DEPLOY_STEP_ID)), m_timer(0),
    m_releaseDeviceAfterLauncherFinish(true),
    m_handleDeviceRemoval(true),
    m_launcher(0),
    m_eventLoop(0),
    m_state(StateUninit),
    m_putWriteOk(false),
    m_putLastChunkSize(0),
Friedemann Kleint's avatar
Friedemann Kleint committed
133
    m_putChunkSize(DEFAULT_CHUNK_SIZE),
134
    m_currentFileIndex(0),
135
    m_channel(S60DeployConfiguration::CommunicationTrkSerialConnection),
136
137
    m_deployCanceled(false),
    m_copyProgress(0)
138
{
139
140
141
142
143
144
145
    ctor();
}

void S60DeployStep::ctor()
{
    //: Qt4 Deploystep display name
    setDefaultDisplayName(tr("Deploy"));
146
147
148
149
150
151
152
153
154
155
156
157
158
}

S60DeployStep::~S60DeployStep()
{
    delete m_timer;
    delete m_launcher;
    delete m_eventLoop;
}


bool S60DeployStep::init()
{
    Qt4BuildConfiguration *bc = static_cast<Qt4BuildConfiguration *>(buildConfiguration());
159
160
    S60DeployConfiguration *deployConfiguration = static_cast<S60DeployConfiguration *>(bc->target()->activeDeployConfiguration());
    if (!deployConfiguration)
161
        return false;
162
    m_serialPortName = deployConfiguration->serialPortName();
163
    m_serialPortFriendlyName = SymbianUtils::SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
164
165
166
167
    m_packageFileNamesWithTarget = deployConfiguration->packageFileNamesWithTargetInfo();
    m_signedPackages = deployConfiguration->signedPackages();
    m_installationDrive = deployConfiguration->installationDrive();
    m_silentInstall = deployConfiguration->silentInstall();
168

169
    switch (deployConfiguration->communicationChannel()) {
170
    case S60DeployConfiguration::CommunicationTrkSerialConnection:
171
        break;
172
    case S60DeployConfiguration::CommunicationCodaTcpConnection:
173
174
        m_address = deployConfiguration->deviceAddress();
        m_port = deployConfiguration->devicePort().toInt();
Pawel Polanski's avatar
Pawel Polanski committed
175
176
    default:
        break;
177
    }
178
    m_channel = deployConfiguration->communicationChannel();
179

180
181
182
183
184
    if (m_signedPackages.isEmpty()) {
        appendMessage(tr("No package has been found. Please specify at least one installation package."), true);
        return false;
    }

185
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
186
187
        QString message;
        if (m_launcher) {
188
            trk::Launcher::releaseToDeviceManager(m_launcher);
189
190
191
192
193
194
195
196
197
198
199
200
201
            delete m_launcher;
            m_launcher = 0;
        }

        m_launcher = trk::Launcher::acquireFromDeviceManager(m_serialPortName, this, &message);
        if (!message.isEmpty() || !m_launcher) {
            if (m_launcher)
                trk::Launcher::releaseToDeviceManager(m_launcher);
            delete m_launcher;
            m_launcher = 0;
            appendMessage(message, true);
            return true;
        }
202
203

        if (debug)
204
            m_launcher->setVerbose(debug);
205

206
207
208
209
210
211
212
213
214
215
216
217
        // Prompt the user to start up the Bluetooth connection
        const trk::PromptStartCommunicationResult src =
                S60RunConfigBluetoothStarter::startCommunication(m_launcher->trkDevice(),
                                                                 0, &message);
        if (src != trk::PromptStartCommunicationConnected) {
            if (!message.isEmpty())
                trk::Launcher::releaseToDeviceManager(m_launcher);
            delete m_launcher;
            m_launcher = 0;
            appendMessage(message, true);
            return false;
        }
218
    }
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
    return true;
}

QVariantMap S60DeployStep::toMap() const
{
    return BuildStep::toMap();
}

bool S60DeployStep::fromMap(const QVariantMap &map)
{
    return BuildStep::fromMap(map);
}

void S60DeployStep::appendMessage(const QString &error, bool isError)
{
    emit addOutput(error, isError?ProjectExplorer::BuildStep::ErrorMessageOutput:
                                  ProjectExplorer::BuildStep::MessageOutput);
}

238
239
240
241
242
243
244
245
246
247
void S60DeployStep::reportError(const QString &error)
{
    emit addOutput(error, ProjectExplorer::BuildStep::ErrorMessageOutput);
    emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
                                       error,
                                       QString(), -1,
                                       ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
    emit finished(false);
}

248
249
bool S60DeployStep::processPackageName(QString &errorMessage)
{
dt's avatar
dt committed
250
251
    for (int i = 0; i < m_signedPackages.count(); ++i) {
        QFileInfo packageInfo(m_signedPackages.at(i));
252
        // support for 4.6.1 and pre, where make sis creates 'targetname_armX_udeb.sis' instead of 'targetname.sis'
dt's avatar
dt committed
253
        QFileInfo packageWithTargetInfo(m_packageFileNamesWithTarget.at(i));
254
255
256
257
258
259
        // does the 4.6.1 version exist?
        if (packageWithTargetInfo.exists() && packageWithTargetInfo.isFile()) {
            // is the 4.6.1 version newer? (to guard against behavior change Qt Creator 1.3 --> 2.0)
            if (!packageInfo.exists() || packageInfo.lastModified() < packageWithTargetInfo.lastModified()) { //TODO change the QtCore
                // the 'targetname_armX_udeb.sis' crap exists and is new, rename it
                appendMessage(tr("Renaming new package '%1' to '%2'")
dt's avatar
dt committed
260
261
262
                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i)),
                                   QDir::toNativeSeparators(m_signedPackages.at(i))), false);
                return renameFile(m_packageFileNamesWithTarget.at(i), m_signedPackages.at(i), &errorMessage);
263
264
265
            } else {
                // the 'targetname_armX_udeb.sis' crap exists but is old, remove it
                appendMessage(tr("Removing old package '%1'")
dt's avatar
dt committed
266
                              .arg(QDir::toNativeSeparators(m_packageFileNamesWithTarget.at(i))),
267
                              false);
dt's avatar
dt committed
268
                ensureDeleteFile(m_packageFileNamesWithTarget.at(i), &errorMessage);
269
270
            }
        }
dt's avatar
dt committed
271
272
273
274
        if (!packageInfo.exists() || !packageInfo.isFile()) {
            errorMessage = tr("'%1': Package file not found").arg(m_signedPackages.at(i));
            return false;
        }
275
276
277
278
279
280
    }
    return true;
}

void S60DeployStep::start()
{
281
282
    QString errorMessage;

283
284
285
286
    bool serialConnection = (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection
            || m_channel == S60DeployConfiguration::CommunicationCodaSerialConnection);
    bool trkClient = m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection;

287
    if ((serialConnection && m_serialPortName.isEmpty())
288
289
290
291
292
293
            || (trkClient && !m_launcher)) {
        errorMessage = tr("No device is connected. Please connect a device and try again.");
        reportError(errorMessage);
        return;
    }
    if (!trkClient) {
294
        QTC_ASSERT(!m_codaDevice.data(), return);
295
        if (m_address.isEmpty() && !serialConnection) {
296
297
298
299
            errorMessage = tr("No address for a device has been defined. Please define an address and try again.");
            reportError(errorMessage);
            return;
        }
300
301
302
303
304
305
    }

    // make sure we have the right name of the sis package
    if (processPackageName(errorMessage)) {
        startDeployment();
    } else {
dt's avatar
dt committed
306
        errorMessage = tr("Failed to find package %1").arg(errorMessage);
307
        reportError(errorMessage);
308
309
310
311
312
313
        stop();
    }
}

void S60DeployStep::stop()
{
314
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
315
316
317
        if (m_launcher)
            m_launcher->terminate();
    } else {
318
319
        if (m_codaDevice) {
            disconnect(m_codaDevice.data(), 0, this, 0);
320
            SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(m_codaDevice);
321
322
323
        }
    }
    emit finished(false);
324
325
326
327
}

void S60DeployStep::setupConnections()
{
328
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
329
330
331
332
333
334
335
336
337
338
339
340
341
        connect(SymbianUtils::SymbianDeviceManager::instance(), SIGNAL(deviceRemoved(SymbianUtils::SymbianDevice)),
                this, SLOT(deviceRemoved(SymbianUtils::SymbianDevice)));
        connect(m_launcher, SIGNAL(finished()), this, SLOT(launcherFinished()));

        connect(m_launcher, SIGNAL(canNotConnect(QString)), this, SLOT(connectFailed(QString)));
        connect(m_launcher, SIGNAL(copyingStarted(QString)), this, SLOT(printCopyingNotice(QString)));
        connect(m_launcher, SIGNAL(canNotCreateFile(QString,QString)), this, SLOT(createFileFailed(QString,QString)));
        connect(m_launcher, SIGNAL(canNotWriteFile(QString,QString)), this, SLOT(writeFileFailed(QString,QString)));
        connect(m_launcher, SIGNAL(canNotCloseFile(QString,QString)), this, SLOT(closeFileFailed(QString,QString)));
        connect(m_launcher, SIGNAL(installingStarted(QString)), this, SLOT(printInstallingNotice(QString)));
        connect(m_launcher, SIGNAL(canNotInstall(QString,QString)), this, SLOT(installFailed(QString,QString)));
        connect(m_launcher, SIGNAL(installingFinished()), this, SLOT(printInstallingFinished()));
        connect(m_launcher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
342
        connect(m_launcher, SIGNAL(copyProgress(int)), this, SLOT(setCopyProgress(int)));
343
    } else {
344
345
        connect(m_codaDevice.data(), SIGNAL(error(QString)), this, SLOT(slotError(QString)));
        connect(m_codaDevice.data(), SIGNAL(logMessage(QString)), this, SLOT(slotTrkLogMessage(QString)));
346
        connect(m_codaDevice.data(), SIGNAL(tcfEvent(Coda::CodaEvent)), this, SLOT(slotCodaEvent(Coda::CodaEvent)), Qt::DirectConnection);
347
        connect(m_codaDevice.data(), SIGNAL(serialPong(QString)), this, SLOT(slotSerialPong(QString)));
348
349
        connect(this, SIGNAL(manualInstallation()), this, SLOT(showManualInstallationInfo()));
    }
350
351
352
353
}

void S60DeployStep::startDeployment()
{
354
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
355
356
        QTC_ASSERT(m_launcher, return);
    }
357
    QTC_ASSERT(!m_codaDevice.data(), return);
358

359
360
    // We need to defer setupConnections() in the case of CommunicationCodaSerialConnection
    //setupConnections();
361

362
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
363
        setupConnections();
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
        QStringList copyDst;
        foreach (const QString &signedPackage, m_signedPackages)
            copyDst << QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(QFileInfo(signedPackage).fileName());

        m_launcher->setCopyFileNames(m_signedPackages, copyDst);
        m_launcher->setInstallFileNames(copyDst);
        m_launcher->setInstallationDrive(m_installationDrive);
        m_launcher->setInstallationMode(m_silentInstall?trk::Launcher::InstallationModeSilentAndUser:
                                                        trk::Launcher::InstallationModeUser);
        m_launcher->addStartupActions(trk::Launcher::ActionCopyInstall);

        // TODO readd information about packages? msgListFile(m_signedPackage)
        appendMessage(tr("Deploying application to '%2'...").arg(m_serialPortFriendlyName), false);

        QString errorMessage;
        if (!m_launcher->startServer(&errorMessage)) {
            errorMessage = tr("Could not connect to phone on port '%1': %2\n"
                              "Check if the phone is connected and App TRK is running.").arg(m_serialPortName, errorMessage);
            reportError(errorMessage);
            stop();
        }
385
386
    } else if (m_channel == S60DeployConfiguration::CommunicationCodaSerialConnection) {
        appendMessage(tr("Deploying application to '%1'...").arg(m_serialPortFriendlyName), false);
387
        m_codaDevice = SymbianUtils::SymbianDeviceManager::instance()->getCodaDevice(m_serialPortName);       
388
        bool ok = m_codaDevice && m_codaDevice->device()->isOpen();
389
        if (!ok) {
390
            QString deviceError = tr("No such port");
391
392
            if (m_codaDevice)
                deviceError = m_codaDevice->device()->errorString();
393
            reportError(tr("Could not open serial device: %1").arg(deviceError));
394
395
396
            stop();
            return;
        }
397
        setupConnections();
398
        m_state = StateConnecting;
399
        m_codaDevice->sendSerialPing(false);
400
    } else {
401
402
        m_codaDevice = QSharedPointer<Coda::CodaDevice>(new Coda::CodaDevice);
        setupConnections();
403
        const QSharedPointer<QTcpSocket> codaSocket(new QTcpSocket);
404
        m_codaDevice->setDevice(codaSocket);
405
        codaSocket->connectToHost(m_address, m_port);
406
407
        m_state = StateConnecting;
        appendMessage(tr("Connecting to %1:%2...").arg(m_address).arg(m_port), false);
408
    }
409
    QTimer::singleShot(4000, this, SLOT(checkForTimeout()));
410
411
412
413
414
}

void S60DeployStep::run(QFutureInterface<bool> &fi)
{
    m_futureInterface = &fi;
415
    m_deployResult = true;
416
417
418
    m_deployCanceled = false;
    disconnect(this);

419
420
    m_futureInterface->setProgressRange(0, 100*m_signedPackages.count());

421
    if (m_channel == S60DeployConfiguration::CommunicationTrkSerialConnection) {
422
423
424
425
426
427
428
429
        connect(this, SIGNAL(finished(bool)), this, SLOT(launcherFinished(bool)));
        connect(this, SIGNAL(finishNow(bool)), this, SLOT(launcherFinished(bool)), Qt::DirectConnection);
    } else {
        connect(this, SIGNAL(finished(bool)), this, SLOT(deploymentFinished(bool)));
        connect(this, SIGNAL(finishNow(bool)), this, SLOT(deploymentFinished(bool)), Qt::DirectConnection);
        connect(this, SIGNAL(allFilesSent()), this, SLOT(startInstalling()), Qt::DirectConnection);
        connect(this, SIGNAL(allFilesInstalled()), this, SIGNAL(finished()), Qt::DirectConnection);
    }
430

431
432
    connect(this, SIGNAL(copyProgressChanged(int)), this, SLOT(updateProgress(int)));

433
434
435
436
437
438
439
440
441
442
    start();
    m_timer = new QTimer();
    connect(m_timer, SIGNAL(timeout()), this, SLOT(checkForCancel()), Qt::DirectConnection);
    m_timer->start(500);
    m_eventLoop = new QEventLoop();
    m_eventLoop->exec();
    m_timer->stop();
    delete m_timer;
    m_timer = 0;

443
444
    if (m_codaDevice) {
        disconnect(m_codaDevice.data(), 0, this, 0);
445
        SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(m_codaDevice);
446
    }
447

448
449
450
451
452
453
    delete m_eventLoop;
    m_eventLoop = 0;
    fi.reportResult(m_deployResult);
    m_futureInterface = 0;
}

454
455
456
457
458
459
460
void S60DeployStep::slotError(const QString &error)
{
    reportError(tr("Error: %1").arg(error));
}

void S60DeployStep::slotTrkLogMessage(const QString &log)
{
461
    if (debug > 1)
462
463
464
465
466
467
468
469
470
        qDebug() << "CODA log:" << log;
}

void S60DeployStep::slotSerialPong(const QString &message)
{
    if (debug)
        qDebug() << "CODA serial pong:" << message;
}

471
void S60DeployStep::slotCodaEvent(const Coda::CodaEvent &event)
472
473
474
475
476
{
    if (debug)
        qDebug() << "CODA event:" << "Type:" << event.type() << "Message:" << event.toString();

    switch (event.type()) {
477
    case Coda::CodaEvent::LocatorHello: {// Commands accepted now
478
        m_state = StateConnected;
479
        emit codaConnected();
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
        startTransferring();
        break;
    }
    default:
        if (debug)
            qDebug() << "Unhandled event:" << "Type:" << event.type() << "Message:" << event.toString();
        break;
    }
}

void S60DeployStep::initFileSending()
{
    QTC_ASSERT(m_currentFileIndex < m_signedPackages.count(), return);
    QTC_ASSERT(m_currentFileIndex >= 0, return);

    const unsigned flags =
496
497
498
            Coda::CodaDevice::FileSystem_TCF_O_WRITE
            |Coda::CodaDevice::FileSystem_TCF_O_CREAT
            |Coda::CodaDevice::FileSystem_TCF_O_TRUNC;
499
500
501
502
    m_putWriteOk = false;

    QString packageName(QFileInfo(m_signedPackages.at(m_currentFileIndex)).fileName());
    QString remoteFileLocation = QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(packageName);
503
    m_codaDevice->sendFileSystemOpenCommand(Coda::CodaCallback(this, &S60DeployStep::handleFileSystemOpen),
504
505
506
507
508
509
510
511
512
513
514
515
                                           remoteFileLocation.toAscii(), flags);
    appendMessage(tr("Copying \"%1\"...").arg(packageName), false);
}

void S60DeployStep::initFileInstallation()
{
    QTC_ASSERT(m_currentFileIndex < m_signedPackages.count(), return);
    QTC_ASSERT(m_currentFileIndex >= 0, return);

    QString packageName(QFileInfo(m_signedPackages.at(m_currentFileIndex)).fileName());
    QString remoteFileLocation = QString::fromLatin1("%1:\\Data\\%2").arg(m_installationDrive).arg(packageName);
    if (m_silentInstall) {
516
        m_codaDevice->sendSymbianInstallSilentInstallCommand(Coda::CodaCallback(this, &S60DeployStep::handleSymbianInstall),
517
518
519
                                                            remoteFileLocation.toAscii(), QString::fromLatin1("%1:").arg(m_installationDrive).toAscii());
        appendMessage(tr("Installing package \"%1\" on drive %2:...").arg(packageName).arg(m_installationDrive), false);
    } else {
520
        m_codaDevice->sendSymbianInstallUIInstallCommand(Coda::CodaCallback(this, &S60DeployStep::handleSymbianInstall),
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
                                                        remoteFileLocation.toAscii());
        appendMessage(tr("Please continue the installation on your device."), false);
        emit manualInstallation();
    }
}

void S60DeployStep::startTransferring()
{
    m_currentFileIndex = 0;
    initFileSending();
    m_state = StateSendingData;
}

void S60DeployStep::startInstalling()
{
    m_currentFileIndex = 0;
    initFileInstallation();
    m_state = StateInstalling;
}

541
void S60DeployStep::handleFileSystemOpen(const Coda::CodaCommandResult &result)
542
{
543
    if (result.type != Coda::CodaCommandResult::SuccessReply) {
544
        reportError(tr("Could not open remote file: %1").arg(result.errorString()));
545
546
547
548
        return;
    }

    if (result.values.size() < 1 || result.values.at(0).data().isEmpty()) {
549
        reportError(QLatin1String("Internal error: No filehandle obtained"));
550
551
552
553
554
        return;
    }

    m_remoteFileHandle = result.values.at(0).data();

555
556
    const QString fileName = m_signedPackages.at(m_currentFileIndex);
    m_putFile.reset(new QFile(fileName));
557
    if (!m_putFile->open(QIODevice::ReadOnly)) { // Should not fail, was checked before
558
        reportError(tr("Could not open local file %1: %2").arg(fileName, m_putFile->errorString()));
559
560
561
562
563
        return;
    }
    putSendNextChunk();
}

564
void S60DeployStep::handleSymbianInstall(const Coda::CodaCommandResult &result)
565
{
566
    if (result.type == Coda::CodaCommandResult::SuccessReply) {
567
568
569
570
571
572
        appendMessage(tr("Installation has finished"), false);
        if (++m_currentFileIndex >= m_signedPackages.count())
            emit allFilesInstalled();
        else
            initFileInstallation();
    } else {
Friedemann Kleint's avatar
Friedemann Kleint committed
573
574
575
576
        reportError(tr("Installation failed: %1; "
                       "see %2 for descriptions of the error codes")
                    .arg(result.errorString(),
                         QLatin1String("http://wiki.forum.nokia.com/index.php/Symbian_OS_Error_Codes")));
577
578
579
580
581
582
583
584
    }
}

void S60DeployStep::putSendNextChunk()
{
    // Read and send off next chunk
    const quint64 pos = m_putFile->pos();
    const QByteArray data = m_putFile->read(m_putChunkSize);
585
    const quint64 size = m_putFile->size();
586
587
588
    if (data.isEmpty()) {
        m_putWriteOk = true;
        closeRemoteFile();
589
        setCopyProgress(100);
590
591
    } else {
        m_putLastChunkSize = data.size();
592
        if (debug > 1)
593
594
595
            qDebug("Writing %llu bytes to remote file '%s' at %llu\n",
                   m_putLastChunkSize,
                   m_remoteFileHandle.constData(), pos);
596
        m_codaDevice->sendFileSystemWriteCommand(Coda::CodaCallback(this, &S60DeployStep::handleFileSystemWrite),
597
                                                m_remoteFileHandle, data, unsigned(pos));
598
        setCopyProgress((100*(m_putLastChunkSize+pos))/size);
599
600
601
602
603
    }
}

void S60DeployStep::closeRemoteFile()
{
604
    m_codaDevice->sendFileSystemCloseCommand(Coda::CodaCallback(this, &S60DeployStep::handleFileSystemClose),
605
606
607
                                            m_remoteFileHandle);
}

608
void S60DeployStep::handleFileSystemWrite(const Coda::CodaCommandResult &result)
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
{
    // Close remote file even if copy fails
    m_putWriteOk = result;
    if (!m_putWriteOk) {
        QString packageName(QFileInfo(m_signedPackages.at(m_currentFileIndex)).fileName());
        reportError(tr("Could not write to file %1 on device: %2").arg(packageName).arg(result.errorString()));
    }

    if (!m_putWriteOk || m_putLastChunkSize < m_putChunkSize) {
        closeRemoteFile();
    } else {
        putSendNextChunk();
    }
}

624
void S60DeployStep::handleFileSystemClose(const Coda::CodaCommandResult &result)
625
{
626
    if (result.type == Coda::CodaCommandResult::SuccessReply) {
627
628
629
630
631
632
633
        if (debug)
            qDebug("File closed.\n");
        if (++m_currentFileIndex >= m_signedPackages.count())
            emit allFilesSent();
        else
            initFileSending();
    } else {
634
        reportError(tr("Failed to close the remote file: %1").arg(result.toString()));
635
636
637
638
639
    }
}

void S60DeployStep::checkForTimeout()
{
640
    if (m_state != StateConnecting)
641
642
        return;

643
    QMessageBox *mb = CodaRunControl::createCodaWaitingMessageBox(Core::ICore::instance()->mainWindow());
644
    connect(this, SIGNAL(codaConnected()), mb, SLOT(close()));
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
    connect(this, SIGNAL(finished()), mb, SLOT(close()));
    connect(this, SIGNAL(finishNow()), mb, SLOT(close()));
    connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTckTrkClosed(int)));
    mb->open();
}

void S60DeployStep::showManualInstallationInfo()
{
    const QString title  = tr("Installation");
    const QString text = tr("Please continue the installation on your device.");
    QMessageBox *mb = new QMessageBox(QMessageBox::Information, title, text,
                                      QMessageBox::Ok, Core::ICore::instance()->mainWindow());
    connect(this, SIGNAL(allFilesInstalled()), mb, SLOT(close()));
    connect(this, SIGNAL(finished()), mb, SLOT(close()));
    connect(this, SIGNAL(finishNow()), mb, SLOT(close()));
    mb->open();
}

void S60DeployStep::slotWaitingForTckTrkClosed(int result)
{
    if (result == QMessageBox::Cancel)
        m_deployCanceled = true;
}

669
670
671
672
673
674
675
676
void S60DeployStep::setReleaseDeviceAfterLauncherFinish(bool v)
{
    m_releaseDeviceAfterLauncherFinish = v;
}

void S60DeployStep::slotLauncherStateChanged(int s)
{
    if (s == trk::Launcher::WaitingForTrk) {
677
        QMessageBox *mb = TrkRunControl::createTrkWaitingMessageBox(m_launcher->trkServerName(),
678
                                                                          Core::ICore::instance()->mainWindow());
679
        connect(m_launcher, SIGNAL(stateChanged(int)), mb, SLOT(close()));
680
        connect(mb, SIGNAL(finished(int)), this, SLOT(slotWaitingForTrkClosed()));
681
682
683
684
685
686
        mb->open();
    }
}

void S60DeployStep::slotWaitingForTrkClosed()
{
687
688
    if (m_launcher && m_launcher->state() == trk::Launcher::WaitingForTrk)
        m_deployCanceled = true;
689
690
}

691
void S60DeployStep::createFileFailed(const QString &filename, const QString &errorMessage)
692
{
693
    reportError(tr("Could not create file %1 on device: %2").arg(filename, errorMessage));
694
695
}

696
void S60DeployStep::writeFileFailed(const QString &filename, const QString &errorMessage)
697
{
698
    reportError(tr("Could not write to file %1 on device: %2").arg(filename, errorMessage));
699
700
}

701
void S60DeployStep::closeFileFailed(const QString &filename, const QString &errorMessage)
702
703
{
    const QString msg = tr("Could not close file %1 on device: %2. It will be closed when App TRK is closed.");
704
    reportError( msg.arg(filename, errorMessage));
705
706
}

707
void S60DeployStep::connectFailed(const QString &errorMessage)
708
{
709
    reportError(tr("Could not connect to App TRK on device: %1. Restarting App TRK might help.").arg(errorMessage));
710
711
}

712
void S60DeployStep::printCopyingNotice(const QString &fileName)
713
{
714
    appendMessage(tr("Copying \"%1\"...").arg(fileName), false);
715
716
}

717
void S60DeployStep::printInstallingNotice(const QString &packageName)
718
{
719
    appendMessage(tr("Installing package \"%1\" on drive %2:...").arg(packageName).arg(m_installationDrive), false);
720
721
722
723
724
725
726
}

void S60DeployStep::printInstallingFinished()
{
    appendMessage(tr("Installation has finished"), false);
}

727
void S60DeployStep::installFailed(const QString &filename, const QString &errorMessage)
728
{
729
    reportError(tr("Could not install from package %1 on device: %2").arg(filename, errorMessage));
730
731
732
733
}

void S60DeployStep::checkForCancel()
{
734
    if ((m_futureInterface->isCanceled() || m_deployCanceled) && m_timer->isActive()) {
735
736
        m_timer->stop();
        stop();
737
738
739
740
741
742
743
        QString canceledText(tr("Deployment has been cancelled."));
        appendMessage(canceledText, true);
        emit addTask(ProjectExplorer::Task(ProjectExplorer::Task::Error,
                                           canceledText,
                                           QString(), -1,
                                           ProjectExplorer::Constants::TASK_CATEGORY_BUILDSYSTEM));
        emit finishNow(false);
744
745
746
    }
}

747
void S60DeployStep::launcherFinished(bool success)
748
{
749
    m_deployResult = success;
750
751
    if(m_deployResult && m_futureInterface)
        m_futureInterface->setProgressValue(m_futureInterface->progressMaximum());
752
753
754
755
    if (m_releaseDeviceAfterLauncherFinish && m_launcher) {
        m_handleDeviceRemoval = false;
        trk::Launcher::releaseToDeviceManager(m_launcher);
    }
756
    if (m_launcher)
757
758
        m_launcher->deleteLater();
    m_launcher = 0;
759
760
761
762
763
764
765
    if (m_eventLoop)
        m_eventLoop->exit();
}

void S60DeployStep::deploymentFinished(bool success)
{
    m_deployResult = success;
766
767
    if(m_deployResult && m_futureInterface)
        m_futureInterface->setProgressValue(m_futureInterface->progressMaximum());
768
    if (m_eventLoop)
769
        m_eventLoop->exit();
770
771
772
773
}

void S60DeployStep::deviceRemoved(const SymbianUtils::SymbianDevice &d)
{
774
775
    if (m_handleDeviceRemoval && d.portName() == m_serialPortName)
        reportError(tr("The device '%1' has been disconnected").arg(d.friendlyName()));
776
777
}

778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
void S60DeployStep::setCopyProgress(int progress)
{
    if (progress < 0)
        progress = 0;
    else if (progress > 100)
        progress = 100;
    if (copyProgress() == progress)
        return;
    m_copyProgress = progress;
    emit copyProgressChanged(m_copyProgress);
}

int S60DeployStep::copyProgress() const
{
    return m_copyProgress;
}

void S60DeployStep::updateProgress(int progress)
{
    //This would show the percentage on the Compile output
    //appendMessage(tr("Copy percentage: %1%").arg((m_currentFileIndex*100 + progress) /m_signedPackages.count()), false);
    int copyProgress = ((m_currentFileIndex*100 + progress) /m_signedPackages.count());
    int entireProgress = copyProgress * 0.8; //the copy progress is just 80% of the whole deployment progress
801
    m_futureInterface->setProgressValueAndText(entireProgress, tr("Copy progress: %1%").arg(copyProgress));
802
803
}

804
805
806
807
808
809
810
// #pragma mark -- S60DeployStepWidget

BuildStepConfigWidget *S60DeployStep::createConfigWidget()
{
    return new S60DeployStepWidget();
}

811
812
813
814
S60DeployStepWidget::S60DeployStepWidget() : ProjectExplorer::BuildStepConfigWidget()
{
}

815
816
817
818
819
820
void S60DeployStepWidget::init()
{
}

QString S60DeployStepWidget::summaryText() const
{
Tobias Hunger's avatar
Tobias Hunger committed
821
    return QString("<b>%1</b>").arg(displayName());
822
823
824
825
}

QString S60DeployStepWidget::displayName() const
{
Tobias Hunger's avatar
Tobias Hunger committed
826
    return tr("Deploy SIS Package");
827
828
829
830
831
}

// #pragma mark -- S60DeployStepFactory

S60DeployStepFactory::S60DeployStepFactory(QObject *parent) :
832
    ProjectExplorer::IBuildStepFactory(parent)
833
834
835
836
837
838
839
{
}

S60DeployStepFactory::~S60DeployStepFactory()
{
}

Tobias Hunger's avatar
Tobias Hunger committed
840
bool S60DeployStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const QString &id) const
841
{
Tobias Hunger's avatar
Tobias Hunger committed
842
    if (parent->id() != QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY))
843
844
845
846
847
848
        return false;
    if (parent->target()->id() != QLatin1String(Constants::S60_DEVICE_TARGET_ID))
        return false;
    return (id == QLatin1String(S60_DEPLOY_STEP_ID));
}

Tobias Hunger's avatar
Tobias Hunger committed
849
ProjectExplorer::BuildStep *S60DeployStepFactory::create(ProjectExplorer::BuildStepList *parent, const QString &id)
850
{
Tobias Hunger's avatar
Tobias Hunger committed
851
    if (!canCreate(parent, id))
852
        return 0;
Tobias Hunger's avatar
Tobias Hunger committed
853
    return new S60DeployStep(parent);
854
855
}

Tobias Hunger's avatar
Tobias Hunger committed
856
bool S60DeployStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source) const
857
{
Tobias Hunger's avatar
Tobias Hunger committed
858
859
860
861
862
    if (!canCreate(parent, source->id()))
        return false;
    if (!qobject_cast<S60DeployStep *>(source))
        return false;
    return true;
863
864
}

Tobias Hunger's avatar
Tobias Hunger committed
865
ProjectExplorer::BuildStep *S60DeployStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *source)
866
{
Tobias Hunger's avatar
Tobias Hunger committed
867
    if (!canClone(parent, source))
868
869
870
871
        return 0;
    return new S60DeployStep(parent, static_cast<S60DeployStep *>(source));
}

Tobias Hunger's avatar
Tobias Hunger committed
872
bool S60DeployStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const
873
874
{
    QString id(ProjectExplorer::idFromMap(map));
Tobias Hunger's avatar
Tobias Hunger committed
875
    return canCreate(parent, id);
876
877
}

Tobias Hunger's avatar
Tobias Hunger committed
878
ProjectExplorer::BuildStep *S60DeployStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map)
879
{
Tobias Hunger's avatar
Tobias Hunger committed
880
    if (!canRestore(parent, map))
881
882
883
884
885
886
887
888
        return 0;
    S60DeployStep *bs = new S60DeployStep(parent);
    if (bs->fromMap(map))
        return bs;
    delete bs;
    return 0;
}

Tobias Hunger's avatar
Tobias Hunger committed
889
QStringList S60DeployStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const
890
{
Tobias Hunger's avatar
Tobias Hunger committed
891
    if (parent->id() == QLatin1String(ProjectExplorer::Constants::BUILDSTEPS_DEPLOY)
892
            && parent->target()->id() == QLatin1String(Constants::S60_DEVICE_TARGET_ID))
893
894
895
896
897
898
899
900
901
902
        return QStringList() << QLatin1String(S60_DEPLOY_STEP_ID);
    return QStringList();
}

QString S60DeployStepFactory::displayNameForId(const QString &id) const
{
    if (id == QLatin1String(S60_DEPLOY_STEP_ID))
        return tr("Deploy SIS Package");
    return QString();
}