cmmanager/cmmgr/cmmserver/src/cmmcache.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 15:35:44 +0300
branchRCL_3
changeset 57 05bc53fe583b
permissions -rw-r--r--
Revision: 201033 Kit: 201035

/*
* Copyright (c) 2009-2010 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:
* Database cache manager.
*
*/


#include <e32base.h>
#include <cmpluginbaseeng.h>
#include <cmdefconnvalues.h>
#include <cmpluginembdestinationdef.h>

#include <es_sock.h>    // RSocketServ, RConnection
#include <in_sock.h>    // KAfInet
#include <es_enum.h>    // TConnectionInfo

#include "cmmdestinationstruct.h"
#include "cmmlistenermanager.h"
#include "cmminstancemapping.h"

#include "cmmcache.h"

#include "cmmdestinationinstance.h"
#include "cmmconnmethodinstance.h"

#include "cmmtransactionhandler.h"
#include "cmmanagerimpl.h"
#include "cmmbearerprioritycache.h"

#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "cmmcacheTraces.h"
#endif


// ---------------------------------------------------------------------------
// Two phased construction.
// ---------------------------------------------------------------------------
//
CCmmCache* CCmmCache::NewL(
        CCmManagerImpl* aCmManagerImpl,
        CArrayPtrFlat<const CCmPluginBaseEng>* aPlugins )
    {
    OstTraceFunctionEntry0( CCMMCACHE_NEWL_ENTRY );

    CCmmCache* self = CCmmCache::NewLC( aCmManagerImpl, aPlugins );
    CleanupStack::Pop( self );

    OstTraceFunctionExit0( CCMMCACHE_NEWL_EXIT );

    return self;
    }

// ---------------------------------------------------------------------------
// Two phased construction.
// ---------------------------------------------------------------------------
//
CCmmCache* CCmmCache::NewLC(
        CCmManagerImpl* aCmManagerImpl,
        CArrayPtrFlat<const CCmPluginBaseEng>* aPlugins )
    {
    OstTraceFunctionEntry0( CCMMCACHE_NEWLC_ENTRY );

    CCmmCache* self = new( ELeave ) CCmmCache( aCmManagerImpl, aPlugins );
    CleanupStack::PushL( self );
    self->ConstructL();

    OstTraceFunctionExit0( CCMMCACHE_NEWLC_EXIT );

    return self;
    }

// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CCmmCache::~CCmmCache()
    {
    OstTraceFunctionEntry0( CCMMCACHE_CCMMCACHE_ENTRY );

    delete iBearerPriorityCache;
    delete iListenerManager;
    delete iInstanceMapping;

    iConnMethodArray.ResetAndDestroy();
    iDestinationArray.ResetAndDestroy();

    iDeletedConnMethods.Close();
    iUpdatedConnMethods.Close();
    iUpdatedConnMethods2.Close(); // Does not own contents.
    iUpdatedDestinations.Close();
    iUpdatedDestinations2.Close(); // Does not own contents.

    OstTraceFunctionExit0( CCMMCACHE_CCMMCACHE_EXIT );
    }

// ---------------------------------------------------------------------------
// Constructor.
// ---------------------------------------------------------------------------
//
CCmmCache::CCmmCache(
        CCmManagerImpl* aCmManagerImpl,
        CArrayPtrFlat<const CCmPluginBaseEng>* aPlugins )
        :
        iPlugins( aPlugins ),
        iCmManagerImpl( aCmManagerImpl )
    {
    OstTraceFunctionEntry0( DUP1_CCMMCACHE_CCMMCACHE_ENTRY );

    iListenerManager = NULL;
    iInstanceMapping = NULL;
    iTrans = NULL;
    iBearerPriorityCache = NULL;
    iCurrentTemporaryId = KTemporaryIdCounterStart;

    iSnapTableId = 0;
    iSnapMetadataTableId = 0;

    OstTraceFunctionExit0( DUP1_CCMMCACHE_CCMMCACHE_EXIT );
    }

// ---------------------------------------------------------------------------
// Second phase constructor.
// ---------------------------------------------------------------------------
//
void CCmmCache::ConstructL()
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONSTRUCTL_ENTRY );

    iTrans = iCmManagerImpl->GetTransactionHandler();

    iBearerPriorityCache = CCmmBearerPriorityCache::NewL(
            iTrans,
            iCmManagerImpl->TableId( ECmmDbBearerPriorityRecord ) );
    iListenerManager = CCmmListenerManager::NewL( this );
    iInstanceMapping = CCmmInstanceMapping::NewL( *this );

    // Create CommsDat listeners to detect changes to the database from external sources.
    RArray<TUint32> tableIdArray;
    CleanupClosePushL( tableIdArray );

    iSnapTableId = iCmManagerImpl->TableId( ECmmDbSnapRecord );
    iSnapMetadataTableId = iCmManagerImpl->TableId( ECmmDestMetadataRecord );

    // Instancemapping needs notifications on following tables.
    tableIdArray.Append( CommsDat::KCDTIdIAPRecord );
    tableIdArray.Append( CommsDat::KCDTIdVPNServiceRecord );
    tableIdArray.Append( iSnapTableId );
    tableIdArray.Append( iSnapMetadataTableId );

    // Destinations need notifications on following tables.
    tableIdArray.Append( CommsDat::KCDTIdNetworkRecord );
    tableIdArray.Append( CommsDat::KCDTIdAccessPointRecord );
    // Destination metadata table was already added.

    // Connection methods need notifications on following tables.
    TInt pluginCount( iPlugins->Count() );
    if ( pluginCount )
        {
        ( *iPlugins )[0]->GetGenericTableIdsToBeObservedL( tableIdArray );
        for( TInt i = 0; i < pluginCount; i++ )
            {
            ( *iPlugins )[i]->GetBearerTableIdsToBeObservedL( tableIdArray );
            }
        }

    for ( TInt i = 0; i < tableIdArray.Count(); i++ )
        {
        iListenerManager->AddListenerL( tableIdArray[i] );
        }
    CleanupStack::PopAndDestroy( &tableIdArray );

    OstTraceFunctionExit0( CCMMCACHE_CONSTRUCTL_EXIT );
    }

// ---------------------------------------------------------------------------
// Opens a destination with given ID (if not already opened), then copies the
// relevant data to the session instance. Checks that destination ID is valid.
// ---------------------------------------------------------------------------
//
void CCmmCache::OpenDestinationL(
        CCmmDestinationInstance& aDestinationInstance,
        const TUint32 aDestinationId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_OPENDESTINATIONL_ENTRY );

    if ( !iInstanceMapping->ValidDestinationId( aDestinationId ) )
        {
        User::Leave( KErrNotFound );
        }

    TInt index = FindDestinationFromCache( aDestinationId );
    if ( index == KErrNotFound )
        {
        // Cache does not have a handle open to this destination. A new handle
        // needs to be created first.
        CCmmDestinationStruct* destination = CCmmDestinationStruct::NewLC(
                this,
                iTrans,
                aDestinationId );

        // Now that cache has a handle on this destination, copy the relevant
        // data to the session instance.
        destination->CreateDestinationInstanceL( aDestinationInstance );

        iDestinationArray.AppendL( destination );
        CleanupStack::Pop( destination );
        }
    else
        {
        // Cache already has a handle on this destination. Copy the relevant
        // data to the session instance.
        iDestinationArray[index]->CreateDestinationInstanceL( aDestinationInstance );
        }

    // Add list of currently contained connection methods.
    iInstanceMapping->GetConnMethodsFromDestinationL(
            aDestinationId,
            aDestinationInstance.iConnMethodItemArray );

    OstTraceFunctionExit0( CCMMCACHE_OPENDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Refresh the data contained in aDestinationInstance. This means reloading
// the data from database if necessary. After this call the contents of
// aDestinationInstance will reflect the current state in the database. 
// ---------------------------------------------------------------------------
//
void CCmmCache::RefreshDestinationL( CCmmDestinationInstance& aDestinationInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REFRESHDESTINATIONL_ENTRY );

    TInt index = FindDestinationFromCache( aDestinationInstance.GetId() );
    if ( index == KErrNotFound )
        {
        index = FindNotSavedDestinationFromCacheL( aDestinationInstance.GetDestinationNameL(), 0 );
        if ( index == KErrNotFound )
            {
            User::Leave( KErrNotFound );
            }
        }

    // If this destination is a newly created one that doesn't yet exist in
    // database, just return silently.
    if ( iDestinationArray[index]->GetStatus() == ECmmDestinationStatusNotSaved )
        {
        OstTraceFunctionExit0( CCMMCACHE_REFRESHDESTINATIONL_EXIT );
        return;
        }

    iDestinationArray[index]->RefreshDestinationInstanceL( aDestinationInstance );

    OstTraceFunctionExit0( DUP1_CCMMCACHE_REFRESHDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Create a new destination into cache (with name and ID) and copy the data
// into session side handle.
// ---------------------------------------------------------------------------
//
void CCmmCache::CreateDestinationL(
        CCmmDestinationInstance& aDestinationInstance,
        const TDesC& aName,
        const TUint32 aId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CREATEDESTINATIONL_ENTRY );

    // Create a new destination with given name.
    CCmmDestinationStruct* destination = CCmmDestinationStruct::NewLC( this, iTrans, aName, aId );

    destination->CreateDestinationInstanceL( aDestinationInstance );

    iDestinationArray.AppendL( destination );
    CleanupStack::Pop( destination );

    OstTraceFunctionExit0( CCMMCACHE_CREATEDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Opens a connection method with given ID (if not already opened), then
// creates and passes an instance of it to the caller.
// If aDestinationInstance is not NULL, connection method is opened from
// destination.
// ---------------------------------------------------------------------------
//
void CCmmCache::OpenConnMethodL(
        CCmmConnMethodInstance& aConnMethodInstance,
        CCmmDestinationInstance* aDestinationInstance,
        const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_OPENCONNMETHODL_ENTRY );

    // Check connection method ID.
    TBool validAttributes( EFalse );
    if ( !aDestinationInstance )
        {
        // Check connection method exists in database.
        // Embedded destinations not included.
        validAttributes = iInstanceMapping->ValidConnMethodId( aConnMethodId );
        }
    else
        {
        // Check connection method is inside the destination.
        if ( aDestinationInstance->
                ValidConnMethodIdInDestinationIncludeEmbedded( aConnMethodId ) )
            {
            // Check connection method (can be embedded destination too)
            // exists in database.
            if ( iInstanceMapping->ValidConnMethodId( aConnMethodId ) ||
                    iInstanceMapping->ValidDestinationId( aConnMethodId ) )
                {
                validAttributes = ETrue;
                }
            }
       }
    if ( !validAttributes )
        {
        User::Leave( KErrNotFound );
        }

    // Create the connection method instance.

    // Check if connection method is already opened in cache.
    TInt index = FindConnMethodFromCache( aConnMethodId );
    if ( index != KErrNotFound )
        {
        // Update data from commsdat if necessary.
        if ( iConnMethodArray[index]->GetRecordStatus() == ECmmRecordStatusExpired )
            {
            iConnMethodArray[index]->ReloadPluginDataIfNeededL();
            // CopyDataL() will set the internal state of aConnMethodInstance.
            }

        // Already open in cache. Copy the connection method data to session
        // instance.
        // Will increase reference counter.
        aConnMethodInstance.CopyDataL( iConnMethodArray[index] );
        }
    else
        {
        // Not yet open in cache, open now.
        OpenConnectionMethodInstanceL( aConnMethodInstance, aConnMethodId );
        }

    OstTraceFunctionExit0( CCMMCACHE_OPENCONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Refresh the data contained in aConnMethodInstance. This means reloading the
// data from database if necessary. After this call the contents of
// aConnMethodInstance will reflect the current state in the database. 
// ---------------------------------------------------------------------------
//
void CCmmCache::RefreshConnMethodL( CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REFRESHCONNMETHODL_ENTRY );

    // If embedded destination --> refresh through destination API.
    if ( aConnMethodInstance.IsEmbeddedDestination() )
        {
        return;
        }

    TInt index = FindConnMethodFromCache( aConnMethodInstance.GetId() );
    if ( index == KErrNotFound )
        {
        User::Leave( KErrNotFound );
        }

    // If this connection method is a newly created one that doesn't yet exist
    // in database, just return silently.
    if ( iConnMethodArray[index]->GetStatus() == ECmmConnMethodStatusNotSaved )
        {
        OstTraceFunctionExit0( CCMMCACHE_REFRESHCONNMETHODL_EXIT );
        return;
        }

    iConnMethodArray[index]->ReloadPluginDataIfNeededL();
    if ( iConnMethodArray[index]->GetStatus() == ECmmConnMethodStatusValid
            || iConnMethodArray[index]->GetStatus() == ECmmConnMethodStatusToBeDeleted )
        {
        iConnMethodArray[index]->GetPlugin()->GetPluginDataL( 
                aConnMethodInstance.GetPluginDataInstance() );
        }
    // Internal state need to be set to the same state as after a successfull update.
    aConnMethodInstance.UpdateSuccessful();

    OstTraceFunctionExit0( DUP1_CCMMCACHE_REFRESHCONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Creates a new connection method (not embedded destinations) into database
// with the given bearer type and optionally ID, then creates and passes an
// instance of it to the caller.
// If destination instance is provided (not NULL), connection method is
// created in that destination.
// If connection method ID is provided, it's availability is verified.
// ---------------------------------------------------------------------------
//
void CCmmCache::CreateConnMethodL(
        CCmmConnMethodInstance& aConnMethodInstance,
        CCmmDestinationInstance* aDestinationInstance,
        const TUint32 aBearerType,
        const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CREATECONNMETHODL_ENTRY );

    // Check that the bearer type is not embedded destination.
    if ( aBearerType == KUidEmbeddedDestination )
        {
        User::Leave( KErrArgument );
        }

    TUint32 connMethodId( aConnMethodId );
    if ( aConnMethodId )
        {
        // Check if a connection method with given ID exists (or is already
        // created but not saved).
        if ( iInstanceMapping->ValidConnMethodId( aConnMethodId ) ||
                ConnMethodOpenWithId( aConnMethodId ) )
            {
            User::Leave( KErrAlreadyExists );
            }
        }
    else
        {
        // Use a temporary ID until a real one is received from database.
        connMethodId = NextFreeTemporaryId();
        }


    // Check bearer type support and create plugin instance.
    CCmPluginBaseEng* plugin = NULL;
    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == aBearerType )
            {
            TCmPluginInitParam pluginParams( Session() );
            plugin = ( *iPlugins )[i]->CreateInstanceL( pluginParams );
            CleanupStack::PushL( plugin );
            plugin->CreateNewL( aConnMethodId ); // Use aConnMethodId here, so ID is either a real ID or 0.
            break;
            }
        }
    if ( !plugin )
        {
        User::Leave( KErrArgument );
        }


    // Store the connection method into cache.
    // Use connMethodId here, so ID is either a real ID or a temporary ID.
    CCmmConnMethodStruct* connMethodStruct = CCmmConnMethodStruct::NewL( connMethodId );
    connMethodStruct->SetPlugin( plugin, aBearerType, ECmmConnMethodStatusNotSaved );
    CleanupStack::Pop( plugin );
    CleanupStack::PushL( connMethodStruct );
    iConnMethodArray.AppendL( connMethodStruct );
    CleanupStack::Pop( connMethodStruct );

    // Copy the connection method data to session instance.
    aConnMethodInstance.CopyDataL( connMethodStruct ); // Will increase reference counter.


    if ( aDestinationInstance )
        {
        // Add connection method to destination.
        aDestinationInstance->AddConnMethodL( aConnMethodInstance );
        }

    OstTraceFunctionExit0( CCMMCACHE_CREATECONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Creates a new connection method into cache as a copy of an existing
// connection method (exists in cache, not necessarily in database), and opens
// a client side handle to it. The new connection method will get a new ID when
// updated to database.
// ---------------------------------------------------------------------------
//
void CCmmCache::CreateCopyOfConnMethodL(
        CCmmConnMethodInstance& aNewConnMethodInstance,
        CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CREATECOPYOFCONNMETHODL_ENTRY );

    // Check bearer type support and create plugin instance.
    TInt index = FindConnMethodFromCache( aConnMethodInstance.GetId() );
    if ( index == KErrNotFound )
        {
        User::Leave( index );
        }

    CCmPluginBaseEng* plugin = iConnMethodArray[index]->GetPlugin();
    if ( !plugin )
        {
        User::Leave( KErrNotFound );
        }

    CCmPluginBaseEng* pluginCopy =
            plugin->CreateCopyL( aConnMethodInstance.GetPluginDataInstance() );
    CleanupStack::PushL( pluginCopy );

    // Store the connection method into cache.
    CCmmConnMethodStruct* connMethodStruct =
            CCmmConnMethodStruct::NewL( NextFreeTemporaryId() );
    connMethodStruct->SetPlugin(
            pluginCopy,
            aConnMethodInstance.GetBearerType(),
            ECmmConnMethodStatusNotSaved );
    CleanupStack::Pop( pluginCopy );
    CleanupStack::PushL( connMethodStruct );
    iConnMethodArray.AppendL( connMethodStruct );
    CleanupStack::Pop( connMethodStruct );

    // Copy the connection method data to session instance.
    aNewConnMethodInstance.CopyDataL( connMethodStruct ); // Will increase reference counter.

    OstTraceFunctionExit0( CCMMCACHE_CREATECOPYOFCONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Reloads a destination record if needed and copies the latest version to
// the session instance given as parameter.
// ---------------------------------------------------------------------------
//
void CCmmCache::LoadDestinationRecordL(
        CCmmDestinationInstance& aDestinationInstance,
        TCmmDbRecords aDestRecordType )
    {
    OstTraceFunctionEntry0( CCMMCACHE_LOADDESTINATIONRECORDL_ENTRY );

    TUint32 id = aDestinationInstance.GetId();
    // If ID is not in the valid range, it means the destination is a newly
    // created one, and doesn't yet exist in database. Thus, record data exists
    // only in session side and can't be loaded from cache. So this is an error.
    if ( id >= KTemporaryIdCounterStart )
        {
        User::Leave( KErrCorrupt );
        }
    TInt index = FindDestinationFromCache( id );
    if ( index < 0 )
        {
        User::Leave( index );
        }

    iDestinationArray[index]->LoadRecordL( aDestinationInstance, aDestRecordType );

    OstTraceFunctionExit0( CCMMCACHE_LOADDESTINATIONRECORDL_EXIT );
    }

// ---------------------------------------------------------------------------
// Saves the modifications in aDestinationInstance into the database. Also all
// connection methods inside this destination are updated (including any
// embedded destination).
// ---------------------------------------------------------------------------
//
void CCmmCache::UpdateDestinationL( CCmmDestinationInstance& aDestinationInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_UPDATEDESTINATIONL_ENTRY );

    // Arrays to temporarily store information of updated destinations and
    // connection methods. Used to update status and ID information after
    // successful commit to database.
    if ( iTrans->GetReferenceCount() == 0 )
        {
        iDeletedConnMethods.Reset();
        iUpdatedConnMethods.Reset();
        iUpdatedConnMethods2.Reset();
        iUpdatedDestinations.Reset();
        iUpdatedDestinations2.Reset();
        }

    TInt index = FindDestinationFromCache( aDestinationInstance.GetId() );
    if ( index < 0 )
        {
        User::Leave( index );
        }

    iTrans->OpenTransactionLC();

    // Check that the connection methods marked for deletion can be deleted.
    for ( TInt i = 0; i < aDestinationInstance.iConnMethodsToBeDeleted.Count(); i++ )
        {
        TUint32 id = aDestinationInstance.iConnMethodsToBeDeleted[i];

        // Remove connection method from delete list if ID is not valid or if
        // referenced from any other destination (in database or in any other
        // client-side destination handles).
        if ( !iInstanceMapping->ValidConnMethodId( id ) ||
                iInstanceMapping->ConnMethodInOtherDestination(
                        id,
                        aDestinationInstance.GetId() ) ||
                aDestinationInstance.ConnMethodInOtherDestinationInSession( id, 0 ) )
            {
            aDestinationInstance.iConnMethodsToBeDeleted.Remove( i );
            i--; // Adjust array index counter.
            }
        else if ( iInstanceMapping->ConnMethodPointedToByVirtualIap( id ) )
            {
            aDestinationInstance.iConnMethodsToBeDeleted.Remove( i );
            User::Leave( KErrLocked );
            }
        else if ( CheckIfCmConnected( id ) )
            {
            User::Leave( KErrInUse );
            }
        }
    // Delete connection methods marked for deletion.
    for ( TInt i = 0; i < aDestinationInstance.iConnMethodsToBeDeleted.Count(); i++ )
        {
        DeleteConnMethodAsPartOfDestinationUpdateL(
                aDestinationInstance.iConnMethodsToBeDeleted[i] );
        }

    // Update the connection methods inside this destination.
    for ( TInt i = 0; i < aDestinationInstance.iConnMethodItemArray.Count(); i++ )
        {
        if ( aDestinationInstance.iConnMethodItemArray[i].IsEmbedded() )
            {
            // Embedded destination.

            //TODO, Maybe check other restrictions on embedded destination.
            // - Only one embedded destination per destination. Check.
            // - No embedded destinations in embedded destination. Check.

            TUint32 id( aDestinationInstance.iConnMethodItemArray[i].iId );
            CCmmDestinationInstance* destinationInstance =
                    aDestinationInstance.FindDestinationInstanceFromSessionById( id );

            if ( destinationInstance )
                {
                // Client has a handle open to this destination, update it.
                destinationInstance->UpdateL();
                }
            else
                {
                // TODO: Or, should we update the embedded destination if the
                // client has an open handle to any of the embedded
                // destinations connection methods.
                //
                // Skip update since client doesn't have an open handle to this
                // destination.
                if ( id >= KTemporaryIdCounterStart ||
                        !iInstanceMapping->ValidDestinationId( id ) )
                    {
                    // Remove destination item from array if:
                    // - New destination, but client has already closed the handle for it.
                    // - Destination ID was valid before, but it does not exist anymore.
                    aDestinationInstance.iConnMethodItemArray.Remove( i );
                    i--; // Adjust array index counter.
                    }
                }
            }
        else
            {
            TBool temporaryConnMethodInstance( EFalse );
            TBool connMethodProtectionMustBeSet( EFalse );
            TBool cmProtected( EFalse );

            TUint32 id( aDestinationInstance.iConnMethodItemArray[i].iId );
            CCmmConnMethodInstance* connMethodInstance =
                    aDestinationInstance.FindConnMethodInstanceFromSessionById( id );

            if ( !connMethodInstance )
                {
                // Remove connection method item from the array if it is a new
                // connection method but client has already closed the handle
                // for it, or the connection method ID was valid before but
                // does not exist anymore.
                if ( id >= KTemporaryIdCounterStart || !iInstanceMapping->ValidConnMethodId( id ) )
                    {
                    aDestinationInstance.iConnMethodItemArray.Remove( i );
                    i--; // Adjust array index counter.
                    continue; // Jump to next connection method.
                    }
                }

            if ( aDestinationInstance.ProtectionChanged() )
                {
                // Check if the connection method protection level needs to be set.
                switch ( aDestinationInstance.CurrentProtectionLevelL() )
                    {
                    case CMManager::EProtLevel0:
                    case CMManager::EProtLevel2:
                        {
                        if ( aDestinationInstance.LastProtectionLevel() ==
                                CMManager::EProtLevel1 ||
                                aDestinationInstance.LastProtectionLevel() ==
                                CMManager::EProtLevel3 )
                            {
                            connMethodProtectionMustBeSet = ETrue;
                            cmProtected = EFalse;
                            }
                        }
                        break;
                    case CMManager::EProtLevel1:
                    case CMManager::EProtLevel3:
                        {
                        connMethodProtectionMustBeSet = ETrue;
                        cmProtected = ETrue;
                        }
                        break;
                    default:
                        break;
                    }
                }

            if ( connMethodProtectionMustBeSet && !connMethodInstance )
                {
                // Client doesn't have an open handle to this connection method,
                // but it still needs to be updated because the destination's
                // protection level has been changed in such a way that
                // requires a change in all contained connection methods also.
                temporaryConnMethodInstance = ETrue;
                connMethodInstance = CCmmConnMethodInstance::NewLC( NULL, this );
                OpenConnMethodL( *connMethodInstance, NULL, id );

                // Read current protection level.
                TBool current = connMethodInstance->GetBoolAttributeL( CMManager::ECmProtected );
                if ( cmProtected == current )
                    {
                    // If the connection method already has the correct
                    // protection setting, skip the unnecessary update.
                    connMethodProtectionMustBeSet = EFalse;
                    temporaryConnMethodInstance = EFalse;
                    CleanupStack::PopAndDestroy( connMethodInstance );
                    connMethodInstance = NULL;
                    }
                }

            // Update the connection method.
            if ( connMethodInstance )
                {
                if ( connMethodProtectionMustBeSet )
                    {
                    connMethodInstance->SetBoolAttributeL( CMManager::ECmProtected, cmProtected );
                    }
                connMethodInstance->UpdateL( temporaryConnMethodInstance );
                }

            // Cleanup connection method handle, if it was temporary.
            if ( temporaryConnMethodInstance )
                {
                CleanupStack::PopAndDestroy( connMethodInstance );
                }
            connMethodInstance = NULL;
            }
        }
    // All connection methods requiring update in the destination should have
    // been updated to database now.

    iDestinationArray[index]->UpdateL( aDestinationInstance, this );
    iTrans->CommitTransactionL();

    TCmmIdStruct idStruct( iDestinationArray[index]->GetRealId(), 0 );
    if ( aDestinationInstance.GetId() >= KTemporaryIdCounterStart )
        {
        idStruct.iTemporaryId = aDestinationInstance.GetId();
        }
    iUpdatedDestinations.AppendL( idStruct );
    iUpdatedDestinations2.AppendL( &aDestinationInstance );

    // Check transaction handler reference count.
    if ( iTrans->GetReferenceCount() == 0 )
        {
        // Successful commit to database. Refresh instance mapping and all
        // necessary status information.
        for ( TInt i = 0; i < iUpdatedConnMethods.Count(); i++ )
            {
            aDestinationInstance.RefreshHandlesForAllSessions( iUpdatedConnMethods[i] );
            }
        for ( TInt i = 0; i < iUpdatedConnMethods2.Count(); i++ )
            {
            iUpdatedConnMethods2[i]->UpdateSuccessful();

            TInt cacheIndex = FindConnMethodFromCache( iUpdatedConnMethods2[i]->GetId() );
            if ( cacheIndex >= 0 )
                {
                iConnMethodArray[cacheIndex]->UpdateSuccessful();
                }
            }
        for ( TInt i = 0; i < iDeletedConnMethods.Count(); i++ )
            {
            TInt cacheIndex = FindConnMethodFromCache( iDeletedConnMethods[i].iRealId );
            if ( cacheIndex >= 0 )
                {
                aDestinationInstance.RefreshHandlesForAllSessions( iDeletedConnMethods[i] );
                iConnMethodArray[cacheIndex]->SetStatus( ECmmConnMethodStatusToBeDeleted );
                iInstanceMapping->AddConnMethodToDeletedListL( iDeletedConnMethods[i].iRealId );
                }
            }
        for ( TInt i = 0; i < iUpdatedDestinations.Count(); i++ )
            {
            aDestinationInstance.RefreshHandlesForAllSessions( iUpdatedDestinations[i] );
            }
        for ( TInt i = 0; i < iUpdatedDestinations2.Count(); i++ )
            {
            iUpdatedDestinations2[i]->UpdateSuccessful();

            TInt cacheIndex = FindDestinationFromCache( iUpdatedDestinations2[i]->GetId() );
            if ( cacheIndex >= 0 )
                {
                iDestinationArray[cacheIndex]->UpdateSuccessful();
                }
            }
        iDeletedConnMethods.Reset();
        iUpdatedConnMethods.Reset();
        iUpdatedConnMethods2.Reset();
        iUpdatedDestinations.Reset();
        iUpdatedDestinations2.Reset();

        iInstanceMapping->RefreshL();
        }

    OstTraceFunctionExit0( CCMMCACHE_UPDATEDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Saves the modifications in aConnMethodInstance into database.
// The second attribute aTemporaryHandle must be true if the connection method
// instance is only a temporary one created for the duration of this update
// process only.
// ---------------------------------------------------------------------------
//
void CCmmCache::UpdateConnMethodL(
        CCmmConnMethodInstance& aConnMethodInstance,
        TBool aTemporaryHandle )
    {
    OstTraceFunctionEntry0( CCMMCACHE_UPDATECONNMETHODL_ENTRY );

    // Embedded destinatios cannot be updated as connection methods.
    if ( aConnMethodInstance.IsEmbeddedDestination() )
        {
        User::Leave( KErrNotSupported );
        }

    // Find connection method from cache.
    TUint32 connMethodId( aConnMethodInstance.GetId() );
    TInt index = FindConnMethodFromCache( connMethodId );
    if ( index < 0 )
        {
        // Should never end up here.
        User::Leave( index );
        }

    iTrans->OpenTransactionLC();
    iConnMethodArray[index]->GetPlugin()->UpdateL( aConnMethodInstance.GetPluginDataInstance() );
    iTrans->CommitTransactionL();

    TCmmIdStruct idStruct( aConnMethodInstance.GetPluginDataInstance()->iIapId, 0 );
    if ( connMethodId >= KTemporaryIdCounterStart )
        {
        idStruct.iTemporaryId = connMethodId;
        }

    if ( iTrans->GetReferenceCount() == 0 )
        {
        // Writing to database is completed, refresh instance mapping and all
        // necessary status information.
        iInstanceMapping->RefreshL();
        aConnMethodInstance.RefreshHandlesForAllSessions( idStruct );
        aConnMethodInstance.UpdateSuccessful();
        iConnMethodArray[index]->UpdateSuccessful();
        }
    else
        {
        // Update request came from destination update. Add connection method
        // information to temporary arrays so it's status information can be
        // updated after successful commit to database.
        iUpdatedConnMethods.AppendL( idStruct );
        if ( !aTemporaryHandle )
            {
            // A temporary handle will be destroyed after update, and so won't
            // need any status updates after commit.
            iUpdatedConnMethods2.AppendL( &aConnMethodInstance );
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_UPDATECONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Deletes the destination refered by aDestinationInstance from the database.
// In case other sessions have open handles to the same destination, the status
// is updated to deleted state, but it is not yet removed from database.
// (ID must be kept reserved in commsdat until all handles are closed). The
// same is done for the connection methods inside this destination.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeleteDestinationL(
        CCmmDestinationInstance& aDestinationInstance,
        TBool aForced ) //TODO, comment on aForced param?
    {
    OstTraceFunctionEntry0( CCMMCACHE_DELETEDESTINATIONL_ENTRY );

    TUint32 destinationId( aDestinationInstance.GetId() );

    // Array to temporarily store deleted connection method IDs. Status
    // information for these connection methods is updated after successful
    // commit to database.
    iDeletedConnMethods.Reset();

    // Find destination from cache.
    TInt index = FindDestinationFromCache( destinationId );
    if ( index == KErrNotFound )
        {
        User::Leave( KErrBadHandle );
        }

    // If not forced, check if the destination is already marked to be deleted.
    if ( !aForced && iDestinationArray[index]->GetStatus() == ECmmDestinationStatusToBeDeleted )
        {
        OstTraceFunctionExit0( CCMMCACHE_DELETEDESTINATIONL_EXIT );
        return;
        }

    // Check if there are any additional handles open. If yes, can't delete
    // detination from database just yet.
    if ( iDestinationArray[index]->GetReferenceCount() <= 1 )
        {
        // No other client handles open to this destination.

        // Get the connection methods in this destination, and then go through
        // them removing those that can't be deleted.
        RArray<TCmmConnMethodItem> connMethodArray;
        CleanupClosePushL( connMethodArray );
        if ( !aForced )
            {
            iInstanceMapping->GetConnMethodsFromDestinationL( destinationId, connMethodArray );
            }
        else
            {
            for ( TInt i = 0; i < aDestinationInstance.iConnMethodItemArray.Count() ; i++ )
                {
                connMethodArray.AppendL( aDestinationInstance.iConnMethodItemArray[i] );
                }
            }

        // Remove embedded destination from list if found.
        TInt count( connMethodArray.Count() );
        if ( count )
            {
            if ( connMethodArray[count - 1].IsEmbedded() )
                {
                connMethodArray.Remove( count - 1 );
                }
            }
        // Remove any connection method that belongs to any other destination.
        for ( TInt i = 0; i < connMethodArray.Count(); i++ )
            {
            if ( iInstanceMapping->ConnMethodInOtherDestination(
                    connMethodArray[i].iId,
                    destinationId ) )
                {
                connMethodArray.Remove( i );
                i--;
                }
            }
        // Remove any connection method that has a virtual IAP pointing to it.
        for ( TInt i = 0; i < connMethodArray.Count(); i++ )
            {
            if ( iInstanceMapping->ConnMethodPointedToByVirtualIap( connMethodArray[i].iId ) )
                {
                connMethodArray.Remove( i );
                i--;
                }
            }

        iTrans->OpenTransactionLC();

        // Delete each connection method inside this destination.
        for ( TInt i = 0; i < connMethodArray.Count(); i++ )
            {
            DeleteConnMethodAsPartOfDestinationDeleteL( connMethodArray[i].iId );
            }

        // Delete the destination.
        iDestinationArray[index]->DeleteL();

        iTrans->CommitTransactionL();
        CleanupStack::PopAndDestroy( &connMethodArray );

        for ( TInt i = 0; i < iDeletedConnMethods.Count(); i++ )
            {
            TInt index = FindConnMethodFromCache( iDeletedConnMethods[i].iRealId );
            if ( index == KErrNotFound )
                {
                // No handles open to this deleted connection method, so it was
                // removed from database. Remove it from instance mapping
                // structures. Refreshing instance mapping would do the same,
                // but more slowly.
                iInstanceMapping->RemoveConnMethod( iDeletedConnMethods[i].iRealId );
                }
            else
                {
                // There is at least one handle open to this deleted connection
                // method, so it still exists in database for now. Remove it
                // from instance mapping structures and add it to the instance
                // mapping's deleted list, so the connection method is ignored
                // if refreshing structures from database. Also mark the
                // connection method handle on cache side as 'to be deleted'.
                //
                // If the connection method is updated from another existing
                // handle after this, the connection method is restored as
                // uncategorized.
                iInstanceMapping->AddConnMethodToDeletedListL( iDeletedConnMethods[i].iRealId );
                aDestinationInstance.RefreshHandlesForAllSessions( iDeletedConnMethods[i] );
                iConnMethodArray[index]->SetStatus( ECmmConnMethodStatusToBeDeleted );
                }
            }
        iInstanceMapping->RemoveDestination( destinationId );
        }
    else
        {
        // There are additional client handles open to this destination. Mark
        // the destination as 'to be deleted'. When the other handles are
        // closed and reference count goes to 0, the destination will be
        // removed from database.
        // If the destination is updated from another existing handle after
        // this, the destination is restored to normal.
        iDestinationArray[index]->SetStatus( ECmmDestinationStatusToBeDeleted );
        iInstanceMapping->AddDestinationToDeletedListL( destinationId );
        }

    // Update status for ALL related destination handles on client side to
    // ECmmDestinationStatusChanged.
    TCmmIdStruct idStruct( destinationId, 0 );
    aDestinationInstance.RefreshHandlesForAllSessions( idStruct );

    iDeletedConnMethods.Reset();
    OstTraceFunctionExit0( DUP1_CCMMCACHE_DELETEDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Deletes a connection method as part of destination delete operation. It is
// already checked that the connection method given as parameter can be deleted
// and a transaction is already open.
// If there are client side handles open to the connection method, the
// connection method is marked as deleted, but the actual database removal will
// be done after the last handle is closed. Updating a connection method marked
// as deleted (through an already open handle) will restore it.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeleteConnMethodAsPartOfDestinationDeleteL( const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONDELETEL_ENTRY );

    // Find connection method from cache side.
    TInt index = FindConnMethodFromCache( aConnMethodId );
    if ( index == KErrNotFound )
        {
        // There is no open handles to this connection method. Open a temporary
        // handle and delete it.

        // Find out the connection method bearer type.
        TUint32 bearerType( 0 );
        TInt err = iInstanceMapping->GetConnMethodBearerType( aConnMethodId, bearerType );
        if ( err || bearerType == KUidEmbeddedDestination )
            {
            // If this is an embedded destination, or the bearer is not
            // supported, skip. The connection method is simply removed from
            // destination.
            OstTraceFunctionExit0( CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONDELETEL_EXIT );
            return;
            }

        // Check bearer type support, create plugin instance and delete the
        // connection method.
        CCmPluginBaseEng* plugin = NULL;
        for ( TInt i = 0; i < iPlugins->Count(); i++ )
            {
            if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == bearerType )
                {
                TCmPluginInitParam pluginParams( Session() );
                plugin = ( *iPlugins )[i]->CreateInstanceL( pluginParams );
                CleanupStack::PushL( plugin );

                // Transaction is already open.
                plugin->LoadL( aConnMethodId );
                plugin->DeleteL();

                CleanupStack::Pop( plugin );
                break;
                }
            }
        if ( !plugin )
            {
            // Skip, if bearer is unsupported. Connection method is simply
            // removed from destination.
            OstTraceFunctionExit0( DUP1_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONDELETEL_EXIT );
            return;
            }
        delete plugin;
        plugin = NULL;
        }
    else
        {
        // If the connection method is already open in cache side, we can't
        // delete it from database just yet. It will only be marked as 'to be
        // deleted' after a successful commit. When the other handles are
        // closed and reference count goes to 0, the connection method will be
        // removed from database.

        // Check destination status in cache.
        switch ( iConnMethodArray[index]->GetStatus() )
            {
            case ECmmConnMethodStatusNotSaved:
                {
                // The connection methods that are deleted with the destination
                // are retrieved through instance mapping, so there can't be
                // any newly created unsaved connection methods among them.
                User::Leave( KErrCorrupt );
                }
                break;
            case ECmmConnMethodStatusValid:
                // Proceed.
                break;
            case ECmmConnMethodStatusToBeDeleted:
                // Connection method has already been deleted.
                OstTraceFunctionExit0( DUP2_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONDELETEL_EXIT );
                return;
            case ECmmConnMethodStatusChanged:
            default:
                {
                ASSERT( 0 ); // Error, illegal status.
                User::Leave( KErrCorrupt );
                }
                break;
            }
        }

    // Add connection method ID to temporary array so it's status information
    // can be updated after successful commit to database.
    TCmmIdStruct idStruct( aConnMethodId, 0 );
    iDeletedConnMethods.AppendL( idStruct );

    OstTraceFunctionExit0( DUP3_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONDELETEL_EXIT );
    }

// ---------------------------------------------------------------------------
// Deletes a connection method as part of destination update operation. It is
// already checked that the connection method given as parameter can be deleted
// and a transaction is already open.
// If there are client side handles open to the connection method, the
// connection method is marked as deleted, but the actual database removal will
// be done after the last handle is closed. Updating a connection method marked
// as deleted (through an already open handle) will restore it.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeleteConnMethodAsPartOfDestinationUpdateL(
        const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONUPDATEL_ENTRY );

    // Find connection method from cache side. If not found, open a temporary handle.
    TInt index = FindConnMethodFromCache( aConnMethodId );
    if ( index == KErrNotFound )
        {
        // There are no open handles to this connection method. Open a
        // temporary handle and delete it.

        // Find out the connection method bearer type.
        TUint32 bearerType( 0 );
        TInt err = iInstanceMapping->GetConnMethodBearerType( aConnMethodId, bearerType );
        if ( err || bearerType == KUidEmbeddedDestination )
            {
            // If this is an embedded destination, or the bearer is not
            // supported, skip. The connection method is simply removed from
            // destination.
            OstTraceFunctionExit0( CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONUPDATEL_EXIT );
            return;
            }

        // Check bearer type support, create plugin instance and delete the connection method.
        CCmPluginBaseEng* plugin = NULL;
        for ( TInt i = 0; i < iPlugins->Count(); i++ )
            {
            if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == bearerType )
                {
                TCmPluginInitParam pluginParams( Session() );
                plugin = ( *iPlugins )[i]->CreateInstanceL( pluginParams );
                CleanupStack::PushL( plugin );
                iTrans->OpenTransactionLC();

                plugin->LoadL( aConnMethodId );
                plugin->DeleteL();

                iTrans->CommitTransactionL();
                CleanupStack::Pop( plugin );
                break;
                }
            }
        if ( !plugin )
            {
            // Skip, if bearer is unsupported. Connection method is simply
            // removed from destination.
            OstTraceFunctionExit0( DUP1_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONUPDATEL_EXIT );
            return;
            }
        delete plugin;
        plugin = NULL;

        // Destination update will refresh instance mapping anyway at the end,
        // so no need to add this connection method to the temporary
        // iDeletedConnMethods-array here.
        }
    else
        {
        // There is one or more open handles to this connection method. We
        // can't delete it from database just yet. It will only be marked as
        // 'to be deleted' after a successful commit. When the other handles
        // are closed and reference count goes to 0, the connection method will
        // be removed from database.

        // Check connection method status.
        switch ( iConnMethodArray[index]->GetStatus() )
            {
            case ECmmConnMethodStatusNotSaved:
                {
                // Destination update will not attempt to delete any non-valid
                // connection methods.
                User::Leave( KErrCorrupt );
                }
                break;
            case ECmmConnMethodStatusValid:
                // Proceed.
                break;
            case ECmmConnMethodStatusToBeDeleted:
                // Connection method has already been deleted.
                OstTraceFunctionExit0( DUP2_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONUPDATEL_EXIT );
                return;
            case ECmmConnMethodStatusChanged:
            default:
                {
                ASSERT( 0 ); // Error, illegal status.
                User::Leave( KErrCorrupt );
                }
                break;
            }
        // Add connection method ID to temporary array so it's status
        // information can be updated after successful commit to database.
        TCmmIdStruct idStruct( aConnMethodId, 0 );
        iDeletedConnMethods.AppendL( idStruct );
        }

    OstTraceFunctionExit0( DUP3_CCMMCACHE_DELETECONNMETHODASPARTOFDESTINATIONUPDATEL_EXIT );
    }

// ---------------------------------------------------------------------------
// Basic connection method delete. Removes a connection method from any
// destination it might belong to, and then deletes it.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeleteConnMethodL( CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( DUP1_CCMMCACHE_DELETECONNMETHODL_ENTRY );

    TUint32 cmId = aConnMethodInstance.GetId();

    // Find connection method from cache.
    TInt index = FindConnMethodFromCache( cmId );
    if ( index == KErrNotFound )
        {
        User::Leave( KErrBadHandle );
        }

    // Check if the connection method is already deleted.
    if ( iConnMethodArray[index]->GetStatus() == ECmmConnMethodStatusToBeDeleted )
        {
        OstTraceFunctionExit0( DUP1_CCMMCACHE_DELETECONNMETHODL_EXIT );
        return;
        }

    // Check if there are any additional handles open. If yes, can't delete
    // connection method from database just yet.
    if ( iConnMethodArray[index]->GetReferenceCounter() <= 1 )
        {
        // No other client handles open to this connection method.

        // Remove connection method from any destination in database and then delete it.
        iTrans->OpenTransactionLC();
        RemoveAllReferencesToConnMethodL( aConnMethodInstance );
        iConnMethodArray[index]->GetPlugin()->DeleteL();
        iTrans->CommitTransactionL();

        // Update instance mapping to reflect the current database state, and
        // notify any possible client handles for the changed destinations.
        RArray<TUint32> changedDestinations;
        iInstanceMapping->RemoveConnMethod( cmId, changedDestinations );
        for ( TInt i = 0; i < changedDestinations.Count(); i++ )
            {
            TCmmIdStruct idStruct( changedDestinations[i], 0 );
            aConnMethodInstance.RefreshHandlesForAllSessions( idStruct );
            }
        changedDestinations.Close();

        // Set status for cache and client handles.
        TUint32 newSecondaryId( NextFreeTemporaryId() );
        aConnMethodInstance.DeleteSuccessful( newSecondaryId );
        iConnMethodArray[index]->DeleteSuccessful( newSecondaryId );
        }
    else
        {
        // There are additional client handles open to this connection method.
        // Mark the connection method as 'to be deleted' and remove it from any
        // destinations in database. When the other handles are closed and
        // reference count goes to 0, the connection method will be removed
        // from database.
        RemoveAllReferencesToConnMethodL( aConnMethodInstance );
        iInstanceMapping->AddConnMethodToDeletedListL( cmId );
        TCmmIdStruct idStruct( cmId, 0 );
        aConnMethodInstance.RefreshHandlesForAllSessions( idStruct );
        iConnMethodArray[index]->SetStatus( ECmmConnMethodStatusToBeDeleted );
        }

    OstTraceFunctionExit0( DUP2_CCMMCACHE_DELETECONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Check if the given ID belongs to a valid existing destination. Attribute
// aId needs to be in the current valid range (0x1001 - 0x10FF atm).
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationExistsWithId( const TUint32 aId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONEXISTSWITHID_ENTRY );

    TBool exists = iInstanceMapping->ValidDestinationId( aId );

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONEXISTSWITHID_EXIT );
    return exists;
    }

// ---------------------------------------------------------------------------
// Check if the given name belongs to an existing destination.
// If a destination ID is given, that destination is skipped.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationExistsWithNameL( const TDesC& aName, const TUint32 aDestinationId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONEXISTSWITHNAMEL_ENTRY );

    TBool found( EFalse );

    // Check AccessPoint-table.
    CommsDat::CCDAccessPointRecord* destApRecord = static_cast<CommsDat::CCDAccessPointRecord*>(
            CommsDat::CCDRecordBase::RecordFactoryL( CommsDat::KCDTIdAccessPointRecord ) );
    CleanupStack::PushL( destApRecord );
    destApRecord->iRecordName.SetL( aName );
    if ( destApRecord->FindL( iTrans->Session() ) ) // Names should be unique.
        {
        if ( aDestinationId )
            {
            // Check the ID is different.
            destApRecord->LoadL( iTrans->Session() );
            if ( aDestinationId != destApRecord->iRecordTag )
                {
                found = ETrue;
                }
            }
        else
            {
            found = ETrue;
            }
        }
    CleanupStack::PopAndDestroy( destApRecord );

    // Check also DataMobilitySelectionPolicy-table.
    if ( !found )
        {
        CCDDataMobilitySelectionPolicyRecord* snapRecord =
                new( ELeave ) CCDDataMobilitySelectionPolicyRecord(
                        iCmManagerImpl->TableId( ECmmDbSnapRecord ) );
        CleanupStack::PushL( snapRecord );
        snapRecord->iRecordName.SetL( aName );
        if ( snapRecord->FindL( iTrans->Session() ) ) // Names should be unique.
            {
            if ( aDestinationId )
                {
                // Check the ID is different.
                snapRecord->LoadL( iTrans->Session() );
                if ( aDestinationId != snapRecord->iSNAP )
                    {
                    found = ETrue;
                    }
                }
            else
                {
                found = ETrue;
                }
            }
        CleanupStack::PopAndDestroy( snapRecord );
        }

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONEXISTSWITHNAMEL_EXIT );
    return found;
    }

// ---------------------------------------------------------------------------
// Check if the given ID belongs to a destination that the cache has an open
// handle on. This will include any destinations created by any client, that
// are not yet saved to database.
// With this check, we can prevent two clients from creating a new destination
// with the same ID (The UpdateL() operation would fail for one of them anyway).
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationOpenWithId( const TUint32 aId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONOPENWITHID_ENTRY );

    TBool result( EFalse );
    TInt index = FindDestinationFromCache( aId );
    if ( index != KErrNotFound )
        {
        result = ETrue;
        }

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONOPENWITHID_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Check if the given name belongs to any new unsaved destinations in the
// cache. This includes any destinations created byany client, that are not
// yet saved to database.
// If a destination ID is provided, the search will exclude the relevant
// destination.
// With this check, we can prevent two clients from creating a new destination
// with the same name (The UpdateL() operation would fail for one of them
// anyway).
// ---------------------------------------------------------------------------
//
TBool CCmmCache::NotSavedDestinationOpenWithNameL(
        const TDesC& aName,
        const TUint32 aDestinationId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_NOTSAVEDDESTINATIONOPENWITHNAMEL_ENTRY );

    TBool res( EFalse );
    TInt index = FindNotSavedDestinationFromCacheL( aName, aDestinationId );
    if ( index != KErrNotFound )
        {
        res = ETrue;
        }

    OstTraceFunctionExit0( CCMMCACHE_NOTSAVEDDESTINATIONOPENWITHNAMEL_EXIT );
    return res;
    }

// ---------------------------------------------------------------------------
// Check if the given ID belongs to a valid existing connection method.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::ConnMethodExistsWithId( const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODEXISTSWITHID_ENTRY );

    TBool exists = iInstanceMapping->ValidConnMethodId( aConnMethodId );

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODEXISTSWITHID_EXIT );
    return exists;
    }

// ---------------------------------------------------------------------------
// Check if the given ID belongs to a connection method that the cache has an
// open handle on. This will include any connection methods created by any
// client, that are not yet saved to database.
// With this check, we can prevent two clients from creating a new connection
// method with the same ID (The UpdateL() operation would fail for one of them
// anyway).
// ---------------------------------------------------------------------------
//
TBool CCmmCache::ConnMethodOpenWithId( const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODOPENWITHID_ENTRY );

    TBool result( EFalse );
    TInt index = FindConnMethodFromCache( aConnMethodId );
    if ( index != KErrNotFound )
        {
        result = ETrue;
        }

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODOPENWITHID_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Checks if there are any destinations with metadata localization
// (ESnapMetadataDestinationIsLocalised) set to aValue. Also checks the
// relevant metadata purpose (ESnapMetadataPurpose) values.
// The destination connected to parameter aDestinationInstance is skipped.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationExistsWithMetadataLocalizedL(
        CCmmDestinationInstance& aDestinationInstance,
        const TUint32 aValue )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONEXISTSWITHMETADATALOCALIZEDL_ENTRY );

    TBool result( EFalse );

    if ( aValue != CMManager::ENotLocalisedDest )
        {
        // Check there is no destination in database that already has the same
        // localization metadata value. Also checks metadata purpose.
        CommsDat::CMDBRecordSet<CCDSNAPMetadataRecord>* metaSet =
                new( ELeave )CommsDat::CMDBRecordSet<CCDSNAPMetadataRecord>(
                        iCmManagerImpl->TableId( ECmmDestMetadataRecord ) );
        CleanupStack::PushL( metaSet );
        TRAP_IGNORE( metaSet->LoadL( iTrans->Session() ) );
        for ( TInt i = 0; i < metaSet->iRecords.Count(); i++ )
            {
            TInt id = ( ( CCDSNAPMetadataRecord* )metaSet->iRecords[i] )->iSNAP;
            TUint32 metadata = ( ( CCDSNAPMetadataRecord* )metaSet->iRecords[i] )->iMetadata;
            TUint32 localizationValue =
                    ( metadata & CMManager::ESnapMetadataDestinationIsLocalised ) >> 4;
            TUint32 purposeValue = ( metadata & CMManager::ESnapMetadataPurpose ) >> 8;

            if ( aDestinationInstance.GetId() != id )
                {
                if ( aValue == localizationValue )
                    {
                    result = ETrue;
                    }
                switch ( aValue )
                    {
                    case CMManager::ELocalisedDestInternet:
                        {
                        if ( purposeValue == CMManager::ESnapPurposeInternet ||
                                ( metadata & CMManager::ESnapMetadataInternet ) )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ELocalisedDestWap:
                        {
                        if ( purposeValue == CMManager::ESnapPurposeOperator )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ELocalisedDestMMS:
                        {
                        if ( purposeValue == CMManager::ESnapPurposeMMS )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ELocalisedDestIntranet:
                        {
                        if ( purposeValue == CMManager::ESnapPurposeIntranet )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    default:
                        break;
                    }
                }
            }
        CleanupStack::PopAndDestroy( metaSet );
        }

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONEXISTSWITHMETADATALOCALIZEDL_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Checks if there are any destinations with metadata purpose
// (ESnapMetadataPurpose) set to aValue. Also checks the relevant metadata
// localization (ESnapMetadataDestinationIsLocalised) values.
// The destination connected to parameter aDestinationInstance is skipped.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationExistsWithMetadataPurposeL(
        CCmmDestinationInstance& aDestinationInstance,
        const TUint32 aValue )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONEXISTSWITHMETADATAPURPOSEL_ENTRY );

    TBool result( EFalse );

    if ( aValue != CMManager::ESnapPurposeUnknown )
        {
        // Check there is no destination in database that already has the same
        // metadata purpose value. Also checks localization metadata.
        CommsDat::CMDBRecordSet<CCDSNAPMetadataRecord>* metaSet =
                new( ELeave )CommsDat::CMDBRecordSet<CCDSNAPMetadataRecord>(
                        iCmManagerImpl->TableId( ECmmDestMetadataRecord ) );
        CleanupStack::PushL( metaSet );
        TRAP_IGNORE( metaSet->LoadL( iTrans->Session() ) );
        for ( TInt i = 0; i < metaSet->iRecords.Count(); i++ )
            {
            TInt id = ( ( CCDSNAPMetadataRecord* )metaSet->iRecords[i] )->iSNAP;
            TUint32 metadata = ( ( CCDSNAPMetadataRecord* )metaSet->iRecords[i] )->iMetadata;
            TUint32 localizationValue =
                    ( metadata & CMManager::ESnapMetadataDestinationIsLocalised ) >> 4;
            TUint32 purposeValue = ( metadata & CMManager::ESnapMetadataPurpose ) >> 8;

            if ( aDestinationInstance.GetId() != id )
                {
                if ( aValue == purposeValue )
                    {
                    result = ETrue;
                    }
                switch ( aValue )
                    {
                    case CMManager::ESnapPurposeInternet:
                        {
                        if ( localizationValue == CMManager::ELocalisedDestInternet ||
                                ( metadata & CMManager::ESnapMetadataInternet ) )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ESnapPurposeOperator:
                        {
                        if ( localizationValue == CMManager::ELocalisedDestWap )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ESnapPurposeMMS:
                        {
                        if ( localizationValue == CMManager::ELocalisedDestMMS )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    case CMManager::ESnapPurposeIntranet:
                        {
                        if ( localizationValue == CMManager::ELocalisedDestIntranet )
                            {
                            result = ETrue;
                            }
                        }
                        break;
                    default:
                        break;
                    }
                }
            }
        CleanupStack::PopAndDestroy( metaSet );
        }

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONEXISTSWITHMETADATAPURPOSEL_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Tells the cache that a database table has changed. If the SNAP/IAP structure
// has possibly changed, the cache will refresh that information immediately.
// For other database tables, the tables are flagged and will be refreshed when
// needed.
// ---------------------------------------------------------------------------
//
void CCmmCache::DbChangeDetectedL( const TUint32 aTableId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DBCHANGEDETECTED_ENTRY );

    if ( aTableId == iSnapMetadataTableId )
        {
        for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
            {
            iDestinationArray[i]->NotifyRecordChange( ECmmDestMetadataRecord );
            }
        }
    else if ( aTableId == CommsDat::KCDTIdNetworkRecord )
        {
        // Affects destinations.
        for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
            {
            iDestinationArray[i]->NotifyRecordChange( ECmmDestNetworkRecord );
            }
        }
    else if ( aTableId == CommsDat::KCDTIdAccessPointRecord )
        {
        // Affects destinations.
        for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
            {
            iDestinationArray[i]->NotifyRecordChange( ECmmDestApRecord );
            }
        }

    // Notify Connection Methods about the table changes in CommsDat.
    NotifyPluginsForTableChangesL( aTableId );

    // Update instancemapping.
    iInstanceMapping->RefreshL();

    OstTraceFunctionExit0( CCMMCACHE_DBCHANGEDETECTED_EXIT );
    }

// ---------------------------------------------------------------------------
// Informs all the loaded iaps if something related to their tables
// changed in commsdat.
// ---------------------------------------------------------------------------
//
void CCmmCache::NotifyPluginsForTableChangesL( const TUint32 aTableId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_NOTIFYPLUGINSFORTABLECHANGESL_ENTRY );

    if ( iConnMethodArray.Count() )
        {
        RArray<TUint32> tableIdArray;
        CleanupClosePushL( tableIdArray );

        // Check if change concerns some table generic for all iaps
        ( *iPlugins )[0]->GetGenericTableIdsToBeObservedL( tableIdArray );
        TBool generic( EFalse );
        for( TInt i = 0; i < tableIdArray.Count(); i++ )
            {
            if ( aTableId == tableIdArray[i] )
                {
                generic = ETrue;
                break;
                }
            }

        if ( generic )
            {
            // generic-->Notify all iaps
            for( TInt i = 0; i < iConnMethodArray.Count(); i++ )
                {
                iConnMethodArray[i]->NotifyRecordChange( aTableId );
                }
            }
        else
            {
            // Not generic: Check bearer specific tables
            RArray<TUint32> affectedBearersArray;
            CleanupClosePushL( affectedBearersArray );
            for( TInt i = 0; i < iPlugins->Count(); i++ )
                {
                tableIdArray.Reset();
                ( *iPlugins )[i]->GetBearerTableIdsToBeObservedL( tableIdArray );
                TInt idCount = tableIdArray.Count();
                for( TInt j = 0; j < idCount; j++ )
                    {
                    if ( aTableId == tableIdArray[j] )
                        {
                        // Save the bearer type id which is affected
                        affectedBearersArray.AppendL(
                                ( *iPlugins )[i]->GetBearerInfoIntL(
                                        CMManager::ECmBearerType ) );
                        }
                    }
                }

            // Go through all the loaded iaps and notify all the iaps
            // which have the same bearer type saved above
            for( TInt i = 0; i < iConnMethodArray.Count(); i++ )
                {
                for( TInt j = 0; j < affectedBearersArray.Count(); j++ )
                    {
                    if ( iConnMethodArray[i]->GetBearerType() == affectedBearersArray[j] )
                        {
                        iConnMethodArray[i]->NotifyRecordChange( aTableId );
                        break;
                        }
                    }
                }
            CleanupStack::PopAndDestroy( &affectedBearersArray );
            }
        CleanupStack::PopAndDestroy( &tableIdArray );
        }
    OstTraceFunctionExit0( CCMMCACHE_NOTIFYPLUGINSFORTABLECHANGESL_EXIT );
    }

// ---------------------------------------------------------------------------
// Tells the cache that an error has occured with a database listener. Any
// reads to this table need go through the database, since cache can't know if
// it has up-to-date information.
// ---------------------------------------------------------------------------
//
void CCmmCache::DbChangeError( const TUint32 aTableId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DBCHANGEERROR_ENTRY );

    (void)aTableId; //TODO
    // Flag the table as: permanently not up-to-date

    //TODO, How to do this?
    // Implement some 'status locked' flags that are always checked before changing status back to 'loaded' after reading database?
    // Or move record status info to CCmmCache-class?
    // What about plugins?
	// Or just ignore errors with notifiers?

    // For now, just ignore errors.

    OstTraceFunctionExit0( CCMMCACHE_DBCHANGEERROR_EXIT );
    }

// ---------------------------------------------------------------------------
// Tells the cache that a hadle to a destination was closed. The cache will
// decrement the related reference counter and perform any cleanup if necessary.
// This should be called automatically from the destructor of
// CCmmDestinationInstance-class.
// ---------------------------------------------------------------------------
//
void CCmmCache::CloseDestination( CCmmDestinationInstance& aDestinationInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CLOSEDESTINATION_ENTRY );

    TInt index = FindDestinationFromCache( aDestinationInstance.GetId() );

    if ( index >= 0 )
        {
        TInt remainingSessionInstances = iDestinationArray[index]->DestinationInstanceClosed();
        if ( remainingSessionInstances <= 0)
            {
            // If status is 'to be deleted', then the last handle keeping this
            // destination 'alive' was closed and it can now be deleted from
            // database.
            if ( iDestinationArray[index]->GetStatus() == ECmmDestinationStatusToBeDeleted )
                {
                // Delete the destination unless an active connection is using
                // one of it's connection methods.
                TRAP_IGNORE( DeleteDestinationForcedL( aDestinationInstance ) );
                // Destination is now removed from database. Tell instance
                // mapping to stop ignoring the related ID.
                iInstanceMapping->RemoveDestinationFromDeletedList( aDestinationInstance.GetId() );
                }

            // No more references, no client has an open handle to this, delete it.
            delete iDestinationArray[index];
            iDestinationArray.Remove( index );
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_CLOSEDESTINATION_EXIT );
    }

// ---------------------------------------------------------------------------
// Tells the cache that a hadle to a connection method was closed. The cache
// will decrement the related reference counter and perform any cleanup if
// necessary.
// This should be called automatically from the destructor of
// CCmmConnMethodInstance-class.
// ---------------------------------------------------------------------------
//
void CCmmCache::CloseConnMethod( CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CLOSECONNMETHOD_ENTRY );

    TInt index = FindConnMethodFromCache( aConnMethodInstance.GetId() );

    if ( index >= 0 )
        {
        TInt remainingSessionInstances = iConnMethodArray[index]->ConnMethodInstanceClosed();
        if ( remainingSessionInstances <= 0 )
            {
            // If status is 'to be deleted', then the last handle keeping this
            // connection method 'alive' was closed and it can now be deleted
            // from database.
            if ( iConnMethodArray[index]->GetStatus() == ECmmConnMethodStatusToBeDeleted )
                {
                // Can't remove a connection method that is in use.
                if ( !CheckIfCmConnected( aConnMethodInstance.GetId() ) )
                    {
                    // Can't leave here.
                    TRAP_IGNORE( DeletePluginL( *( iConnMethodArray[index] ) ) );
                    }

                // Connection method is now removed from database. Tell
                // instance mapping to stop ignoring the related ID.
                iInstanceMapping->RemoveConnMethodFromDeletedList( aConnMethodInstance.GetId() );
                }

            // No more references, no client has an open handle to this, delete it.
            delete iConnMethodArray[index];
            iConnMethodArray.Remove( index );
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_CLOSECONNMETHOD_EXIT );
    }

//-----------------------------------------------------------------------------
// Returns a reference to the CommsDat session.
//-----------------------------------------------------------------------------
//
CommsDat::CMDBSession& CCmmCache::Session() const
    {
    // No traces.
    return iTrans->Session();
    }

//-----------------------------------------------------------------------------
// Finds out the bearer type and priority of the service type from given IAP
// record. performs LoadL()-call on the provided IAP record.
//-----------------------------------------------------------------------------
//
void CCmmCache::BearerInfoFromIapRecordL(
        CommsDat::CCDIAPRecord* aIapRecord,
        TUint32& aBearerType,
        TUint& aBearerPriority ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_BEARERINFOFROMIAPRECORDL_ENTRY );

    // Load the IAP record from IAP table. This is an optimization so that
    // plugins don't have to do it every time the CanHandleIapIdL() is called.
    aIapRecord->LoadL( Session() );
    BearerPriorityFromIapRecordL( aIapRecord, aBearerPriority );
    BearerTypeFromIapRecordL( aIapRecord, aBearerType );

    OstTraceFunctionExit0( CCMMCACHE_BEARERINFOFROMIAPRECORDL_EXIT );
    }

//-----------------------------------------------------------------------------
// Finds out the priority of the service type in given IAP record.
//-----------------------------------------------------------------------------
//
void CCmmCache::BearerPriorityFromIapRecordL(
        CommsDat::CCDIAPRecord* aIapRecord,
        TUint& aBearerPriority ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_BEARERPRIORITYFROMIAPRECORDL_ENTRY );

    aBearerPriority = CMManager::KDataMobilitySelectionPolicyPriorityWildCard;
    if ( !aIapRecord->iServiceType.IsNull() )
        {
        aBearerPriority = iBearerPriorityCache->GetPriority( aIapRecord->iServiceType.GetL() );
        }

    OstTraceFunctionExit0( CCMMCACHE_BEARERPRIORITYFROMIAPRECORDL_EXIT );
    }

//-----------------------------------------------------------------------------
// Finds out the bearer type of a connection method from given IAP record.
//-----------------------------------------------------------------------------
//
void CCmmCache::BearerTypeFromIapRecordL(
        CommsDat::CCDIAPRecord* aIapRecord,
        TUint32& aBearerType ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_BEARERTYPEFROMIAPRECORDL_ENTRY );

    TInt err( KErrNone );
    TUint32 extLevel( 0 );
    TBool canHandle( EFalse );

    TInt foundIndex( KErrNotFound );

    // Check which bearer handles the given IAP ID.
    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        TRAP( err, canHandle = (*iPlugins)[i]->CanHandleIapIdL( aIapRecord ) );
        if ( !err && canHandle )
            {
            TUint32 thisExtLevel = (*iPlugins)[i]->GetBearerInfoIntL( ECmExtensionLevel );
            if ( extLevel < thisExtLevel )
                {
                extLevel = thisExtLevel;
                foundIndex = i;
                }
            }
        }
    if ( foundIndex == KErrNotFound )
        {
        // No supporting plugin found.
        User::Leave( KErrNotSupported );
        }
    aBearerType = (*iPlugins)[foundIndex]->GetBearerInfoIntL( CMManager::ECmBearerType );

    OstTraceFunctionExit0( CCMMCACHE_BEARERTYPEFROMIAPRECORDL_EXIT );
    }

// ---------------------------------------------------------------------------
// Find and return a copy of a connection method item matching the given ID.
// Returns KErrNotFound, if the connection method is not found.
// ---------------------------------------------------------------------------
//
TInt CCmmCache::GetConnMethodItem(
        const TUint32 aConnMethodId,
        TCmmConnMethodItem& aConnMethodItem ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNMETHODITEM_ENTRY );

    TInt result = iInstanceMapping->GetConnMethodItem( aConnMethodId, aConnMethodItem );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNMETHODITEM_EXIT );
    return result;
    }

//-----------------------------------------------------------------------------
// Returns all conenction method IDs. Unsupported connection methods are
// included if aCheckBearerType is set to EFalse.
//-----------------------------------------------------------------------------
//
void CCmmCache::GetAllConnMethodsL(
        RArray<TUint32>& aConnMethodArray,
        TBool aCheckBearerType ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETALLCONNMETHODSL_ENTRY );

    iInstanceMapping->GetAllConnMethodsL( aConnMethodArray, aCheckBearerType );

    OstTraceFunctionExit0( CCMMCACHE_GETALLCONNMETHODSL_EXIT );
    }

//-----------------------------------------------------------------------------
// Returns the number of destinations the provided connection method belongs to.
//-----------------------------------------------------------------------------
//
TInt CCmmCache::DestinationsContainingConnMethod( const TUint32 aConnMethodId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODREFERENCECOUNT_ENTRY );

    TInt count = iInstanceMapping->DestinationsContainingConnMethod( aConnMethodId );

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODREFERENCECOUNT_EXIT );
    return count;
    }

//-----------------------------------------------------------------------------
// Return the EasyWLAN IAP ID, zero if not found or WLAN not supported.
//-----------------------------------------------------------------------------
//
TUint32 CCmmCache::EasyWlanIdL() const
    {
    OstTraceFunctionEntry0( CCMMCACHE_EASYWLANIDL_ENTRY );

    TUint32 easyWlanId = iInstanceMapping->EasyWlanIdL();

    OstTraceFunctionExit0( CCMMCACHE_EASYWLANIDL_EXIT );
    return easyWlanId;
    }

//-----------------------------------------------------------------------------
// Check if WLAN is supported on phone.
//-----------------------------------------------------------------------------
//
TBool CCmmCache::WlanSupported() const
    {
    OstTraceFunctionEntry0( CCMMCACHE_WLANSUPPORTED_ENTRY );

    TBool supported = iCmManagerImpl->WlanSupported();

    OstTraceFunctionExit0( CCMMCACHE_WLANSUPPORTED_EXIT );
    return supported;
    }

//-----------------------------------------------------------------------------
// Find out the internet destination ID. Set to 0 if not found.
//-----------------------------------------------------------------------------
//
void CCmmCache::InternetDestinationIdL( TUint& aInternetDestinationId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_INTERNETDESTINATIONIDL_ENTRY );

    iInstanceMapping->InternetDestinationIdL( aInternetDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_INTERNETDESTINATIONIDL_EXIT );
    }

//-----------------------------------------------------------------------------
// Return the number of destinations in database.
//-----------------------------------------------------------------------------
//
TInt CCmmCache::GetDestinationCount() const
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETDESTINATIONCOUNT_ENTRY );

    TInt count = iInstanceMapping->GetDestinationCount();

    OstTraceFunctionExit0( CCMMCACHE_GETDESTINATIONCOUNT_EXIT );
    return count;
    }

//-----------------------------------------------------------------------------
// Return an array containing all destination IDs.
//-----------------------------------------------------------------------------
//
void CCmmCache::GetDestinationsL( RArray<TUint32>& aDestinationArray ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETDESTINATIONSL_ENTRY );

    iInstanceMapping->GetDestinationsL( aDestinationArray );

    OstTraceFunctionExit0( CCMMCACHE_GETDESTINATIONSL_EXIT );
    }

//-----------------------------------------------------------------------------
// Returns all the valid connection methods under given destination.
//-----------------------------------------------------------------------------
//
void CCmmCache::GetConnMethodsFromDestinationL(
        const TUint32 aDestinationId,
        RArray<TCmmConnMethodItem>& aConnMethodArray ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNMETHODSFROMDESTINATIONL_ENTRY );

    iInstanceMapping->GetConnMethodsFromDestinationL(
            aDestinationId, aConnMethodArray );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNMETHODSFROMDESTINATIONL_EXIT );
    }

//-----------------------------------------------------------------------------
// Copies the bearer priority array's contents to aArray.
//-----------------------------------------------------------------------------
//
void CCmmCache::CopyBearerPriorityArrayL( RPointerArray<CCmmBearerPriority>& aArray ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_COPYBEARERPRIORITYARRAYL_ENTRY );

    iBearerPriorityCache->CopyL( aArray );

    OstTraceFunctionExit0( CCMMCACHE_COPYBEARERPRIORITYARRAYL_EXIT );
    }

//-----------------------------------------------------------------------------
// Updates the bearer priority array with the contents of aArray.
//-----------------------------------------------------------------------------
//
void CCmmCache::UpdateBearerPriorityArrayL( const RPointerArray<CCmmBearerPriority>& aArray )
    {
    OstTraceFunctionEntry0( CCMMCACHE_UPDATEBEARERPRIORITYARRAYL_ENTRY );

    iBearerPriorityCache->UpdateL( aArray );

    OstTraceFunctionExit0( CCMMCACHE_UPDATEBEARERPRIORITYARRAYL_EXIT );
    }

//-----------------------------------------------------------------------------
// Check from database if the given connection method belongs to any other
// destination than the one provided.
//-----------------------------------------------------------------------------
//
TBool CCmmCache::ConnMethodInOtherDestination(
        const TUint32 aConnMethodId,
        const TUint32 aDestinationId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODINOTHERDESTINATION_ENTRY );

    TBool cmInOtherDestination = iInstanceMapping->
            ConnMethodInOtherDestination( aConnMethodId, aDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODINOTHERDESTINATION_EXIT );
    return cmInOtherDestination;
    }

//-----------------------------------------------------------------------------
// Get the next free temporary ID.
//-----------------------------------------------------------------------------
//
TUint32 CCmmCache::NextFreeTemporaryId()
    {
    OstTraceFunctionEntry0( CCMMCACHE_NEXTFREETEMPORARYID_ENTRY );

    // Zero is not a valid ID.
    if ( iCurrentTemporaryId == KMaxTUint32 )
        {
        //TODO, add flag to indicate a rollover has occured. after that need to check if temp ID is free before giving it out.

        iCurrentTemporaryId = KTemporaryIdCounterStart;
        }

    iCurrentTemporaryId++;

    OstTraceFunctionExit0( CCMMCACHE_NEXTFREETEMPORARYID_EXIT );
    return iCurrentTemporaryId;
    }

//-----------------------------------------------------------------------------
// Find an open destination matching the give ID from cache.
// Returns either a valid array index or KErrNotFound.
//-----------------------------------------------------------------------------
//
TInt CCmmCache::FindDestinationFromCache( const TUint32 aId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_FINDDESTINATIONFROMCACHE_ENTRY );

    TInt result( KErrNotFound );

    // Skip if ID is 0.
    if ( aId )
        {
        for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
            {
            if ( aId == iDestinationArray[i]->GetId() )
                {
                result = i;
                break;
                }
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_FINDDESTINATIONFROMCACHE_EXIT );
    return result;
    }

//-----------------------------------------------------------------------------
// Find an open destination matching the given name from cache. If a destination
// ID is provided, the search will exclude the relevant destination.
// Returns either a valid array index or KErrNotFound.
//-----------------------------------------------------------------------------
//
TInt CCmmCache::FindNotSavedDestinationFromCacheL(
        const TDesC& aName,
        const TUint32 aDestinationId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_FINDNOTSAVEDDESTINATIONFROMCACHEL_ENTRY );

    TInt result( KErrNotFound );

    // Go through destination array.
    for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
        {
        // Only check new destinations that are not yet in database.
        if ( iDestinationArray[i]->GetStatus() == ECmmDestinationStatusNotSaved )
            {
            if ( aDestinationId )
                {
                if ( aDestinationId != iDestinationArray[i]->GetId() )
                    {
                    if ( !aName.Compare( iDestinationArray[i]->GetDestinationNameL() ) )
                        {
                        result = i;
                        break;
                        }
                    }
                }
            else
                {
                if ( !aName.Compare( iDestinationArray[i]->GetDestinationNameL() ) )
                    {
                    result = i;
                    break;
                    }
                }
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_FINDNOTSAVEDDESTINATIONFROMCACHEL_EXIT );
    return result;
    }

//-----------------------------------------------------------------------------
// Find an open connection method matching the given ID from cache.
// Returns either a valid array index or KErrNotFound.
//-----------------------------------------------------------------------------
//
TInt CCmmCache::FindConnMethodFromCache( const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_FINDCONNMETHODFROMCACHE_ENTRY );

    TInt result( KErrNotFound );

    // Skip if ID is 0.
    if ( aConnMethodId )
        {
        for ( TInt i = 0; i < iConnMethodArray.Count(); i++ )
            {
            if ( aConnMethodId == iConnMethodArray[i]->GetId() )
                {
                result = i;
                break;
                }
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_FINDCONNMETHODFROMCACHE_EXIT );
    return result;
    }

//-----------------------------------------------------------------------------
// Loads and initializes a connection method instance.
//-----------------------------------------------------------------------------
//
void CCmmCache::OpenConnectionMethodInstanceL(
        CCmmConnMethodInstance& aConnMethodInstance,
        const TUint32 aConnMethodId )
    {
    OstTraceFunctionEntry0( CCMMCACHE_OPENCONNECTIONMETHODINSTANCEL_ENTRY );

    // Find out the connection method bearer type.
    TUint32 bearerType( 0 );
    User::LeaveIfError( iInstanceMapping->
            GetConnMethodBearerType( aConnMethodId, bearerType ) );

    // Check bearer type support and create plugin instance.
    CCmPluginBaseEng* plugin = NULL;
    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL(
                CMManager::ECmBearerType ) == bearerType )
            {
            TCmPluginInitParam pluginParams( Session() );
            plugin = ( *iPlugins )[i]->CreateInstanceL( pluginParams );
            CleanupStack::PushL( plugin );
            plugin->LoadL( aConnMethodId );
            break;
            }
        }
    if ( !plugin )
        {
        User::Leave( KErrNotSupported );
        }

    // Store the connection method into cache.
    CCmmConnMethodStruct* connMethodStruct = CCmmConnMethodStruct::NewL( aConnMethodId );
    connMethodStruct->SetPlugin( plugin, bearerType, ECmmConnMethodStatusValid );
    CleanupStack::Pop( plugin );
    CleanupStack::PushL( connMethodStruct );
    iConnMethodArray.AppendL( connMethodStruct );
    CleanupStack::Pop( connMethodStruct );

    // Copy the connection method data to session instance.
    aConnMethodInstance.CopyDataL( connMethodStruct ); // Will increase reference counter.

    OstTraceFunctionExit0( CCMMCACHE_OPENCONNECTIONMETHODINSTANCEL_EXIT );
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetConnectionMethodInfoIntL
//-----------------------------------------------------------------------------
//
TUint32 CCmmCache::GetConnectionMethodInfoIntL(
        const TUint32 aCmId,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNECTIONMETHODINFOINTL_ENTRY );

    TUint32 retVal( 0 );

    CCmmConnMethodInstance* cmInstance = CCmmConnMethodInstance::NewLC( NULL, this );

    // Check if connection method is already opened in cache.
    TInt index = FindConnMethodFromCache( aCmId );
    if ( index != KErrNotFound )
        {
        // Already open in cache. Copy the connection method to session instance.
        // Will increase reference counter.
        cmInstance->CopyDataL( iConnMethodArray[index] );
        }
    else
        {
        OpenConnectionMethodInstanceL( *cmInstance, aCmId );
        }

    retVal = cmInstance->GetIntAttributeL( aAttribute );

    CleanupStack::PopAndDestroy( cmInstance );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNECTIONMETHODINFOINTL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetConnectionMethodInfoBoolL
//-----------------------------------------------------------------------------
//
TBool CCmmCache::GetConnectionMethodInfoBoolL(
        const TUint32 aCmId,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNECTIONMETHODINFOBOOLL_ENTRY );

    TBool retVal( EFalse );

    CCmmConnMethodInstance* cmInstance = CCmmConnMethodInstance::NewLC( NULL, this );

    // Check if connection method is already opened in cache.
    TInt index = FindConnMethodFromCache( aCmId );
    if ( index != KErrNotFound )
        {
        // Already open in cache. Copy the connection method to session instance.
        // Will increase reference counter.
        cmInstance->CopyDataL( iConnMethodArray[index] );
        }
    else
        {
        OpenConnectionMethodInstanceL( *cmInstance, aCmId );
        }

    retVal = cmInstance->GetBoolAttributeL( aAttribute );

    CleanupStack::PopAndDestroy( cmInstance );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNECTIONMETHODINFOBOOLL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetConnectionMethodInfoStringL
//-----------------------------------------------------------------------------
//
HBufC* CCmmCache::GetConnectionMethodInfoStringL(
        const TUint32 aCmId,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNECTIONMETHODINFOSTRINGL_ENTRY );

    HBufC* retVal( NULL );

    CCmmConnMethodInstance* cmInstance = CCmmConnMethodInstance::NewLC( NULL, this );

    // Check if connection method is already opened in cache.
    TInt index = FindConnMethodFromCache( aCmId );
    if ( index != KErrNotFound )
        {
        // Already open in cache. Copy the connection method to session instance.
        // Will increase reference counter.
        cmInstance->CopyDataL( iConnMethodArray[index] );
        }
    else
        {
        OpenConnectionMethodInstanceL( *cmInstance, aCmId );
        }

    retVal = cmInstance->GetStringAttributeL( aAttribute );

    CleanupStack::PopAndDestroy( cmInstance );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNECTIONMETHODINFOSTRINGL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetConnectionMethodInfoString8L
//-----------------------------------------------------------------------------
//
HBufC8* CCmmCache::GetConnectionMethodInfoString8L(
        const TUint32 aCmId,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETCONNECTIONMETHODINFOSTRING8L_ENTRY );

    HBufC8* retVal( NULL );

    CCmmConnMethodInstance* cmInstance = CCmmConnMethodInstance::NewLC( NULL, this );

    // Check if connection method is already opened in cache.
    TInt index = FindConnMethodFromCache( aCmId );
    if ( index != KErrNotFound )
        {
        // Already open in cache. Copy the connection method to session instance.
        cmInstance->CopyDataL( iConnMethodArray[index] ); // Will increase reference counter.
        }
    else
        {
        OpenConnectionMethodInstanceL( *cmInstance, aCmId );
        }

    retVal = cmInstance->GetString8AttributeL( aAttribute );

    CleanupStack::PopAndDestroy( cmInstance );

    OstTraceFunctionExit0( CCMMCACHE_GETCONNECTIONMETHODINFOSTRING8L_EXIT );
    return retVal;
    }


//-----------------------------------------------------------------------------
// CCmmCache::GetBearerInfoIntL
//-----------------------------------------------------------------------------
//
TUint32 CCmmCache::GetBearerInfoIntL(
        const TUint32 aBearerType,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETBEARERINFOINTL_ENTRY );

    TUint32 retVal( 0 );
    TBool found( EFalse );

    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == aBearerType )
            {
            found = ETrue;
            retVal = ( *iPlugins )[i]->GetBearerInfoIntL( aAttribute );
            break;
            }
        }
    if ( !found )
        {
        User::Leave( KErrArgument );
        }

    OstTraceFunctionExit0( CCMMCACHE_GETBEARERINFOINTL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetBearerInfoBoolL
//-----------------------------------------------------------------------------
//
TBool CCmmCache::GetBearerInfoBoolL(
        const TUint32 aBearerType,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETBEARERINFOBOOLL_ENTRY );

    TBool retVal( EFalse );
    TBool found( EFalse );

    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == aBearerType )
            {
            found = ETrue;
            retVal = ( *iPlugins )[i]->GetBearerInfoBoolL( aAttribute );
            break;
            }
        }
    if ( !found )
        {
        User::Leave( KErrArgument );
        }

    OstTraceFunctionExit0( CCMMCACHE_GETBEARERINFOBOOLL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetBearerInfoStringL
//-----------------------------------------------------------------------------
//
HBufC* CCmmCache::GetBearerInfoStringL(
        const TUint32 aBearerType,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETBEARERINFOSTRINGL_ENTRY );

    HBufC* retVal( NULL );
    TBool found( EFalse );

    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == aBearerType )
            {
            found = ETrue;
            retVal = ( *iPlugins )[i]->GetBearerInfoStringL( aAttribute );
            break;
            }
        }
    if ( !found )
        {
        User::Leave( KErrArgument );
        }

    OstTraceFunctionExit0( CCMMCACHE_GETBEARERINFOSTRINGL_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCmmCache::GetBearerInfoString8L
//-----------------------------------------------------------------------------
//
HBufC8* CCmmCache::GetBearerInfoString8L(
        const TUint32 aBearerType,
        const TUint32 aAttribute )
    {
    OstTraceFunctionEntry0( CCMMCACHE_GETBEARERINFOSTRING8L_ENTRY );

    HBufC8* retVal( NULL );
    TBool found( EFalse );

    for ( TInt i = 0; i < iPlugins->Count(); i++ )
        {
        if ( ( *iPlugins )[i]->GetBearerInfoIntL( CMManager::ECmBearerType ) == aBearerType )
            {
            found = ETrue;
            retVal = ( *iPlugins )[i]->GetBearerInfoString8L( aAttribute );
            break;
            }
        }
    if ( !found )
        {
        User::Leave( KErrArgument );
        }

    OstTraceFunctionExit0( CCMMCACHE_GETBEARERINFOSTRING8L_EXIT );
    return retVal;
    }

//-----------------------------------------------------------------------------
// Read general connection settings from database.
//-----------------------------------------------------------------------------
//
void CCmmCache::ReadGenConnSettingsL( TCmGenConnSettings& aGenConnSettings ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_READGENCONNSETTINGSL_ENTRY );

    CommsDat::CMDBRecordSet<CCDDefConnRecord>* defConnRecordSet =
            new( ELeave ) CommsDat::CMDBRecordSet<CCDDefConnRecord>(
                    iCmManagerImpl->TableId( ECmmDbDefConnRecord ) );
    CleanupStack::PushL( defConnRecordSet );

    CCDDefConnRecord* defConnRecord =
            new( ELeave ) CCDDefConnRecord(
                    iCmManagerImpl->TableId( ECmmDbDefConnRecord ) );
    CleanupStack::PushL( defConnRecord );

    TRAP_IGNORE( defConnRecordSet->LoadL( Session() ) );
    if ( defConnRecordSet->iRecords.Count() > 0 )
        {
        defConnRecord->SetElementId( defConnRecordSet->iRecords[0]->ElementId() );
        defConnRecord->LoadL( Session() );

        aGenConnSettings.iUsageOfWlan =
                TCmUsageOfWlan( ( TInt )defConnRecord->iUsageOfWlan );
        aGenConnSettings.iCellularDataUsageHome =
                TCmCellularDataUsage( ( TInt )defConnRecord->iCellularDataUsageHome );
        aGenConnSettings.iCellularDataUsageVisitor =
                TCmCellularDataUsage( ( TInt )defConnRecord->iCellularDataUsageVisitor );
        }
    else
        {
        iTrans->OpenTransactionLC();

        aGenConnSettings.iUsageOfWlan = ECmUsageOfWlanKnown;
        aGenConnSettings.iCellularDataUsageHome = ECmCellularDataUsageAutomatic;
        aGenConnSettings.iCellularDataUsageVisitor = ECmCellularDataUsageConfirm;

        defConnRecord->SetRecordId( KCDNewRecordRequest );
        defConnRecord->iUsageOfWlan = ( TUint )aGenConnSettings.iUsageOfWlan;
        defConnRecord->iCellularDataUsageHome =
                ( TUint )aGenConnSettings.iCellularDataUsageHome;
        defConnRecord->iCellularDataUsageVisitor =
                ( TUint )aGenConnSettings.iCellularDataUsageVisitor;
        defConnRecord->StoreL( Session() );

        iTrans->CommitTransactionL();
        }

    CleanupStack::PopAndDestroy( defConnRecord );
    CleanupStack::PopAndDestroy( defConnRecordSet );

    OstTraceFunctionExit0( CCMMCACHE_READGENCONNSETTINGSL_EXIT );
    }

//-----------------------------------------------------------------------------
// Write general connection settings to database.
//-----------------------------------------------------------------------------
//
void CCmmCache::WriteGenConnSettingsL( const TCmGenConnSettings& aGenConnSettings )
    {
    OstTraceFunctionEntry0( CCMMCACHE_WRITEGENCONNSETTINGSL_ENTRY );

    iTrans->OpenTransactionLC();

    ReplaceGenConnSettingsL( aGenConnSettings );

    iTrans->CommitTransactionL();

    OstTraceFunctionExit0( CCMMCACHE_WRITEGENCONNSETTINGSL_EXIT );
    }

//-----------------------------------------------------------------------------
//  CCmmCache::ReplaceGenConnSettingsL()
//-----------------------------------------------------------------------------
//
void CCmmCache::ReplaceGenConnSettingsL( const TCmGenConnSettings& aGenConnSettings )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REPLACEGENCONNSETTINGSL_ENTRY );
    TBool oldRecordExists( EFalse );

    CommsDat::CMDBRecordSet<CCDDefConnRecord>* defConnRecordSet =
            new( ELeave ) CommsDat::CMDBRecordSet<CCDDefConnRecord>(
                    iCmManagerImpl->TableId( ECmmDbDefConnRecord ) );
    CleanupStack::PushL( defConnRecordSet );

    CCDDefConnRecord* defConnRecord =
            new( ELeave ) CCDDefConnRecord(
                    iCmManagerImpl->TableId( ECmmDbDefConnRecord ) );
    CleanupStack::PushL( defConnRecord );

    TRAPD( err, defConnRecordSet->LoadL( Session() ) );
    if ( err == KErrNone )
        {
        defConnRecord->SetElementId( defConnRecordSet->iRecords[0]->ElementId() );
        defConnRecord->LoadL( Session() );
        oldRecordExists = ETrue;
        }
    else if ( err == KErrNotFound )
        {
        defConnRecord->SetRecordId( KCDNewRecordRequest );
        }
    else
        {
        User::Leave( err );
        }

    defConnRecord->iUsageOfWlan = ( TUint )aGenConnSettings.iUsageOfWlan;
    defConnRecord->iCellularDataUsageHome = ( TUint )aGenConnSettings.iCellularDataUsageHome;
    defConnRecord->iCellularDataUsageVisitor = ( TUint )aGenConnSettings.iCellularDataUsageVisitor;

    if ( oldRecordExists )
        {
        defConnRecord->ModifyL( Session() );
        }
    else
        {
        defConnRecord->StoreL( Session() );
        }

    CleanupStack::PopAndDestroy( defConnRecord );
    CleanupStack::PopAndDestroy( defConnRecordSet );

    OstTraceFunctionExit0( CCMMCACHE_REPLACEGENCONNSETTINGSL_EXIT );
    }

// ---------------------------------------------------------------------------
// Opens a transaction and deletes the given connection method.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeletePluginL( CCmmConnMethodStruct& aConnMethodStruct )
    {
    iTrans->OpenTransactionLC();
    aConnMethodStruct.GetPlugin()->DeleteL();
    iTrans->CommitTransactionL();
    }

// ---------------------------------------------------------------------------
// Enumerates connections and checks if the given connection method is
// connected.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::CheckIfCmConnected( const TUint32 aCmId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_CHECKIFCMCONNECTED_ENTRY );

    TBool result( EFalse );
    RSocketServ ss;
    RConnection connection;
    TUint connectionCount( 0 );

    if ( ss.Connect() == KErrNone )
        {
        if ( connection.Open( ss, KAfInet ) == KErrNone )
            {
            if ( connection.EnumerateConnections( connectionCount ) == KErrNone )
                {
                TPckgBuf<TConnectionInfo> connInfo;

                for ( TInt i = 1; i <= connectionCount; i++ )
                    {
                    connection.GetConnectionInfo( i, connInfo );

                    if ( connInfo().iIapId == aCmId )
                        {
                        result = ETrue;
                        break;
                        }
                    }

                }
            connection.Close();
            }
        ss.Close();
        }

    OstTraceFunctionExit0( CCMMCACHE_CHECKIFCMCONNECTED_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Enumerates connections and checks if any of the connection methods in the
// given destination is connected.
// If pointer to destination instance is given, then information about
// relevant connection methods is retrieved from that. Otherwise the
// information is retrieved from instance mapping using the given ID.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationConnectedL(
        const TUint32 aDestinationId,
        CCmmDestinationInstance* aDestinationInstance ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONCONNECTEDL_ENTRY );

    TBool result( EFalse );
    RSocketServ ss;
    RConnection connection;
    TUint connectionCount( 0 );

    if ( ss.Connect() == KErrNone )
        {
        if ( connection.Open( ss, KAfInet ) == KErrNone )
            {
            if ( connection.EnumerateConnections( connectionCount ) == KErrNone )
                {
                RArray<TCmmConnMethodItem> connMethodArray;
                CleanupClosePushL( connMethodArray );

                // If destination instance provided, take the destination's
                // connection methods from there. Otherwise get the connection
                // methods from instance mapping. (If the destination is marked
                // to be deleted, instance mapping won't have information about
                // it.)
                if ( aDestinationInstance )
                    {
                    for ( TInt i = 0; i < aDestinationInstance->iConnMethodItemArray.Count(); i++ )
                        {
                        connMethodArray.AppendL( aDestinationInstance->iConnMethodItemArray[i] );
                        }
                    }
                else
                    {
                    iInstanceMapping->GetConnMethodsFromDestinationL(
                            aDestinationId,
                            connMethodArray );
                    }

                // Iterate through all connections.
                TPckgBuf<TConnectionInfo> connInfo;
                for ( TUint i = 1; i <= connectionCount; i++ )
                    {
                    connection.GetConnectionInfo( i, connInfo );

                    // Iterate through all connection methods in destinations.
                    for ( TInt j = 0; j < connMethodArray.Count(); j++ )
                        {
                        if ( connInfo().iIapId == connMethodArray[j].iId )
                            {
                            result = ETrue;
                            break;
                            }
                        }
                    if ( result )
                        {
                        break;
                        }
                    }

                CleanupStack::PopAndDestroy( &connMethodArray );
                }
            connection.Close();
            }
        ss.Close();
        }

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONCONNECTEDL_EXIT );
    return result;
    }

// ---------------------------------------------------------------------------
// Check from database if the given destination is an embedded destination in
// any other destination.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationIsEmbedded( const TUint32 aDestinationId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONISEMBEDDED_ENTRY );

    TBool isEmbedded = iInstanceMapping->DestinationIsEmbedded( aDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONISEMBEDDED_EXIT );
    return isEmbedded;
    }

// ---------------------------------------------------------------------------
// Check from database if the given destination has an embedded destination.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationHasEmbedded( const TUint32 aDestinationId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONHASEMBEDDED_ENTRY );

    TBool hasEmbedded = iInstanceMapping->DestinationHasEmbedded( aDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONHASEMBEDDED_EXIT );
    return hasEmbedded;
    }

// ---------------------------------------------------------------------------
// Check from database if the given destination is pointed to by any virtual
// IAP.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::DestinationPointedToByVirtualIap( const TUint32 aDestinationId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_DESTINATIONPOINTEDTOBYVIRTUALIAP_ENTRY );

    TBool pointedByVirtual =
            iInstanceMapping->DestinationPointedToByVirtualIap( aDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_DESTINATIONPOINTEDTOBYVIRTUALIAP_EXIT );
    return pointedByVirtual;
    }

// ---------------------------------------------------------------------------
// Check from database if the given connection method is pointed to by any
// virtual IAP.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::ConnMethodPointedToByVirtualIap( const TUint32 aConnMethodId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODPOINTEDTOBYVIRTUALIAP_ENTRY );

    TBool pointedToByVirtual =
            iInstanceMapping->ConnMethodPointedToByVirtualIap( aConnMethodId );

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODPOINTEDTOBYVIRTUALIAP_EXIT );
    return pointedToByVirtual;
    }

// ---------------------------------------------------------------------------
// Check if the given connection method is the only connection method in the
// given destination and if a virtual IAP points to that destination.
// ---------------------------------------------------------------------------
//
TBool CCmmCache::ConnMethodInDestinationButLocked(
        const TUint32 aConnMethodId,
        const TUint32 aDestinationId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_CONNMETHODINDESTINATIONBUTLOCKED_ENTRY );

    TBool inAndlocked = iInstanceMapping->
            ConnMethodInDestinationButLocked( aConnMethodId, aDestinationId );

    OstTraceFunctionExit0( CCMMCACHE_CONNMETHODINDESTINATIONBUTLOCKED_EXIT );
    return inAndlocked;
    }

// ---------------------------------------------------------------------------
// Remove all references to the given connection method from the datamobility
// selection policy records. Then update instance mapping to reflect the
// current database state, and notify any possible client handles for the
// changed destinations. Also removes the connection method from any
// destination handles the client has open.
// ---------------------------------------------------------------------------
//
void CCmmCache::RemoveAllReferencesToConnMethodL(
        CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REMOVEALLREFERENCESTOCONNMETHODL_ENTRY );

    TUint32 connMethodId( aConnMethodInstance.GetId() );

    iTrans->OpenTransactionLC();

    // Create DataMobilitySelectionPolicy-record set.
    CommsDat::CMDBRecordSet<CCDDataMobilitySelectionPolicyRecord>* snapRecordSet =
            new( ELeave ) CommsDat::CMDBRecordSet<CCDDataMobilitySelectionPolicyRecord>(
                    iCmManagerImpl->TableId( ECmmDbSnapRecord ) );
    CleanupStack::PushL( snapRecordSet );

    // Create a DataMobilitySelectionPolicy-record.
    CCDDataMobilitySelectionPolicyRecord* snapRecord =
            new( ELeave ) CCDDataMobilitySelectionPolicyRecord(
                    iCmManagerImpl->TableId( ECmmDbSnapRecord ) );
    CleanupStack::PushL( snapRecord );

    TRAP_IGNORE( snapRecordSet->LoadL( iTrans->Session() ) );

    // Read IAP ID from each record and delete any that match the connection
    // method we are removing all references for.
    TUint32 connMethodIdInRecord( 0 );
    TInt snapRecordCount( snapRecordSet->iRecords.Count() );
    for ( TInt i = 0; i < snapRecordCount; i++ )
        {
        snapRecord->SetElementId( snapRecordSet->iRecords[i]->ElementId() );
        snapRecord->LoadL( iTrans->Session() );

        connMethodIdInRecord = ( snapRecord->iIAP & KCDMaskShowRecordId ) >> 8;
        if ( connMethodIdInRecord == connMethodId )
            {
            snapRecord->DeleteL( iTrans->Session() );
            }
        }
    CleanupStack::PopAndDestroy( snapRecord );
    CleanupStack::PopAndDestroy( snapRecordSet );

    iTrans->CommitTransactionL();

    // Reference count will be zero if this method call is not part of some
    // bigger operation (e.g. connection method delete).
    if ( iTrans->GetReferenceCount() == 0 )
        {
        // Update instance mapping to reflect the current database state, and
        // notify any possible client handles for the changed destinations.
        RArray<TUint32> changedDestinations;
        iInstanceMapping->RemoveConnMethodFromDestinations( connMethodId, changedDestinations );
        for ( TInt i = 0; i < changedDestinations.Count(); i++ )
            {
            TCmmIdStruct idStruct( changedDestinations[i], 0 );
            aConnMethodInstance.RefreshHandlesForAllSessions( idStruct );
            }
        changedDestinations.Close();

        // Remove the connection method from any destination handles the client
        // has open.
        aConnMethodInstance.RemoveConnMethodFromSessionDestinationHandles( connMethodId );
        }

    OstTraceFunctionExit0( CCMMCACHE_REMOVEALLREFERENCESTOCONNMETHODL_EXIT );
    }

// ---------------------------------------------------------------------------
// Update the ID of a new destination from temporary ID to real ID after a
// successful update to database.
// ---------------------------------------------------------------------------
//
void CCmmCache::RefreshDestinationId( const TCmmIdStruct& aIdStruct )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REFRESHDESTINATIONID_ENTRY );

    // Iterate destinations in cache and update the ID if match found.
    for ( TInt i = 0; i < iDestinationArray.Count(); i++ )
        {
        if ( iDestinationArray[i]->GetId() == aIdStruct.iTemporaryId )
            {
            iDestinationArray[i]->SetId( aIdStruct.iRealId );
            break;
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_REFRESHDESTINATIONID_EXIT );
    }

// ---------------------------------------------------------------------------
// Update the ID of a new connection method from temporary ID to real ID after
// a successful update to database.
// ---------------------------------------------------------------------------
//
void CCmmCache::RefreshConnMethodId( const TCmmIdStruct& aIdStruct )
    {
    OstTraceFunctionEntry0( CCMMCACHE_REFRESHCONNMETHODID_ENTRY );

    // Iterate connection methods in cache and update the ID if match found.
    for ( TInt i = 0; i < iConnMethodArray.Count(); i++ )
        {
        if ( iConnMethodArray[i]->GetId() == aIdStruct.iTemporaryId )
            {
            iConnMethodArray[i]->SetId( aIdStruct.iRealId );
            break; // Can only be 1 match.
            }
        }

    OstTraceFunctionExit0( CCMMCACHE_REFRESHCONNMETHODID_EXIT );
    }

// ---------------------------------------------------------------------------
// During destination update, after updating connection methods inside the
// destination, the database records containing the information of what
// connection methods are inside the destination need to be updated with real
// IDs for any newly created connection methods. These real IDs are held in the
// temporary array iUpdatedConnMethods until successful commit to database.
// This method is used to find out those real IDs before that.
// ---------------------------------------------------------------------------
//
void CCmmCache::TranslateTemporaryId( const TUint32 aTemporaryId, TUint32& aRealId ) const
    {
    OstTraceFunctionEntry0( CCMMCACHE_TRANSLATETEMPORARYID_ENTRY );

    aRealId = 0;

    for ( TInt i = 0; i < iUpdatedConnMethods.Count(); i++ )
        {
        if ( iUpdatedConnMethods[i].iTemporaryId == aTemporaryId )
            {
            aRealId = iUpdatedConnMethods[i].iRealId;
            break;
            }
        }
    if ( !aRealId )
        {
        ASSERT( 0 );
        }

    OstTraceFunctionExit0( CCMMCACHE_TRANSLATETEMPORARYID_EXIT );
    }

// ---------------------------------------------------------------------------
// Check if the given connection method can have all references removed and
// made into an uncategorized connection method.
// ---------------------------------------------------------------------------
//
void CCmmCache::CheckIfConnMethodReferencesCanBeRemovedL(
        const CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CHECKIFCONNMETHODREFERENCESCANBEREMOVEDL_ENTRY );

    TUint32 connMethodId( aConnMethodInstance.GetId() );

    // Check that connection method exists in database.
    if ( !iInstanceMapping->ValidConnMethodId( connMethodId ) )
        {
        User::Leave( KErrNotFound );
        }

    // Can't remove an embedded destination this way.
    if ( aConnMethodInstance.IsEmbeddedDestination() )
        {
        User::Leave( KErrArgument );
        }

    // Iterate all destinations in database and check if possible reference can
    // be removed.
    RArray<TUint32> dbDestinations;
    CleanupClosePushL( dbDestinations );
    iInstanceMapping->GetDestinationsL( dbDestinations );
    for ( TInt i = 0; i < dbDestinations.Count(); i++ )
        {
        if ( iInstanceMapping->ConnMethodInDestinationButLocked(
                connMethodId,
                dbDestinations[i] ) )
            {
            User::Leave( KErrLocked );
            }
        }
    CleanupStack::PopAndDestroy( &dbDestinations );

    // Can't remove a connection method that is in use.
    if ( CheckIfCmConnected( connMethodId ) )
        {
        User::Leave( KErrInUse );
        }

    OstTraceFunctionExit0( CCMMCACHE_CHECKIFCONNMETHODREFERENCESCANBEREMOVEDL_EXIT );
    }

// ---------------------------------------------------------------------------
// Check if given connection method is referenced from any protected destination.
// ---------------------------------------------------------------------------
//
void CCmmCache::CheckIfConnMethodBelongsToProtectedDestinationL(
        const CCmmConnMethodInstance& aConnMethodInstance,
        TBool& aBelongsToProtectedDestination )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CHECKIFCONNMETHODBELONGSTOPROTECTEDDESTINATIONL_ENTRY );

    TUint32 connMethodId( aConnMethodInstance.GetId() );

    // Check that connection method exists in database.
    if ( !iInstanceMapping->ValidConnMethodId( connMethodId ) )
        {
        User::Leave( KErrNotFound );
        }

    aBelongsToProtectedDestination = EFalse;
    // Get destinations which have references to connection method passed as parameter.
    RArray<TUint32> dbDestinations;
    CleanupClosePushL( dbDestinations );
    iInstanceMapping->DestinationsContainingConnMethodL( connMethodId, dbDestinations );
    TUint32 metadata( 0 );
    for ( TInt i = 0; i < dbDestinations.Count(); i++ )
        {
        // Check if any of destinations is protected.
        metadata = iInstanceMapping->DestinationMetadata( dbDestinations[i] );
        TUint32 protlevel =
                ( metadata & KDestProtectionLevelMask ) >> KBitsToShiftDestProtectionLevel;
        if ( protlevel == CMManager::EProtLevel1 || protlevel == CMManager::EProtLevel3 )
            {
            aBelongsToProtectedDestination = ETrue;
            break;
            }
        }
    CleanupStack::PopAndDestroy( &dbDestinations );

    OstTraceFunctionExit0( CCMMCACHE_CHECKIFCONNMETHODBELONGSTOPROTECTEDDESTINATIONL_EXIT );
    }

// ---------------------------------------------------------------------------
// Check if the given connection method can be deleted.
// ---------------------------------------------------------------------------
//
void CCmmCache::CheckIfConnMethodCanBeDeletedL(
        const CCmmConnMethodInstance& aConnMethodInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CHECKIFCONNMETHODCANBEDELETEDL_ENTRY );

    TUint32 connMethodId( aConnMethodInstance.GetId() );

    // Find connection method from cache.
    TInt index = FindConnMethodFromCache( connMethodId );
    if ( index == KErrNotFound )
        {
        User::Leave( KErrBadHandle );
        }

    // Check connection method status at cache side.
    switch ( iConnMethodArray[index]->GetStatus() )
        {
        case ECmmConnMethodStatusNotSaved:
            {
            // Connection method is not in database, nothing to delete.
            User::Leave( KErrNotFound );
            }
            break;
        case ECmmConnMethodStatusValid:
            // Proceed.
            break;
        case ECmmConnMethodStatusToBeDeleted:
            // Connection method has already been deleted.
            return;
        case ECmmConnMethodStatusChanged:
        default:
            {
            ASSERT( 0 ); // Error, illegal status.
            User::Leave( KErrCorrupt );
            }
            break;
        }

    // Removing all references is part of deleting a connection method. Check
    // if can do that.
    CheckIfConnMethodReferencesCanBeRemovedL( aConnMethodInstance );

    // Check that no virtual IAP points to this connection method.
    if ( iInstanceMapping->ConnMethodPointedToByVirtualIap( connMethodId ) )
        {
        User::Leave( KErrLocked );
        }

    OstTraceFunctionExit0( CCMMCACHE_CHECKIFCONNMETHODCANBEDELETEDL_EXIT );
    }

// ---------------------------------------------------------------------------
// Check if the given destination can be deleted.
// ---------------------------------------------------------------------------
//
void CCmmCache::CheckIfDestinationCanBeDeletedL(
        const CCmmDestinationInstance& aDestinationInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_CHECKIFDESTINATIONCANBEDELETEDL_ENTRY );

    TUint32 destinationId( aDestinationInstance.GetId() );

    // Find destination from cache.
    TInt index = FindDestinationFromCache( destinationId );
    if ( index == KErrNotFound )
        {
        User::Leave( KErrBadHandle );
        }

    // Check destination status in cache.
    switch ( iDestinationArray[index]->GetStatus() )
        {
        case ECmmDestinationStatusNotSaved:
            {
            User::Leave( KErrNotFound );
            }
            break;
        case ECmmDestinationStatusValid:
            // Proceed.
            break;
        case ECmmDestinationStatusToBeDeleted:
            // Destination has already been deleted.
            return;
        case ECmmDestinationStatusChanged:
        default:
            {
            ASSERT( 0 ); // Error, illegal status.
            User::Leave( KErrCorrupt );
            }
            break;
        }

    // Check destination exists in database.
    if ( !iInstanceMapping->ValidDestinationId( destinationId ) )
        {
        User::Leave( KErrNotFound );
        }

    // Check if any virtual IAP points to this destination. Don't check session side.
    if ( iInstanceMapping->DestinationPointedToByVirtualIap( destinationId ) )
        {
        User::Leave( KErrLocked );
        }

    // Check if any of the connection methods in this destination are currently in use.
    if ( DestinationConnectedL( destinationId ) )
        {
        User::Leave( KErrInUse );
        }

    OstTraceFunctionExit0( CCMMCACHE_CHECKIFDESTINATIONCANBEDELETEDL_EXIT );
    }

// ---------------------------------------------------------------------------
// Return the requested table ID.
// ---------------------------------------------------------------------------
//
CommsDat::TMDBElementId CCmmCache::TableId( TCmmDbRecords aRecord )
    {
    return iCmManagerImpl->TableId( aRecord );
    }

// ---------------------------------------------------------------------------
// Initiate the deletion of given destination if none of the connection
// methods inside it are connected.
// ---------------------------------------------------------------------------
//
void CCmmCache::DeleteDestinationForcedL( CCmmDestinationInstance& aDestinationInstance )
    {
    OstTraceFunctionEntry0( CCMMCACHE_DELETEDESTINATIONFORCEDL_ENTRY );

    if ( !DestinationConnectedL( 0, &aDestinationInstance ) )
        {
        DeleteDestinationL( aDestinationInstance, ETrue );
        }

    OstTraceFunctionExit0( CCMMCACHE_DELETEDESTINATIONFORCEDL_EXIT );
    }

// End of file