/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** 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.
**
** 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, 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.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qnetworksession_impl.h"
#include "symbianengine.h"

#include <es_enum.h>
#include <es_sock.h>
#include <in_sock.h>
#include <stdapis/sys/socket.h>
#include <stdapis/net/if.h>

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
#include <cmmanager.h>
#endif

#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
#include <extendedconnpref.h>
#endif

#ifndef QT_NO_BEARERMANAGEMENT

QT_BEGIN_NAMESPACE

QNetworkSessionPrivateImpl::QNetworkSessionPrivateImpl(SymbianEngine *engine)
:   CActive(CActive::EPriorityUserInput), engine(engine),
    iDynamicUnSetdefaultif(0), ipConnectionNotifier(0),
    iHandleStateNotificationsFromManager(false), iFirstSync(true), iStoppedByUser(false),
    iClosedByUser(false), iError(QNetworkSession::UnknownSessionError), iALREnabled(0),
    iConnectInBackground(false), isOpening(false)
{
    CActiveScheduler::Add(this);

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    iMobility = NULL;
#endif
    // Try to load "Open C" dll dynamically and
    // try to attach to unsetdefaultif function dynamically.
    // This is to avoid build breaks with old OpenC versions.
    if (iOpenCLibrary.Load(_L("libc")) == KErrNone) {
        iDynamicUnSetdefaultif = (TOpenCUnSetdefaultifFunction)iOpenCLibrary.Lookup(597);
    }
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - ";
    if (iDynamicUnSetdefaultif)
        qDebug() << "dynamic unsetdefaultif() is present in PIPS library. ";
    else
        qDebug() << "dynamic unsetdefaultif() not present in PIPS library. ";
#endif

    TRAP_IGNORE(iConnectionMonitor.ConnectL());
}

QNetworkSessionPrivateImpl::~QNetworkSessionPrivateImpl()
{
    isOpen = false;
    isOpening = false;

    // Cancel Connection Progress Notifications first.
    // Note: ConnectionNotifier must be destroyed before Canceling RConnection::Start()
    //       => deleting ipConnectionNotifier results RConnection::CancelProgressNotification()
    delete ipConnectionNotifier;
    ipConnectionNotifier = NULL;

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        delete iMobility;
        iMobility = NULL;
    }
#endif

    // Cancel possible RConnection::Start()
    Cancel();
    iSocketServ.Close();

    // Close global 'Open C' RConnection
    // Clears also possible unsetdefaultif() flags.
    setdefaultif(0);

    iConnectionMonitor.Close();
    iOpenCLibrary.Close();
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this)
             << " - destroyed (and setdefaultif(0))";
#endif
}

void QNetworkSessionPrivateImpl::configurationStateChanged(quint32 accessPointId, quint32 connMonId, QNetworkSession::State newState)
{
    if (iHandleStateNotificationsFromManager) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                 << "configurationStateChanged from manager for IAP : " << QString::number(accessPointId)
                 << "connMon ID : " << QString::number(connMonId) << " : to a state: " << newState
                 << "whereas my current state is: " << state;
#else
      Q_UNUSED(connMonId);
#endif
        this->newState(newState, accessPointId);
    }
}

void QNetworkSessionPrivateImpl::configurationRemoved(QNetworkConfigurationPrivatePointer config)
{
    if (!publicConfig.isValid())
        return;

    TUint32 publicNumericId =
        toSymbianConfig(privateConfiguration(publicConfig))->numericIdentifier();

    if (toSymbianConfig(config)->numericIdentifier() == publicNumericId) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                 << "configurationRemoved IAP: " << QString::number(publicNumericId) << " : going to State: Invalid";
#endif
        this->newState(QNetworkSession::Invalid, publicNumericId);
    }
}

void QNetworkSessionPrivateImpl::configurationAdded(QNetworkConfigurationPrivatePointer config)
{
    Q_UNUSED(config);
    // If session is based on service network, some other app may create new access points
    // to the SNAP --> synchronize session's state with that of interface's.
    if (!publicConfig.isValid() || publicConfig.type() != QNetworkConfiguration::ServiceNetwork)
        return;

#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                 << "configurationAdded IAP: "
                 << toSymbianConfig(privateConfiguration(config))->numericIdentifier();
#endif

        syncStateWithInterface();
}

// Function sets the state of the session to match the state
// of the underlying interface (the configuration this session is based on)
void QNetworkSessionPrivateImpl::syncStateWithInterface()
{
    if (!publicConfig.isValid())
        return;

    if (iFirstSync) {
        QObject::connect(engine,
                         SIGNAL(configurationStateChanged(quint32,quint32,QNetworkSession::State)),
                         this,
                         SLOT(configurationStateChanged(quint32,quint32,QNetworkSession::State)));
        // Listen to configuration removals, so that in case the configuration
        // this session is based on is removed, session knows to enter Invalid -state.
        QObject::connect(engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)),
                         this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer)));
        // Connect to configuration additions, so that in case a configuration is added
        // in a SNAP this session is based on, the session knows to synch its state with its
        // interface.
        QObject::connect(engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)),
                         this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer)));
    }
    // Start listening IAP state changes from QNetworkConfigurationManagerPrivate
    iHandleStateNotificationsFromManager = true;    

    // Check what is the state of the configuration this session is based on
    // and set the session in appropriate state.
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
             << "syncStateWithInterface() state of publicConfig is: " << publicConfig.state();
#endif
    switch (publicConfig.state()) {
    case QNetworkConfiguration::Active:
        newState(QNetworkSession::Connected);
        break;
    case QNetworkConfiguration::Discovered:
        newState(QNetworkSession::Disconnected);
        break;
    case QNetworkConfiguration::Defined:
        newState(QNetworkSession::NotAvailable);
        break;
    case QNetworkConfiguration::Undefined:
    default:
        newState(QNetworkSession::Invalid);
    }
}

#ifndef QT_NO_NETWORKINTERFACE
QNetworkInterface QNetworkSessionPrivateImpl::interface(TUint iapId) const
{
    QString interfaceName;

    TSoInetInterfaceInfo ifinfo;
    TPckg<TSoInetInterfaceInfo> ifinfopkg(ifinfo);
    TSoInetIfQuery ifquery;
    TPckg<TSoInetIfQuery> ifquerypkg(ifquery);
 
    // Open dummy socket for interface queries
    RSocket socket;
    TInt retVal = socket.Open(iSocketServ, _L("udp"));
    if (retVal != KErrNone) {
        return QNetworkInterface();
    }
 
    // Start enumerating interfaces
    socket.SetOpt(KSoInetEnumInterfaces, KSolInetIfCtrl);
    while(socket.GetOpt(KSoInetNextInterface, KSolInetIfCtrl, ifinfopkg) == KErrNone) {
        ifquery.iName = ifinfo.iName;
        TInt err = socket.GetOpt(KSoInetIfQueryByName, KSolInetIfQuery, ifquerypkg);
        if(err == KErrNone && ifquery.iZone[1] == iapId) { // IAP ID is index 1 of iZone
            if(ifinfo.iAddress.Address() > 0) {
                interfaceName = QString::fromUtf16(ifinfo.iName.Ptr(),ifinfo.iName.Length());
                break;
            }
        }
    }
 
    socket.Close();
 
    if (interfaceName.isEmpty()) {
        return QNetworkInterface();
    }
 
    return QNetworkInterface::interfaceFromName(interfaceName);
}
#endif

#ifndef QT_NO_NETWORKINTERFACE
QNetworkInterface QNetworkSessionPrivateImpl::currentInterface() const
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
             << "currentInterface() requested, state: " << state
             << "publicConfig validity: " << publicConfig.isValid();
    if (activeInterface.isValid())
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                 << "interface is: " << activeInterface.humanReadableName();
#endif

    if (!publicConfig.isValid() || state != QNetworkSession::Connected) {
        return QNetworkInterface();
    }
    
    return activeInterface;
}
#endif

QVariant QNetworkSessionPrivateImpl::sessionProperty(const QString& key) const
{
    if (key == "ConnectInBackground") {
        return QVariant(iConnectInBackground);
    }
    return QVariant();
}

void QNetworkSessionPrivateImpl::setSessionProperty(const QString& key, const QVariant& value)
{
    // Valid value means adding property, invalid means removing it.
    if (key == "ConnectInBackground") {
        if (value.isValid()) {
            iConnectInBackground = value.toBool();
        } else {
            iConnectInBackground = EFalse;
        }
    }
}

QString QNetworkSessionPrivateImpl::errorString() const
{
    switch (iError) {
    case QNetworkSession::UnknownSessionError:
        return tr("Unknown session error.");
    case QNetworkSession::SessionAbortedError:
        return tr("The session was aborted by the user or system.");
    case QNetworkSession::OperationNotSupportedError:
        return tr("The requested operation is not supported by the system.");
    case QNetworkSession::InvalidConfigurationError:
        return tr("The specified configuration cannot be used.");
    case QNetworkSession::RoamingError:
        return tr("Roaming was aborted or is not possible.");
    }
 
    return QString();
}

QNetworkSession::SessionError QNetworkSessionPrivateImpl::error() const
{
    return iError;
}

void QNetworkSessionPrivateImpl::open()
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                << "open() called, session state is: " << state << " and isOpen is: "
                << isOpen << isOpening;
#endif

    if (isOpen || isOpening)
        return;

    isOpening = true;

    // Stop handling IAP state change signals from QNetworkConfigurationManagerPrivate
    // => RConnection::ProgressNotification will be used for IAP/SNAP monitoring
    iHandleStateNotificationsFromManager = false;

    // Configuration may have been invalidated after session creation by platform
    // (e.g. configuration has been deleted).
    if (!publicConfig.isValid()) {
        newState(QNetworkSession::Invalid);
        iError = QNetworkSession::InvalidConfigurationError;
        emit QNetworkSessionPrivate::error(iError);
        return;
    }
    // If opening a undefined configuration, session emits error and enters
    // NotAvailable -state. Note that we will try ones in 'defined' state to avoid excessive
    // need for WLAN scans (via updateConfigurations()), because user may have walked
    // into a WLAN range, but periodic background scan has not occurred yet -->
    // we don't want to force application to make frequent updateConfigurations() calls
    // to be able to try if e.g. home WLAN is available.
    if (publicConfig.state() == QNetworkConfiguration::Undefined) {
        newState(QNetworkSession::NotAvailable);
        iError = QNetworkSession::InvalidConfigurationError;
        emit QNetworkSessionPrivate::error(iError);
        return;
    }
    // Clear possible previous states
    iStoppedByUser = false;
    iClosedByUser = false;

    TInt error = iSocketServ.Connect();
    if (error != KErrNone) {
        // Could not open RSocketServ
        newState(QNetworkSession::Invalid);
        iError = QNetworkSession::UnknownSessionError;
        emit QNetworkSessionPrivate::error(iError);
        syncStateWithInterface();    
        return;
    }
    
    error = iConnection.Open(iSocketServ);
    if (error != KErrNone) {
        // Could not open RConnection
        iSocketServ.Close();
        newState(QNetworkSession::Invalid);
        iError = QNetworkSession::UnknownSessionError;
        emit QNetworkSessionPrivate::error(iError);
        syncStateWithInterface();    
        return;
    }
    
    // Use RConnection::ProgressNotification for IAP/SNAP monitoring
    // (<=> ConnectionProgressNotifier uses RConnection::ProgressNotification)
    if (!ipConnectionNotifier) {
        ipConnectionNotifier = new ConnectionProgressNotifier(*this,iConnection);
    }
    if (ipConnectionNotifier) {
        ipConnectionNotifier->StartNotifications();
    }
    
    if (publicConfig.type() == QNetworkConfiguration::InternetAccessPoint) {
            SymbianNetworkConfigurationPrivate *symbianConfig =
                toSymbianConfig(privateConfiguration(publicConfig));

#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
            // With One Click Connectivity (Symbian^3 onwards) it is possible
            // to connect silently, without any popups.
            TConnPrefList pref;
            TExtendedConnPref prefs;

            prefs.SetIapId(symbianConfig->numericIdentifier());
            if (iConnectInBackground) {
                prefs.SetNoteBehaviour( TExtendedConnPref::ENoteBehaviourConnSilent );
            }
            pref.AppendL(&prefs);
#else
            TCommDbConnPref pref;
            pref.SetDialogPreference(ECommDbDialogPrefDoNotPrompt);

            pref.SetIapId(symbianConfig->numericIdentifier());
#endif
            iConnection.Start(pref, iStatus);
            if (!IsActive()) {
                SetActive();
            }
            // Avoid flip flop of states if the configuration is already
            // active. IsOpen/opened() will indicate when ready.
            if (state != QNetworkSession::Connected) {
                newState(QNetworkSession::Connecting);
            }
    } else if (publicConfig.type() == QNetworkConfiguration::ServiceNetwork) {
        SymbianNetworkConfigurationPrivate *symbianConfig =
            toSymbianConfig(privateConfiguration(publicConfig));

#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
        // On Symbian^3 if service network is not reachable, it triggers a UI (aka EasyWLAN) where
        // user can create new IAPs. To detect this, we need to store the number of IAPs
        // there was before connection was started.
        iKnownConfigsBeforeConnectionStart = engine->accessPointConfigurationIdentifiers();
        TConnPrefList snapPref;
        TExtendedConnPref prefs;
        prefs.SetSnapId(symbianConfig->numericIdentifier());
        if (iConnectInBackground) {
            prefs.SetNoteBehaviour( TExtendedConnPref::ENoteBehaviourConnSilent );
        }
        snapPref.AppendL(&prefs);
#else
        TConnSnapPref snapPref(symbianConfig->numericIdentifier());
#endif
        iConnection.Start(snapPref, iStatus);
        if (!IsActive()) {
            SetActive();
        }
        // Avoid flip flop of states if the configuration is already
        // active. IsOpen/opened() will indicate when ready.
        if (state != QNetworkSession::Connected) {
            newState(QNetworkSession::Connecting);
        }
    } else if (publicConfig.type() == QNetworkConfiguration::UserChoice) {
        iKnownConfigsBeforeConnectionStart = engine->accessPointConfigurationIdentifiers();
        iConnection.Start(iStatus);
        if (!IsActive()) {
            SetActive();
        }
        newState(QNetworkSession::Connecting);
    }
 
    if (error != KErrNone) {
        isOpen = false;
        isOpening = false;
        iError = QNetworkSession::UnknownSessionError;
        emit QNetworkSessionPrivate::error(iError);
        if (ipConnectionNotifier) {
            ipConnectionNotifier->StopNotifications();
        }
        syncStateWithInterface();    
    }
}

TUint QNetworkSessionPrivateImpl::iapClientCount(TUint aIAPId) const
{
    TRequestStatus status;
    TUint connectionCount;
    iConnectionMonitor.GetConnectionCount(connectionCount, status);
    User::WaitForRequest(status);
    if (status.Int() == KErrNone) {
        for (TUint i = 1; i <= connectionCount; i++) {
            TUint connectionId;
            TUint subConnectionCount;
            iConnectionMonitor.GetConnectionInfo(i, connectionId, subConnectionCount);
            TUint apId;
            iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status);
            User::WaitForRequest(status);
            if (apId == aIAPId) {
                TConnMonClientEnumBuf buf;
                iConnectionMonitor.GetPckgAttribute(connectionId, 0, KClientInfo, buf, status);
                User::WaitForRequest(status);
                if (status.Int() == KErrNone) {
                    return buf().iCount;
                }
            }
        }
    }
    return 0;
}

void QNetworkSessionPrivateImpl::close(bool allowSignals)
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "close() called, session state is: " << state << " and isOpen is : "
            << isOpen;
#endif

    if (!isOpen && state != QNetworkSession::Connecting) {
        return;
    }
    // Mark this session as closed-by-user so that we are able to report
    // distinguish between stop() and close() state transitions
    // when reporting.
    iClosedByUser = true;
    isOpen = false;
    isOpening = false;

    serviceConfig = QNetworkConfiguration();
    
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        delete iMobility;
        iMobility = NULL;
    }
#endif

    if (ipConnectionNotifier && !iHandleStateNotificationsFromManager) {
        ipConnectionNotifier->StopNotifications();
        // Start handling IAP state change signals from QNetworkConfigurationManagerPrivate
        iHandleStateNotificationsFromManager = true;
    }
    
    Cancel(); // closes iConnection
    iSocketServ.Close();

    // Close global 'Open C' RConnection. If OpenC supports,
    // close the defaultif for good to avoid difficult timing
    // and bouncing issues of network going immediately back up
    //  because of e.g. select() thread etc.
    if (iDynamicUnSetdefaultif) {
        iDynamicUnSetdefaultif();
    } else {
        setdefaultif(0);
    }

    // If UserChoice, go down immediately. If some other configuration,
    // go down immediately if there is no reports expected from the platform;
    // in practice Connection Monitor is aware of connections only after
    // KFinishedSelection event, and hence reports only after that event, but
    // that does not seem to be trusted on all Symbian versions --> safest
    // to go down.
    if (publicConfig.type() == QNetworkConfiguration::UserChoice || state == QNetworkSession::Connecting) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                 << "going disconnected right away, since either UserChoice or Connecting";
#endif
        newState(QNetworkSession::Closing);
        newState(QNetworkSession::Disconnected);
    }
    if (allowSignals) {
        emit closed();
    }
}

void QNetworkSessionPrivateImpl::stop()
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "stop() called, session state is: " << state << " and isOpen is : "
            << isOpen;
#endif
    if (!isOpen &&
        publicConfig.isValid() &&
        publicConfig.type() == QNetworkConfiguration::InternetAccessPoint) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "since session is not open, using RConnectionMonitor to stop() the interface";
#endif
        iStoppedByUser = true;
        // If the publicConfig is type of IAP, enumerate through connections at
        // connection monitor. If publicConfig is active in that list, stop it.
        // Otherwise there is nothing to stop. Note: because this QNetworkSession is not open,
        // activeConfig is not usable.
        TUint count;
        TRequestStatus status;
        iConnectionMonitor.GetConnectionCount(count, status);
        User::WaitForRequest(status);
        if (status.Int() != KErrNone) {
            return;
        }
        TUint numSubConnections; // Not used but needed by GetConnectionInfo i/f
        TUint connectionId;
        for (TUint i = 1; i <= count; ++i) {
            // Get (connection monitor's assigned) connection ID
            TInt ret = iConnectionMonitor.GetConnectionInfo(i, connectionId, numSubConnections);            
            if (ret == KErrNone) {
                SymbianNetworkConfigurationPrivate *symbianConfig =
                    toSymbianConfig(privateConfiguration(publicConfig));

                // See if connection Id matches with our Id. If so, stop() it.
                if (symbianConfig->connectionIdentifier() == connectionId) {
                    ret = iConnectionMonitor.SetBoolAttribute(connectionId,
                                                              0, // subConnectionId don't care
                                                              KConnectionStop,
                                                              ETrue);
                }
            }
            // Enter disconnected state right away since the session is not even open.
            // Symbian^3 connection monitor does not emit KLinkLayerClosed when
            // connection is stopped via connection monitor.
            newState(QNetworkSession::Disconnected);
        }
    } else if (isOpen) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "since session is open, using RConnection to stop() the interface";
#endif
        // Since we are open, use RConnection to stop the interface
        isOpen = false;
        isOpening = false;
        iStoppedByUser = true;
        newState(QNetworkSession::Closing);
        if (ipConnectionNotifier) {
            ipConnectionNotifier->StopNotifications();
            // Start handling IAP state change signals from QNetworkConfigurationManagerPrivate
            iHandleStateNotificationsFromManager = true;
        }
        iConnection.Stop(RConnection::EStopAuthoritative);
        isOpen = true;
        isOpening = false;
        close(false);
        emit closed();
    }
}

void QNetworkSessionPrivateImpl::migrate()
{
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        // Close global 'Open C' RConnection. If openC supports, use the 'heavy'
        // version to block all subsequent requests.
        if (iDynamicUnSetdefaultif) {
            iDynamicUnSetdefaultif();
        } else {
            setdefaultif(0);
        }
        // Start migrating to new IAP
        iMobility->MigrateToPreferredCarrier();
    }
#endif
}

void QNetworkSessionPrivateImpl::ignore()
{
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        iMobility->IgnorePreferredCarrier();

        if (!iALRUpgradingConnection) {
            newState(QNetworkSession::Disconnected);
        } else {
            newState(QNetworkSession::Connected,iOldRoamingIap);
        }
    }
#endif
}

void QNetworkSessionPrivateImpl::accept()
{
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        iMobility->NewCarrierAccepted();

        QNetworkConfiguration newActiveConfig = activeConfiguration(iNewRoamingIap);

        // Use name of the new IAP to open global 'Open C' RConnection
        QByteArray nameAsByteArray = newActiveConfig.name().toUtf8();
        ifreq ifr;
        memset(&ifr, 0, sizeof(struct ifreq));
        strcpy(ifr.ifr_name, nameAsByteArray.constData());
        setdefaultif(&ifr);

        newState(QNetworkSession::Connected, iNewRoamingIap);
    }
#endif
}

void QNetworkSessionPrivateImpl::reject()
{
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (iMobility) {
        iMobility->NewCarrierRejected();

        if (!iALRUpgradingConnection) {
            newState(QNetworkSession::Disconnected);
        } else {
            QNetworkConfiguration newActiveConfig = activeConfiguration(iOldRoamingIap);

            // Use name of the old IAP to open global 'Open C' RConnection
            QByteArray nameAsByteArray = newActiveConfig.name().toUtf8();
            ifreq ifr;
            memset(&ifr, 0, sizeof(struct ifreq));
            strcpy(ifr.ifr_name, nameAsByteArray.constData());
            setdefaultif(&ifr);

            newState(QNetworkSession::Connected, iOldRoamingIap);
        }
    }
#endif
}

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
void QNetworkSessionPrivateImpl::PreferredCarrierAvailable(TAccessPointInfo aOldAPInfo,
                                                       TAccessPointInfo aNewAPInfo,
                                                       TBool aIsUpgrade,
                                                       TBool aIsSeamless)
{
    iOldRoamingIap = aOldAPInfo.AccessPoint();
    iNewRoamingIap = aNewAPInfo.AccessPoint();
    newState(QNetworkSession::Roaming);
    if (iALREnabled > 0) {
        iALRUpgradingConnection = aIsUpgrade;
        QList<QNetworkConfiguration> configs = publicConfig.children();
        for (int i=0; i < configs.count(); i++) {
            SymbianNetworkConfigurationPrivate *symbianConfig =
                toSymbianConfig(privateConfiguration(configs[i]));

            if (symbianConfig->numericIdentifier() == aNewAPInfo.AccessPoint()) {
                // Any slot connected to the signal might throw an std::exception,
                // which must not propagate into Symbian code (this function is a callback
                // from platform). We could convert exception to a symbian Leave, but since the
                // prototype of this function bans this (no trailing 'L'), we just catch
                // and drop.
                QT_TRY {
                    emit preferredConfigurationChanged(configs[i], aIsSeamless);
                }
                QT_CATCH (std::exception&) {}
            }
        }
    } else {
        migrate();
    }
}

void QNetworkSessionPrivateImpl::NewCarrierActive(TAccessPointInfo /*aNewAPInfo*/, TBool /*aIsSeamless*/)
{
    if (iALREnabled > 0) {
        QT_TRY {
            emit newConfigurationActivated();
        }
        QT_CATCH (std::exception&) {}
    } else {
        accept();
    }
}

void QNetworkSessionPrivateImpl::Error(TInt /*aError*/)
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "roaming Error() occured, isOpen is: " << isOpen;
#endif
    if (isOpen) {
        isOpen = false;
        isOpening = false;
        activeConfig = QNetworkConfiguration();
        serviceConfig = QNetworkConfiguration();
        iError = QNetworkSession::RoamingError;
        emit QNetworkSessionPrivate::error(iError);
        Cancel();
        if (ipConnectionNotifier) {
            ipConnectionNotifier->StopNotifications();
        }
        QT_TRY {
            syncStateWithInterface();
            // In some cases IAP is still in Connected state when
            // syncStateWithInterface(); is called
            // => Following call makes sure that Session state
            //    changes immediately to Disconnected.
            newState(QNetworkSession::Disconnected);
            emit closed();
        }
        QT_CATCH (std::exception&) {}
    } else if (iStoppedByUser) {
        // If the user of this session has called the stop() and
        // configuration is based on internet SNAP, this needs to be
        // done here because platform might roam.
        QT_TRY {
            newState(QNetworkSession::Disconnected);
        }
        QT_CATCH (std::exception&) {}
    }
}
#endif

void QNetworkSessionPrivateImpl::setALREnabled(bool enabled)
{
    if (enabled) {
        iALREnabled++;
    } else {
        iALREnabled--;
    }
}

QNetworkConfiguration QNetworkSessionPrivateImpl::bestConfigFromSNAP(const QNetworkConfiguration& snapConfig) const
{
    QNetworkConfiguration config;
    QList<QNetworkConfiguration> subConfigurations = snapConfig.children();
    for (int i = 0; i < subConfigurations.count(); i++ ) {
        if (subConfigurations[i].state() == QNetworkConfiguration::Active) {
            config = subConfigurations[i];
            break;
        } else if (!config.isValid() && subConfigurations[i].state() == QNetworkConfiguration::Discovered) {
            config = subConfigurations[i];
        }
    }
    if (!config.isValid() && subConfigurations.count() > 0) {
        config = subConfigurations[0];
    }
    return config;
}

quint64 QNetworkSessionPrivateImpl::bytesWritten() const
{
    return transferredData(KUplinkData);
}

quint64 QNetworkSessionPrivateImpl::bytesReceived() const
{
    return transferredData(KDownlinkData);
}

quint64 QNetworkSessionPrivateImpl::transferredData(TUint dataType) const
{
    if (!publicConfig.isValid()) {
        return 0;
    }
    
    QNetworkConfiguration config;
    if (publicConfig.type() == QNetworkConfiguration::UserChoice) {
        if (serviceConfig.isValid()) {
            config = serviceConfig;
        } else {
            if (activeConfig.isValid()) {
                config = activeConfig;
            }
        }
    } else {
        config = publicConfig;
    }

    if (!config.isValid()) {
        return 0;
    }
    
    TUint count;
    TRequestStatus status;
    iConnectionMonitor.GetConnectionCount(count, status);
    User::WaitForRequest(status);
    if (status.Int() != KErrNone) {
        return 0;
    }
    
    TUint transferredData = 0;
    TUint numSubConnections;
    TUint connectionId;
    bool configFound;
    for (TUint i = 1; i <= count; i++) {
        TInt ret = iConnectionMonitor.GetConnectionInfo(i, connectionId, numSubConnections);
        if (ret == KErrNone) {
            TUint apId;
            iConnectionMonitor.GetUintAttribute(connectionId, 0, KIAPId, apId, status);
            User::WaitForRequest(status);
            if (status.Int() == KErrNone) {
                configFound = false;
                if (config.type() == QNetworkConfiguration::ServiceNetwork) {
                    QList<QNetworkConfiguration> configs = config.children();
                    for (int i=0; i < configs.count(); i++) {
                        SymbianNetworkConfigurationPrivate *symbianConfig =
                            toSymbianConfig(privateConfiguration(configs[i]));

                        if (symbianConfig->numericIdentifier() == apId) {
                            configFound = true;
                            break;
                        }
                    }
                } else {
                    SymbianNetworkConfigurationPrivate *symbianConfig =
                        toSymbianConfig(privateConfiguration(config));

                    if (symbianConfig->numericIdentifier() == apId)
                        configFound = true;
                }
                if (configFound) {
                    TUint tData;
                    iConnectionMonitor.GetUintAttribute(connectionId, 0, dataType, tData, status );
                    User::WaitForRequest(status);
                    if (status.Int() == KErrNone) {
                    transferredData += tData;
                    }
                }
            }
        }
    }
    
    return transferredData;
}

quint64 QNetworkSessionPrivateImpl::activeTime() const
{
    if (!isOpen || startTime.isNull()) {
        return 0;
    }
    return startTime.secsTo(QDateTime::currentDateTime());
}

QNetworkConfiguration QNetworkSessionPrivateImpl::activeConfiguration(TUint32 iapId) const
{
    if (iapId == 0) {
        _LIT(KSetting, "IAP\\Id");
        iConnection.GetIntSetting(KSetting, iapId);
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
        // Check if this is an Easy WLAN configuration. On Symbian^3 RConnection may report
        // the used configuration as 'EasyWLAN' IAP ID if someone has just opened the configuration
        // from WLAN Scan dialog, _and_ that connection is still up. We need to find the
        // real matching configuration. Function alters the Easy WLAN ID to real IAP ID (only if
        // easy WLAN):
        easyWlanTrueIapId(iapId);
#endif
    }

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    if (publicConfig.type() == QNetworkConfiguration::ServiceNetwork) {
        // Try to search IAP from the used SNAP using IAP Id
        QList<QNetworkConfiguration> children = publicConfig.children();
        for (int i=0; i < children.count(); i++) {
            SymbianNetworkConfigurationPrivate *childConfig =
                toSymbianConfig(privateConfiguration(children[i]));

            if (childConfig->numericIdentifier() == iapId)
                return children[i];
        }

        // Given IAP Id was not found from the used SNAP
        // => Try to search matching IAP using mappingName
        //    mappingName contains:
        //      1. "Access point name" for "Packet data" Bearer
        //      2. "WLAN network name" (= SSID) for "Wireless LAN" Bearer
        //      3. "Dial-up number" for "Data call Bearer" or "High Speed (GSM)" Bearer
        //    <=> Note: It's possible that in this case reported IAP is
        //              clone of the one of the IAPs of the used SNAP
        //              => If mappingName matches, clone has been found
        QNetworkConfiguration pt = QNetworkConfigurationManager().configurationFromIdentifier(
                QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)));

        SymbianNetworkConfigurationPrivate *symbianConfig =
            toSymbianConfig(privateConfiguration(pt));
        if (symbianConfig) {
            for (int i=0; i < children.count(); i++) {
                SymbianNetworkConfigurationPrivate *childConfig =
                    toSymbianConfig(privateConfiguration(children[i]));

                if (childConfig->configMappingName() == symbianConfig->configMappingName()) {
                    return children[i];
                }
            }
        } else {
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
            // On Symbian^3 (only, not earlier or Symbian^4) if the SNAP was not reachable, it
            // triggers user choice type of activity (EasyWLAN). As a result, a new IAP may be
            // created, and hence if was not found yet. Therefore update configurations and see if
            // there is something new.

            // 1. Update knowledge from the databases.
            if (thread() != engine->thread())
                QMetaObject::invokeMethod(engine, "requestUpdate", Qt::BlockingQueuedConnection);
            else
                engine->requestUpdate();

            // 2. Check if new configuration was created during connection creation
            QList<QString> knownConfigs = engine->accessPointConfigurationIdentifiers();
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
            qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                    << "opened configuration was not known beforehand, looking for new.";
#endif
            if (knownConfigs.count() > iKnownConfigsBeforeConnectionStart.count()) {
                // Configuration count increased => new configuration was created
                // => Search new, created configuration
                QString newIapId;
                for (int i=0; i < iKnownConfigsBeforeConnectionStart.count(); i++) {
                    if (knownConfigs[i] != iKnownConfigsBeforeConnectionStart[i]) {
                        newIapId = knownConfigs[i];
                        break;
                    }
                }
                if (newIapId.isEmpty()) {
                    newIapId = knownConfigs[knownConfigs.count()-1];
                }
                pt = QNetworkConfigurationManager().configurationFromIdentifier(newIapId);
                if (pt.isValid()) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                            << "new configuration was found, name, IAP id: " << pt.name() << pt.identifier();
#endif
                    return pt;
                }
            }
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
            qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                    << "configuration was not found, returning invalid.";
#endif
#endif
            // Given IAP Id was not found from known IAPs array
            return QNetworkConfiguration();
        }
        // Matching IAP was not found from used SNAP
        // => IAP from another SNAP is returned
        //    (Note: Returned IAP matches to given IAP Id)
        return pt;
    }
#endif
    if (publicConfig.type() == QNetworkConfiguration::UserChoice) {
        if (engine) {
            QNetworkConfiguration pt = QNetworkConfigurationManager().configurationFromIdentifier(
                    QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)));
            // Try to found User Selected IAP from known IAPs (accessPointConfigurations)
            if (pt.isValid()) {
                return pt;
            } else {
                // Check if new (WLAN) IAP was created in IAP/SNAP dialog
                // 1. Sync internal configurations array to commsdb first
                if (thread() != engine->thread()) {
                    QMetaObject::invokeMethod(engine, "requestUpdate",
                                              Qt::BlockingQueuedConnection);
                } else {
                    engine->requestUpdate();
                }
                // 2. Check if new configuration was created during connection creation
                QStringList knownConfigs = engine->accessPointConfigurationIdentifiers();
                if (knownConfigs.count() > iKnownConfigsBeforeConnectionStart.count()) {
                    // Configuration count increased => new configuration was created
                    // => Search new, created configuration
                    QString newIapId;
                    for (int i=0; i < iKnownConfigsBeforeConnectionStart.count(); i++) {
                        if (knownConfigs[i] != iKnownConfigsBeforeConnectionStart[i]) {
                            newIapId = knownConfigs[i];
                            break;
                        }
                    }
                    if (newIapId.isEmpty()) {
                        newIapId = knownConfigs[knownConfigs.count()-1];
                    }
                    pt = QNetworkConfigurationManager().configurationFromIdentifier(newIapId);
                    if (pt.isValid())
                        return pt;
                }
            }
        }
        return QNetworkConfiguration();
    }

    return publicConfig;
}

void QNetworkSessionPrivateImpl::RunL()
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
            << "RConnection::RunL with status code: " << iStatus.Int();
#endif
    TInt statusCode = iStatus.Int();

    switch (statusCode) {
        case KErrNone: // Connection created successfully
            {
            TInt error = KErrNone;
            QNetworkConfiguration newActiveConfig = activeConfiguration();
            if (!newActiveConfig.isValid()) {
                // RConnection startup was successfull but no configuration
                // was found. That indicates that user has chosen to create a
                // new WLAN configuration (from scan results), but that new
                // configuration does not have access to Internet (Internet
                // Connectivity Test, ICT, failed).
                error = KErrGeneral;
            } else {
                // Use name of the IAP to open global 'Open C' RConnection
                ifreq ifr;
                memset(&ifr, 0, sizeof(struct ifreq));
                QByteArray nameAsByteArray = newActiveConfig.name().toUtf8();
                strcpy(ifr.ifr_name, nameAsByteArray.constData());
                error = setdefaultif(&ifr);
            }
            if (error != KErrNone) {
                isOpen = false;
                isOpening = false;
                iError = QNetworkSession::UnknownSessionError;
                QT_TRYCATCH_LEAVING(emit QNetworkSessionPrivate::error(iError));
                if (ipConnectionNotifier) {
                    ipConnectionNotifier->StopNotifications();
                }
                if (!newActiveConfig.isValid()) {
                    // No valid configuration, bail out.
                    // Status updates from QNCM won't be received correctly
                    // because there is no configuration to associate them with so transit here.
                    iConnection.Close();
                    newState(QNetworkSession::Closing);
                    newState(QNetworkSession::Disconnected);
                } else {
                    Cancel();
                }
                QT_TRYCATCH_LEAVING(syncStateWithInterface());
                return;
            }
 
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
            if (publicConfig.type() == QNetworkConfiguration::ServiceNetwork) {
                // Activate ALR monitoring
                iMobility = CActiveCommsMobilityApiExt::NewL(iConnection, *this);
            }
#endif

            isOpen = true;
            isOpening = false;
            activeConfig = newActiveConfig;

            SymbianNetworkConfigurationPrivate *symbianConfig =
                toSymbianConfig(privateConfiguration(activeConfig));

#ifndef QT_NO_NETWORKINTERFACE
            activeInterface = interface(symbianConfig->numericIdentifier());
#endif
            if (publicConfig.type() == QNetworkConfiguration::UserChoice) {
                serviceConfig = QNetworkConfigurationManager()
                    .configurationFromIdentifier(activeConfig.identifier());
            }

            startTime = QDateTime::currentDateTime();

            QT_TRYCATCH_LEAVING({
                    newState(QNetworkSession::Connected);
                    emit quitPendingWaitsForOpened();
                });
            }
            break;
        case KErrNotFound: // Connection failed
            isOpen = false;
            isOpening = false;
            activeConfig = QNetworkConfiguration();
            serviceConfig = QNetworkConfiguration();
            iError = QNetworkSession::InvalidConfigurationError;
            QT_TRYCATCH_LEAVING(emit QNetworkSessionPrivate::error(iError));
            Cancel();
            if (ipConnectionNotifier) {
                ipConnectionNotifier->StopNotifications();
            }
            QT_TRYCATCH_LEAVING(syncStateWithInterface());
            break;
        case KErrCancel: // Connection attempt cancelled
        case KErrAlreadyExists: // Connection already exists
        default:
            isOpen = false;
            isOpening = false;
            activeConfig = QNetworkConfiguration();
            serviceConfig = QNetworkConfiguration();
            if (statusCode == KErrCancel) {
                iError = QNetworkSession::SessionAbortedError;
            } else if (publicConfig.state() == QNetworkConfiguration::Undefined ||
                publicConfig.state() == QNetworkConfiguration::Defined) {
                iError = QNetworkSession::InvalidConfigurationError;
            } else {
                iError = QNetworkSession::UnknownSessionError;
            }
            QT_TRYCATCH_LEAVING(emit QNetworkSessionPrivate::error(iError));
            Cancel();
            if (ipConnectionNotifier) {
                ipConnectionNotifier->StopNotifications();
            }
            QT_TRYCATCH_LEAVING(syncStateWithInterface());
            break;
    }
}

void QNetworkSessionPrivateImpl::DoCancel()
{
    iConnection.Close();
}

// Enters newState if feasible according to current state.
// AccessPointId may be given as parameter. If it is zero, state-change is assumed to
// concern this session's configuration. If non-zero, the configuration is looked up
// and checked if it matches the configuration this session is based on.
bool QNetworkSessionPrivateImpl::newState(QNetworkSession::State newState, TUint accessPointId)
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
             << "NEW STATE, IAP ID : " << QString::number(accessPointId) << " , newState : " << QString::number(newState);
#endif
    // Make sure that activeConfig is always updated when SNAP is signaled to be
    // connected.
    if (isOpen && publicConfig.type() == QNetworkConfiguration::ServiceNetwork &&
        newState == QNetworkSession::Connected) {
        activeConfig = activeConfiguration(accessPointId);

#ifndef QT_NO_NETWORKINTERFACE
        SymbianNetworkConfigurationPrivate *symbianConfig =
            toSymbianConfig(privateConfiguration(activeConfig));

        activeInterface = interface(symbianConfig->numericIdentifier());
#endif

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
        // Use name of the IAP to set default IAP
        QByteArray nameAsByteArray = activeConfig.name().toUtf8();
        ifreq ifr;
        strcpy(ifr.ifr_name, nameAsByteArray.constData());

        setdefaultif(&ifr);
#endif
    }

    // Make sure that same state is not signaled twice in a row.
    if (state == newState) {
        return true;
    }

    // Make sure that Connecting state does not overwrite Roaming state
    if (state == QNetworkSession::Roaming && newState == QNetworkSession::Connecting) {
        return false;
    }
    
    // Make sure that Connected state is not reported when Connection is
    // already Closing.
    // Note: Stopping connection results sometimes KLinkLayerOpen
    //       to be reported first (just before KLinkLayerClosed).
    if (state == QNetworkSession::Closing && newState == QNetworkSession::Connected) {
        return false;
    }

    // Make sure that some lagging 'closing' state-changes do not overwrite
    // if we are already disconnected or closed.
    if (state == QNetworkSession::Disconnected && newState == QNetworkSession::Closing) {
        return false;
    }

    // Make sure that some lagging 'connecting' state-changes do not overwrite
    // if we are already connected (may righfully still happen with roaming though).
    if (state == QNetworkSession::Connected && newState == QNetworkSession::Connecting) {
        return false;
    }

    bool emitSessionClosed = false;

    // If we abruptly go down and user hasn't closed the session, we've been aborted.
    // Note that session may be in 'closing' state and not in 'connected' state, because
    // depending on platform the platform may report KConfigDaemonStartingDeregistration
    // event before KLinkLayerClosed
    if ((isOpen && state == QNetworkSession::Connected && newState == QNetworkSession::Disconnected) ||
        (isOpen && !iClosedByUser && newState == QNetworkSession::Disconnected)) {
        // Active & Connected state should change directly to Disconnected state
        // only when something forces connection to close (eg. when another
        // application or session stops connection or when network drops
        // unexpectedly).
        isOpen = false;
        isOpening = false;
        activeConfig = QNetworkConfiguration();
        serviceConfig = QNetworkConfiguration();
        iError = QNetworkSession::SessionAbortedError;
        emit QNetworkSessionPrivate::error(iError);
        Cancel();
        if (ipConnectionNotifier) {
            ipConnectionNotifier->StopNotifications();
        }
        // Start handling IAP state change signals from QNetworkConfigurationManagerPrivate
        iHandleStateNotificationsFromManager = true;
        emitSessionClosed = true; // Emit SessionClosed after state change has been reported
    }

    bool retVal = false;
    if (accessPointId == 0) {
        state = newState;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed A to: " << state;
#endif
        emit stateChanged(state);
        retVal = true;
    } else {
        if (publicConfig.type() == QNetworkConfiguration::InternetAccessPoint) {
            SymbianNetworkConfigurationPrivate *symbianConfig =
                toSymbianConfig(privateConfiguration(publicConfig));

            if (symbianConfig->numericIdentifier() == accessPointId) {
                state = newState;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed B to: " << state;
#endif
                emit stateChanged(state);
                retVal = true;
            }
        } else if (publicConfig.type() == QNetworkConfiguration::UserChoice && isOpen) {
            SymbianNetworkConfigurationPrivate *symbianConfig =
                toSymbianConfig(privateConfiguration(activeConfig));

            if (symbianConfig->numericIdentifier() == accessPointId) {
                state = newState;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed C to: " << state;
#endif
                emit stateChanged(state);
                retVal = true;
            }
        } else if (publicConfig.type() == QNetworkConfiguration::ServiceNetwork) {
            QList<QNetworkConfiguration> subConfigurations = publicConfig.children();
            for (int i = 0; i < subConfigurations.count(); i++) {
                SymbianNetworkConfigurationPrivate *symbianConfig =
                    toSymbianConfig(privateConfiguration(subConfigurations[i]));

                if (symbianConfig->numericIdentifier() == accessPointId) {
                    if (newState != QNetworkSession::Disconnected) {
                        state = newState;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                        qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed D  to: " << state;
#endif
                        emit stateChanged(state);
                        retVal = true;
                    } else {
                        QNetworkConfiguration config = bestConfigFromSNAP(publicConfig);
                        if ((config.state() == QNetworkConfiguration::Defined) ||
                            (config.state() == QNetworkConfiguration::Discovered)) {
                            activeConfig = QNetworkConfiguration();
                            state = newState;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                            qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed E  to: " << state;
#endif
                            emit stateChanged(state);
                            retVal = true;
                        } else if (config.state() == QNetworkConfiguration::Active) {
                            // Connection to used IAP was closed, but there is another
                            // IAP that's active in used SNAP
                            // => Change state back to Connected
                            state =  QNetworkSession::Connected;
                            emit stateChanged(state);
                            retVal = true;
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                            qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed F  to: " << state;
#endif
                        }
                    }
                }
            }
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
            // If the retVal is not true here, it means that the status update may apply to an IAP outside of
            // SNAP (session is based on SNAP but follows IAP outside of it), which may occur on Symbian^3 EasyWlan.
            if (retVal == false && activeConfig.isValid() &&
                toSymbianConfig(privateConfiguration(activeConfig))->numericIdentifier() == accessPointId) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "===> EMIT State changed G  to: " << state;
#endif
                if (newState == QNetworkSession::Disconnected) {
                    activeConfig = QNetworkConfiguration();
                }
                state = newState;
                emit stateChanged(state);
                retVal = true;
            }
#endif
        }
    }
    if (emitSessionClosed) {
        emit closed();
    }
    if (state == QNetworkSession::Disconnected) {
        // Just in case clear activeConfiguration.
        activeConfig = QNetworkConfiguration();
    }
    return retVal;
}

void QNetworkSessionPrivateImpl::handleSymbianConnectionStatusChange(TInt aConnectionStatus,
                                                                 TInt aError,
                                                                 TUint accessPointId)
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNS this : " << QString::number((uint)this) << " - " << QString::number(accessPointId) << " , status : " << QString::number(aConnectionStatus);
#endif
    switch (aConnectionStatus)
        {
        // Connection unitialised
        case KConnectionUninitialised:
            break;

        // Starting connetion selection
        case KStartingSelection:
            break;

        // Selection finished
        case KFinishedSelection:
            if (aError == KErrNone)
                {
                break;
                }
            else
                {
                // The user pressed e.g. "Cancel" and did not select an IAP
                newState(QNetworkSession::Disconnected,accessPointId);
                }
            break;

        // Connection failure
        case KConnectionFailure:
            newState(QNetworkSession::NotAvailable);
            break;

        // Prepearing connection (e.g. dialing)
        case KPsdStartingConfiguration:
        case KPsdFinishedConfiguration:
        case KCsdFinishedDialling:
        case KCsdScanningScript:
        case KCsdGettingLoginInfo:
        case KCsdGotLoginInfo:
            break;

        case KConfigDaemonStartingRegistration:
        // Creating connection (e.g. GPRS activation)
        case KCsdStartingConnect:
        case KCsdFinishedConnect:
            newState(QNetworkSession::Connecting,accessPointId);
            break;

        // Starting log in
        case KCsdStartingLogIn:
            break;

        // Finished login
        case KCsdFinishedLogIn:
            break;

        // Connection open
        case KConnectionOpen:
            break;

        case KLinkLayerOpen:
            newState(QNetworkSession::Connected,accessPointId);
            break;

        // Connection blocked or suspended
        case KDataTransferTemporarilyBlocked:
            break;

        case KConfigDaemonStartingDeregistration:
        // Hangup or GRPS deactivation
        case KConnectionStartingClose:
            newState(QNetworkSession::Closing,accessPointId);
            break;

        // Connection closed
        case KConnectionClosed:
        case KLinkLayerClosed:
            newState(QNetworkSession::Disconnected,accessPointId);
            // Report manager about this to make sure this event
            // is received by all interseted parties (mediated by
            // manager because it does always receive all events from
            // connection monitor).
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
            qDebug() << "QNS this : " << QString::number((uint)this) << " - " << "reporting disconnection to manager.";
#endif
            if (publicConfig.isValid()) {
                SymbianNetworkConfigurationPrivate *symbianConfig =
                    toSymbianConfig(privateConfiguration(publicConfig));

                engine->configurationStateChangeReport(symbianConfig->numericIdentifier(),
                                                       QNetworkSession::Disconnected);
            }
            break;
        // Unhandled state
        default:
            break;
        }
}

#if defined(SNAP_FUNCTIONALITY_AVAILABLE)
bool QNetworkSessionPrivateImpl::easyWlanTrueIapId(TUint32 &trueIapId) const
{
    RCmManager iCmManager;
    TRAPD(err, iCmManager.OpenL());
    if (err != KErrNone)
        return false;

    // Check if this is easy wlan id in the first place
    if (trueIapId != iCmManager.EasyWlanIdL()) {
        iCmManager.Close();
        return false;
    }

    iCmManager.Close();

    // Loop through all connections that connection monitor is aware
    // and check for IAPs based on easy WLAN
    TRequestStatus status;
    TUint connectionCount;
    iConnectionMonitor.GetConnectionCount(connectionCount, status);
    User::WaitForRequest(status);
    TUint connectionId;
    TUint subConnectionCount;
    TUint apId;
    if (status.Int() == KErrNone) {
        for (TUint i = 1; i <= connectionCount; i++) {
            iConnectionMonitor.GetConnectionInfo(i, connectionId, subConnectionCount);
            iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount,
                                                KIAPId, apId, status);
            User::WaitForRequest(status);
            if (apId == trueIapId) {
                TBuf<50>easyWlanNetworkName;
                iConnectionMonitor.GetStringAttribute(connectionId, 0, KNetworkName,
                                                      easyWlanNetworkName, status);
                User::WaitForRequest(status);
                if (status.Int() != KErrNone)
                    continue;

                const QString ssid = QString::fromUtf16(easyWlanNetworkName.Ptr(),
                                                            easyWlanNetworkName.Length());

                QNetworkConfigurationPrivatePointer ptr = engine->configurationFromSsid(ssid);
                if (ptr) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                    qDebug() << "QNCM easyWlanTrueIapId(), found true IAP ID: "
                             << toSymbianConfig(ptr)->numericIdentifier();
#endif
                    trueIapId = toSymbianConfig(ptr)->numericIdentifier();
                    return true;
                }
            }
        }
    }
    return false;
}
#endif

ConnectionProgressNotifier::ConnectionProgressNotifier(QNetworkSessionPrivateImpl& owner, RConnection& connection)
    : CActive(CActive::EPriorityUserInput), iOwner(owner), iConnection(connection)
{
    CActiveScheduler::Add(this);
}

ConnectionProgressNotifier::~ConnectionProgressNotifier()
{
    Cancel();
}

void ConnectionProgressNotifier::StartNotifications()
{
    if (!IsActive()) {
        SetActive();
    }
    iConnection.ProgressNotification(iProgress, iStatus);
}

void ConnectionProgressNotifier::StopNotifications()
{
    Cancel();
}

void ConnectionProgressNotifier::DoCancel()
{
    iConnection.CancelProgressNotification();
}

void ConnectionProgressNotifier::RunL()
{
    if (iStatus == KErrNone) {
        QT_TRYCATCH_LEAVING(iOwner.handleSymbianConnectionStatusChange(iProgress().iStage, iProgress().iError));
    
        SetActive();
        iConnection.ProgressNotification(iProgress, iStatus);
    }
}

QT_END_NAMESPACE

#endif //QT_NO_BEARERMANAGEMENT

