diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
index 079928f14bb0ab439a1631afa5b085a16b120d46..d60ac111ab155e83d14ef1b810d47d779077af6c 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfiguration.cpp
@@ -114,7 +114,7 @@ S60DeviceRunConfiguration::S60DeviceRunConfiguration(Project *project, const QSt
     m_serialPortName(QLatin1String("COM5")),
     m_communicationType(SerialPortCommunication),
 #else
-    m_serialPortName(QLatin1String(SerialDeviceLister::linuxBlueToothDeviceRootC) + QLatin1Char('0')),
+    m_serialPortName(QLatin1String(SymbianDeviceManager::linuxBlueToothDeviceRootC) + QLatin1Char('0')),
     m_communicationType(BlueToothCommunication),
 #endif
     m_signingMode(SignSelf)
@@ -498,7 +498,7 @@ S60DeviceRunControlBase::S60DeviceRunControlBase(RunConfiguration *runConfigurat
     QTC_ASSERT(s60runConfig, return);
     m_toolChain = s60runConfig->toolChainType();
     m_serialPortName = s60runConfig->serialPortName();
-    m_serialPortFriendlyName = S60Manager::instance()->serialDeviceLister()->friendlyNameForPort(m_serialPortName);
+    m_serialPortFriendlyName = SymbianDeviceManager::instance()->friendlyNameForPort(m_serialPortName);
     m_communicationType = s60runConfig->communicationType();
     m_targetName = s60runConfig->targetName();
     m_baseFileName = s60runConfig->basePackageFilePath();
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
index f578eb88ad01440225689e0cb7bff59e31f46cba..a64d1dd5535c92bf1b65b3dee9c23d1e85445eab 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.cpp
@@ -56,7 +56,7 @@
 #include <QtGui/QMainWindow>
 #include <QtGui/QMessageBox>
 
-Q_DECLARE_METATYPE(Qt4ProjectManager::Internal::CommunicationDevice)
+Q_DECLARE_METATYPE(Qt4ProjectManager::Internal::SymbianDevice)
 
 namespace Qt4ProjectManager {
 namespace Internal {
@@ -100,7 +100,7 @@ S60DeviceRunConfigurationWidget::S60DeviceRunConfigurationWidget(
     formLayout->addRow(tr("Install File:"), m_sisxFileLabel);
 
     updateSerialDevices();
-    connect(S60Manager::instance()->serialDeviceLister(), SIGNAL(updated()),
+    connect(SymbianDeviceManager::instance(), SIGNAL(updated()),
             this, SLOT(updateSerialDevices()));
     // Serial devices control
     connect(m_serialPortsCombo, SIGNAL(activated(int)), this, SLOT(setSerialPort(int)));
@@ -180,12 +180,12 @@ void S60DeviceRunConfigurationWidget::updateSerialDevices()
     m_serialPortsCombo->clear();
     clearDeviceInfo();
     const QString previousRunConfigurationPortName = m_runConfiguration->serialPortName();
-    const QList<CommunicationDevice> devices = S60Manager::instance()->serialDeviceLister()->communicationDevices();
+    const QList<SymbianDevice> devices = SymbianDeviceManager::instance()->devices();
     int newIndex = -1;
     for (int i = 0; i < devices.size(); ++i) {
-        const CommunicationDevice &device = devices.at(i);
-        m_serialPortsCombo->addItem(device.friendlyName, qVariantFromValue(device));
-        if (device.portName == previousRunConfigurationPortName)
+        const SymbianDevice &device = devices.at(i);
+        m_serialPortsCombo->addItem(device.friendlyName(), qVariantFromValue(device));
+        if (device.portName() == previousRunConfigurationPortName)
             newIndex = i;
     }
     // Set new index: prefer to keep old or set to 0, if available.
@@ -197,23 +197,23 @@ void S60DeviceRunConfigurationWidget::updateSerialDevices()
         m_runConfiguration->setSerialPortName(QString());
     } else {
         m_deviceInfoButton->setEnabled(true);
-        const QString newPortName = device(newIndex).portName;
+        const QString newPortName = device(newIndex).portName();
         if (newPortName != previousRunConfigurationPortName)
             m_runConfiguration->setSerialPortName(newPortName);
     }
 }
 
-CommunicationDevice S60DeviceRunConfigurationWidget::device(int i) const
+SymbianDevice S60DeviceRunConfigurationWidget::device(int i) const
 {
     if (i >= 0) {
         const QVariant data = m_serialPortsCombo->itemData(i);
-        if (qVariantCanConvert<Qt4ProjectManager::Internal::CommunicationDevice>(data))
-            return qVariantValue<Qt4ProjectManager::Internal::CommunicationDevice>(data);
+        if (qVariantCanConvert<Qt4ProjectManager::Internal::SymbianDevice>(data))
+            return qVariantValue<Qt4ProjectManager::Internal::SymbianDevice>(data);
     }
-    return CommunicationDevice(SerialPortCommunication);
+    return SymbianDevice();
 }
 
-CommunicationDevice S60DeviceRunConfigurationWidget::currentDevice() const
+SymbianDevice S60DeviceRunConfigurationWidget::currentDevice() const
 {
     return device(m_serialPortsCombo->currentIndex());
 }
@@ -242,9 +242,9 @@ void S60DeviceRunConfigurationWidget::updateTargetInformation()
 
 void S60DeviceRunConfigurationWidget::setSerialPort(int index)
 {
-    const CommunicationDevice d = device(index);
-    m_runConfiguration->setSerialPortName(d.portName);
-    m_runConfiguration->setCommunicationType(d.type);
+    const SymbianDevice d = device(index);
+    m_runConfiguration->setSerialPortName(d.portName());
+    m_runConfiguration->setCommunicationType(d.type());
     m_deviceInfoButton->setEnabled(index >= 0);
     clearDeviceInfo();
 }
@@ -323,15 +323,15 @@ void S60DeviceRunConfigurationWidget::updateDeviceInfo()
     // go asynchronous afterwards to pop up launch trk box if a timeout occurs.
     m_infoLauncher = new trk::Launcher(trk::Launcher::ActionPingOnly, QSharedPointer<trk::TrkDevice>(), this);
     connect(m_infoLauncher, SIGNAL(stateChanged(int)), this, SLOT(slotLauncherStateChanged(int)));
-    const CommunicationDevice commDev = currentDevice();
-    m_infoLauncher->setSerialFrame(commDev.type == SerialPortCommunication);
-    m_infoLauncher->setTrkServerName(commDev.portName);
+    const SymbianDevice commDev = currentDevice();
+    m_infoLauncher->setSerialFrame(commDev.type() == SerialPortCommunication);
+    m_infoLauncher->setTrkServerName(commDev.portName());
     // Prompt user
     QString message;
     const trk::PromptStartCommunicationResult src =
             S60RunConfigBluetoothStarter::startCommunication(m_infoLauncher->trkDevice(),
-                                                             commDev.portName,
-                                                             commDev.type, this,
+                                                             commDev.portName(),
+                                                             commDev.type(), this,
                                                              &message);
     switch (src) {
     case trk::PromptStartCommunicationConnected:
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
index 6d32f8af0fab15e1a49ababb50dd19b92b2b5f31..191f837ae31f334c0e71ef5de1484f31d9898690 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60devicerunconfigurationwidget.h
@@ -52,7 +52,7 @@ namespace trk {
 namespace Qt4ProjectManager {
 namespace Internal {
 
-struct CommunicationDevice;
+class SymbianDevice;
 class S60DeviceRunConfiguration;
 
 /* Configuration widget for S60 devices on serial ports that are
@@ -81,8 +81,8 @@ private slots:
     void slotWaitingForTrkClosed();
 
 private:
-    inline CommunicationDevice device(int i) const;
-    inline CommunicationDevice currentDevice() const;
+    inline SymbianDevice device(int i) const;
+    inline SymbianDevice currentDevice() const;
 
     void setDeviceInfoLabel(const QString &message, bool isError = false);
 
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60manager.cpp b/src/plugins/qt4projectmanager/qt-s60/s60manager.cpp
index e4bc1eed2ac9035af20d9c86a22d63c0f91ee083..b1d67c3209c9287fe87d4b68e111fde61779615d 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60manager.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/s60manager.cpp
@@ -98,8 +98,7 @@ S60Manager *S60Manager::instance() { return m_instance; }
 
 S60Manager::S60Manager(QObject *parent)
         : QObject(parent),
-        m_devices(new S60Devices(this)),
-        m_serialDeviceLister(new SerialDeviceLister(this))
+        m_devices(new S60Devices(this))
 {
     m_instance = this;
 #ifdef QTCREATOR_WITH_S60
@@ -127,7 +126,7 @@ S60Manager::S60Manager(QObject *parent)
     connect(m_devices, SIGNAL(qtVersionsChanged()),
             this, SLOT(updateQtVersions()));
     connect(Core::ICore::instance()->mainWindow(), SIGNAL(deviceChange()),
-            m_serialDeviceLister, SLOT(update()));
+            SymbianDeviceManager::instance(), SLOT(update()));
 }
 
 S60Manager::~S60Manager()
diff --git a/src/plugins/qt4projectmanager/qt-s60/s60manager.h b/src/plugins/qt4projectmanager/qt-s60/s60manager.h
index 1633b1bb9c9aabbb7b0591467c63cb1bca260bde..c807534f595e4eb397d52e4dc46f6170a14f616c 100644
--- a/src/plugins/qt4projectmanager/qt-s60/s60manager.h
+++ b/src/plugins/qt4projectmanager/qt-s60/s60manager.h
@@ -42,8 +42,6 @@ class ToolChain;
 namespace Qt4ProjectManager {
 namespace Internal {
 
-class SerialDeviceLister;
-
 class S60Manager : public QObject
 {
     Q_OBJECT
@@ -62,8 +60,6 @@ public:
     S60Devices::Device deviceForQtVersion(const Qt4ProjectManager::QtVersion *version) const;
     QString deviceIdFromDetectionSource(const QString &autoDetectionSource) const;
 
-    SerialDeviceLister *serialDeviceLister() const { return m_serialDeviceLister; }
-
 private slots:
     void updateQtVersions();
 
@@ -74,7 +70,6 @@ private:
 
     S60Devices *m_devices;
     QObjectList m_pluginObjects;
-    SerialDeviceLister *m_serialDeviceLister;
 };
 
 } // namespace Internal
diff --git a/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.cpp b/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.cpp
index bcfac48f4a0afe417bec7d37db17273a6d851351..495aa5366e90fa14683958a1955127ca15e10037 100644
--- a/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.cpp
+++ b/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.cpp
@@ -32,103 +32,290 @@
 #include <QtCore/QSettings>
 #include <QtCore/QStringList>
 #include <QtCore/QFileInfo>
-#include <QtGui/QApplication>
-#include <QtGui/QWidget>
-#include <QtDebug>
+#include <QtCore/QtDebug>
+#include <QtCore/QTextStream>
+#include <QtCore/QSharedData>
+#include <QtCore/QScopedPointer>
 
-using namespace Qt4ProjectManager::Internal;
+namespace Qt4ProjectManager {
+namespace Internal {
 
-namespace {
-    const char * const REGKEY_CURRENT_CONTROL_SET = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
-    const char * const USBSER = "Services/usbser/Enum";
+enum { debug = 0 };
+
+static const char REGKEY_CURRENT_CONTROL_SET[] = "HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet";
+static const char USBSER[] = "Services/usbser/Enum";
+
+const char *SymbianDeviceManager::linuxBlueToothDeviceRootC = "/dev/rfcomm";
+
+// ------------- SymbianDevice
+class SymbianDeviceData : public QSharedData {
+public:
+    SymbianDeviceData() : type(SerialPortCommunication) {}
+
+    QString portName;
+    QString friendlyName;
+    QString deviceDesc;
+    QString manufacturer;
+    DeviceCommunicationType type;
+};
+
+SymbianDevice::SymbianDevice(SymbianDeviceData *data) :
+    m_data(data)
+{
+
+}
+
+SymbianDevice::SymbianDevice() :
+    m_data(new SymbianDeviceData)
+{
+}
+SymbianDevice::SymbianDevice(const SymbianDevice &rhs) :
+        m_data(rhs.m_data)
+{
+}
+
+SymbianDevice &SymbianDevice::operator=(const SymbianDevice &rhs)
+{
+    if (this != &rhs)
+        m_data = rhs.m_data;
+    return *this;
+}
+
+SymbianDevice::~SymbianDevice()
+{
+}
+
+QString SymbianDevice::portName() const
+{
+    return m_data->portName;
+}
+
+QString SymbianDevice::friendlyName() const
+{
+    return m_data->friendlyName;
+}
+
+QString SymbianDevice::deviceDesc() const
+{
+    return m_data->deviceDesc;
+}
+
+QString SymbianDevice::manufacturer() const
+{
+    return m_data->manufacturer;
+}
+
+DeviceCommunicationType SymbianDevice::type() const
+{
+    return m_data->type;
 }
 
-const char *SerialDeviceLister::linuxBlueToothDeviceRootC = "/dev/rfcomm";
+bool SymbianDevice::isNull() const
+{
+    return !m_data->portName.isEmpty();
+}
 
-CommunicationDevice::CommunicationDevice(DeviceCommunicationType t,
-                                         const QString &p,
-                                         const QString &f) :
-    portName(p),
-    friendlyName(f),
-    type(t)
+QString SymbianDevice::toString() const
 {
+    QString rc;
+    QTextStream str(&rc);
+    format(str);
+    return rc;
 }
 
-SerialDeviceLister::SerialDeviceLister(QObject *parent)
-        : QObject(parent),
-        m_initialized(false)
+void SymbianDevice::format(QTextStream &str) const
 {
+    str << (m_data->type == BlueToothCommunication ? "Bluetooth: " : "Serial: ")
+        << m_data->portName;
+    if (!m_data->friendlyName.isEmpty()) {
+        str << " (" << m_data->friendlyName;
+        if (!m_data->deviceDesc.isEmpty())
+          str << " / " << m_data->deviceDesc;
+        str << ')';
+    }
+    if (!m_data->manufacturer.isEmpty())
+        str << " [" << m_data->manufacturer << ']';
+}
 
+// Compare by port and friendly name
+int SymbianDevice::compare(const SymbianDevice &rhs) const
+{
+    if (const int prc = m_data->portName.compare(rhs.m_data->portName))
+        return prc;
+    if (const int frc = m_data->friendlyName.compare(rhs.m_data->friendlyName))
+        return frc;
+    return 0;
+}
+
+QDebug operator<<(QDebug d, const SymbianDevice &cd)
+{
+    d.nospace() << cd.toString();
+    return d;
+}
+
+// ------------- SymbianDeviceManagerPrivate
+struct SymbianDeviceManagerPrivate {
+    SymbianDeviceManagerPrivate() : m_initialized(false) {}
+
+    bool m_initialized;
+    SymbianDeviceManager::SymbianDeviceList m_devices;
+};
+
+SymbianDeviceManager::SymbianDeviceManager(QObject *parent) :
+    QObject(parent),
+    d(new SymbianDeviceManagerPrivate)
+{
+}
+
+SymbianDeviceManager::~SymbianDeviceManager()
+{
+    delete d;
 }
 
-SerialDeviceLister::~SerialDeviceLister()
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::devices() const
 {
+    if (!d->m_initialized)
+        const_cast<SymbianDeviceManager*>(this)->update(false);
+    return d->m_devices;
 }
 
-QList<CommunicationDevice> SerialDeviceLister::communicationDevices() const
+QString SymbianDeviceManager::toString() const
 {
-    if (!m_initialized) {
-        updateSilently();
-        m_initialized = true;
+    QString rc;
+    QTextStream str(&rc);
+    const int count = d->m_devices.size();
+    for (int i = 0; i < count; i++) {
+        str << '#' << i << ' ';
+        d->m_devices.at(i).format(str);
+        str << '\n';
     }
-    return m_devices2;
+    return rc;
 }
 
-QString SerialDeviceLister::friendlyNameForPort(const QString &port) const
+QString SymbianDeviceManager::friendlyNameForPort(const QString &port) const
 {
-    foreach (const CommunicationDevice &device, m_devices2) {
-        if (device.portName == port)
-            return device.friendlyName;
+    foreach (const SymbianDevice &device, d->m_devices) {
+        if (device.portName() == port)
+            return device.friendlyName();
     }
     return QString();
 }
 
-void SerialDeviceLister::update()
+void SymbianDeviceManager::update()
 {
-    updateSilently();
-    emit updated();
+    update(true);
 }
 
-void SerialDeviceLister::updateSilently() const
+void SymbianDeviceManager::update(bool emitSignals)
 {
-    m_devices2 = serialPorts() + blueToothDevices();
+    typedef SymbianDeviceList::iterator SymbianDeviceListIterator;
+
+    if (debug)
+        qDebug(">SerialDeviceLister::update(%d)\n%s", int(emitSignals),
+               qPrintable(toString()));
+
+    d->m_initialized = true;
+    // Get ordered new list
+    SymbianDeviceList newDevices = serialPorts() + blueToothDevices();
+    if (newDevices.size() > 1)
+        qStableSort(newDevices.begin(), newDevices.end());
+    if (d->m_devices == newDevices) // Happy, nothing changed.
+        return;
+    // Merge the lists and emit the respective added/removed signals, assuming
+    // no one can plug a different device on the same port at the speed of lightning
+    if (!d->m_devices.isEmpty()) {
+        // Find deleted devices
+        for (SymbianDeviceListIterator oldIt = d->m_devices.begin(); oldIt != d->m_devices.end(); ) {
+            if (newDevices.contains(*oldIt)) {
+                ++oldIt;
+            } else {
+                const SymbianDevice toBeDeleted = *oldIt;
+                oldIt = d->m_devices.erase(oldIt);
+                if (emitSignals)
+                    emit deviceRemoved(toBeDeleted);
+            }
+        }
+    }
+    if (!newDevices.isEmpty()) {
+        // Find new devices and insert in order
+        foreach(const SymbianDevice &newDevice, newDevices) {
+            if (!d->m_devices.contains(newDevice)) {
+                d->m_devices.append(newDevice);
+                if (emitSignals)
+                    emit deviceAdded(newDevice);
+            }
+        }
+        if (d->m_devices.size() > 1)
+            qStableSort(d->m_devices.begin(), d->m_devices.end());
+    }
+    if (emitSignals)
+        emit updated();
+
+    if (debug)
+        qDebug("<SerialDeviceLister::update\n%s\n", qPrintable(toString()));
 }
 
-QList<CommunicationDevice> SerialDeviceLister::serialPorts() const
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::serialPorts() const
 {
-    QList<CommunicationDevice> rc;
-#ifdef Q_OS_WIN32
-    QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
-    const int count = registry.value(QString::fromLatin1("%1/Count").arg(USBSER)).toInt();
+    SymbianDeviceList rc;
+#ifdef Q_OS_WIN
+    const QSettings registry(REGKEY_CURRENT_CONTROL_SET, QSettings::NativeFormat);
+    const QString usbSerialRootKey = QLatin1String(USBSER) + QLatin1Char('/');
+    const int count = registry.value(usbSerialRootKey + QLatin1String("Count")).toInt();
     for (int i = 0; i < count; ++i) {
-        QString driver = registry.value(QString::fromLatin1("%1/%2").arg(USBSER).arg(i)).toString();
-        if (driver.contains("JAVACOMM")) {
-            driver.replace('\\', '/');
-            CommunicationDevice device(SerialPortCommunication);
-            device.friendlyName = registry.value(QString::fromLatin1("Enum/%1/FriendlyName").arg(driver)).toString();
-            device.portName = registry.value(QString::fromLatin1("Enum/%1/Device Parameters/PortName").arg(driver)).toString();
-            rc.append(device);
+        QString driver = registry.value(usbSerialRootKey + QString::number(i)).toString();
+        if (driver.contains(QLatin1String("JAVACOMM"))) {
+            driver.replace(QLatin1Char('\\'), QLatin1Char('/'));
+            const QString driverRootKey = QLatin1String("Enum/") + driver + QLatin1Char('/');
+            if (debug > 1)
+                qDebug() << "SerialDeviceLister::serialPorts(): Checking " << i << count
+                         << REGKEY_CURRENT_CONTROL_SET << usbSerialRootKey << driverRootKey;
+            QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+            device->type = SerialPortCommunication;
+            device->friendlyName = registry.value(driverRootKey + QLatin1String("FriendlyName")).toString();
+            device->portName = registry.value(driverRootKey + QLatin1String("Device Parameters/PortName")).toString();
+            device->deviceDesc = registry.value(driverRootKey + QLatin1String("DeviceDesc")).toString();
+            device->manufacturer = registry.value(driverRootKey + QLatin1String("Mfg")).toString();
+            rc.append(SymbianDevice(device.take()));
         }
     }
 #endif
     return rc;
 }
 
-QList<CommunicationDevice> SerialDeviceLister::blueToothDevices() const
+SymbianDeviceManager::SymbianDeviceList SymbianDeviceManager::blueToothDevices() const
 {
-    QList<CommunicationDevice> rc;
+    SymbianDeviceList rc;
 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
     // Bluetooth devices are created on connection. List the existing ones
     // or at least the first one.
     const QString prefix = QLatin1String(linuxBlueToothDeviceRootC);
     const QString friendlyFormat = QLatin1String("Bluetooth device (%1)");
     for (int d = 0; d < 4; d++) {
-        CommunicationDevice device(BlueToothCommunication, prefix + QString::number(d));
-        if (d == 0 || QFileInfo(device.portName).exists()) {
-            device.friendlyName = friendlyFormat.arg(device.portName);
-            rc.push_back(device);
+        QScopedPointer<SymbianDeviceData> device(new SymbianDeviceData);
+        device->type = BlueToothCommunication;
+        device->portName = prefix + QString::number(d);
+        if (d == 0 || QFileInfo(device->portName).exists()) {
+            device->friendlyName = friendlyFormat.arg(device->portName);
+            rc.push_back(SymbianDevice(device.take()));
         }
     }
 #endif
     return rc;
 }
+
+Q_GLOBAL_STATIC(SymbianDeviceManager, symbianDeviceManager)
+
+SymbianDeviceManager *SymbianDeviceManager::instance()
+{
+    return symbianDeviceManager();
+}
+
+QDebug operator<<(QDebug d, const SymbianDeviceManager &sdm)
+{
+    d.nospace() << sdm.toString();
+    return d;
+}
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
diff --git a/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.h b/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.h
index b1fb65507ce79d55661badefcc2b0e9ebd8430a8..6abcf045c334383bbfe672edaf19fdffffa6fdcb 100644
--- a/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.h
+++ b/src/plugins/qt4projectmanager/qt-s60/serialdevicelister.h
@@ -31,34 +31,81 @@
 #define SERIALDEVICELISTER_H
 
 #include <QtCore/QObject>
+#include <QtCore/QExplicitlySharedDataPointer>
+
+QT_BEGIN_NAMESPACE
+class QDebug;
+class QTextStream;
+QT_END_NAMESPACE
 
 namespace Qt4ProjectManager {
 namespace Internal {
 
+struct SymbianDeviceManagerPrivate;
+class SymbianDeviceData;
+
 enum DeviceCommunicationType {
     SerialPortCommunication = 0,
     BlueToothCommunication = 1
 };
 
-struct CommunicationDevice {
-    explicit CommunicationDevice(DeviceCommunicationType type = SerialPortCommunication,
-                                 const QString &portName = QString(),
-                                 const QString &friendlyName = QString());
-    QString portName;
-    QString friendlyName;
-    DeviceCommunicationType type;
+// SymbianDevice, explicitly shared.
+class SymbianDevice {
+    explicit SymbianDevice(SymbianDeviceData *data);
+    friend class SymbianDeviceManager;
+public:
+    SymbianDevice();
+    SymbianDevice(const SymbianDevice &rhs);
+    SymbianDevice &operator=(const SymbianDevice &rhs);
+    ~SymbianDevice();
+    int compare(const SymbianDevice &rhs) const;
+
+    DeviceCommunicationType type() const;
+    bool isNull() const;
+    QString portName() const;
+    QString friendlyName() const;
+
+    // Windows only.
+    QString deviceDesc() const;
+    QString manufacturer() const;
+
+    void format(QTextStream &str) const;
+    QString toString() const;
+
+private:
+    QExplicitlySharedDataPointer<SymbianDeviceData> m_data;
 };
 
-class SerialDeviceLister : public QObject
+QDebug operator<<(QDebug d, const SymbianDevice &);
+
+inline bool operator==(const SymbianDevice &d1, const SymbianDevice &d2)
+    { return d1.compare(d2) == 0; }
+inline bool operator!=(const SymbianDevice &d1, const SymbianDevice &d2)
+    { return d1.compare(d2) != 0; }
+inline bool operator<(const SymbianDevice &d1, const SymbianDevice &d2)
+    { return d1.compare(d2) < 0; }
+
+/* SymbianDeviceManager: Singleton that maintains a list of Symbian devices.
+ * and emits change signals.
+ * On Windows, the update slot must be connected to a signal
+ * emitted from an event handler listening for WM_DEVICECHANGE. */
+class SymbianDeviceManager : public QObject
 {
     Q_OBJECT
 public:
+    typedef QList<SymbianDevice> SymbianDeviceList;
+
     static const char *linuxBlueToothDeviceRootC;
 
-    explicit SerialDeviceLister(QObject *parent = 0);
-    ~SerialDeviceLister();
+    // Do not use this constructor, it is just public for Q_GLOBAL_STATIC
+    explicit SymbianDeviceManager(QObject *parent = 0);
+    virtual ~SymbianDeviceManager();
+
+    // Singleton access.
+    static SymbianDeviceManager *instance();
 
-    QList<CommunicationDevice> communicationDevices() const;
+    SymbianDeviceList devices() const;
+    QString toString() const;
 
     QString friendlyNameForPort(const QString &port) const;
 
@@ -66,17 +113,20 @@ public slots:
     void update();
 
 signals:
+    void deviceRemoved(const SymbianDevice &d);
+    void deviceAdded(const SymbianDevice &d);
     void updated();
 
 private:
-    void updateSilently() const;
-    QList<CommunicationDevice> serialPorts() const;
-    QList<CommunicationDevice> blueToothDevices() const;
+    void update(bool emitSignals);
+    SymbianDeviceList serialPorts() const;
+    SymbianDeviceList blueToothDevices() const;
 
-    mutable bool m_initialized;
-    mutable QList<CommunicationDevice> m_devices2;
+    SymbianDeviceManagerPrivate *d;
 };
 
+QDebug operator<<(QDebug d, const SymbianDeviceManager &);
+
 } // Internal
 } // Qt4ProjectManager