/****************************************************************************
**
** 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 Qt Mobility Components.
**
** $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 "qnetworkconfigmanager_s60_p.h"

#include <commdb.h>
#include <cdbcols.h>
#include <d32dbms.h>
#include <nifvar.h>
#include <QTimer>
#include <QTime>  // For randgen seeding
#include <QtCore> // For randgen seeding


#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
#include <QDebug>
#endif

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    #include <cmdestination.h>
    #include <cmconnectionmethod.h>
    #include <cmconnectionmethoddef.h>
    #include <cmpluginwlandef.h>
    #include <cmpluginpacketdatadef.h>
    #include <cmplugindialcommondefs.h>
#else
    #include <apaccesspointitem.h>
    #include <apdatahandler.h>
    #include <aputils.h> 
#endif

QTM_BEGIN_NAMESPACE

static const int KUserChoiceIAPId = 0;

QNetworkConfigurationManagerPrivate::QNetworkConfigurationManagerPrivate()
    : QObject(0), CActive(CActive::EPriorityHigh), capFlags(0),
    iFirstUpdate(true), iInitOk(true), iUpdatePending(false),
    iTimeToWait(0)
{
    CActiveScheduler::Add(this);

    // Seed the randomgenerator
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()) + QCoreApplication::applicationPid());

    registerPlatformCapabilities();
    TRAPD(error, ipCommsDB = CCommsDatabase::NewL(EDatabaseTypeIAP));
    if (error != KErrNone) {
        iInitOk = false;
        return;
    }

    TRAP_IGNORE(iConnectionMonitor.ConnectL());
#ifdef SNAP_FUNCTIONALITY_AVAILABLE    
    TRAP_IGNORE(iConnectionMonitor.SetUintAttribute(EBearerIdAll, 0, KBearerGroupThreshold, 1));
#endif    
    TRAP_IGNORE(iConnectionMonitor.NotifyEventL(*this));

#ifdef SNAP_FUNCTIONALITY_AVAILABLE    
    TRAP(error, iCmManager.OpenL());
    if (error != KErrNone) {
        iInitOk = false;
        return;
    }
#endif
    
    QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate();
    cpPriv->name = "UserChoice";
    cpPriv->bearer = QNetworkConfigurationPrivate::BearerUnknown;
    cpPriv->state = QNetworkConfiguration::Discovered;
    cpPriv->isValid = true;
    cpPriv->id = QString::number(qHash(KUserChoiceIAPId));
    cpPriv->numericId = KUserChoiceIAPId;
    cpPriv->connectionId = 0;
    cpPriv->type = QNetworkConfiguration::UserChoice;
    cpPriv->purpose = QNetworkConfiguration::UnknownPurpose;
    cpPriv->roamingSupported = false;
    cpPriv->manager = this;
    QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> ptr(cpPriv);
    userChoiceConfigurations.insert(cpPriv->id, ptr);
    updateConfigurations();
    updateStatesToSnaps();
    updateAvailableAccessPoints(); // On first time updates synchronously (without WLAN scans)
    // Start monitoring IAP and/or SNAP changes in Symbian CommsDB
    startCommsDatabaseNotifications();
    iFirstUpdate = false;
}

QNetworkConfigurationManagerPrivate::~QNetworkConfigurationManagerPrivate() 
{
    Cancel();

    foreach (const QString &oldIface, snapConfigurations.keys()) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = snapConfigurations.take(oldIface);
        priv->isValid = false;
        priv->id.clear();
    }

    foreach (const QString &oldIface, accessPointConfigurations.keys()) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.take(oldIface);
        priv->isValid = false;
        priv->id.clear();
    }

    foreach (const QString &oldIface, userChoiceConfigurations.keys()) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = userChoiceConfigurations.take(oldIface);
        priv->isValid = false;
        priv->id.clear();
        priv->manager = 0;
    }

    delete ipAccessPointsAvailabilityScanner;
    iConnectionMonitor.CancelNotifications();
    iConnectionMonitor.Close();

    // CCommsDatabase destructor and RCmManager.Close() use cleanup stack. Since QNetworkConfigurationManager
    // is a global static, but the time we are here, E32Main() has been exited already and
    // the thread's default cleanup stack has been deleted. Without this line, a
    // 'E32USER-CBase 69' -panic will occur.
    CTrapCleanup* cleanup = CTrapCleanup::New();
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    iCmManager.Close();
#endif
    delete ipCommsDB;
    delete cleanup;
}

void QNetworkConfigurationManagerPrivate::delayedConfigurationUpdate()
{
    if (iUpdatePending) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug("QNCM delayed configuration update (ECommit or ERecover occurred).");
#endif
        TRAPD(error, updateConfigurationsL());
        if (error == KErrNone) {
            updateStatesToSnaps();
        }
        iUpdatePending = false;
        // Start monitoring again.
        if (!IsActive()) {
            SetActive();
            // Start waiting for new notification
            ipCommsDB->RequestNotification(iStatus);
        }
    }
}

void QNetworkConfigurationManagerPrivate::registerPlatformCapabilities()
{
    capFlags |= QNetworkConfigurationManager::CanStartAndStopInterfaces;
    capFlags |= QNetworkConfigurationManager::DirectConnectionRouting;
    capFlags |= QNetworkConfigurationManager::SystemSessionSupport;
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    capFlags |= QNetworkConfigurationManager::ApplicationLevelRoaming;
    capFlags |= QNetworkConfigurationManager::ForcedRoaming;
#endif
    capFlags |= QNetworkConfigurationManager::DataStatistics;
    capFlags |= QNetworkConfigurationManager::NetworkSessionRequired;
}

void QNetworkConfigurationManagerPrivate::performAsyncConfigurationUpdate()
{
    if (!iInitOk || iUpdateGoingOn) {
        return;
    }
    iUpdateGoingOn = true;

    stopCommsDatabaseNotifications();
    updateConfigurations(); // Synchronous call
    updateAvailableAccessPoints(); // Asynchronous call
}

void QNetworkConfigurationManagerPrivate::updateConfigurations()
{
    if (!iInitOk) {
        return;
    }
    TRAP_IGNORE(updateConfigurationsL());
}

void QNetworkConfigurationManagerPrivate::updateConfigurationsL()
{
    QList<QString> knownConfigs = accessPointConfigurations.keys();
    QList<QString> knownSnapConfigs = snapConfigurations.keys();

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    // S60 version is >= Series60 3rd Edition Feature Pack 2
    TInt error = KErrNone;
    
    // Loop through all IAPs
    RArray<TUint32> connectionMethods; // IAPs
    CleanupClosePushL(connectionMethods);
    iCmManager.ConnectionMethodL(connectionMethods);
    for(int i = 0; i < connectionMethods.Count(); i++) {
        RCmConnectionMethod connectionMethod = iCmManager.ConnectionMethodL(connectionMethods[i]);
        CleanupClosePushL(connectionMethod);
        TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId);
        QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId));
        if (accessPointConfigurations.contains(ident)) {
            knownConfigs.removeOne(ident);
        } else {
            QNetworkConfigurationPrivate* cpPriv = NULL;
            TRAP(error, cpPriv = configFromConnectionMethodL(connectionMethod));
            if (error == KErrNone) {
                QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> ptr(cpPriv);
                accessPointConfigurations.insert(cpPriv->id, ptr);
                if (!iFirstUpdate) {
                    QNetworkConfiguration item;
                    item.d = ptr;
                    // Emit configuration added. Connected slots may throw execptions
                    // which propagate here --> must be converted to leaves (standard
                    // std::exception would cause any TRAP trapping this function to terminate
                    // program).
                    QT_TRYCATCH_LEAVING(updateActiveAccessPoints());
                    updateStatesToSnaps();
                    QT_TRYCATCH_LEAVING(emit configurationAdded(item));
                }
            }
        }
        CleanupStack::PopAndDestroy(&connectionMethod);
    }
    CleanupStack::PopAndDestroy(&connectionMethods);
    
    // Loop through all SNAPs
    RArray<TUint32> destinations;
    CleanupClosePushL(destinations);
    iCmManager.AllDestinationsL(destinations);
    for(int i = 0; i < destinations.Count(); i++) {
        RCmDestination destination;

        // Some destinatsions require ReadDeviceData -capability (MMS/WAP)
        // The below function will leave in these cases. Don't. Proceed to
        // next destination (if any).
        TRAPD(error, destination = iCmManager.DestinationL(destinations[i]));
        if (error == KErrPermissionDenied) {
            continue;
        } else {
            User::LeaveIfError(error);
        }

        CleanupClosePushL(destination);
        QString ident = QT_BEARERMGMT_CONFIGURATION_SNAP_PREFIX+QString::number(qHash(destination.Id()));
        if (snapConfigurations.contains(ident)) {
            knownSnapConfigs.removeOne(ident);
        } else {
            QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate();
            HBufC *pName = destination.NameLC();
            QT_TRYCATCH_LEAVING(cpPriv->name = QString::fromUtf16(pName->Ptr(),pName->Length()));
            CleanupStack::PopAndDestroy(pName);
            pName = NULL;
            cpPriv->isValid = true;
            cpPriv->id = ident;
            cpPriv->numericId = destination.Id();
            cpPriv->connectionId = 0;
            cpPriv->state = QNetworkConfiguration::Defined;
            cpPriv->type = QNetworkConfiguration::ServiceNetwork;
            cpPriv->purpose = QNetworkConfiguration::UnknownPurpose;
            cpPriv->roamingSupported = false;
            cpPriv->manager = this;

            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> ptr(cpPriv);
            snapConfigurations.insert(ident, ptr);
            if (!iFirstUpdate) {
                QNetworkConfiguration item;
                item.d = ptr;
                QT_TRYCATCH_LEAVING(updateActiveAccessPoints());
                updateStatesToSnaps();
                QT_TRYCATCH_LEAVING(emit configurationAdded(item));
            }
        }
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> privSNAP = snapConfigurations.value(ident);
            
        for (int j=0; j < destination.ConnectionMethodCount(); j++) {
            RCmConnectionMethod connectionMethod = destination.ConnectionMethodL(j);
            CleanupClosePushL(connectionMethod);
            
            TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId);
            QString iface = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId));
            // Check that IAP can be found from accessPointConfigurations list
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
            if (priv.data() == 0) {
                QNetworkConfigurationPrivate* cpPriv = NULL; 
                TRAP(error, cpPriv = configFromConnectionMethodL(connectionMethod));
                if (error == KErrNone) {
                    QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> ptr(cpPriv);
                    ptr.data()->serviceNetworkPtr = privSNAP;
                    accessPointConfigurations.insert(cpPriv->id, ptr);
                    privSNAP->serviceNetworkMembers.append(ptr);
                    if (!iFirstUpdate) {
                        QNetworkConfiguration item;
                        item.d = ptr;
                        QT_TRYCATCH_LEAVING(updateActiveAccessPoints());
                        updateStatesToSnaps();
                        QT_TRYCATCH_LEAVING(emit configurationAdded(item));
                    }
                }
            } else {
                knownConfigs.removeOne(iface);
                // Check that IAP can be found from related SNAP's configuration list
                bool iapFound = false;
                for (int i=0; i<privSNAP->serviceNetworkMembers.count(); i++) {
                    if (privSNAP->serviceNetworkMembers[i]->numericId == iapId) {
                        iapFound = true;
                        break;
                    }
                }
                if (!iapFound) {
                    priv.data()->serviceNetworkPtr = privSNAP; 
                    privSNAP->serviceNetworkMembers.append(priv);
                }
            }
            CleanupStack::PopAndDestroy(&connectionMethod);
        }
        if (privSNAP->serviceNetworkMembers.count() > 1) {
            // Roaming is supported only if SNAP contains more than one IAP
            privSNAP->roamingSupported = true;
        }
        CleanupStack::PopAndDestroy(&destination);
    }
    CleanupStack::PopAndDestroy(&destinations);

#else
    // S60 version is < Series60 3rd Edition Feature Pack 2
    CCommsDbTableView* pDbTView = ipCommsDB->OpenTableLC(TPtrC(IAP));

    // Loop through all IAPs
    TUint32 apId = 0;
    TInt retVal = pDbTView->GotoFirstRecord();
    while (retVal == KErrNone) {
        pDbTView->ReadUintL(TPtrC(COMMDB_ID), apId);
        QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId));
        if (accessPointConfigurations.contains(ident)) {
            knownConfigs.removeOne(ident);
        } else {
            QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate();
            if (readNetworkConfigurationValuesFromCommsDb(apId, cpPriv)) {
                QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> ptr(cpPriv);
                accessPointConfigurations.insert(ident, ptr);
                if (!iFirstUpdate) {
                    QNetworkConfiguration item;
                    item.d = ptr;
                    QT_TRYCATCH_LEAVING(updateActiveAccessPoints());
                    updateStatesToSnaps();
                    QT_TRYCATCH_LEAVING(emit configurationAdded(item));
                }
            } else {
                delete cpPriv;
            }
        }
        retVal = pDbTView->GotoNextRecord();
    }
    CleanupStack::PopAndDestroy(pDbTView);
#endif

    QT_TRYCATCH_LEAVING(updateActiveAccessPoints());

    foreach (const QString &oldIface, knownConfigs) {
        //remove non existing IAP
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.take(oldIface);
        priv->isValid = false;
        if (!iFirstUpdate) {
            QNetworkConfiguration item;
            item.d = priv;
            QT_TRYCATCH_LEAVING(emit configurationRemoved(item));
        }
        // Remove non existing IAP from SNAPs
        foreach (const QString &iface, snapConfigurations.keys()) {
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv2 = snapConfigurations.value(iface);
            // => Check if one of the IAPs of the SNAP is active
            for (int i=0; i<priv2->serviceNetworkMembers.count(); i++) {
                if (priv2->serviceNetworkMembers[i]->numericId == priv->numericId) {
                    priv2->serviceNetworkMembers.removeAt(i);
                    break;
                }
            }
        }    
    }
    foreach (const QString &oldIface, knownSnapConfigs) {
        //remove non existing SNAPs
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = snapConfigurations.take(oldIface);
        priv->isValid = false;
        if (!iFirstUpdate) {
            QNetworkConfiguration item;
            item.d = priv;
            QT_TRYCATCH_LEAVING(emit configurationRemoved(item));
        }
    }
#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    updateStatesToSnaps();
#endif
}

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
QNetworkConfigurationPrivate* QNetworkConfigurationManagerPrivate::configFromConnectionMethodL(
        RCmConnectionMethod& connectionMethod)
{
    QNetworkConfigurationPrivate* cpPriv = new QNetworkConfigurationPrivate();
    TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId);
    QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId));
    
    HBufC *pName = connectionMethod.GetStringAttributeL(CMManager::ECmName);
    CleanupStack::PushL(pName);
    QT_TRYCATCH_LEAVING(cpPriv->name = QString::fromUtf16(pName->Ptr(),pName->Length()));
    CleanupStack::PopAndDestroy(pName);
    pName = NULL;
    
    TUint32 bearerId = connectionMethod.GetIntAttributeL(CMManager::ECmCommsDBBearerType);
    switch (bearerId) {
    case KCommDbBearerCSD:
        cpPriv->bearer = QNetworkConfigurationPrivate::Bearer2G;
        break;
    case KCommDbBearerWcdma:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerWCDMA;
        break;
    case KCommDbBearerLAN:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerEthernet;
        break;
    case KCommDbBearerVirtual:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerUnknown;
        break;
    case KCommDbBearerPAN:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerUnknown;
        break;
    case KCommDbBearerWLAN:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerWLAN;
        break;
    default:
        cpPriv->bearer = QNetworkConfigurationPrivate::BearerUnknown;
        break;
    }
    
    TInt error = KErrNone;
    TUint32 bearerType = connectionMethod.GetIntAttributeL(CMManager::ECmBearerType);
    switch (bearerType) {
    case KUidPacketDataBearerType:
        // "Packet data" Bearer => Mapping is done using "Access point name"
        TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EPacketDataAPName));
        break;
    case KUidWlanBearerType:
        // "Wireless LAN" Bearer => Mapping is done using "WLAN network name" = SSID
        TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EWlanSSID));
        break;
    }
    if (!pName) {
        // "Data call" Bearer or "High Speed (GSM)" Bearer => Mapping is done using "Dial-up number"
        TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EDialDefaultTelNum));
    }

    if (error == KErrNone && pName) {
        CleanupStack::PushL(pName);
        QT_TRYCATCH_LEAVING(cpPriv->mappingName = QString::fromUtf16(pName->Ptr(),pName->Length()));
        CleanupStack::PopAndDestroy(pName);
        pName = NULL;
    }
 
    cpPriv->state = QNetworkConfiguration::Defined;
    TBool isConnected = connectionMethod.GetBoolAttributeL(CMManager::ECmConnected);
    if (isConnected) {
        cpPriv->state = QNetworkConfiguration::Active;
    }
    
    cpPriv->isValid = true;
    cpPriv->id = ident;
    cpPriv->numericId = iapId;
    cpPriv->connectionId = 0;
    cpPriv->type = QNetworkConfiguration::InternetAccessPoint;
    cpPriv->purpose = QNetworkConfiguration::UnknownPurpose;
    cpPriv->roamingSupported = false;
    cpPriv->manager = this;
    return cpPriv;
}
#else
bool QNetworkConfigurationManagerPrivate::readNetworkConfigurationValuesFromCommsDb(
        TUint32 aApId, QNetworkConfigurationPrivate* apNetworkConfiguration)
{
    TRAPD(error, readNetworkConfigurationValuesFromCommsDbL(aApId,apNetworkConfiguration));
    if (error != KErrNone) {
        return false;        
    }
    return true;
}

void QNetworkConfigurationManagerPrivate::readNetworkConfigurationValuesFromCommsDbL(
        TUint32 aApId, QNetworkConfigurationPrivate* apNetworkConfiguration)
{
    CApDataHandler* pDataHandler = CApDataHandler::NewLC(*ipCommsDB); 
    CApAccessPointItem* pAPItem = CApAccessPointItem::NewLC(); 
    TBuf<KCommsDbSvrMaxColumnNameLength> name;
    
    CApUtils* pApUtils = CApUtils::NewLC(*ipCommsDB);
    TUint32 apId = pApUtils->WapIdFromIapIdL(aApId);
    
    pDataHandler->AccessPointDataL(apId,*pAPItem);
    pAPItem->ReadTextL(EApIapName, name);
    if (name.Compare(_L("Easy WLAN")) == 0) {
        // "Easy WLAN" won't be accepted to the Configurations list
        User::Leave(KErrNotFound);
    }
    
    QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(aApId));
    
    QT_TRYCATCH_LEAVING(apNetworkConfiguration->name = QString::fromUtf16(name.Ptr(),name.Length()));
    apNetworkConfiguration->isValid = true;
    apNetworkConfiguration->id = ident;
    apNetworkConfiguration->numericId = aApId;
    apNetworkConfiguration->connectionId = 0;
    apNetworkConfiguration->state = (QNetworkConfiguration::Defined);
    apNetworkConfiguration->type = QNetworkConfiguration::InternetAccessPoint;
    apNetworkConfiguration->purpose = QNetworkConfiguration::UnknownPurpose;
    apNetworkConfiguration->roamingSupported = false;
    switch (pAPItem->BearerTypeL()) {
    case EApBearerTypeCSD:      
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::Bearer2G;
        break;
    case EApBearerTypeGPRS:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::Bearer2G;
        break;
    case EApBearerTypeHSCSD:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerHSPA;
        break;
    case EApBearerTypeCDMA:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerCDMA2000;
        break;
    case EApBearerTypeWLAN:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerWLAN;
        break;
    case EApBearerTypeLAN:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerEthernet;
        break;
    case EApBearerTypeLANModem:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerEthernet;
        break;
    default:
        apNetworkConfiguration->bearer = QNetworkConfigurationPrivate::BearerUnknown;
        break;
    }
    apNetworkConfiguration->manager = this;
    
    CleanupStack::PopAndDestroy(pApUtils);
    CleanupStack::PopAndDestroy(pAPItem);
    CleanupStack::PopAndDestroy(pDataHandler);
}
#endif

QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfiguration()
{
    QNetworkConfiguration config;

    if (iInitOk) {
        stopCommsDatabaseNotifications();
        TRAP_IGNORE(config = defaultConfigurationL());
        startCommsDatabaseNotifications();
    }
    return config;
}

QNetworkConfiguration QNetworkConfigurationManagerPrivate::defaultConfigurationL()
{
    QNetworkConfiguration item;

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
    // Check Default Connection (SNAP or IAP)
    TCmDefConnValue defaultConnectionValue;
    iCmManager.ReadDefConnL(defaultConnectionValue);
    if (defaultConnectionValue.iType == ECmDefConnDestination) {
        QString iface = QT_BEARERMGMT_CONFIGURATION_SNAP_PREFIX+QString::number(qHash(defaultConnectionValue.iId));
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = snapConfigurations.value(iface);
        if (priv.data() != 0) {
            item.d = priv;
        }
    } else if (defaultConnectionValue.iType == ECmDefConnConnectionMethod) {
        QString iface = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(defaultConnectionValue.iId));
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
        if (priv.data() != 0) {
            item.d = priv;
        }
    } 
#endif
    
    if (!item.isValid()) {
        QString iface = QString::number(qHash(KUserChoiceIAPId));
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = userChoiceConfigurations.value(iface);
        if (priv.data() != 0) {
            item.d = priv;
        }
    }
    
    return item;
}

void QNetworkConfigurationManagerPrivate::updateActiveAccessPoints()
{
    bool online = false;
    QList<QString> inactiveConfigs = accessPointConfigurations.keys();

    TRequestStatus status;
    TUint connectionCount;
    iConnectionMonitor.GetConnectionCount(connectionCount, status);
    User::WaitForRequest(status);
    
    // Go through all connections and set state of related IAPs to Active.
    // Status needs to be checked carefully, because ConnMon lists also e.g.
    // WLAN connections that are being currently tried --> we don't want to
    // state these as active.
    TUint connectionId;
    TUint subConnectionCount;
    TUint apId;
    TInt connectionStatus;
    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);
            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
            if (!priv.data()) {
                // If IAP was not found, check if the update was about EasyWLAN
                priv = configurationFromEasyWlan(apId, connectionId);
                // Change the ident correspondingly
                if (priv.data()) {
                    ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(priv.data()->numericId));
                }
            }
#endif
            if (priv.data()) {
                iConnectionMonitor.GetIntAttribute(connectionId, subConnectionCount, KConnectionStatus, connectionStatus, status);
                User::WaitForRequest(status);

                if (connectionStatus == KLinkLayerOpen) {
                    online = true;
                    inactiveConfigs.removeOne(ident);
                    priv.data()->connectionId = connectionId;
                    // Configuration is Active
                    changeConfigurationStateTo(priv, QNetworkConfiguration::Active);
                }
            }
        }
    }

    // Make sure that state of rest of the IAPs won't be Active
    foreach (const QString &iface, inactiveConfigs) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
        if (priv.data()) {
            // Configuration is either Defined or Discovered
            changeConfigurationStateAtMaxTo(priv, QNetworkConfiguration::Discovered);
        }
    }

    if (iOnline != online) {
        iOnline = online;
        emit this->onlineStateChanged(online);
    }
}

void QNetworkConfigurationManagerPrivate::updateAvailableAccessPoints()
{
    if (!ipAccessPointsAvailabilityScanner) {
        ipAccessPointsAvailabilityScanner = new AccessPointsAvailabilityScanner(*this, iConnectionMonitor);
    }
    if (ipAccessPointsAvailabilityScanner) {
        // Scanning may take a while because WLAN scanning will be done (if device supports WLAN).
        ipAccessPointsAvailabilityScanner->StartScanning();
    }
}

void QNetworkConfigurationManagerPrivate::accessPointScanningReady(TBool scanSuccessful, TConnMonIapInfo iapInfo)
{
    iUpdateGoingOn = false;
    if (scanSuccessful) {
        QList<QString> unavailableConfigs = accessPointConfigurations.keys();
        
        // Set state of returned IAPs to Discovered
        // if state is not already Active
        for(TUint i=0; i<iapInfo.iCount; i++) {
            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapInfo.iIap[i].iIapId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
            if (priv.data()) {
                unavailableConfigs.removeOne(ident);
                if (priv.data()->state < QNetworkConfiguration::Active) {
                    // Configuration is either Discovered or Active
                    changeConfigurationStateAtMinTo(priv, QNetworkConfiguration::Discovered);
                }
            }
        }
        
        // Make sure that state of rest of the IAPs won't be Active
        foreach (const QString &iface, unavailableConfigs) {
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
            if (priv.data()) {
                // Configuration is Defined
                changeConfigurationStateAtMaxTo(priv, QNetworkConfiguration::Discovered);
            }
        }
    }

    updateStatesToSnaps();
    
    if (!iFirstUpdate) {
        startCommsDatabaseNotifications();
        emit this->configurationUpdateComplete();
    }
}

void QNetworkConfigurationManagerPrivate::updateStatesToSnaps()
{
    // Go through SNAPs and set correct state to SNAPs
    foreach (const QString &iface, snapConfigurations.keys()) {
        bool discovered = false;
        bool active = false;
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = snapConfigurations.value(iface);
        // => Check if one of the IAPs of the SNAP is discovered or active
        //    => If one of IAPs is active, also SNAP is active
        //    => If one of IAPs is discovered but none of the IAPs is active, SNAP is discovered
        for (int i=0; i<priv->serviceNetworkMembers.count(); i++) {
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv2 = priv->serviceNetworkMembers[i];
            if ((priv->serviceNetworkMembers[i]->state & QNetworkConfiguration::Active) 
                    == QNetworkConfiguration::Active) {
                active = true;
                break;
            } else if ((priv->serviceNetworkMembers[i]->state & QNetworkConfiguration::Discovered) 
                        == QNetworkConfiguration::Discovered) {
                discovered = true;
            }
        }
        if (active) {
            changeConfigurationStateTo(priv, QNetworkConfiguration::Active);
        } else if (discovered) {
            changeConfigurationStateTo(priv, QNetworkConfiguration::Discovered);
        } else {
            changeConfigurationStateTo(priv, QNetworkConfiguration::Defined);
        }
    }    
}

#ifdef SNAP_FUNCTIONALITY_AVAILABLE
void QNetworkConfigurationManagerPrivate::updateMobileBearerToConfigs(TConnMonBearerInfo bearerInfo)
{
    foreach (const QString &ii, accessPointConfigurations.keys()) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> p = 
            accessPointConfigurations.value(ii);
        if (p->bearer >= QNetworkConfigurationPrivate::Bearer2G &&
            p->bearer <= QNetworkConfigurationPrivate::BearerHSPA) {
            switch (bearerInfo) {
            case EBearerInfoCSD:      p->bearer = QNetworkConfigurationPrivate::Bearer2G; break;  
            case EBearerInfoWCDMA:    p->bearer = QNetworkConfigurationPrivate::BearerWCDMA; break;
            case EBearerInfoCDMA2000: p->bearer = QNetworkConfigurationPrivate::BearerCDMA2000; break;
            case EBearerInfoGPRS:     p->bearer = QNetworkConfigurationPrivate::Bearer2G; break;
            case EBearerInfoHSCSD:    p->bearer = QNetworkConfigurationPrivate::Bearer2G; break;
            case EBearerInfoEdgeGPRS: p->bearer = QNetworkConfigurationPrivate::Bearer2G; break;
            case EBearerInfoWcdmaCSD: p->bearer = QNetworkConfigurationPrivate::BearerWCDMA; break;
            case EBearerInfoHSDPA:    p->bearer = QNetworkConfigurationPrivate::BearerHSPA; break;
            case EBearerInfoHSUPA:    p->bearer = QNetworkConfigurationPrivate::BearerHSPA; break;
            case EBearerInfoHSxPA:    p->bearer = QNetworkConfigurationPrivate::BearerHSPA; break;
            }
        }
    }
}
#endif

bool QNetworkConfigurationManagerPrivate::changeConfigurationStateTo(QExplicitlySharedDataPointer<QNetworkConfigurationPrivate>& sharedData,
                                                                     QNetworkConfiguration::StateFlags newState)
{
    if (newState != sharedData.data()->state) {
        sharedData.data()->state = newState;
        QNetworkConfiguration item;
        item.d = sharedData;
        if (!iFirstUpdate) {
            emit configurationChanged(item);
        }
        return true;
    }
    return false;
}

/* changeConfigurationStateAtMinTo function does not overwrite possible better
 * state (e.g. Discovered state does not overwrite Active state) but
 * makes sure that state is at minimum given state.
*/
bool QNetworkConfigurationManagerPrivate::changeConfigurationStateAtMinTo(QExplicitlySharedDataPointer<QNetworkConfigurationPrivate>& sharedData,
                                                                          QNetworkConfiguration::StateFlags newState)
{
    if ((newState | sharedData.data()->state) != sharedData.data()->state) {
        sharedData.data()->state = (sharedData.data()->state | newState);
        QNetworkConfiguration item;
        item.d = sharedData;
        if (!iFirstUpdate) {
            emit configurationChanged(item);
        }
        return true;
    }
    return false;
}

/* changeConfigurationStateAtMaxTo function overwrites possible better
 * state (e.g. Discovered state overwrites Active state) and
 * makes sure that state is at maximum given state (e.g. Discovered state
 * does not overwrite Defined state).
*/
bool QNetworkConfigurationManagerPrivate::changeConfigurationStateAtMaxTo(QExplicitlySharedDataPointer<QNetworkConfigurationPrivate>& sharedData,
                                                                          QNetworkConfiguration::StateFlags newState)
{
    if ((newState & sharedData.data()->state) != sharedData.data()->state) {
        sharedData.data()->state = (newState & sharedData.data()->state);
        QNetworkConfiguration item;
        item.d = sharedData;
        if (!iFirstUpdate) {
            emit configurationChanged(item);
        }
        return true;
    }
    return false;
}

void QNetworkConfigurationManagerPrivate::startCommsDatabaseNotifications()
{
    if (!iWaitingCommsDatabaseNotifications) {
        iWaitingCommsDatabaseNotifications = ETrue;
        if (!IsActive()) {
            SetActive();
            // Start waiting for new notification
            ipCommsDB->RequestNotification(iStatus);
        }
    }
}

void QNetworkConfigurationManagerPrivate::stopCommsDatabaseNotifications()
{
    if (iWaitingCommsDatabaseNotifications) {
        iWaitingCommsDatabaseNotifications = EFalse;
        if (!IsActive()) {
            SetActive();
            // Make sure that notifier recorded events will not be returned
            // as soon as the client issues the next RequestNotification() request.
            ipCommsDB->RequestNotification(iStatus);
            ipCommsDB->CancelRequestNotification();
        } else {
            ipCommsDB->CancelRequestNotification();
        }
    }
}

void QNetworkConfigurationManagerPrivate::RunL()
{
    if (iStatus != KErrCancel) {
        // By default, start relistening notifications. Stop only if interesting event occurred.
        iWaitingCommsDatabaseNotifications = true;
        RDbNotifier::TEvent event = STATIC_CAST(RDbNotifier::TEvent, iStatus.Int());
        switch (event) {
        case RDbNotifier::ECommit:   /** A transaction has been committed.  */ 
        case RDbNotifier::ERecover:  /** The database has been recovered    */
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
            qDebug("QNCM CommsDB event (of type RDbNotifier::TEvent) received: %d", iStatus.Int());
#endif
            // Mark that there is update pending. No need to ask more events,
            // as we know we will be updating anyway when the timer expires.
            if (!iUpdatePending) {
                iUpdatePending = true;
                iWaitingCommsDatabaseNotifications = false;
                // Update after random time, so that many processes won't
                // start updating simultaneously
                updateConfigurationsAfterRandomTime();
            }
            break;           
        default:
            // Do nothing
            break;
        }
    }
    if (iWaitingCommsDatabaseNotifications) {
        if (!IsActive()) {
            SetActive();
            // Start waiting for new notification
            ipCommsDB->RequestNotification(iStatus);
        }
    }
}

void QNetworkConfigurationManagerPrivate::DoCancel()
{
    ipCommsDB->CancelRequestNotification();
}

void QNetworkConfigurationManagerPrivate::EventL(const CConnMonEventBase& aEvent)
{
    switch (aEvent.EventType()) {
#ifdef SNAP_FUNCTIONALITY_AVAILABLE     
    case EConnMonBearerInfoChange:
        {
        CConnMonBearerInfoChange* realEvent;
        realEvent = (CConnMonBearerInfoChange*) &aEvent;
        TUint connectionId = realEvent->ConnectionId();
        if (connectionId == EBearerIdAll) {
            //Network level event
            TConnMonBearerInfo bearerInfo = (TConnMonBearerInfo)realEvent->BearerInfo();
            updateMobileBearerToConfigs(bearerInfo);
        }
        break;
        }
#endif        
    case EConnMonConnectionStatusChange:
        {
        CConnMonConnectionStatusChange* realEvent;
        realEvent = (CConnMonConnectionStatusChange*) &aEvent;
        TInt connectionStatus = realEvent->ConnectionStatus();
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
        qDebug() << "QNCM Connection status : " << QString::number(connectionStatus) << " , connection monitor Id : " << realEvent->ConnectionId();
#endif
        if (connectionStatus == KConfigDaemonStartingRegistration) {
            TUint connectionId = realEvent->ConnectionId();
            TUint subConnectionCount = 0;
            TUint apId;            
            TRequestStatus status;
            iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status);
            User::WaitForRequest(status);

            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
            if (!priv.data()) {
                // Check if status was regarding EasyWLAN
                priv = configurationFromEasyWlan(apId, connectionId);
            }
#endif
            if (priv.data()) {
                priv.data()->connectionId = connectionId;
                QT_TRYCATCH_LEAVING(emit this->configurationStateChanged(priv.data()->numericId, connectionId, QNetworkSession::Connecting));
            }
        } else if (connectionStatus == KLinkLayerOpen) {
            // Connection has been successfully opened
            TUint connectionId = realEvent->ConnectionId();
            TUint subConnectionCount = 0;
            TUint apId;            
            TRequestStatus status;
            iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status);
            User::WaitForRequest(status);
            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
            if (!priv.data()) {
                // Check for EasyWLAN
                priv = configurationFromEasyWlan(apId, connectionId);
            }
#endif
            if (priv.data()) {
                priv.data()->connectionId = connectionId;
                // Configuration is Active
                QT_TRYCATCH_LEAVING(
                    if (changeConfigurationStateTo(priv, QNetworkConfiguration::Active)) {
                        updateStatesToSnaps();
                    }
                    emit this->configurationStateChanged(priv.data()->numericId, connectionId, QNetworkSession::Connected);
                    if (!iOnline) {
                        iOnline = true;
                        emit this->onlineStateChanged(iOnline);
                    }
                );
            }
        } else if (connectionStatus == KConfigDaemonStartingDeregistration) {
            TUint connectionId = realEvent->ConnectionId();
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = dataByConnectionId(connectionId);
            if (priv.data()) {
                QT_TRYCATCH_LEAVING(emit this->configurationStateChanged(priv.data()->numericId, connectionId, QNetworkSession::Closing));
            }
        } else if (connectionStatus == KLinkLayerClosed ||
                   connectionStatus == KConnectionClosed) {
            // Connection has been closed. Which of the above events is reported, depends on the Symbian
            // platform.
            TUint connectionId = realEvent->ConnectionId();
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = dataByConnectionId(connectionId);
            if (priv.data()) {
                // Configuration is either Defined or Discovered
                QT_TRYCATCH_LEAVING(
                    if (changeConfigurationStateAtMaxTo(priv, QNetworkConfiguration::Discovered)) {
                        updateStatesToSnaps();
                    }
                    emit this->configurationStateChanged(priv.data()->numericId, connectionId, QNetworkSession::Disconnected);
                );
            }
            
            bool online = false;
            foreach (const QString &iface, accessPointConfigurations.keys()) {
                QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
                if (priv.data()->state == QNetworkConfiguration::Active) {
                    online = true;
                    break;
                }
            }
            if (iOnline != online) {
                iOnline = online;
                QT_TRYCATCH_LEAVING(emit this->onlineStateChanged(iOnline));
            }
        }
        }
        break;    

    case EConnMonIapAvailabilityChange:
        {
        CConnMonIapAvailabilityChange* realEvent;
        realEvent = (CConnMonIapAvailabilityChange*) &aEvent;
        TConnMonIapInfo iaps = realEvent->IapAvailability();
        QList<QString> unDiscoveredConfigs = accessPointConfigurations.keys();
        for ( TUint i = 0; i < iaps.Count(); i++ ) {
            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iaps.iIap[i].iIapId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
            if (priv.data()) {
                // Configuration is either Discovered or Active 
                QT_TRYCATCH_LEAVING(changeConfigurationStateAtMinTo(priv, QNetworkConfiguration::Discovered));
                unDiscoveredConfigs.removeOne(ident);
            }
        }
        foreach (const QString &iface, unDiscoveredConfigs) {
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(iface);
            if (priv.data()) {
                // Configuration is Defined
                QT_TRYCATCH_LEAVING(changeConfigurationStateAtMaxTo(priv, QNetworkConfiguration::Defined));
            }
        }
        // Something has in IAPs, update states to SNAPs
        updateStatesToSnaps();
        }
        break;

    case EConnMonCreateConnection:
        {
        // This event is caught to keep connection monitor IDs up-to-date.
        CConnMonCreateConnection* realEvent;
        realEvent = (CConnMonCreateConnection*) &aEvent;
        TUint subConnectionCount = 0;
        TUint apId;
        TUint connectionId = realEvent->ConnectionId();
        TRequestStatus status;
        iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status);
        User::WaitForRequest(status);
        QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId));
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
        if (!priv.data()) {
            // If IAP was not found, check if the update was about EasyWLAN
            priv = configurationFromEasyWlan(apId, connectionId);
        }
#endif
        if (priv.data()) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
            qDebug() << "QNCM updating connection monitor ID : from, to, whose: " << priv.data()->connectionId << connectionId << priv->name;
#endif
            priv.data()->connectionId = connectionId;
        }
        }
        break;
    default:
        // For unrecognized events
        break;
    }
}

#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
// Tries to derive configuration from EasyWLAN.
// First checks if the interface brought up was EasyWLAN, then derives the real SSID,
// and looks up configuration based on that one.
QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfigurationManagerPrivate::configurationFromEasyWlan(TUint32 apId, TUint connectionId)
{
    if (apId == iCmManager.EasyWlanIdL()) {
        TRequestStatus status;
        TBuf<50> easyWlanNetworkName;
        iConnectionMonitor.GetStringAttribute( connectionId, 0, KNetworkName,
                                               easyWlanNetworkName, status );
        User::WaitForRequest(status);
        if (status.Int() == KErrNone) {
            QString realSSID = QString::fromUtf16(easyWlanNetworkName.Ptr(), easyWlanNetworkName.Length());

            // Browser through all items and check their name for match
            QNetworkConfiguration item;
            QHash<QString, QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> >::const_iterator i =
                    accessPointConfigurations.constBegin();
            while (i != accessPointConfigurations.constEnd()) {
                QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = i.value();
                if (priv.data()->name == realSSID) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                    qDebug() << "QNCM EasyWlan uses real SSID: " << realSSID;
#endif
                    return priv;
                }
                ++i;
            }
        }
    }
    return QExplicitlySharedDataPointer<QNetworkConfigurationPrivate>();
}

bool QNetworkConfigurationManagerPrivate::easyWlanTrueIapId(TUint32& trueIapId)
{
    // Check if this is easy wlan id in the first place
    if (trueIapId != iCmManager.EasyWlanIdL()) {
        return false;
    }
    // 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) {
                QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv =
                        configurationFromEasyWlan(apId, connectionId);
                if (priv.data()) {
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
                    qDebug() << "QNCM easyWlanTrueIapId(), found true IAP ID: " << priv.data()->numericId;
#endif
                    trueIapId = priv.data()->numericId;
                    return true;
                }
            }
        }
    }
    return false;
}

#endif

// Sessions may use this function to report configuration state changes,
// because on some Symbian platforms (especially Symbian^3) all state changes are not
// reported by the RConnectionMonitor, in particular in relation to stop() call,
// whereas they _are_ reported on RConnection progress notifier used by sessions --> centralize
// this data here so that other sessions may benefit from it too (not all sessions necessarily have
// RConnection progress notifiers available but they relay on having e.g. disconnected information from
// manager). Currently only 'Disconnected' state is of interest because it has proven to be troublesome.
void QNetworkConfigurationManagerPrivate::configurationStateChangeReport(TUint32 accessPointId, QNetworkSession::State newState)
{
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug() << "QNCM A session reported state change for IAP ID: " << accessPointId << " whose new state is: " << newState;
#endif
    switch (newState) {
    case QNetworkSession::Disconnected:
        {
            QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(accessPointId));
            QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = accessPointConfigurations.value(ident);
            if (priv.data()) {
                // Configuration is either Defined or Discovered
                if (changeConfigurationStateAtMaxTo(priv, QNetworkConfiguration::Discovered)) {
                    updateStatesToSnaps();
                }
                emit this->configurationStateChanged(
                        priv.data()->numericId, priv.data()->connectionId, QNetworkSession::Disconnected);
            }
        }
        break;
    default:
        break;
    }
}

// Waits for 2..6 seconds.
void QNetworkConfigurationManagerPrivate::updateConfigurationsAfterRandomTime()
{
    iTimeToWait = (qAbs(qrand()) % 68) * 100;
    if (iTimeToWait < 1000) {
        iTimeToWait = 1000;
    }
#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
    qDebug("QNCM waiting random time: %d ms", iTimeToWait);
#endif
    QTimer::singleShot(iTimeToWait, this, SLOT(delayedConfigurationUpdate()));
}

QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> QNetworkConfigurationManagerPrivate::dataByConnectionId(TUint aConnectionId)
{
    QNetworkConfiguration item;
    QHash<QString, QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> >::const_iterator i =
            accessPointConfigurations.constBegin();
    while (i != accessPointConfigurations.constEnd()) {
        QExplicitlySharedDataPointer<QNetworkConfigurationPrivate> priv = i.value();
        if (priv.data()->connectionId == aConnectionId) {
            return priv;
        }
        ++i;
    }
    return QExplicitlySharedDataPointer<QNetworkConfigurationPrivate>();
}

AccessPointsAvailabilityScanner::AccessPointsAvailabilityScanner(QNetworkConfigurationManagerPrivate& owner,
                                                               RConnectionMonitor& connectionMonitor)
    : CActive(CActive::EPriorityHigh), iOwner(owner), iConnectionMonitor(connectionMonitor)
{
    CActiveScheduler::Add(this);  
}

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

void AccessPointsAvailabilityScanner::DoCancel()
{
    iConnectionMonitor.CancelAsyncRequest(EConnMonGetPckgAttribute);
}

void AccessPointsAvailabilityScanner::StartScanning()
{
    if (iOwner.iFirstUpdate) {
        // On first update (the mgr is being instantiated) update only those bearers who
        // don't need time-consuming scans (WLAN).
        // Note: EBearerIdWCDMA covers also GPRS bearer
        iConnectionMonitor.GetPckgAttribute(EBearerIdWCDMA, 0, KIapAvailability, iIapBuf, iStatus);
        User::WaitForRequest(iStatus);
        if (iStatus.Int() == KErrNone) {
            iOwner.accessPointScanningReady(true,iIapBuf());
        }
    } else {
        iConnectionMonitor.GetPckgAttribute(EBearerIdAll, 0, KIapAvailability, iIapBuf, iStatus);
        if (!IsActive()) {
            SetActive();
        }
    }
}

void AccessPointsAvailabilityScanner::RunL()
{
    if (iStatus.Int() != KErrNone) {
        iIapBuf().iCount = 0;
        QT_TRYCATCH_LEAVING(iOwner.accessPointScanningReady(false,iIapBuf()));
    } else {
        QT_TRYCATCH_LEAVING(iOwner.accessPointScanningReady(true,iIapBuf()));
    }
}
#include "moc_qnetworkconfigmanager_s60_p.cpp"
QTM_END_NAMESPACE
