Commit 5c36c594 authored by Tobias Nätterlund's avatar Tobias Nätterlund Committed by Tobias Nätterlund

QNX: Refactored connection handling for BlackBerry devices

The blackberry-connect connection is now established at
Qt Creator startup, and taken down when Qt Creator shuts down.
The user also has the ability to disconnect manually in the
Device settings widget.

The connection status is shown in the Devices settings widget,
as well as in the project selector. And it is impossible run or
debug an application without having the device connected.

Change-Id: I64f2e48a647e6d850851d2f20d0ec7bbd5980ea5
Reviewed-by: default avatarChristian Kandeler <christian.kandeler@digia.com>
Reviewed-by: Nicolas Arnaud-Cormos's avatarNicolas Arnaud-Cormos <nicolas@kdab.com>
Reviewed-by: default avatarMehdi Fekari <mfekari@rim.com>
parent 44200e0f
......@@ -281,16 +281,18 @@ void DeviceManager::removeDevice(Core::Id id)
void DeviceManager::setDeviceState(Core::Id deviceId, IDevice::DeviceState deviceState)
{
// To see the state change in the DeviceSettingsWidget. This has to happen before
// the pos check below, in case the device is only present in the cloned instance.
if (this == instance() && d->clonedInstance)
d->clonedInstance->setDeviceState(deviceId, deviceState);
const int pos = d->indexForId(deviceId);
QTC_ASSERT(pos != -1, return);
if (pos < 0)
return;
IDevice::Ptr &device = d->devices[pos];
if (device->deviceState() == deviceState)
return;
// To see the state change in the DeviceSettingsWidget
if (this == instance() && d->clonedInstance)
d->clonedInstance->setDeviceState(deviceId, deviceState);
device->setDeviceState(deviceState);
emit deviceUpdated(deviceId);
emit updated();
......
......@@ -131,7 +131,8 @@ void DeviceManagerModel::handleDeviceRemoved(Core::Id id)
void DeviceManagerModel::handleDeviceUpdated(Core::Id id)
{
const int idx = indexForId(id);
QTC_ASSERT(idx != -1, return);
if (idx < 0) // This occurs when a device not matching the type filter is updated
return;
d->devices[idx] = d->deviceManager->find(id);
const QModelIndex changedIndex = index(idx, 0);
emit dataChanged(changedIndex, changedIndex);
......
......@@ -31,7 +31,6 @@
#include "blackberrydebugsupport.h"
#include "blackberryapplicationrunner.h"
#include "blackberryconnect.h"
#include <debugger/debuggerrunner.h>
#include <debugger/debuggerengine.h>
......@@ -46,36 +45,24 @@ BlackBerryDebugSupport::BlackBerryDebugSupport(BlackBerryRunConfiguration *runCo
, m_engine(runControl->engine())
{
m_runner = new BlackBerryApplicationRunner(true, runConfig, this);
m_connector = BlackBerryConnect::instance(runConfig);
connect(m_engine, SIGNAL(requestRemoteSetup()), this, SLOT(launchRemoteApplication()));
connect(m_engine, SIGNAL(stateChanged(Debugger::DebuggerState)),
this, SLOT(handleDebuggerStateChanged(Debugger::DebuggerState)));
connect(m_connector, SIGNAL(error(QString)), this, SLOT(handleConnectorError(QString)));
connect(m_connector, SIGNAL(connected()), m_runner, SLOT(start()));
connect(m_connector, SIGNAL(output(QString,Utils::OutputFormat)),
runControl, SLOT(appendMessage(QString,Utils::OutputFormat)));
connect(m_runner, SIGNAL(started()), this, SLOT(handleStarted()));
connect(m_runner, SIGNAL(started()), m_runner, SLOT(checkSlog2Info()));
connect(m_runner, SIGNAL(startFailed(QString)), this, SLOT(handleStartFailed(QString)));
connect(m_runner, SIGNAL(output(QString,Utils::OutputFormat)),
this, SLOT(handleApplicationOutput(QString,Utils::OutputFormat)));
connect(m_runner, SIGNAL(finished()), m_connector, SLOT(disconnectFromDevice()));
connect(this, SIGNAL(output(QString,Utils::OutputFormat)),
runControl, SLOT(appendMessage(QString,Utils::OutputFormat)));
}
BlackBerryDebugSupport::~BlackBerryDebugSupport()
{
BlackBerryConnect::cleanup(m_connector);
}
void BlackBerryDebugSupport::launchRemoteApplication()
{
m_connector->connectToDevice();
m_runner->start();
}
void BlackBerryDebugSupport::handleStarted()
......@@ -97,11 +84,6 @@ void BlackBerryDebugSupport::handleDebuggerStateChanged(Debugger::DebuggerState
}
}
void BlackBerryDebugSupport::handleConnectorError(const QString &message)
{
m_engine->notifyEngineRemoteSetupFailed(message);
}
void BlackBerryDebugSupport::handleApplicationOutput(const QString &msg, Utils::OutputFormat format)
{
Q_UNUSED(format)
......
......@@ -57,7 +57,6 @@ class BlackBerryDebugSupport : public QObject
public:
explicit BlackBerryDebugSupport(BlackBerryRunConfiguration *runConfig,
Debugger::DebuggerRunControl *runControl);
~BlackBerryDebugSupport();
signals:
void output(const QString &msg, Utils::OutputFormat format);
......@@ -70,15 +69,12 @@ private slots:
void handleDebuggerStateChanged(Debugger::DebuggerState state);
void handleConnectorError(const QString &message);
void handleApplicationOutput(const QString &msg, Utils::OutputFormat format);
private:
Debugger::DebuggerEngine *m_engine;
BlackBerryApplicationRunner *m_runner;
BlackBerryConnect *m_connector;
};
} // namespace Internal
......
......@@ -33,6 +33,7 @@
#include "qnxconstants.h"
#include "blackberrydeviceconfigurationwidget.h"
#include "blackberrydeviceconnectionmanager.h"
#include "blackberrydeviceprocesssupport.h"
#include <projectexplorer/kitinformation.h>
......@@ -41,6 +42,11 @@ using namespace Qnx;
using namespace Qnx::Internal;
using namespace ProjectExplorer;
namespace {
const char ConnectToDeviceActionId[] = "Qnx.BlackBerry.ConnectToDeviceAction";
const char DisconnectFromDeviceActionId[] = "Qnx.BlackBerry.DisconnectFromDeviceAction";
}
BlackBerryDeviceConfiguration::BlackBerryDeviceConfiguration()
: RemoteLinux::LinuxDevice()
{
......@@ -116,19 +122,34 @@ IDeviceWidget *BlackBerryDeviceConfiguration::createWidget()
QList<Core::Id> BlackBerryDeviceConfiguration::actionIds() const
{
return QList<Core::Id>();
return QList<Core::Id>() << Core::Id(ConnectToDeviceActionId)
<< Core::Id(DisconnectFromDeviceActionId);
}
QString BlackBerryDeviceConfiguration::displayNameForActionId(Core::Id actionId) const
{
Q_UNUSED(actionId);
if (actionId == Core::Id(ConnectToDeviceActionId))
return tr("Connect to device");
else if (actionId == Core::Id(DisconnectFromDeviceActionId))
return tr("Disconnect from device");
return QString();
}
void BlackBerryDeviceConfiguration::executeAction(Core::Id actionId, QWidget *parent) const
{
Q_UNUSED(actionId);
Q_UNUSED(parent);
const BlackBerryDeviceConfiguration::ConstPtr device =
sharedFromThis().staticCast<const BlackBerryDeviceConfiguration>();
BlackBerryDeviceConnectionManager *connectionManager =
BlackBerryDeviceConnectionManager::instance();
if (actionId == Core::Id(ConnectToDeviceActionId))
connectionManager->connectDevice(device);
else if (actionId == Core::Id(DisconnectFromDeviceActionId)
&& connectionManager->isConnected(id()))
connectionManager->disconnectDevice(device);
}
QVariantMap BlackBerryDeviceConfiguration::toMap() const
......
......@@ -33,9 +33,12 @@
#include "blackberrydebugtokenuploader.h"
#include "blackberrydebugtokenrequestdialog.h"
#include "ui_blackberrydeviceconfigurationwidget.h"
#include "blackberrydeviceconnectionmanager.h"
#include "qnxconstants.h"
#include <ssh/sshconnection.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/fontsettings.h>
#include <utils/pathchooser.h>
#include <utils/fancylineedit.h>
......@@ -53,6 +56,9 @@ BlackBerryDeviceConfigurationWidget::BlackBerryDeviceConfigurationWidget(const I
uploader(new BlackBerryDebugTokenUploader(this))
{
ui->setupUi(this);
ui->connectionLog->setFont(TextEditor::TextEditorSettings::instance()->fontSettings().font());
connect(ui->hostLineEdit, SIGNAL(editingFinished()), this, SLOT(hostNameEditingFinished()));
connect(ui->pwdLineEdit, SIGNAL(editingFinished()), this, SLOT(passwordEditingFinished()));
connect(ui->keyFileLineEdit, SIGNAL(editingFinished()), this, SLOT(keyFileEditingFinished()));
......@@ -63,6 +69,11 @@ BlackBerryDeviceConfigurationWidget::BlackBerryDeviceConfigurationWidget(const I
connect(ui->debugToken, SIGNAL(browsingFinished()), this, SLOT(debugTokenEditingFinished()));
connect(uploader, SIGNAL(finished(int)), this, SLOT(uploadFinished(int)));
connect(BlackBerryDeviceConnectionManager::instance(), SIGNAL(connectionOutput(Core::Id, QString)),
this, SLOT(appendConnectionLog(Core::Id, QString)));
connect(BlackBerryDeviceConnectionManager::instance(), SIGNAL(deviceAboutToConnect(Core::Id)),
this, SLOT(clearConnectionLog(Core::Id)));
ui->debugToken->addButton(tr("Request"), this, SLOT(requestDebugToken()));
ui->debugToken->addButton(tr("Upload"), this, SLOT(uploadDebugToken()));
uploadButton = ui->debugToken->buttonAtIndex(2);
......@@ -173,6 +184,18 @@ void BlackBerryDeviceConfigurationWidget::uploadFinished(int status)
QMessageBox::critical(this, tr("Error"), errorString);
}
void BlackBerryDeviceConfigurationWidget::appendConnectionLog(Core::Id deviceId, const QString &line)
{
if (deviceId == device()->id())
ui->connectionLog->appendPlainText(line.trimmed());
}
void BlackBerryDeviceConfigurationWidget::clearConnectionLog(Core::Id deviceId)
{
if (deviceId == device()->id())
ui->connectionLog->clear();
}
void BlackBerryDeviceConfigurationWidget::updateDeviceFromUi()
{
hostNameEditingFinished();
......@@ -210,6 +233,8 @@ void BlackBerryDeviceConfigurationWidget::initGui()
progressDialog->setLabelText(tr("Uploading debug token"));
progressDialog->setMinimum(0);
progressDialog->setMaximum(0);
ui->connectionLog->setPlainText(BlackBerryDeviceConnectionManager::instance()->connectionLog(device()->id()).trimmed());
}
BlackBerryDeviceConfiguration::Ptr BlackBerryDeviceConfigurationWidget::deviceConfiguration() const
......
......@@ -69,6 +69,8 @@ private slots:
void uploadDebugToken();
void updateUploadButton();
void uploadFinished(int status);
void appendConnectionLog(Core::Id deviceId, const QString &line);
void clearConnectionLog(Core::Id deviceId);
private:
void updateDeviceFromUi();
......
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>334</width>
<height>131</height>
<height>208</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
......@@ -79,6 +79,20 @@
<item row="3" column="1">
<widget class="Utils::PathChooser" name="keyFileLineEdit" native="true"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Connection log:</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QPlainTextEdit" name="connectionLog">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
<zorder>keyFileLineEdit</zorder>
<zorder>hostNameLabel</zorder>
......@@ -86,6 +100,8 @@
<zorder>passwordLabel</zorder>
<zorder>keyLabel</zorder>
<zorder>debugTokenLabel</zorder>
<zorder>label</zorder>
<zorder>connectionLog</zorder>
</widget>
<customwidgets>
<customwidget>
......
......@@ -29,162 +29,117 @@
**
****************************************************************************/
#include "blackberryconnect.h"
#include "blackberryrunconfiguration.h"
#include "blackberrydeployconfiguration.h"
#include "blackberrydeviceconnection.h"
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/target.h>
#include "blackberryconfiguration.h"
#include "qnxutils.h"
#include <projectexplorer/devicesupport/devicemanager.h>
#include <ssh/sshconnection.h>
#include <utils/qtcassert.h>
#include <utils/environment.h>
#include <QProcess>
#include <QApplication>
using namespace ProjectExplorer;
using namespace Qnx;
using namespace Qnx::Internal;
namespace {
const char CONNECT_CMD[] = "java";
const char CONNECT_SUCCESS_MSG[] = "Successfully connected";
}
QMap<QString, BlackBerryConnect *> BlackBerryConnect::m_instances = QMap<QString, BlackBerryConnect *>();
QMap<QString, int> BlackBerryConnect::m_usageCount = QMap<QString, int>();
BlackBerryConnect *BlackBerryConnect::instance(BlackBerryRunConfiguration *runConfig)
BlackBerryDeviceConnection::BlackBerryDeviceConnection() :
QObject(),
m_connectionState(Disconnected),
m_process(new QProcess(this))
{
BlackBerryDeviceConfiguration::ConstPtr device
= BlackBerryDeviceConfiguration::device(runConfig->target()->kit());
QString deviceHost;
if (device)
deviceHost = device->sshParameters().host;
if (!m_instances.contains(deviceHost)) {
m_instances[deviceHost] = new BlackBerryConnect(runConfig);
m_usageCount[deviceHost] = 1;
} else {
++m_usageCount[deviceHost];
}
return m_instances[deviceHost];
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished()));
connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processFinished()));
connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError()));
}
void BlackBerryConnect::cleanup(BlackBerryConnect *instance)
void BlackBerryDeviceConnection::connectDevice(const ProjectExplorer::IDevice::ConstPtr &device)
{
const QString deviceHost = instance->m_deviceHost;
QTC_ASSERT(m_usageCount.contains(deviceHost), return);
--m_usageCount[deviceHost];
QTC_ASSERT(m_usageCount[deviceHost] >= 0, return);
Utils::Environment env = Utils::Environment::systemEnvironment();
QnxUtils::prependQnxMapToEnvironment(BlackBerryConfiguration::instance().qnxEnv(), env);
m_process->setEnvironment(env.toStringList());
if (m_usageCount[deviceHost] == 0) {
m_instances.remove(deviceHost);
m_usageCount.remove(deviceHost);
instance->deleteLater();
}
}
m_host = device->sshParameters().host;
const QString password = device->sshParameters().password;
const QString publicKeyFile = device->sshParameters().privateKeyFile + QLatin1String(".pub");
BlackBerryConnect::BlackBerryConnect(BlackBerryRunConfiguration *runConfig)
: QObject()
, m_connected(false)
{
m_process = new QProcess(this);
// Since killing the blackberry-connect script won't kill the java process it launches,
// let's just call the java process directly instead.
QString command = env.searchInPath(QLatin1String("java"));
Utils::Environment env;
Target *target = runConfig->target();
if (target->activeBuildConfiguration())
env = target->activeBuildConfiguration()->environment();
QStringList args;
args << QLatin1String("-Xmx512M");
args << QLatin1String("-jar");
args << env.value(QLatin1String("QNX_HOST")) + QLatin1String("/usr/lib/Connect.jar");
m_process->setEnvironment(env.toStringList());
m_connectCmd = env.searchInPath(QLatin1String(CONNECT_CMD));
m_qnxHost = env.value(QLatin1String("QNX_HOST"));
BlackBerryDeviceConfiguration::ConstPtr device
= BlackBerryDeviceConfiguration::device(target->kit());
m_deviceHost = device->sshParameters().host;
m_password = device->sshParameters().password;
m_publicKeyFile = device->sshParameters().privateKeyFile + QLatin1String(".pub");
args << QLatin1String("-targetHost") << m_host;
if (!password.isEmpty())
args << QLatin1String("-password") << password;
args << QLatin1String("-sshPublicKey") << publicKeyFile;
connect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput()));
connect(m_process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError()));
m_connectionState = Connecting;
m_process->start(command, args);
m_messageLog.clear();
emit deviceAboutToConnect();
}
void BlackBerryConnect::connectToDevice()
void BlackBerryDeviceConnection::disconnectDevice()
{
if (m_connected) {
emit connected();
return;
}
QTC_ASSERT(!m_connectCmd.isEmpty() && !m_qnxHost.isEmpty(), return);
// Since killing the blackberry-connect script won't kill the java process it launches, let's just call
// the java process directly instead.
QStringList connectArgs;
connectArgs << QLatin1String("-Xmx512M");
connectArgs << QLatin1String("-jar") << m_qnxHost + QLatin1String("/usr/lib/Connect.jar");
connectArgs << QLatin1String("-targetHost") << m_deviceHost;
if (!m_password.isEmpty())
connectArgs << QLatin1String("-password") << m_password;
connectArgs << QLatin1String("-sshPublicKey") << m_publicKeyFile;
m_process->terminate();
if (!m_process->waitForFinished(5000))
m_process->kill();
m_connectionState = Disconnected;
}
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(handleProcessFinished(int,QProcess::ExitStatus)), Qt::UniqueConnection);
QString BlackBerryDeviceConnection::host() const
{
return m_host;
}
m_process->start(m_connectCmd, connectArgs);
BlackBerryDeviceConnection::State BlackBerryDeviceConnection::connectionState()
{
return m_connectionState;
}
void BlackBerryConnect::disconnectFromDevice()
QString BlackBerryDeviceConnection::messageLog() const
{
if (m_process->state() != QProcess::Running)
return;
if (m_usageCount[m_deviceHost] == 1) {
disconnect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)),
this, SLOT(handleProcessFinished(int,QProcess::ExitStatus)));
m_process->terminate();
if (!m_process->waitForFinished(5000))
m_process->kill();
m_connected = false;
}
return m_messageLog;
}
void BlackBerryConnect::handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
void BlackBerryDeviceConnection::processFinished()
{
m_connected = false;
if (exitCode != 0 || exitStatus != QProcess::NormalExit)
emit error(m_process->errorString());
m_connectionState = Disconnected;
emit deviceDisconnected();
}
void BlackBerryConnect::readStandardOutput()
void BlackBerryDeviceConnection::readStandardOutput()
{
m_process->setReadChannel(QProcess::StandardOutput);
while (m_process->canReadLine()) {
const QString line = QString::fromLocal8Bit(m_process->readLine());
emit output(line, Utils::StdOutFormat);
emit processOutput(line);
m_messageLog.append(line);
if (line.contains(QLatin1String(CONNECT_SUCCESS_MSG))) {
m_connected = true;
emit connected();
m_connectionState = Connected;
emit deviceConnected();
}
}
}
void BlackBerryConnect::readStandardError()
void BlackBerryDeviceConnection::readStandardError()
{
m_process->setReadChannel(QProcess::StandardError);
QStringList errorLines;
while (m_process->canReadLine()) {
const QString line = QString::fromLocal8Bit(m_process->readLine());
emit output(line, Utils::StdErrFormat);
if (line.contains(QLatin1String("Error:")))
errorLines << line.mid(7);
}
// TODO: Handle error messages better
if (!errorLines.isEmpty())
emit error(errorLines.join(QLatin1String("\n")));
emit processOutput(line);
m_messageLog.append(line);
}
}
......@@ -29,57 +29,63 @@
**
****************************************************************************/
#ifndef QNX_INTERNAL_BLACKBERRYCONNECT_H
#define QNX_INTERNAL_BLACKBERRYCONNECT_H
#include <utils/outputformat.h>
#ifndef QNX_INTERNAL_BLACKBERRYDEVICECONNECTION_H
#define QNX_INTERNAL_BLACKBERRYDEVICECONNECTION_H
#include <QObject>
#include <QProcess>
#include <coreplugin/id.h>
#include <projectexplorer/devicesupport/idevice.h>
QT_BEGIN_NAMESPACE
class QProcess;
QT_END_NAMESPACE
namespace Qnx {
namespace Internal {
class BlackBerryRunConfiguration;
class BlackBerryConnect : public QObject
class BlackBerryDeviceConnection : public QObject
{
Q_OBJECT
public:
static BlackBerryConnect *instance(BlackBerryRunConfiguration *runConfig);
static void cleanup(BlackBerryConnect *instance);
enum State {
Disconnected,
Connecting,
Connected
};
explicit BlackBerryDeviceConnection();
void connectDevice(const ProjectExplorer::IDevice::ConstPtr &device);
void disconnectDevice();
QString host() const;
State connectionState();
QString messageLog() const;
signals:
void connected();
void error(const QString &msg);
void output(const QString &msg, Utils::OutputFormat format);
void deviceAboutToConnect();
void deviceConnected();
void deviceDisconnected();
public slots:
void connectToDevice();
void disconnectFromDevice();
void processOutput(const QString &output);
private slots:
void handleProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void processFinished();
void readStandardOutput();
void readStandardError();
private:
explicit BlackBerryConnect(BlackBerryRunConfiguration *runConfig);
QString m_host;
State m_connectionState;
static QMap<QString, BlackBerryConnect *> m_instances;
static QMap<QString, int> m_usageCount;
QString m_messageLog;
QProcess *m_process;
QString m_connectCmd;
QString m_deviceHost;
QString m_password;
QString m_publicKeyFile;
QString m_qnxHost;
bool m_connected;
};
} // namespace Internal
} // namespace Qnx
#endif // QNX_INTERNAL_BLACKBERRYCONNECT_H
#endif // QNX_INTERNAL_BLACKBERRYDEVICECONNECTION_H
This diff is collapsed.
/**************************************************************************
**
** Copyright (C) 2011 - 2013 Research In Motion
**
** Contact: Research In Motion (blackberry-qt@qnx.com)
** Contact: KDAB (info@kdab.com)
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QNX_INTERNAL_BLACKBERRYDEVICECONNECTIONMANAGER_H
#define QNX_INTERNAL_BLACKBERRYDEVICECONNECTIONMANAGER_H
#include <QObject>
#include <coreplugin/id.h>
#include <projectexplorer/devicesupport/idevice.h>
namespace Qnx {
namespace Internal {
class BlackBerryDeviceConnection;
class BlackBerryDeviceConnectionManager : public QObject
{
Q_OBJECT