connectionmonitoring/connmon/dataconnectionlogger/src/dcl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:43:42 +0300
changeset 66 ed07dcc72692
parent 0 5a93021fdf25
child 72 0c32cf868819
permissions -rw-r--r--
Revision: 201038 Kit: 201039

/*
* Copyright (c) 2003-2004 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Data Connection Logger.
*
*/

#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <logengdurations.h>
#include <logwraplimits.h>
#include <logengevents.h>
#endif
#include <cdbcols.h>
#include <bldvariant.hrh> // Feature IDs
#include <featmgr.h>
#include <utf.h>

#include <centralrepository.h>
#include "DclPrivateCRKeys.h"

#include "dcl.h"
#include "dcl_log.h"

// --------------------------------------------------------------------------
// CEngine::CEngine
// Constructor
// --------------------------------------------------------------------------
//
CEngine::CEngine()
        :
        CActive( EPriorityStandard ),
        iLogClient( NULL ),
        iTimerInterval( KTimerInterval )
    {
    CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CEngine::NewLC
// 1st phase constructor - for stack pointers to object
// --------------------------------------------------------------------------
//
CEngine* CEngine::NewLC( )
    {
    CEngine* self = new( ELeave ) CEngine();
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }

// --------------------------------------------------------------------------
// CEngine::NewL
// 1st phase constructor - for heap pointers to object
// --------------------------------------------------------------------------
//
CEngine* CEngine::NewL( )
    {
    CEngine* self = NewLC();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CEngine::ConstructL
// 2nd phase constructor
// --------------------------------------------------------------------------
//
void CEngine::ConstructL()
    {
    LOGENTRFN("CEngine::ConstructL()")
    // Renaming the thread with the real process name helps debugging panics.
    RThread::RenameMe( KDCLName );

    FeatureManager::InitializeLibL();

    iConnectionMonitor.ConnectL();

    User::LeaveIfError( iFsEventLog.Connect() );

    iLogWrap = CLogWrapper::NewL( iFsEventLog, CActive::EPriorityStandard );
    iLogEventQueue = new( ELeave ) CArrayPtrFlat<CEvent>( KEventStateMaxCount );
    iLogEventQueue->Reset();

    iConnections = new( ELeave ) CArrayPtrFlat<CEvent>( KEventStateMaxCount );
    iConnections->Reset();

    iCurrentLogEvent = CLogEvent::NewL();

    iLogClient = CLogClient::NewL( iFsEventLog );

    // Connect to ETel server
    // The RTelServer::Connect() might not always work with the first trial,
    // because of a coding error related to using semaphores in the method.

    TInt err( KErrNotReady );
    TInt a = 0;
    while( a < KPhoneRetryCount && err != KErrNone )
        {
        if ( a )
            {
            User::After( KPhoneRetryTimeout ); // Don't wait on first try
            }
        err = iTelServer.Connect();
        LOGIT1("Connecting to ETel server <%d>", err)
        a++;
        }
    User::LeaveIfError( err );

    // Try to load phone.tsy
    TBuf<KCommsDbSvrMaxColumnNameLength> tsyName;
    tsyName.Copy( KMmTsyModuleName );
    err = iTelServer.LoadPhoneModule( tsyName );

    if ( err == KErrNone )
        {
        iTSYLoaded = 1;
        // Phone info can be retrieved with value 0 if there is only 1 phone
        RTelServer::TPhoneInfo info;
        User::LeaveIfError( iTelServer.GetPhoneInfo( 0, info ) );
        User::LeaveIfError( iMobilePhone.Open( iTelServer, info.iName ) );

        // Open packet service
        err = iPacketService.Open( iMobilePhone );
        if ( err == KErrNone )
            {
            iPacketServLoaded = 1;
            }
        }
    if ( FeatureManager::FeatureSupported( KFeatureIdLoggerGprs ) )
        {
        LOGIT0("LoggerGprs supported")

        // Get the Attach Mode setting from CommDb and set ETel to use that
        // value. Must not leave if an error occurs.
        TRAPD( leaveCode, SetAttachModeL() );
        LOGIT1("SetAttachModeL() result: <%d>", leaveCode)

        // Get the default APN from CommDb and set ETel to use that APN.
        // Must not leave if an error occurs.
        TRAP( leaveCode, SetAPNL() );
        LOGIT1("SetAPNL() result: <%d>", leaveCode)
        }
    // Adds the event type Wlan. Otherwise the LogClient
    // will not understand this type of event.
    AddEventTypeL();

    // Read ini file settings
    ReadIniFile();

    // Create timer mode AO
    iDclTimerAO = new( ELeave ) CDclTimerAO ( this, iTimerInterval );
    iDclTimerAO->ConstructL();

    // Create saeObserver for updating P&S variables
    iSaeObserver = CSaeObserver::NewL( iConnectionMonitor );
    LOGEXITFN("CEngine::ConstructL()")
    }

// --------------------------------------------------------------------------
// CEngine::~CEngine
// Destructor
// Frees reserved resources.
// --------------------------------------------------------------------------
//
CEngine::~CEngine()
    {
    LOGENTRFN("CEngine::~CEngine()")
    FeatureManager::UnInitializeLib();

    delete iSaeObserver;
    iSaeObserver = NULL;

    // Disconnect from CM server.
    iConnectionMonitor.Close();

    Cancel();
    // The queue could be not empty if the active object
    // is aborted and destructed.
    if ( iLogEventQueue != 0 )
        {
        iLogEventQueue->ResetAndDestroy();
        }

    if ( iConnections != 0 )
        {
        iConnections->ResetAndDestroy();
        }

    delete iLogWrap;
    iLogWrap = NULL;

    delete iLogEventQueue;
    iLogEventQueue = NULL;

    delete iConnections;
    iConnections = NULL;

    delete iCurrentLogEvent;
    iCurrentLogEvent = NULL;

    delete iLogEventType;
    iLogEventType = NULL;

    delete iLogClient;
    iLogClient = NULL;

    iFsEventLog.Close();

    // Close "global" ETEL objects
    if ( iPacketServLoaded == 1 )
        {
        iPacketService.Close();
        }

    iMobilePhone.Close();
    iTelServer.Close();

    delete iDclTimerAO;
    iDclTimerAO = NULL;
    LOGEXITFN("CEngine::~CEngine()")
    }

// --------------------------------------------------------------------------
// CEngine::NotifyL
// Request Connection Monitor server to send notifications of events.
// --------------------------------------------------------------------------
//
void CEngine::NotifyL()
    {
    iConnectionMonitor.NotifyEventL( *this );
    }

// --------------------------------------------------------------------------
// CEngine::EventL
// Receives notifications of events from Connection Monitor server.
// --------------------------------------------------------------------------
//
void CEngine::EventL( const CConnMonEventBase& aConnMonEvent )
    {
    //LOGENTRFN("CEngine::EventL()")
    LOGIT1("-> CEngine::EventL() entered, event type (%d)", aConnMonEvent.EventType())
    TInt err( KErrNone );
    TUint connectionId( 0 );

    // Update P&S variables first
    if ( iSaeObserver )
        {
        iSaeObserver->EventL( aConnMonEvent );
        }

    switch ( aConnMonEvent.EventType() )
        {
        case EConnMonCreateConnection:
            {
            TInt bearer( 0 );
            TRequestStatus status( KErrNotReady );

            const CConnMonCreateConnection* eventCreate;
            eventCreate  = static_cast<const CConnMonCreateConnection*>( &aConnMonEvent );
            connectionId = eventCreate->ConnectionId();

            LOGIT1("*** EConnMonCreateConnection event, id %d ***", connectionId)

            // Get the bearer info from CM server.
            err = KErrNotReady;
            TInt a = 0;
            while( a < KPhoneRetryCount && err != KErrNone )
                {
                // ConnMon might not be able to provide KBearer with the first trial.
                if ( a )
                    {
                    User::After( KPhoneRetryTimeout ); // Don't wait on first try
                    }
                iConnectionMonitor.GetIntAttribute(
                        connectionId,
                        KSubConnectionId,
                        KBearer,
                        bearer,
                        status );
                User::WaitForRequest( status );
                err = status.Int();
                LOGIT2("Got bearer %d from ConnMon <%d>", bearer, err)
                a++;
                }
            if ( err != KErrNone )
                {
                LOGIT1("FAILED to get KBearer from ConnMon <%d>", err)
                break;
                }

            // Check that it is GPRS or WCDMA.
            if ( bearer == EBearerGPRS     ||
                 bearer == EBearerEdgeGPRS ||
                 bearer == EBearerWCDMA )
                {
                LOGIT0("New internal connection")
                // Set the uplink and downlink thresholds for this particular connection.
                TUint threshold( iGPRSThreshold );
                if ( bearer == EBearerWCDMA )
                    {
                    threshold = iWCDMAThreshold;
                    }

                err = iConnectionMonitor.SetUintAttribute(
                        connectionId,
                        KSubConnectionId,
                        KDownlinkDataThreshold,
                        threshold );
                if ( err != KErrNone )
                    {
                    LOGIT1("FAILED to set KDownlinkDataThreshold to ConnMon <%d>", err)
                    }

                err = iConnectionMonitor.SetUintAttribute(
                        connectionId,
                        KSubConnectionId,
                        KUplinkDataThreshold,
                        threshold );
                if ( err != KErrNone )
                    {
                    LOGIT1("FAILED to set KUplinkDataThreshold to ConnMon <%d>", err)
                    }

                TName iapName;
                iapName.FillZ();
                iapName.Zero();

                // Get String Attribute -> KIAPName.
                // Get IAP name from CM server.
                err = KErrNotReady;
                a = 0;
                while( a < KPhoneRetryCount && err != KErrNone )
                    {
                    // ConnMon might not be able to provide KIAPName with the first trial.
                    if ( a )
                        {
                        User::After( KPhoneRetryTimeout ); // Don't wait on first try
                        }
                    iConnectionMonitor.GetStringAttribute(
                            connectionId,
                            KSubConnectionId,
                            KIAPName,
                            iapName,
                            status );
                    User::WaitForRequest( status );
                    err = status.Int();
                    LOG( TBuf<KMaxName+1> iapName2( iapName ); )
                    LOGIT2("Got KIAPName from ConnMon <%s>, <%d>", iapName2.PtrZ(), err)
                    a++;
                    }

                if ( err == KErrNone )
                    {
                    LogDataAddEventL(
                            connectionId,
                            R_LOG_CON_CONNECTED,
                            iapName,
                            R_LOG_DIR_OUT,
                            KNullDesC,
                            KLogPacketDataEventTypeUid );
                    }
                else
                    {
                    LOGIT1("FAILED to get KIAPName from ConnMon <%d>", err)
                    }
                }
            else if ( bearer == EBearerWLAN )
                // Add EBearerExternalWLAN in order to enable DCL to log
                // external WLAN connections, i.e. then the mobile phone
                // functions as a WLAN modem
                {
                LOGIT0("New WLAN connection")

                // Set the uplink and downlink thresholds for this particular connection.
                err = iConnectionMonitor.SetUintAttribute(
                        connectionId,
                        KSubConnectionId,
                        KDownlinkDataThreshold,
                        iWLANThreshold );
                if ( err != KErrNone )
                    {
                    LOGIT1("FAILED to set KDownlinkDataThreshold to ConnMon <%d>", err)
                    }

                // Even if KDownlinkDataThreshold and KUplinkDataThreshold
                // are used here ( 4096 bytes ), the Connection Monitor
                // server actually sets the threshold to 20480 bytes =
                // 20 kilobytes.
                err = iConnectionMonitor.SetUintAttribute(
                        connectionId,
                        KSubConnectionId,
                        KUplinkDataThreshold,
                        iWLANThreshold );
                if ( err != KErrNone )
                    {
                    LOGIT1("FAILED to set KUplinkDataThreshold to ConnMon <%d>", err)
                    }

                TName iapName;
                iapName.FillZ();
                iapName.Zero();
                // Get Uint Attribute -> KIAPId.
                // Get IAP id from CM server.
                err = KErrNotReady;
                a = 0;
                TUint iapId( 0 );
                while( a < KPhoneRetryCount && err != KErrNone )
                    {
                    // ConnMon might not be able to provide KIAPId with the first trial.
                    if ( a )
                        {
                        User::After( KPhoneRetryTimeout ); // Don't wait on first try
                        }
                    iConnectionMonitor.GetUintAttribute(
                            connectionId,
                            KSubConnectionId,
                            KIAPId,
                            iapId,
                            status );
                    User::WaitForRequest( status );
                    err = status.Int();
                    LOGIT2("Got IAP ID %d from ConnMon <%d>", iapId, err)
                    a++;
                    }
                // For each new WLAN connection, we need to check the used IAP.
                // For a normal IAP, the IAP name is used for logging, but when
                // customer is using the 'Search for WLAN' functionality and
                // chooses a WLAN network that is not found in any current IAP,
                // the Easy WLAN IAP is used instead. This IAP has some internal
                // name, e.g. 'Easy WLAN', that should not be visible anywhere
                // in the UI. So SSID of used network is used in logging instead.
                //
                if ( err == KErrNone )
                    {
                    // Create a session with the latest version CommsDat
                    CMDBSession* db = CMDBSession::NewLC( CMDBSession::LatestVersion() );
                    db->SetAttributeMask( ECDProtectedWrite );

                    // Load IAP record
                    CCDIAPRecord* iapRecord = static_cast<CCDIAPRecord*>(
                            CCDRecordBase::RecordFactoryL( KCDTIdIAPRecord ) );
                    CleanupStack::PushL( iapRecord );
                    iapRecord->SetRecordId( iapId );
                    iapRecord->LoadL( *db );
                    LOGIT0("Read IAP record from CommsDat")

                    // Load wlan service record
                    CCDWlanServiceRecord* wlanRecord = new( ELeave )CCDWlanServiceRecord(
                            CCDWlanServiceRecord::TableIdL( *db ) );
                    CleanupStack::PushL( wlanRecord );
                    wlanRecord->iWlanServiceId = iapRecord->iService;

                    if ( wlanRecord->FindL( *db ) )
                        {
                        wlanRecord->LoadL( *db );
                        LOGIT0("Read WLAN record from CommsDat")

                        if ( wlanRecord->iWLanSSID.IsNull() )
                            {
                            // Used IAP is the special 'Easy WLAN' IAP, so we need to
                            // log the used WLAN network SSID instead of 'Easy WLAN'.
                            // This can be read from the 'iWLanUsedSSID'-field.
                            if ( !( wlanRecord->iWLanUsedSSID.IsNull() ) )
                                {
                                iapName = wlanRecord->iWLanUsedSSID.GetL();
                                LOGIT1("Easy WLAN detected, got SSID <%s> from CommsDat", &iapName)
                                }
                            else
                                {
                                LOGIT0("Error, Easy WLAN detected but no SSID found from CommsDat")
                                }
                            }
                        else
                            {
                            // A noraml WLAN IAP, read IAP name and use it for logging
                            iapName = iapRecord->iRecordName.GetL();
                            LOGIT1("Got IAP name <%s> from CommsDat", &iapName)
                            }
                        }
                    else
                        {
                        LOGIT0("Error, WLAN record not found from CommsDat")
                        }
                    LogDataAddEventL(
                            connectionId,
                            R_LOG_CON_CONNECTED,
                            iapName,
                            R_LOG_DIR_OUT,
                            KNullDesC,
                            KLogWlanDataEventTypeUid );

                    CleanupStack::PopAndDestroy( wlanRecord );
                    CleanupStack::PopAndDestroy( iapRecord );
                    CleanupStack::PopAndDestroy( db );
                    }
                else
                    {
                    LOGIT1("FAILED to get KIAPId from ConnMon", err)
                    }
                }
            // Check if it is EXTERNAL GPRS or WCDMA.
            else if ( bearer == EBearerExternalGPRS     ||
                      bearer == EBearerExternalEdgeGPRS ||
                      bearer == EBearerExternalWCDMA )
                {
                LOGIT0("New external connection")

                TName apName;
                apName.FillZ();
                apName.Zero();

                err = KErrNotReady;
                a = 0;
                // We loop until the retry count is reached or
                // we get a name with length > 0
                // This is because the connection monitor asks the PSD Fax
                // for the name and PSD fax sometines returns an empty string
                // without returning an error code (KErrNone is returned)
                // However if no name for the APN has been defined this will
                // loop for KPhoneRetryCount times for vain, but it's better than
                // to use an empty name if one has been defined...
                while( a < KPhoneRetryCount &&
                        ( ( err != KErrNone ) || ( apName.Length() == 0 ) ) )
                    {
                    // ConnMon might not be able to provide KAccessPointName with first trial.
                    if ( a )
                        {
                        User::After( KPhoneRetryTimeout ); // Don't wait on first try
                        }
                    // Get String Attribute -> KAccessPointName.
                    // Get APN from CM server.
                    iConnectionMonitor.GetStringAttribute(
                            connectionId,
                            KSubConnectionId,
                            KAccessPointName,
                            apName,
                            status );
                    User::WaitForRequest( status );
                    err = status.Int();
                    LOG( TBuf<KMaxName+1> apName2( apName ); )
                    LOGIT2("Got KAccessPointName from ConnMon <%s>, <%d>", apName2.PtrZ(), err)
                    a++;
                    }
                if ( err == KErrNone )
                    {
                    LogDataAddEventL(
                            connectionId,
                            R_LOG_CON_CONNECTED,
                            apName,
                            R_LOG_DIR_OUT,
                            KNullDesC,
                            KLogPacketDataEventTypeUid );
                    }
                else
                    {
                    LOGIT1("FAILED to get KAccessPointName from ConnMon <%d>", err)
                    }
                }
            else
                {
                LOGIT0("Bearer neither GPRS nor WCDMA nor WLAN, ignored")
                break;
                }
            break;
            }

        case EConnMonConnectionStatusChange:
            {
            TInt bearer( 0 );
            TRequestStatus status( KErrNotReady );

            const CConnMonConnectionStatusChange* eventStatus;
            eventStatus  = static_cast<const CConnMonConnectionStatusChange*>( &aConnMonEvent );
            connectionId = eventStatus->ConnectionId();

            LOGIT2("*** EConnMonConnectionStatusChange event, id %d, status %d ***",
                    connectionId, eventStatus->ConnectionStatus())

            if ( eventStatus->ConnectionStatus() == KPsdFinishedActivation )
                {
                // Get the bearer info from CM server.
                iConnectionMonitor.GetIntAttribute(
                        connectionId,
                        KSubConnectionId,
                        KBearer,
                        bearer,
                        status );
                User::WaitForRequest( status );
                err = status.Int();
                if ( err != KErrNone )
                    {
                    LOGIT1("FAILED to get bearer from ConnMon <%d>", err)
                    break;
                    }

                // Check that it is external GPRS or WCDMA.
                if ( bearer == EBearerExternalGPRS      ||
                     bearer == EBearerExternalEdgeGPRS  ||
                     bearer == EBearerExternalWCDMA )
                    // Add EBearerLAN in order to enable debugging in wins.
                    {
                    LOGIT1("Status: KPsdFinishedActivation, bearer: %d (valid external), updating AP name", bearer)
                    TName apName;
                    apName.FillZ();
                    apName.Zero();

                    err = KErrNotReady;
                    TInt a = 0;
                    // We loop until the retry count is reached or
                    // we get a name with length > 0
                    // This is because the connection monitor asks the PSD Fax
                    // for the name and PSD fax sometines returns an empty string
                    // without returning an error code (KErrNone is returned)
                    // However if no name for the APN has been defined this will
                    // loop for KPhoneRetryCount times for vain, but it's better than
                    // to use an empty name if one has been defined...
                    while( a < KPhoneRetryCount &&  (
                            ( err != KErrNone ) ||
                            ( apName.Length() == 0 )
                            ) )
                        {
                        // ConnMon might not be able to provide KAccessPointName with first trial.
                        if ( a )
                            {
                            User::After( KPhoneRetryTimeout ); // Don't wait on first try
                            }
                        // Get String Attribute -> KAccessPointName.
                        // Get APN from CM server.
                        iConnectionMonitor.GetStringAttribute(
                                connectionId,
                                KSubConnectionId,
                                KAccessPointName,
                                apName,
                                status );
                        User::WaitForRequest( status );
                        err = status.Int();
                        LOG( TBuf<KMaxName+1> apName2( apName ); )
                        LOGIT2("Got KAccessPointName from ConnMon <%s>, <%d>", apName2.PtrZ(), err)
                        a++;
                        }

                    if ( err == KErrNone )
                        {
                        // We want only to change the existing entry in the log,
                        // not add a new one, thus LogDataChangeEventL instead of
                        // LogDataAddEventL
                        LogDataChangeEventL( connectionId, apName );
                        }
                    else
                        {
                        LOGIT1("FAILED to get KAccessPointName from ConnMon <%d>", err)
                        }
                    }
                else
                    {
                    LOGIT1("Status: KPsdFinishedActivation, bearer: %d (not valid external)", bearer)
                    }
                }
            }
            break;

        case EConnMonDeleteConnection:
            {
            const CConnMonDeleteConnection* eventDelete;
            eventDelete  = static_cast<const CConnMonDeleteConnection*>( &aConnMonEvent );
            connectionId = eventDelete->ConnectionId();

            LOGIT1("*** EConnMonDeleteConnection event, id %d ***", connectionId)
            LOGIT1("sent: %d bytes", eventDelete->UplinkData())
            LOGIT1("recv: %d bytes", eventDelete->DownlinkData())

            // First update SharedData downlink and uplink data
            UpdateSharedDataL(
                    connectionId,
                    eventDelete->UplinkData(),
                    eventDelete->DownlinkData() );

            // Then update connection specific downlink and uplink data
            err = LogDataUpdateEventL(
                    connectionId,
                    R_LOG_CON_DISCONNECTED,
                    TUid::Null(),
                    eventDelete->UplinkData(),
                    eventDelete->DownlinkData() );
            if ( err != KErrNone )
                {
                LOGIT1("FAILED to log connection deletion <%d>", err)
                }

            // Stop timer mode for this connection.
            // It is ok to call this even if the connection was not in timer mode.
            iDclTimerAO->Remove( connectionId );

            }
            break;

        case EConnMonDownlinkDataThreshold:
            {
            TInt oldDuration( 0 );
            TInt newDuration( 0 );

            const CConnMonDownlinkDataThreshold* down;
            down = static_cast<const CConnMonDownlinkDataThreshold*>( &aConnMonEvent );
            connectionId = down->ConnectionId();

            LOGIT1("*** EConnMonDownlinkDataThreshold event, id %d ***", connectionId)

            if ( !GetDuration( connectionId, oldDuration ) )
                {
                return;
                }

            // First update SharedData downlink data
            UpdateSharedDataL( connectionId, 0, down->DownlinkData() );

            // Then update connection specific downlink data
            // Also uplink data has to be updated at the same time,
            // since CEventLog function SetDataL requires both values.
            TRequestStatus status;
            TUint uplinkData( 0 );
            // Get the amount uplink data from CM server
            iConnectionMonitor.GetUintAttribute(
                    connectionId,
                    KSubConnectionId,
                    KUplinkData,
                    uplinkData,
                    status );
            User::WaitForRequest( status );
            if ( status.Int() != KErrNone )
                {
                LOGIT1("FAILED to get KUplinkData from ConnMon <%d>", err)
                }

            // The actual status of connection is R_LOG_CON_CONNECTED, but
            // due to the performance reasons KConnectionStatusIdNotAvailable
            // is used here. There is no need to read LOGWRAP.RSC every time.
            err = LogDataUpdateEventL(
                    connectionId,
                    KConnectionStatusIdNotAvailable,
                    TUid::Null(),
                    uplinkData,
                    down->DownlinkData() );
            if ( err != KErrNone )
                {
                LOGIT1("FAILED to update log event <%d>", err)
                }

            // Switch to timer if we are updating log too often
            if ( GetDuration( connectionId, newDuration ) )
                {
                if ( ( newDuration - oldDuration ) < iTimerInterval )
                    {
                    SwitchToTimerMode( connectionId );
                    }
                }
            }
            break;

        case EConnMonUplinkDataThreshold:
            {
            TInt oldDuration( 0 );
            TInt newDuration( 0 );

            const CConnMonUplinkDataThreshold* up;
            up = static_cast<const CConnMonUplinkDataThreshold*>( &aConnMonEvent );
            connectionId = up->ConnectionId();

            LOGIT1("*** EConnMonUplinkDataThreshold event, id %d ***", connectionId)

            if ( !GetDuration( connectionId, oldDuration ) )
                {
                return;
                }

            // First update SharedData uplink data
            UpdateSharedDataL( connectionId, up->UplinkData(), 0 );

            // Then update connection specific uplink data
            // Also downlink data has to be updated at the same time,
            // since CEventLog function SetDataL requires both values.
            TRequestStatus status;
            TUint downlinkData( 0 );

            // Get the amount of downlink data from CM server
            iConnectionMonitor.GetUintAttribute(
                    connectionId,
                    KSubConnectionId,
                    KDownlinkData,
                    downlinkData,
                    status );
            User::WaitForRequest( status );
            if ( status.Int() != KErrNone )
                {
                LOGIT1("FAILED to get KDownlinkData from ConnMon <%d>", err)
                }

            // The actual status of connection is R_LOG_CON_CONNECTED, but
            // due to the performance reasons KConnectionStatusIdNotAvailable
            // is used here. There is no need to read LOGWRAP.RSC every time.
            err = LogDataUpdateEventL(
                    connectionId,
                    KConnectionStatusIdNotAvailable,
                    TUid::Null(),
                    up->UplinkData(),
                    downlinkData );
            if ( err != KErrNone )
                {
                LOGIT1("FAILED to update log event <%d>", err)
                }

            // Switch to timer if we are updating log too often
            if ( GetDuration( connectionId, newDuration ) )
                {
                if ( ( newDuration - oldDuration ) < iTimerInterval )
                    {
                    SwitchToTimerMode( connectionId );
                    }
                }
            }
            break;

        default:
            /*
             *  By default Connection Monitor server sends all the events to
             *  all the clients, which have subscribed to reveive events. It
             *  is not possible to subscribe to only selected events.
             *
             *  Therefore these events are not written to log.
             */
            break;
        }
    LOGEXITFN("CEngine::EventL()")
    }

// --------------------------------------------------------------------------
// CEngine::LogDataAddEventL
// Adds log events.
//
// Handles also multiple data connections.
// --------------------------------------------------------------------------
//
void CEngine::LogDataAddEventL(
        TLogId       aConnectionId,
        TInt         aRConnectionStatusId,
        const TDesC& aRemote,
        TInt         aLogDir,
        const TDesC& aTelNum,
        const TUid&  aDataEventType )
    {
    LOGENTRFN("CEngine::LogDataAddEventL()")
    // It is possible to add a new logevent with a new log id for the same
    // connection (reconnect case) assuming that all the next updates will
    // be for the new event and not the old one.
    TTime time;
    time.UniversalTime();
    TBuf<KLogMaxStatusLength> logStatusBuf;
    TBuf<KLogMaxDirectionLength> logDirBuf;

    // Set the parameters for CEvent
    CEvent* eventUpdate = CEvent::NewL();
    CleanupStack::PushL( eventUpdate );
    eventUpdate->iConnectionId = aConnectionId;
    eventUpdate->iLogWrapEvent->SetId( KGenconnLogWaitingForLogId );
    eventUpdate->iLogWrapEvent->SetTime( time );
    // Ignore error - string blank on error which is ok.
    iLogWrap->Log().GetString( logStatusBuf, aRConnectionStatusId );
    eventUpdate->iLogWrapEvent->SetStatus( logStatusBuf );
    eventUpdate->iLogWrapEvent->SetRemoteParty( aRemote );
    // Ignore error - string blank on error which is ok.
    iLogWrap->Log().GetString( logDirBuf, aLogDir );
    eventUpdate->iLogWrapEvent->SetDirection( logDirBuf );
    eventUpdate->iLogWrapEvent->SetNumber( aTelNum );
    eventUpdate->iLogWrapEvent->SetEventType( *CONST_CAST( TUid*, &aDataEventType ) );
    eventUpdate->iLogWrapEvent->SetDurationType( KLogDurationValid );

    // If there are no requests in the queue, then write to log immediately.
    if ( !IsActive() && ( iLogEventQueue->Count() == 0 ) )
        {
        iConnections->AppendL( eventUpdate );
        iStatus = KRequestPending;
        iLogWrap->Log().AddEvent( *eventUpdate->iLogWrapEvent, iStatus );
        SetActive();
        }
    else
        {
        LOGIT0("Adding event to eventqueue")
        // Add the request to the queue, it will be processed ASAP.
        iLogEventQueue->AppendL( eventUpdate );
        }
    CleanupStack::Pop( eventUpdate );
    LOGEXITFN("CEngine::LogDataAddEventL()")
    }

// --------------------------------------------------------------------------
// CEngine::LogDataChangeEventL
// Changes log events. Used to update the access point name of the
// external GPRS ie. dial-up connections.
//
// Handles also multiple data connections.
// --------------------------------------------------------------------------
//
void CEngine::LogDataChangeEventL( TLogId aConnectionId, const TDesC& aRemote )
    {
    LOGENTRFN("CEngine::LogDataChangeEventL()")

    // Check event queue first, if a match is found, the connection hasn't been
    // added to iConnections array yet. Update event in queue with correct
    // access point name.
    for ( TInt index = 0; index < iLogEventQueue->Count(); index++ )
        {
        LOGIT2("Checking event queue index %d, id %d", index, iLogEventQueue->At( index )->iConnectionId)
        if ( iLogEventQueue->At( index )->iConnectionId == aConnectionId )
            {
            LOGIT0("Match found, merging updated apn to queued event")
            iLogEventQueue->At( index )->iLogWrapEvent->SetRemoteParty( aRemote );
            LOGEXITFN("CEngine::LogDataChangeEventL()")
            return;
            }
        }

    // Most likely event queue was empty, so next we need to find the relevant
    // connection from iConnections array.
    for ( TInt index = 0; index < iConnections->Count(); index++ )
        {
        LOGIT2("Checking connections index %d, id %d", index, iConnections->At( index )->iConnectionId)

        if ( iConnections->At( index )->iConnectionId == aConnectionId )
            {
            // Can update now, or need add to queue
            if ( !IsActive() && ( iLogEventQueue->Count() == 0 ) )
                {
                LOGIT0("Match found, updating apn immediately")

                // Update apn and initiate LogWrapper update.
                iConnections->At( index )->iLogWrapEvent->SetRemoteParty( aRemote );
                iStatus = KRequestPending;
                iLogWrap->Log().ChangeEvent( *iConnections->At( index )->iLogWrapEvent, iStatus );
                SetActive();
                }
            else
                {
                LOGIT0("Match found, adding apn update event to queue")

                // Make a copy of the CLogEvent, update apn and add the updated
                // copy to event queue.
                CEvent* eventUpdate = CEvent::NewL();
                CleanupStack::PushL( eventUpdate );

                eventUpdate->iConnectionId = aConnectionId;
                eventUpdate->iLogWrapEvent->CopyL( *iConnections->At( index )->iLogWrapEvent );
                eventUpdate->iLogWrapEvent->SetRemoteParty( aRemote );
                iLogEventQueue->AppendL( eventUpdate );

                CleanupStack::Pop( eventUpdate );
                }
            break; // for
            }
        }
    LOGEXITFN("CEngine::LogDataChangeEventL()")
    }

// --------------------------------------------------------------------------
// CEngine::LogDataUpdateEventL
// Updates log events.
//
// Handles also multiple data connections.
// --------------------------------------------------------------------------
//
TInt CEngine::LogDataUpdateEventL(
        TLogId        aConnectionId,
        TInt          aRConnectionStatusId,
        const TUid&   aDataEventType,
        const TInt64& aBytesSent,
        const TInt64& aBytesReceived )
    {
    LOGENTRFN("CEngine::LogDataUpdateEventL()")
    TInt ret = KErrNone;
    TLogId connectionId;

    for ( TInt index = 0; index < iConnections->Count(); index++ )
        {
        connectionId = iConnections->At( index )->iConnectionId;

        if ( connectionId == aConnectionId )
            {
            // Check if there is no request pending, then start it. Otherwise
            // wait until the previous request is finished and keep going on.
            if ( !IsActive() && ( iLogEventQueue->Count() == 0 ) )
                {
                // Request update straight on.
                UpdateLogEventParamL(
                        aConnectionId,
                        *iConnections->At( index )->iLogWrapEvent,
                        aRConnectionStatusId,
                        aDataEventType,
                        aBytesSent,
                        aBytesReceived );

                iConnections->At( index )->iPrevDuration =
                        iConnections->At( index )->iLogWrapEvent->Duration();

                iLogWrap->Log().ChangeEvent( *iConnections->At( index )->iLogWrapEvent, iStatus );
                SetActive();
                }
            else
                {
                LOGIT0("Adding event update to queue")
                // Add the request to the queue, it will be processed ASAP.
                CEvent* eventUpdate = CEvent::NewL();
                CleanupStack::PushL( eventUpdate );
                eventUpdate->iConnectionId = aConnectionId;
                eventUpdate->iLogWrapEvent->CopyL( *iConnections->At( index )->iLogWrapEvent );
                ret = UpdateLogEventParamL(
                        aConnectionId,
                        *eventUpdate->iLogWrapEvent,
                        aRConnectionStatusId,
                        aDataEventType,
                        aBytesSent,
                        aBytesReceived );

                iConnections->At( index )->iPrevDuration =
                        eventUpdate->iLogWrapEvent->Duration();

                // Add to the queue.
                iLogEventQueue->AppendL( eventUpdate );
                CleanupStack::Pop( eventUpdate );
                }
            }
        }
    LOGEXITFN1("CEngine::LogDataUpdateEventL()", ret)
    return ret;
    }

// --------------------------------------------------------------------------
// CEngine::UpdateLogEventParamL
// Updates log event parameters.
// --------------------------------------------------------------------------
//
TInt CEngine::UpdateLogEventParamL(
        TLogId        /*aConnectionId*/,
        CLogEvent&    aLogEvent,
        TInt          aRConnectionStatusId,
        const TUid&   aDataEventType,
        const TInt64& aBytesSent,
        const TInt64& aBytesReceived )
    {
    LOGENTRFN("CEngine::UpdateLogEventParamL()")
    TInt ret( KErrNone );
    TTime now;
    TTimeIntervalSeconds seconds( 0 );
    now.UniversalTime();

    if ( now.SecondsFrom( aLogEvent.Time(), seconds ) != KErrNone )
        {
        seconds = 0; // No duration available -> error.
        }
    aLogEvent.SetDuration( seconds.Int() );

    if ( KConnectionStatusIdNotAvailable != aRConnectionStatusId )
        {
        // Status needs to be updated.
        TBuf<KLogMaxStatusLength> logStatusBuf;
        // Ignore error - string blank on error which is ok.
        iLogWrap->Log().GetString( logStatusBuf, aRConnectionStatusId );
        aLogEvent.SetStatus( logStatusBuf );
        }
    if ( aDataEventType != TUid::Null() )
        {
        aLogEvent.SetEventType( *CONST_CAST( TUid*, &aDataEventType ) );
        }
    // Check if data metrics need to be updated.
    TInt64 byteInfoNotAvailable( KBytesInfoNotAvailable );
    if ( ( aBytesReceived != byteInfoNotAvailable ) &&
         ( aBytesSent     != byteInfoNotAvailable ) )
        {
        TBuf8<KDatabufferSize> dataBuffer;
        dataBuffer.Num( aBytesSent );
        dataBuffer.Append( TChar(',') );
        dataBuffer.AppendNum( aBytesReceived );
        TRAP( ret, aLogEvent.SetDataL( dataBuffer ) );
        }
    LOGEXITFN1("CEngine::UpdateLogEventParamL()", ret)
    return ret;
    }

// --------------------------------------------------------------------------
// CEngine::UpdateSharedDataL
// Updates cumulative counters of SharedData.
// --------------------------------------------------------------------------
//
void CEngine::UpdateSharedDataL(
        TLogId aConnectionId,
        const TInt64& aBytesSent,
        const TInt64& aBytesReceived )
    {
    LOGENTRFN("CEngine::UpdateSharedDataL()")
    TInt64   sent( 0 );
    TInt64   received( 0 );
    TInt64   increment( 0 );
    TLogId   connId;
    TBuf<50> receivedBuf,  sentBuf;
    TBuf<50> receivedBuf2, sentBuf2;
    TUid     dataEventType = KLogPacketDataEventTypeUid;
    TInt     err, index;

    // First find out the data event type (GPRS or WLAN)
    for ( index = 0; index < iConnections->Count(); index++ )
        {
        connId = iConnections->At( index )->iConnectionId;
        if ( connId == aConnectionId )
            {
            dataEventType = iConnections->At( index )->iLogWrapEvent->EventType();

            if ( aBytesSent == 0 && aBytesReceived == 0 )
                {
                break;
                }

            CRepository* repository = NULL;
            repository = CRepository::NewL( KCRUidDCLLogs );
            if ( repository )
                {
                err = repository->StartTransaction( CRepository::EConcurrentReadWriteTransaction );
                if ( err )
                    {
                    LOGIT1("UpdateSharedDataL: StartTransaction() returned error: %d", err)
                    delete repository;
                    break;
                    }

                if ( aBytesReceived != 0 )
                    {
                    // Read current value.
                    if ( dataEventType == KLogPacketDataEventTypeUid )
                        {
                        err = repository->Get( KLogsGPRSReceivedCounter, receivedBuf );
                        }
                    else
                        {
                        err = repository->Get( KLogsWLANReceivedCounter, receivedBuf );
                        }
                    if ( err == KErrNone )
                        {
                        TLex lex( receivedBuf );
                        lex.Val( received );
                        }
                    else
                        {
                        LOGIT1("UpdateSharedDataL: aBytesReceived: Get() returned error: %d", err)
                        repository->CancelTransaction();
                        delete repository;
                        break;
                        }

                    // Calculate the increment
                    increment = aBytesReceived - iConnections->At( index )->iPrevDownValue;
                    // And add _only_ the increment
                    received += increment;
                    // Update the previous downlink value
                    iConnections->At( index )->iPrevDownValue = aBytesReceived;
                    receivedBuf2.Num( received );

                    // Store updated value.
                    if ( dataEventType == KLogPacketDataEventTypeUid )
                        {
                        err = repository->Set( KLogsGPRSReceivedCounter, receivedBuf2 );
                        }
                    else
                        {
                        err = repository->Set( KLogsWLANReceivedCounter, receivedBuf2 );
                        }
                    if ( err != KErrNone )
                        {
                        LOGIT1("UpdateSharedDataL: aBytesReceived: Set() returned error: %d", err)
                        repository->CancelTransaction();
                        delete repository;
                        break;
                        }
                    }
                if ( aBytesSent != 0 )
                    {
                    // Read current value.
                    if ( dataEventType == KLogPacketDataEventTypeUid )
                        {
                        err = repository->Get( KLogsGPRSSentCounter, sentBuf );
                        }
                    else
                        {
                        err = repository->Get( KLogsWLANSentCounter, sentBuf );
                        }
                    if ( err == KErrNone )
                        {
                        TLex lex( sentBuf );
                        lex.Val( sent );
                        }
                    else
                        {
                        LOGIT1("UpdateSharedDataL: aBytesSent: Get() returned error: %d", err)
                        repository->CancelTransaction();
                        delete repository;
                        break;
                        }

                    // Calculate the increment
                    increment = aBytesSent - iConnections->At( index )->iPrevUpValue;
                    // And add _only_ the increment
                    sent += increment;
                    // Update the previous uplink value
                    iConnections->At( index )->iPrevUpValue = aBytesSent;
                    sentBuf2.Num( sent );

                    // Store updated value.
                    if ( dataEventType == KLogPacketDataEventTypeUid )
                        {
                        err = repository->Set( KLogsGPRSSentCounter, sentBuf2 );
                        }
                    else
                        {
                        err = repository->Set( KLogsWLANSentCounter, sentBuf2 );
                        }
                    if ( err != KErrNone )
                        {
                        LOGIT1("UpdateSharedDataL: aBytesSent: Set() returned error: %d", err)
                        repository->CancelTransaction();
                        delete repository;
                        break;
                        }
                    }
                TUint32 keyInfo;
                err = repository->CommitTransaction( keyInfo );
                if ( err != KErrNone )
                    {
                    LOGIT2("UpdateSharedDataL: repository->CommitTransaction returned error: %d, %d", err, keyInfo)
                    repository->CancelTransaction();
                    delete repository;
                    break;
                    }
                }
            delete repository;
            break;
            }
        } // for
    LOGEXITFN("CEngine::UpdateSharedDataL()")
    }

// --------------------------------------------------------------------------
// CEngine::RunL
// Handles the completion of an asynchronous request.
// This function is defined as pure virtual in CActive and any class derived
// from CActive must define and implement it.
// --------------------------------------------------------------------------
//
void CEngine::RunL()
    {
    LOGENTRFN("CEngine::RunL()")

    // Request has completed.
    // Delete the disconnected element of iConnections, if it has already been
    // logged and the connection has been disconnected
    if ( iConnections->Count() > 0 )
        {
        LOGIT1("RunL: iConnections->Count() = %d (>0), clearing old connections", iConnections->Count())

        // Ignore error - string blank on error which is ok.
        TBuf<KLogMaxStatusLength> logStatusBuf;
        iLogWrap->Log().GetString( logStatusBuf, R_LOG_CON_DISCONNECTED );

        for ( TInt i = iConnections->Count()-1; i >= 0; i-- )
            {
            CEvent* elemPtr = iConnections->At( i );

            __ASSERT_DEBUG( ( elemPtr != NULL ), User::Panic( KDCLName, KErrNotReady ) );
            LOGIT2("Checking index %d, connection id %d", i, elemPtr->iConnectionId)
            if ( logStatusBuf == elemPtr->iLogWrapEvent->Status() )
                {
                LOGIT0("Status: disconnected, removing")

                // Remove the pointer from the queue.
                iConnections->Delete( i );
                // Delete the completed CEvent
                delete elemPtr;
                }
            }
        }

    // Delete completed event and check if there is a next event pending.
    // If LogEng is not supported, a dummy logeng just returns error straight
    // on, but we carry on doing all the requests.
    if ( iLogEventQueue->Count() > 0 )
        {
        LOGIT1("RunL: iLogEventQueue->Count() = %d (>0), processing next event", iLogEventQueue->Count())

        CEvent* nextEventPtr = iLogEventQueue->At( 0 );
        __ASSERT_DEBUG( ( nextEventPtr != NULL ), User::Panic( KDCLName, KErrNotReady ) );
        // In general, before the elements of this kind of array are deleted,
        // the CBase derived objects to which those elements point should be
        // destroyed. If they are not destroyed, then a separate copy of
        // those elements (i.e. the pointers), must be taken to avoid
        // orphaning the CBase derived objects on the heap.
        iCurrentLogEvent->CopyL( *nextEventPtr->iLogWrapEvent );
        iLogWrap->Log().ChangeEvent( *iCurrentLogEvent, iStatus );
        SetActive();
        // Remove the pointer from the queue.
        iLogEventQueue->Delete( 0 );
        // Delete the completed CEvent
        delete nextEventPtr;
        }
    LOGEXITFN("CEngine::RunL()")
    }

// --------------------------------------------------------------------------
// CEngine::DoCancel
// Handles a cancel request to the service provider.
// This function is defined as pure virtual in CActive and any class derived
// from CActive must define and implement it.
// --------------------------------------------------------------------------
//
void CEngine::DoCancel()
    {
    if ( iLogWrap )
        {
        iLogWrap->Log().Cancel();
        }
    // Usually you do not need to cancel an update on events, just let them
    // go and be removed from the queue when update is done.
    // If we cancel the logger, most likely the whole Logger Object will be
    // destroyed.
}

// --------------------------------------------------------------------------
// CEngine::SetAttachModeL
// Gets the Attach Mode setting from CommDb and sets ETel to use that value.
// --------------------------------------------------------------------------
//
void CEngine::SetAttachModeL() const
    {
    LOGENTRFN("CEngine::SetAttachModeL()")

    TUint32 commdbAM = RPacketService::EAttachWhenNeeded;
    TRequestStatus status;
    RPacketService::TAttachMode etelAM;
    CCommsDatabase* commDb = NULL;

    if ( iPacketServLoaded != 1 )
        {
        LOGIT0("RPacketService not loaded")
        return;
        }

    commDb = CCommsDatabase::NewL();

    // Get the Attach Mode setting from CommDb
    TRAPD( error, commDb->GetGlobalSettingL( TPtrC( GPRS_ATTACH_MODE ), commdbAM ) );
    switch ( error )
        {
        case KErrNone:
            // Attach Mode setting found in CommDb
            break;
        case KErrNotFound:
            {
            // Attach Mode setting not found in CommDb, it is set as EAttachWhenNeeded
            TRAPD( leaveCode, commDb->SetGlobalSettingL( TPtrC( GPRS_ATTACH_MODE ), commdbAM ) );
            if ( leaveCode )
                {
                LOGIT1("FAILED SetGlobalSettingL() <%d>", leaveCode)
                }
            }
            break;
        default:
            {
            LOGIT1("FAILED to get attach mode from CommDb <%d>", error)
            }
            break;
        }

    // And set ETel to use the Attach Mode value from CommDb
    etelAM = STATIC_CAST( RPacketService::TAttachMode, commdbAM );

    TInt err( KErrNotReady );
    TInt a = 0;
    while ( a < KPhoneRetryCount && err != KErrNone )
        {
        if ( a )
            {
            User::After( KPhoneRetryTimeout ); // Don't wait on first try
            }
        iPacketService.SetAttachMode( status, etelAM );
        User::WaitForRequest( status );
        err = status.Int();
        LOGIT1("Set attach Mode to ETel <%d>", err)
        a++;
        }
    if ( err != KErrNone )
        {
        LOGIT1("FAILED to set attach mode to ETel <%d>", err)
        }
    delete commDb;
    LOGEXITFN("CEngine::SetAttachModeL()")
    }

// --------------------------------------------------------------------------
// CEngine::SetAPNL
// Gets the default Access Point Name from CommDb and
// sets ETel to use that APN.
// --------------------------------------------------------------------------
//
void CEngine::SetAPNL() const
    {
    LOGENTRFN("CEngine::SetAPNL()")

    TBuf<KMaxAPName> apName;
    TRequestStatus status( KErrNotReady );
    CCommsDatabase* commDb = NULL;

    if ( iPacketServLoaded != 1 )
        {
        LOGIT0("RPacketService not loaded")
        return;
        }

    commDb = CCommsDatabase::NewL();
    CleanupStack::PushL( commDb );

    // Get the default Access Point Name from CommDb
    CCommsDbTableView* table = commDb->OpenTableLC( TPtrC( DEFAULT_GPRS ) );

    table->GotoFirstRecord();

    //If APN is not initialized, ReadTextL will leave
    apName.SetLength( 0 );

    table->ReadTextL( TPtrC( DEFAULT_GPRS_APN ), apName );

    CleanupStack::PopAndDestroy( table );

    // Get DefaultContextParams from ETel
    RPacketContext::TContextConfigGPRS* getParams = NULL;
    getParams = new( ELeave ) RPacketContext::TContextConfigGPRS();

    TPckg<RPacketContext::TContextConfigGPRS> getParamsPckg( *getParams );

    TInt err( KErrNotReady );
    TInt a = 0;
    while ( a < KPhoneRetryCount && err != KErrNone )
        {
        // The RPacketService::GetDefaultContextParams() does not always work
        // with the first trial, it might return KErrNotReady.
        if ( a )
            {
            User::After( KPhoneRetryTimeout ); // Don't wait on first try
            }
        iPacketService.GetDefaultContextParams( status, getParamsPckg );
        User::WaitForRequest( status );
        err = status.Int();
        LOGIT1("Got DefaultContextParams from ETel <%d>", err)
        a++;
        }

    if ( err != KErrNone )
        {
        LOGIT1("FAILED to get DefaultContextParams from ETel <%d>", err)
        }

    // Replace ETel APN with CommDb APN
    ReplaceAPN( getParams->iAccessPointName, apName );

    // Set DefaultContextParams to ETel
    iPacketService.SetDefaultContextParams( status, getParamsPckg );
    User::WaitForRequest( status );

    err = status.Int();
    if ( err != KErrNone )
        {
        LOGIT1("FAILED to set DefaultContextParams to ETel <%d>", err)
        }

    delete getParams;
    CleanupStack::PopAndDestroy( commDb );
    LOGEXITFN("CEngine::SetAPNL()")
    }

// --------------------------------------------------------------------------
// CEngine::AddEventTypeL
// Adds the event type Wlan. Otherwise the LogClient
// will not understand this type of event.
// --------------------------------------------------------------------------
//
void CEngine::AddEventTypeL()
    {
    iLogEventType = CLogEventType::NewL();
    iLogEventType->SetUid( KLogWlanDataEventTypeUid );
    iLogEventType->SetDescription( _L("Wlan") );
    iLogEventType->SetLoggingEnabled( ETrue );

    iStatus = KRequestPending;
    iLogClient->AddEventType( *iLogEventType, iStatus );

    SetActive();
    }

// -----------------------------------------------------------------------------
// CEngine::ReplaceAPN
// This method will be used in platforms where ETel APN (aResult) is UNICODE.
// -----------------------------------------------------------------------------
//
void CEngine::ReplaceAPN( TDes& aResult, const TDesC& aName ) const
    {
    aResult = aName;
    }

// -----------------------------------------------------------------------------
// CEngine::ReplaceAPN
// This method will be used in platforms where ETel APN (aResult) is UTF8.
// -----------------------------------------------------------------------------
//
void CEngine::ReplaceAPN( TDes8& aResult, const TDesC& aName ) const
    {
    CnvUtfConverter::ConvertFromUnicodeToUtf8( aResult, aName );
    }

// -----------------------------------------------------------------------------
// CEngine::LogDataInTimerModeL
// -----------------------------------------------------------------------------
//
TInt CEngine::LogDataInTimerModeL( const TUint aConnectionId )
    {
    LOGENTRFN("CEngine::LogDataInTimerModeL()")
    TInt index( 0 );
    TInt64 prevDown( 0 );
    TInt64 prevUp( 0 );

    TInt count( iConnections->Count() );

    LOGIT1("LogDataInTimerMode, id %d", aConnectionId)

    // Does the connection still exist
    for ( index = 0; index < count; index++ )
        {
        TUint connId( iConnections->At( index )->iConnectionId );

        if ( connId == aConnectionId )
            {
            prevDown = iConnections->At(index)->iPrevDownValue;
            prevUp = iConnections->At(index)->iPrevUpValue;
            break;
            }
        }

    if ( index == count )
        {
        return KErrNotFound;
        }

    // Read data volumes
    TRequestStatus status;
    TUint uplinkData( 0 );
    TUint downlinkData( 0 );

    // Get the amount uplink data from CM server
    iConnectionMonitor.GetUintAttribute(
            aConnectionId,
            KSubConnectionId,
            KUplinkData,
            uplinkData,
            status );
    User::WaitForRequest( status );

    if ( status.Int() != KErrNone )
        {
        LOGIT1("FAILED to get KUplinkData from ConnMon <%d>", status.Int())
        }

    // Get the amount of downlink data from CM server
    iConnectionMonitor.GetUintAttribute(
            aConnectionId,
            KSubConnectionId,
            KDownlinkData,
            downlinkData,
            status );
    User::WaitForRequest( status );

    if ( status.Int() != KErrNone )
        {
        LOGIT1("FAILED to get KDownlinkData from ConnMon <%d>", status.Int())
        }

    // Update SharedData downlink and uplink data
    UpdateSharedDataL( aConnectionId, uplinkData, downlinkData );

    TInt ret = KErrNone;
    // The actual status of connection is R_LOG_CON_CONNECTED, but
    // due to the performance reasons KConnectionStatusIdNotAvailable
    // is used here. There is no need to read LOGWRAP.RSC every time.
    ret = LogDataUpdateEventL(
            aConnectionId,
            KConnectionStatusIdNotAvailable,
            TUid::Null(),
            uplinkData,
            downlinkData );
    if ( ret != KErrNone )
        {
        LOGIT1("FAILED to update log data (timer mode) <%d>", ret)
        }

    // Check data volumes
    TUint threshold( iGPRSThreshold );

    ret = GetDefaultThreshold( aConnectionId, threshold );

    if ( ret != KErrNotFound )
        {
        // If the increase both in uplink and downlink data volume is less than threshold/2
        // switch the connection back to the threshold mode.
        if ( ( ( iConnections->At(index)->iPrevDownValue - prevDown ) < threshold/2 )
               &&
               ( ( iConnections->At(index)->iPrevUpValue - prevUp ) < threshold/2 ))
            {
            SwitchToThresholdMode( aConnectionId, threshold );

            // Return an error to stop the timer mode for this connection
            ret = KErrCancel;
            }
        else
            {
            ret = KErrNone;
            }
        }
    LOGEXITFN1("CEngine::LogDataInTimerModeL()", ret)
    return ret;
    }

// -----------------------------------------------------------------------------
// CEngine::SwitchToThresholdMode
// -----------------------------------------------------------------------------
//
void CEngine::SwitchToThresholdMode( const TUint aConnectionId, const TUint aThreshold  )
    {
    LOGENTRFN("CEngine::SwitchToThresholdMode()")
    // Set thresholds to start the threshold mode
    TUint threshold( aThreshold );

    if ( threshold == 0 )
        {
        TInt ret = GetDefaultThreshold( aConnectionId, threshold );

        if ( ret == KErrNotFound )
            {
            // Connection has closed
            return;
            }
        }

    iConnectionMonitor.SetUintAttribute(
            aConnectionId,
            KSubConnectionId,
            KDownlinkDataThreshold,
            threshold );

    iConnectionMonitor.SetUintAttribute(
            aConnectionId,
            KSubConnectionId,
            KUplinkDataThreshold,
            threshold );

    LOGIT1("Switched to threshold mode, id %d", aConnectionId)

    LOGEXITFN("CEngine::SwitchToThresholdMode()")
    }

// -----------------------------------------------------------------------------
// CEngine::SwitchToTimerMode
// -----------------------------------------------------------------------------
//
void CEngine::SwitchToTimerMode( const TUint aConnectionId )
    {
    LOGENTRFN("CEngine::SwitchToTimerMode()")
    if ( iDclTimerAO != 0 )
        {
        // Set thresholds to zero.
        iConnectionMonitor.SetUintAttribute(
                aConnectionId,
                KSubConnectionId,
                KDownlinkDataThreshold,
                0 );

        iConnectionMonitor.SetUintAttribute(
                aConnectionId,
                KSubConnectionId,
                KUplinkDataThreshold,
                0 );

        iDclTimerAO->Add( aConnectionId );

        LOGIT1("Switched to timer mode, id %d", aConnectionId)
        }
    LOGEXITFN("CEngine::SwitchToTimerMode()")
    }

// -----------------------------------------------------------------------------
// CEngine::GetDuration
// Retrieves iPrevDuration from connection in connection table with matching id.
// -----------------------------------------------------------------------------
//
TBool CEngine::GetDuration( const TUint aConnectionId, TInt& aDuration )
    {
    TInt index( 0 );

    for ( index = 0; index < iConnections->Count(); index++ )
        {
        TUint connId( iConnections->At( index )->iConnectionId );

        if ( aConnectionId == connId )
            {
            aDuration = iConnections->At( index )->iPrevDuration;
            return ETrue;
            }
        }

    LOGIT1("FAILED to get duration for connection id %d", aConnectionId)
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CEngine::GetDefaultThreshold
// -----------------------------------------------------------------------------
//
TInt CEngine::GetDefaultThreshold( const TUint aConnectionId, TUint& aThreshold )
    {
    LOGENTRFN("CEngine::GetDefaultThreshold()")
    TInt bearer( 0 );
    TRequestStatus status( 0 );

    // Initialize to the smallest value
    aThreshold = iGPRSThreshold;

    iConnectionMonitor.GetIntAttribute(
            aConnectionId,
            KSubConnectionId,
            KBearer,
            bearer,
            status );
    User::WaitForRequest( status );

    if ( status.Int() != KErrNotFound )
        {
        if ( bearer == EBearerWCDMA || bearer == EBearerExternalWCDMA )
            {
            aThreshold = iWCDMAThreshold;
            }
        else if ( bearer == EBearerWLAN || bearer == EBearerExternalWLAN )
            {
            aThreshold = iWLANThreshold;
            }
        else
            {
            aThreshold = iGPRSThreshold;
            }
        }

    LOGEXITFN1("CEngine::GetDefaultThreshold()", status.Int())
    return status.Int();
    }

// -----------------------------------------------------------------------------
// CEngine::ReadIniFile
// -----------------------------------------------------------------------------
//
void CEngine::ReadIniFile()
    {
    LOGENTRFN("CEngine::ReadIniFile()")
    TInt err( KErrNone );
    TInt value( 0 );

    // Initialize parameters to defaults because they might not be in SharedData.
    iTimerInterval  = KTimerInterval;
    iGPRSThreshold  = KGPRSDataThreshold;
    iWCDMAThreshold = KWCDMADataThreshold;
    iWLANThreshold  = KWLANDataThreshold;

    // Connecting and initialization:
    CRepository* repository = NULL;

    TRAPD( leaveCode, repository = CRepository::NewL( KCRUidDCL ) );

    if ( leaveCode )
        {
        LOGIT1("CRepository::NewL() FAILED: %d", leaveCode)
        // Go on using defaults
        return;
        }

    err = repository->Get( KDCLGprsThreshold, value );

    if ( err == KErrNone )
        {
        iGPRSThreshold = value;
        }
    else
        {
        LOGIT1("Reading KDCLGprsThreshold from CRepository  FAILED: %d", err)
        }

    err = repository->Get( KDCLWcdmaThreshold, value );

    if ( err == KErrNone )
        {
        iWCDMAThreshold = value;
        }
     else
        {
        LOGIT1("Reading KDCLWcdmaThreshold from CRepository  FAILED: %d", err)
        }

    err = repository->Get( KDCLWlanThreshold, value );

    if ( err == KErrNone )
        {
        iWLANThreshold = value;
        }
    else
        {
        LOGIT1("Reading KDCLWlanThreshold from CRepository  FAILED: %d", err)
        }

    err = repository->Get( KDCLTimerInterval, value );

    if ( err == KErrNone )
        {
        iTimerInterval = value;
        }
    else
        {
        LOGIT1("Reading KDCLTimerInterval from CRepository  FAILED: %d", err)
        }

    // Closing connection:
    delete repository;
    LOGEXITFN("CEngine::ReadIniFile()")
    }

// End of file