Some fixes to connection manager.
[jenirok] / src / common / connectionmanager.cpp
index 174228e..f7b0f32 100644 (file)
  *
  */
 
-#include <QDebug>
-#include <glib-object.h>
-#include <dbus/dbus-glib-lowlevel.h>
+#include <QtCore/QDebug>
+#include <QtCore/QTimerEvent>
+#include <QtCore/QVariant>
+#include <QtCore/QStringList>
+#include <QtGui/QApplication>
+#include <QtDBus/QDBusArgument>
+#include <QtDBus/QDBusConnection>
+#include <icd/dbus_api.h>
 #include "connectionmanager.h"
 
-ConnectionManager* ConnectionManager::instance_ = 0;
+bool ConnectionManager::connected_ = false;
 
-ConnectionManager::ConnectionManager(): connection_(0), connected_(false)
+ConnectionManager::ConnectionManager(QObject* parent): QObject(parent),
+stateReady_(false), connectionReady_(false), scanReady_(false),
+timeout_(false), numberOfConnections_(0),
+scannedConnections_(0), timer_(0), searchType_(NO_TYPE), error_(NO_ERROR), connections_(0)
 {
-    DBusError err;
-    DBusConnection* conn;
-    dbus_error_init(&err);
-    conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+    QDBusConnection systemBus = QDBusConnection::systemBus();
 
-    if(conn)
+    icd2interface_ = new QDBusInterface(ICD_DBUS_API_INTERFACE,
+                                        ICD_DBUS_API_PATH, ICD_DBUS_API_INTERFACE,
+                                        systemBus, this);
+
+    systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                      ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
+                      this, SLOT(stateChange(const QDBusMessage&)));
+
+    systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                      ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
+                      this, SLOT(connectionChange(const QDBusMessage&)));
+
+    systemBus.connect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                      ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
+                      this, SLOT(scanResult(const QDBusMessage&)));
+
+}
+
+ConnectionManager::~ConnectionManager()
+{
+    QDBusConnection systemBus = QDBusConnection::systemBus();
+
+    systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                         ICD_DBUS_API_INTERFACE, ICD_DBUS_API_STATE_SIG,
+                         this, SLOT(stateChange(const QDBusMessage&)));
+
+    systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                         ICD_DBUS_API_INTERFACE, ICD_DBUS_API_CONNECT_SIG,
+                         this, SLOT(connectionChange(const QDBusMessage&)));
+
+    systemBus.disconnect(ICD_DBUS_API_INTERFACE, ICD_DBUS_API_PATH,
+                         ICD_DBUS_API_INTERFACE, ICD_DBUS_API_SCAN_SIG,
+                         this, SLOT(scanResult(const QDBusMessage&)));
+}
+
+bool ConnectionManager::connect()
+{
+    connectionReady_ = false;
+    unsigned int flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
+    icd2interface_->call(ICD_DBUS_API_CONNECT_REQ, QVariant(flags));
+
+    waitSignal(&connectionReady_);
+
+    if(connected_)
     {
-        dbus_connection_setup_with_g_main(conn, NULL);
+        sleep(WAIT_AFTER_CONNECT);
     }
-    else
+
+    return connected_;
+}
+
+bool ConnectionManager::connect(ConnectionManager::Connection const& connection)
+{
+    return connect(connection.id);
+}
+
+bool ConnectionManager::connect(QString const& id)
+{
+    QDBusMessage msg = QDBusMessage::createMethodCall("com.nokia.icd",
+                                                      "/com/nokia/icd",
+                                                      "com.nokia.icd",
+                                                      "connect");
+    QList<QVariant> arguments;
+
+    arguments.append(QVariant(id));
+
+    unsigned int val = 0;
+
+    arguments.append(QVariant(val));
+
+    msg.setArguments(arguments);
+
+    QDBusMessage rep = QDBusConnection::systemBus().call(msg);
+
+    if(rep.type() == QDBusMessage::ErrorMessage)
     {
-        qDebug() << "Unable to connect to DBUS: " << err.message;
-        dbus_error_free(&err);
-    }
+        if(rep.errorName() == "com.nokia.icd.error.invalid_iap")
+        {
+            error_ = INVALID_IAP;
+        }
+        else
+        {
+            error_ = UNKNOWN_ERROR;
+        }
 
-    connection_ = con_ic_connection_new();
+        connected_ = false;
 
-    g_signal_connect(G_OBJECT(connection_), "connection-event", G_CALLBACK(connectionHandler), NULL);
+        return false;
+    }
+
+    connected_ = true;
 
+    sleep(WAIT_AFTER_CONNECT);
 
+    return true;
 }
 
-ConnectionManager& ConnectionManager::instance()
+bool ConnectionManager::getBestConnection(Connection& connection, ConnectionType type)
 {
-    if(!instance_)
+    QList<Connection> connections;
+
+    if(!scanConnections(connections))
     {
-        instance_ = new ConnectionManager;
+        qDebug() << "Unable to scan connections";
+        return false;
     }
 
-    return *instance_;
-}
+    if(connections.size() == 0)
+    {
+        error_ = NO_AVAILABLE_CONNECTIONS;
+        return false;
+    }
+
+    int biggestWlan = -1;
+    int biggestGprs = -1;
+    int bestWlan = -1;
+    int bestGprs = -1;
+
+    for(int i = 0; i < connections.size(); i++)
+    {
+        switch(connections.at(i).type)
+        {
+        case WLAN:
+            if(type != GPRS && connections.at(i).strength > biggestWlan)
+            {
+                biggestWlan = connections.at(i).strength;
+                bestWlan = i;
+            }
+            break;
+
+        case GPRS:
+            if(type != WLAN && connections.at(i).strength > biggestGprs)
+            {
+                biggestGprs = connections.at(i).strength;
+                bestGprs = i;
+            }
+            break;
+
+        default:
+            qDebug() << "Unknown connection type";
+        }
+    }
+
+    if(bestWlan >= 0)
+    {
+        connection = connections.at(bestWlan);
+        return true;
+    }
+    else if(bestGprs >= 0)
+    {
+        connection = connections.at(bestGprs);
+        return true;
+    }
+    else
+    {
+        error_ = NO_AVAILABLE_CONNECTIONS;
+        return false;
+    }
 
-bool ConnectionManager::connect()
-{
-    return con_ic_connection_connect(connection_, CON_IC_CONNECT_FLAG_NONE);
 }
 
-bool ConnectionManager::disconnect()
+bool ConnectionManager::disconnect(bool force)
 {
-    return con_ic_connection_disconnect(connection_);
+    if(force)
+    {
+        QDBusMessage msg = QDBusMessage::createSignal("/com/nokia/icd_ui",
+                                                      "com.nokia.icd_ui",
+                                                      "disconnect");
+
+        QList<QVariant> arguments;
+        bool val = true;
+        arguments.append(QVariant(val));
+        msg.setArguments(arguments);
+
+        bool ret = QDBusConnection::systemBus().send(msg);
+
+        if(ret)
+        {
+            connected_ = false;
+        }
+
+        return ret;
+    }
+
+    connectionReady_ = false;
+    unsigned int flags;
+
+    flags = static_cast<unsigned int>(ICD_CONNECTION_FLAG_USER_EVENT);
+
+    icd2interface_->call(ICD_DBUS_API_DISCONNECT_REQ, QVariant(flags));
+    connected_ = false;
+    return true;
 }
 
 bool ConnectionManager::isConnected()
 {
+    stateReady_ = false;
+    QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_STATE_REQ);
+
+    unsigned int numOfReplies = rep.arguments().value(0).value<unsigned int>();
+
+    if(numOfReplies == 0)
+    {
+        return false;
+    }
+
+    waitSignal(&stateReady_);
     return connected_;
 }
-void ConnectionManager::connectionHandler(ConIcConnection *connection,
-                                          ConIcConnectionEvent *event,
-                                          gpointer user_data)
+
+bool ConnectionManager::scanConnections(QList<ConnectionManager::Connection>& connections,
+                                        ConnectionManager::ConnectionType type)
 {
-    Q_UNUSED(connection);
-    Q_UNUSED(user_data);
+    unsigned int flags = static_cast<unsigned int>(ICD_SCAN_REQUEST_ACTIVE_SAVED);
+    scanReady_ = false;
+    scannedConnections_ = 0;
+    connections_ = &connections;
+    searchType_ = type;
 
-    ConIcConnectionStatus status = con_ic_connection_event_get_status(event);
+    QDBusMessage rep = icd2interface_->call(ICD_DBUS_API_SCAN_REQ,
+                                            QVariant(flags));
 
-    switch(status)
+    numberOfConnections_ = rep.arguments().value(0).toList().size();
+
+    // For some reason, during call icd2 doesn't return any connections
+    // it is going to scan. However, it still scans them so use default value
+    // of 2.
+    if(numberOfConnections_ == 0)
     {
+        numberOfConnections_ = 2;
+    }
+
+    bool ret = waitSignal(&scanReady_);
+    connections_ = 0;
+    return ret;
+}
 
-    case CON_IC_STATUS_CONNECTED:
-        qDebug() << "Connected";
-        instance_->emit connected();
-        instance_->connected_ = true;
+ConnectionManager::Error ConnectionManager::error() const
+{
+    return error_;
+}
+
+void ConnectionManager::stateChange(const QDBusMessage& rep)
+{
+    unsigned int status = rep.arguments().value(7).value<unsigned int>();
+
+    switch(status)
+    {
+    case ICD_STATE_CONNECTING:
+        break;
+    case ICD_STATE_CONNECTED:
+        connected_ = true;
+        stateReady_ = true;
+        break;
+    case ICD_STATE_DISCONNECTING:
+        break;
+    case ICD_STATE_DISCONNECTED:
+        connected_ = false;
+        stateReady_ = true;
+        break;
+    case ICD_STATE_LIMITED_CONN_ENABLED:
+        connected_ = true;
+        stateReady_ = true;
+        break;
+    case ICD_STATE_LIMITED_CONN_DISABLED:
+        connected_ = false;
+        stateReady_ = true;
+        break;
+    case ICD_STATE_SEARCH_START:
         break;
+    case ICD_STATE_SEARCH_STOP:
+        break;
+    case ICD_STATE_INTERNAL_ADDRESS_ACQUIRED:
+        break;
+    default:
+        qDebug() << "Unknown connection status";
+        break;
+    }
+
+}
 
-    case CON_IC_STATUS_DISCONNECTING:
-    case CON_IC_STATUS_NETWORK_UP:
+void ConnectionManager::connectionChange(const QDBusMessage& rep)
+{
+    unsigned int status = rep.arguments().value(6).value<unsigned int>();
+
+    switch(status)
+    {
+    case ICD_CONNECTION_SUCCESSFUL:
+        connected_ = true;
+        connectionReady_ = true;
+        break;
+    case ICD_CONNECTION_NOT_CONNECTED:
+        connected_ = false;
+        connectionReady_ = true;
         break;
+    case ICD_CONNECTION_DISCONNECTED:
+        connected_ = false;
+        connectionReady_ = true;
+        break;
+    default:
+        qDebug() << "Unknown connection status";
+        break;
+    }
+
+}
+
+void ConnectionManager::scanResult(const QDBusMessage& rep)
+{
+    if(!connections_)
+    {
+        return;
+    }
+
+    QList<QVariant> args = rep.arguments();
+
+    unsigned int status = args.value(0).value<unsigned int>();
+
+    if(status == ICD_SCAN_COMPLETE)
+    {
+        scannedConnections_++;
+    }
+
+    if(scannedConnections_ >= numberOfConnections_)
+    {
+        scanReady_ = true;
+        connections_ = 0;
+        return;
+    }
+
+    if(status != ICD_SCAN_NEW && status != ICD_SCAN_NOTIFY)
+    {
+        return;
+    }
+
+    Connection connection;
 
-    case CON_IC_STATUS_DISCONNECTED:
-        ConIcConnectionError err = con_ic_connection_event_get_error(event);
-        switch(err)
+    QString type = args.value(7).toString();
+
+    if(type == "GPRS")
+    {
+        if(searchType_ == WLAN)
         {
-        case CON_IC_CONNECTION_ERROR_NONE:
-        case CON_IC_CONNECTION_ERROR_USER_CANCELED:
-            qDebug() << "Disconnected";
-            instance_->emit disconnected();
-            instance_->connected_ = false;
-            break;
+            return;
+        }
 
-        case CON_IC_CONNECTION_ERROR_INVALID_IAP:
-            qDebug() << "Invalid IAP";
-            instance_->emit error("Invalid IAP");
-            break;
+        connection.type = GPRS;
+    }
+    else if(type == "WLAN_INFRA" || type == "WLAN_ADHOC")
+    {
+        if(searchType_ == GPRS)
+        {
+            return;
+        }
 
-        case CON_IC_CONNECTION_ERROR_CONNECTION_FAILED:
-            qDebug() << "Connection failed";
-            instance_->emit error("Connection failed");
-            break;
+        connection.type = WLAN;
+    }
+    else
+    {
+        qDebug() << "Unknown connection type: " << type;
+        return;
+    }
 
-        default:
-            break;
+    connection.id = QString(args.value(10).toByteArray());
+    connection.name = args.value(8).toString();
+    connection.strength = args.value(11).toInt();
+
+    for(int i = 0; i < connections_->size(); i++)
+    {
+        if(connections_->at(i).id == connection.id)
+        {
+            if(status == ICD_SCAN_NEW)
+            {
+                connections_->replace(i, connection);
+            }
+
+            return;
         }
-        break;
     }
+
+    connections_->push_back(connection);
+}
+
+bool ConnectionManager::waitSignal(bool* ready)
+{
+    timeout_ = false;
+    timer_ = startTimer(TIMEOUT);
+
+    while(!*ready && !timeout_)
+    {
+        QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+    }
+
+    killTimer(timer_);
+
+    if(timeout_)
+    {
+        qDebug() << "Connection request timed out";
+    }
+
+    return *ready || !timeout_;
+}
+
+void ConnectionManager::sleep(unsigned int ms)
+{
+    timeout_ = false;
+    timer_ = startTimer(ms);
+
+    while(!timeout_)
+    {
+        QCoreApplication::processEvents(QEventLoop::WaitForMoreEvents);
+    }
+
+    killTimer(timer_);
+}
+
+void ConnectionManager::timerEvent(QTimerEvent* event)
+{
+    Q_UNUSED(event);
+    killTimer(timer_);
+    timeout_ = true;
+    timer_ = 0;
 }