iaupdate/IAD/engine/controller/src/iaupdatecontrollerimpl.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:51:10 +0200
changeset 0 ba25891c3a9e
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2007-2009 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:   This module contains the implementation of 
*                CIAUpdateController class member functions.
*
*/



// For the NCD Engine ECOM session closing.
#include <ecom/ecom.h>

#include <catalogsuids.h>
#include <catalogsutils.h>
#include <catalogsengine.h>
#include <ncdprovider.h>
#include <ncdutils.h>
#include <ncdconfigurationkeys.h>
#include <ncdcapabilities.h>
#include <ncdprogress.h>
#include <ncdnode.h>
#include <ncdnodecontainer.h>
#include <ncdconnectionmethod.h>
#include <ncdserverreportoperation.h>
#include <ncdserverreportmanager.h>
#include <ncdquery.h>
#include <ncdpurchasehistory.h>
#include <ncdutils.h>
#include <ncdprovideroptions.h>
#include <ncdoperation.h>
#include <ncddownloadoperation.h>
#include <ncderrors.h>

// Required for NCD debug logging.
#include <catalogsdebug.h>

#include "iaupdatecontrollerimpl.h"
#include "iaupdateloader.h"
#include "iaupdatenodecontainer.h"
#include "iaupdatenodefactory.h"
#include "iaupdatenodeimpl.h"
#include "iaupdatefwnodeimpl.h"
#include "iaupdateutils.h"
#include "iaupdatehistoryimpl.h"
#include "iaupdateenginexmlparser.h"
#include "iaupdateengineconfigdata.h"
#include "iaupdatectrlconsts.h"
#include "iaupdateprotocolconsts.h"
#include "iaupdatectrlfileconsts.h"
#include "iaupdateselfupdaterctrl.h"
#include "iaupdatecontentoperationmanager.h"
#include "iaupdatecachecleaner.h"
#include "iaupdatecontrollerfile.h"
#include "iaupdateridentifier.h"
#include "iaupdateerrorcodes.h"
#include "iaupdatetimer.h"
#include "iaupdatedebug.h"


// -----------------------------------------------------------------------------
// CIAUpdateController::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
// 
CIAUpdateController* CIAUpdateController::NewLC( 
    const TUid& aFamilyUid, 
    MIAUpdateControllerObserver& aObserver )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NewLC() begin");
    CIAUpdateController* self = 
        new( ELeave ) CIAUpdateController( aFamilyUid, aObserver );
    CleanupStack::PushL(self);
    self->ConstructL();
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NewLC end");
    return self;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//    
CIAUpdateController* CIAUpdateController::NewL( 
    const TUid& aFamilyUid, 
    MIAUpdateControllerObserver& aObserver )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NewL() begin");
    CIAUpdateController* self = 
        CIAUpdateController::NewLC( aFamilyUid, aObserver );
    CleanupStack::Pop( self );
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NewL end");
    return self;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::CIAUpdateController
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CIAUpdateController::CIAUpdateController( 
    const TUid& aFamilyUid,
    MIAUpdateControllerObserver& aObserver ) 
: CActive( CActive::EPriorityStandard ),
  iFamilyUid( aFamilyUid ),
  iObserver( aObserver ),
  iControllerState( ENotRunning )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CIAUpdateController() begin");

    // Required for NCD debug logging.
    DLINIT;

    CActiveScheduler::Add( this );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CIAUpdateController() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CIAUpdateController::ConstructL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ConstructL() begin");
    
    iEngine = CCatalogsEngine::NewL( *this );

    if ( !iEngine )
        {
        IAUPDATE_TRACE("[IAUPDATE] !iEngine");
        User::Leave( KErrNotFound );
        }
    
    iNodeContainer = CIAUpdateNodeContainer::NewL( *this );
    
    iSelfUpdaterCtrl = CIAUpdateSelfUpdaterCtrl::NewL( *this );
    iContentOperationManager = CIAUpdateContentOperationManager::NewL();

    // Notice, that this will read the data from the file 
    // and adjust variables from the file if the file exists.
    iCacheClearFile =
        CIAUpdateControllerFile::NewL( 
            IAUpdateCtrlFileConsts::KCacheClearFile );
    
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ConstructL() end");
    }    
   
    
// -----------------------------------------------------------------------------
// CIAUpdateController::~CIAUpdateController
// Destructor
// -----------------------------------------------------------------------------
//    
CIAUpdateController::~CIAUpdateController()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::~CIAUpdateController() begin");

    // This is always a good thing to do with active objects.
    Cancel();
    
    // Also cancel possible report operation.
    CancelReporting();

    delete iReportTimer;
    delete iContentOperationManager;
    delete iLoader;    
    delete iNodeContainer;
    delete iCacheCleaner;
    delete iCacheClearFile;
        
    // Before releasing engine, be sure to release or
    // delete all other objects that may contain connetions
    // to the engine.
    
    // History uses the services that are provided throught the provider.
    // So, delete history here before releasing the provider and closing
    // the engine.
    delete iHistory;
    
    // Do not delete the contents here.
    // This array does not own the content.
    iNodes.Reset();

    delete iSelfUpdaterCtrl;

    if ( iServerReportManager )
        {
        iServerReportManager->Release();
        }

    if ( iProvider )
        {
        iProvider->Release();
        }

    if ( iBaseProvider )
        {
        iBaseProvider->Release();
        }
         
    if ( iEngine )
        {
        iEngine->Close();
        delete iEngine;
        }    

    // Make sure that the NCD Engine ECOM session is closed. 
    // SKD help describes this:
    // Direct users of ECOM plugins must call this method when all 
    // implementations they have created have been destroyed and they 
    // are finished using ECOM e.g. library shutdown. It will garbage 
    // collect the last previously destroyed implementation and close 
    // the REComSession if no longer in use.
    REComSession::FinalClose();

    // Required for NCD debug logging.
    DLUNINIT;

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::~CIAUpdateController() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::FamilyUid
//
// -----------------------------------------------------------------------------
//    
const TUid& CIAUpdateController::FamilyUid() const
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::FamilyUid()");
    IAUPDATE_TRACE_1("[IAUPDATE] family uid: %x", iFamilyUid.iUid );
    return iFamilyUid;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::ProviderL
// 
// -----------------------------------------------------------------------------
//
MNcdProvider& CIAUpdateController::ProviderL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ProviderL() begin");

    if ( !iProvider )
        {
        User::Leave( KErrNotFound );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ProviderL() end");

    return *iProvider;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SelfUpdaterCtrl
// 
// -----------------------------------------------------------------------------
//
CIAUpdateSelfUpdaterCtrl& CIAUpdateController::SelfUpdaterCtrl()
    {
    return *iSelfUpdaterCtrl;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::ContentOperationManager
// 
// -----------------------------------------------------------------------------
//
CIAUpdateContentOperationManager& CIAUpdateController::ContentOperationManager()
    {
    return *iContentOperationManager;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::Startup
// 
// -----------------------------------------------------------------------------
//
TInt CIAUpdateController::Startup()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::Startup() begin");

    if ( iControllerState == EStarting )
        {
        IAUPDATE_TRACE("[IAUPDATE] Error code: KErrInUse");        
        return KErrInUse;
        }
    else if ( iControllerState != ENotRunning )
        {
        IAUPDATE_TRACE("[IAUPDATE] Error code: KErrAlreadyExists");
        return KErrAlreadyExists;
        }
    
    // Turn off the cache cleaner because we do not want it to remove any items
    // even if allowed db size is exceeded.
    // Make sure that IMEI is send in server requests.
    // Disable HEAD requests for optimization. With SISX content this is 
    // safe to do.
    TUint32 providerOptions( ENcdProviderDisableNodeCacheCleaner
                             | ENcdProviderSendImei
                             | ENcdProviderDisableHttpHeadRequest );
    
    TInt connectErr = iEngine->Connect( FamilyUid() );
    if ( connectErr != KErrNone )
        {
        return connectErr;
        }
    
    TRAPD ( err, 
            iEngine->CreateProviderL( KNcdProviderUid, 
                                      iBaseProvider, 
                                      iStatus, 
                                      providerOptions ) );
    IAUPDATE_TRACE_1("[IAUPDATE] error code: %d", err );        
    if ( err != KErrNone )
        {
        return err;
        }
    
    SetActive();

    iControllerState = EStarting;

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::Startup() end");

    return KErrNone;
    }
 
    
// -----------------------------------------------------------------------------
// CIAUpdateController::StartRefreshL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::StartRefreshL( TBool aAllowNetConnection )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartRefreshL() begin");
    IAUPDATE_TRACE_2("[IAUPDATE] iControllerState %d allow net connection %d", iControllerState, aAllowNetConnection );

    if ( iControllerState == ENotRunning )
        {
        User::Leave( KErrNotReady );
        }
    else if ( iControllerState != EIdle )
        {
        User::Leave( KErrInUse );
        }

    if ( !iLoader )
        {
        IAUPDATE_TRACE("[IAUPDATE] Create loader");
        iLoader = CIAUpdateLoader::NewL( *iProvider, *this );
        // For optimization reasons, skip child count refreshes.
        iLoader->SetSkipChildCountRefresh( ETrue );       
        }

    // Reset the node list. New one will be created when this completes. 
    // Notice, that the array content is not owned by this array.
    iNodeContainer->Clear();
    iNodes.Reset();
        
    if ( aAllowNetConnection )
        {
        IAUPDATE_TRACE("[IAUPDATE] Net connection allowed");

        if ( iLoader->RootExpiredL() )
            {
            IAUPDATE_TRACE("[IAUPDATE] Root has expired");

            // Notice, that we will clean the cache only if the
            // root load is required. Otherwise, if cache was cleaned,
            // the root would be reloaded also. Then, unwanted
            // CDB connections would occur.
            
            // Update the data from the file just in case.
            // Actually the data was already read when the object
            // was created if the file exists. But, this way we
            // can be sure that the data matches the file even if
            // in some situation the write operation has failed
            // and the object data could not be synchronized into
            // the file.
            iCacheClearFile->ReadControllerDataL();

            TLanguage currentLanguage = User::Language();
            TLanguage lastTimeLanguage = iCacheClearFile->Language();
            TTime lastClearTime( iCacheClearFile->RefreshTime() );
            const TTimeIntervalDays KCacheClearInterval( 
                IAUpdateCtrlConsts::KCacheClearIntervalDays );
            TTime expireTime( lastClearTime + KCacheClearInterval );
            TTime universalTime;
            universalTime.UniversalTime();

            if ( currentLanguage != lastTimeLanguage
                 || expireTime < universalTime
                 || lastClearTime > universalTime  )
                {
                IAUPDATE_TRACE("[IAUPDATE] Clear cache");
                // Database is expired because languages do not match
                // or current time has passed the expiration time. 
                // Also, sanity check is made. If last refresh time is larger 
                // than current time, then the last refresh value has been set wrong, 
                // and the database can be thought as expired. This might be the case 
                // if the user has changed the time in the phone.

                // Before starting to load data from the net,
                // clear an old cache. This way files related to unfinished
                // updates will be deleted and there is no danger of them becoming
                // hanging data that can not be handled when old nodes are removed.
                if ( !iCacheCleaner )
                    {
                    iCacheCleaner = CIAUpdateCacheCleaner::NewL( *iProvider );
                    }
                iCacheCleaner->ClearL( iStatus );
                SetActive();
                iControllerState = EInClearCache;
                }
            }

        if ( iControllerState == EIdle )
            {
            IAUPDATE_TRACE("[IAUPDATE] Refresh directly from net");
            // Because controller state is still idle, we did not start
            // cache cleaning above.
            // There is no need to clean the cache here. 
            // So, skip that step and start loading directly.     
            // This starts an asynchronous operation which will end
            // when callback is called.
            iLoader->LoadNodesL();
            iControllerState = EInLoadOperation;
            }
        }
    else
        {
        IAUPDATE_TRACE("[IAUPDATE] Start local refresh");
        // Load should be done locally.
        // So, just delegate the thing to the RunL which will handle
        // the local getting of the data in a next asynchronous step.
        iStatus = KRequestPending;
        SetActive();
        TRequestStatus* ptrStatus = &iStatus;
        User::RequestComplete( ptrStatus, KErrNone );
        iControllerState = EInLocalLoadOperation;
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartRefreshL() end");    
    }

        
// -----------------------------------------------------------------------------
// CIAUpdateController::CancelRefresh
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CancelRefresh()
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateController::CancelRefresh() begin: %d",
                     iControllerState);

    switch ( iControllerState )
        {
        case EInClearCache:
            // Cache cleaner is responsible of this operation.
            iCacheCleaner->Cancel();
            break;

        case EInLoadOperation:
            // Loader is responsible of this operation.
            iLoader->Cancel();
            break;

        case EInLocalLoadOperation:
            // Use the normal cancellation of this active class because 
            // these operations are handled by this active object itself.
            Cancel();
            break;

        default:
            break;
        }

    // New state should be idle.
    iControllerState = EIdle;

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CancelRefresh() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::CancelReporting
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CancelReporting()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CancelReport() begin");

    // Because report operation is cancelled, no need for the timer
    // anymore. So, delete timer if it exists. Deletion will automatically
    // cancel the timer operation. The timer may be started for some special
    // cases even if report operation was not created. So, try to delete it
    // here just in case.
    delete iReportTimer;
    iReportTimer = NULL;

    if ( iReportOperation )
        {
        // Set this for the callback because operation call operation complete
        // when the operation is cancelled. We do not want to call ui callback there.
        iCancellingReportOperation = ETrue;

        iReportOperation->CancelOperation();

        // Notice, that the operation complete will release the operation and set the
        // operation pointer to NULL. Do not do it here.

        // Set the cancelling flag to EFalse because cancell operation is finished.
        iCancellingReportOperation = EFalse;
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CancelReport() end");
    }
    
    
// -----------------------------------------------------------------------------
// CIAUpdateController::HistoryL
//
// -----------------------------------------------------------------------------
//    
MIAUpdateHistory& CIAUpdateController::HistoryL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::HistoryL() begin");

    if ( !iHistory )
        {
        iHistory = 
            CIAUpdateHistory::NewL( FamilyUid(), ProviderL() );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::HistoryL() end");

    return *iHistory;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SetDefaultConnectionMethodL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::SetDefaultConnectionMethodL( 
    const TIAUpdateConnectionMethod& aMethod )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::SetDefaultAccessPointL begin");
    IAUPDATE_TRACE_1("[IAUPDATE] access point: %d", aMethod.iId );

    if ( !iProvider )
        {
        User::Leave( KErrNotFound );
        }

    TNcdConnectionMethodType type( ENcdConnectionMethodTypeAlwaysAsk );
    switch ( aMethod.iType )
        {
        case TIAUpdateConnectionMethod::EConnectionMethodTypeAlwaysAsk:
            type = ENcdConnectionMethodTypeAlwaysAsk;
            break;

        case TIAUpdateConnectionMethod::EConnectionMethodTypeDestination:
            type = ENcdConnectionMethodTypeDestination;
            break;

        case TIAUpdateConnectionMethod::EConnectionMethodTypeAccessPoint:
            type = ENcdConnectionMethodTypeAccessPoint;
            break;

        case TIAUpdateConnectionMethod::EConnectionMethodTypeDefault:
            type = ENcdConnectionMethodTypeDefault;
            break;

        default:
            User::Leave( KErrNotSupported );
            break;
        }
        
    TNcdConnectionMethod method( aMethod.iId, type );
    iProvider->SetDefaultConnectionMethodL( method );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::SetDefaultAccessPointL end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SelfUpdateDataExists
//
// -----------------------------------------------------------------------------
//
TBool CIAUpdateController::SelfUpdateDataExists() const
    {
    return iSelfUpdaterCtrl->DataExists();
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::StartPossibleSelfUpdateL
//
// -----------------------------------------------------------------------------
//
TBool CIAUpdateController::StartPossibleSelfUpdateL( 
    TInt aIndex, 
    TInt aTotalCount,
    const RPointerArray< MIAUpdateNode >& aPendingNodes,
    TBool aSilent )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartPossibleSelfUpdateL");
    return SelfUpdaterCtrl().StartL( aIndex, aTotalCount, aPendingNodes, aSilent );
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::ResetSelfUpdate
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::ResetSelfUpdate()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ResetSelfUpdate() begin");

    SelfUpdaterCtrl().Reset();

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ResetSelfUpdate() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SelfUpdateRestartInfo
//
// -----------------------------------------------------------------------------
//
CIAUpdateRestartInfo* CIAUpdateController::SelfUpdateRestartInfo()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::SelfUpdateRestartInfo");
    return SelfUpdaterCtrl().SelfUpdateRestartInfo();    
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::NodeL
//
// -----------------------------------------------------------------------------
//
MIAUpdateNode& CIAUpdateController::NodeL( 
    const CIAUpdaterIdentifier& aIdentifier )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeL begin");

    CIAUpdateNode* node( NULL );

    for ( TInt i = 0; i < iNodeContainer->AllNodes().Count(); ++i )
        {
        CIAUpdateNode* tmpNode( iNodeContainer->AllNodes()[ i ] );
        if ( tmpNode->MetaNamespace() == aIdentifier.Namespace()
             && tmpNode->MetaId() == aIdentifier.Id() ) 
            {
            node = tmpNode;
            break;
            }
        }

    if ( !node )
        {
        IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeL node was not in the list");
        for ( TInt i = 0; i < iNodeContainer->ExcessNodes().Count(); ++i )
            {
            CIAUpdateNode* tmpNode( iNodeContainer->ExcessNodes()[ i ] );
            if ( tmpNode->MetaNamespace() == aIdentifier.Namespace()
                 && tmpNode->MetaId() == aIdentifier.Id() ) 
                {
                IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeL excess node found");
                node = tmpNode;
                break;
                }
            }        
        }

    if ( node == NULL )
        {
        IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeL create excess node");
        MNcdNode* ncdNode( NodeFromPurchaseHistoryL( aIdentifier ) );
        node = IAUpdateNodeFactory::CreateNodeLC( ncdNode, *this );
        iNodeContainer->AddExcessNodeL( node );
        CleanupStack::Pop( node );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeL end");

    return *node;        
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::StartingUpdatesL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::StartingUpdatesL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartingUpdatesL begin");

    if ( !iServerReportManager )
        {
        IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartingUpdatesL not found");
        User::Leave( KErrNotFound );
        }    

    // Start collecting server reports because updates are going to be downloaded
    // and installed.
    // Set the reporting method to the manager method instead of letting NCD Engine
    // do sending automatically. 
    // After we have manually sent the reports, we may change the method back to 
    // automatic and let the NCD Engine to do its thing as it seems best.
    // The reason why to set the reporting method here when the updates start is that
    // if for some reason the application was not able to send all the reports last time,
    // in background mode they will be sent automatically when there is process time
    // for that, for example before starting to do new updates.
    iServerReportManager->SetReportingMethodL( MNcdServerReportManager::EReportingManaged );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::StartingUpdatesL end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::FinishedUpdatesL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::FinishedUpdatesL( 
    TBool aOperationsAllowed, TInt aMaxWaitTime )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::FinishedUpdatesL begin");

    if ( !iProvider )
        {
        IAUPDATE_TRACE("[IAUPDATE] ERROR: Provider not found");
        User::Leave( KErrNotFound );
        }

    // Reset the timer related flag. 
    iReportTimerCompleted = EFalse;
        
    if ( aOperationsAllowed ) 
        {
        IAUPDATE_TRACE("[IAUPDATE] Operations allowed");
        if ( !iReportOperation )
            {
            IAUPDATE_TRACE("[IAUPDATE] Create operation");
            // Send the reports that have been collected.
            // Notice, that the created operation needs to be released
            // at some point.
            MNcdServerReportOperation* tmpOperation(
                iServerReportManager->SendL( *this ) );
            if ( tmpOperation )
                {
                IAUPDATE_TRACE("[IAUPDATE] Start operation");

                // Make sure that if the starting of the operation
                // leaves, then the operation will be released correctly.
                CleanupReleasePushL( *tmpOperation );

                // Start the operation
                tmpOperation->StartOperationL();

                CleanupStack::Pop( tmpOperation );

                // Because operation was successfully started,
                // it can now be inserted to memeber variable.
                iReportOperation = tmpOperation;

                // Notice, that we do not Release the operation here. 
                // We release it when the operation has finished and
                // the callback is called.
                }
            }
        }

    if ( !iReportOperation )
        {
        IAUPDATE_TRACE("[IAUPDATE] Operation was not created");
        // Set aMaxWaitTime as one. Then, we will get the timer
        // to do only one quick asynchronous loop and callback
        // is called after that. This way we make sure that
        // callback is always called even if operation is not
        // created here.
        aMaxWaitTime = 0;
        }

    // Create timer if needed.
    if ( aMaxWaitTime >= 0 )
        {
        IAUPDATE_TRACE_1("[IAUPDATE] Max wait time: %d", aMaxWaitTime);
        if ( !iReportTimer )
            {
            IAUPDATE_TRACE("[IAUPDATE] Create timer");
            // Timer does not exist yet. So, create new one.
            iReportTimer = CIAUpdateTimer::NewL( *this );
            }
        else
            {
            IAUPDATE_TRACE("[IAUPDATE] Cancel timer");
            // Timer already exists.
            // Cancel possible already ongoing operation
            // before starting the new one.
            iReportTimer->Cancel();
            }
        IAUPDATE_TRACE("[IAUPDATE] Start timer");
        iReportTimer->After( aMaxWaitTime );
        }

    // Because operation has now been started there is nothing to manage anymore.
    // So, set the NCD Engine to automatic mode. Then, if there is something to do later
    // in the background, the engine can do it when it suits it best.
    iServerReportManager->SetReportingMethodL( MNcdServerReportManager::EReportingBackground );
       
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::FinishedUpdatesL end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::LoadComplete
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::LoadComplete( TInt aError )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LoadComplete begin");
    IAUPDATE_TRACE_1("[IAUPDATE] error code: %d", aError );

    // Because node hierarchy is now concidered up-to-date,
    // get all the required nodes from the local cache to the
    // head node list that will be given to the observer.

    TRAPD ( trapError, LocalLoadL() );
    if ( trapError != KErrNone )
        {
        aError = trapError;        
        }

    // Update the state, now that everything has been done.
    iControllerState = EIdle;

    // Inform, the observer.            
    iObserver.RefreshComplete( iNodes, aError );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LoadComplete end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SelfUpdaterComplete
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::SelfUpdaterComplete( TInt aErrorCode )
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateController::SelfUpdaterComplete() error code: %d", aErrorCode );
    iObserver.SelfUpdaterComplete( aErrorCode );
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CatalogsEngineShutdown()
    {
    // IAD handles self updates in its own updater.
    // Therefore, self updates are not started by NCD Engine itself.
    // So, this callback function should be never called.  
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::CatalogsUpdateNotification
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CatalogsUpdateNotification( 
    const TDesC& /*aTarget*/, 
    const TDesC& /*aId*/,
    const TDesC& /*aVersion*/,
    const TDesC& /*aUri*/,
    TBool /*aForce*/ )
    {
    // Called when a Catalogs OTA update is available.
    // IAD handles self updates in its own updater.    
    // This callback function should be never called.
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::CatalogsConnectionEvent
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CatalogsConnectionEvent( 
    TBool /*aConnectionActive*/ )
    {
    // This callback function is called when data is transferred in the
    // network connections. IAD does not use this information for now.
    }    


// -----------------------------------------------------------------------------
// CIAUpdateController::ForceExpirationInformationReceived
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::ForceExpirationInformationReceived( 
    RCatalogsArray< MNcdNode >& /*aExpiredNodes*/ )
    {
    // IAD UI is not forced to be updated by the server side.
    // It is up to the UI to decide when to update its content.
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::LocalizeString
//
// -----------------------------------------------------------------------------
//
HBufC* CIAUpdateController::LocalizeString( 
    const TDesC& /*aLocalizationKey*/ )
    {
    return NULL;
    }


// ---------------------------------------------------------------------------
// CIAUpdateController::ReportProgress
// 
// ---------------------------------------------------------------------------
//
void CIAUpdateController::ReportProgress( 
    MNcdServerReportOperation& /*aOperation*/, 
    TNcdProgress /*aProgress*/ )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::ReportProgress()");
    }


// ---------------------------------------------------------------------------
// CIAUpdateController::QueryReceived
// 
// ---------------------------------------------------------------------------
//
void CIAUpdateController::QueryReceived( 
    MNcdServerReportOperation& aOperation, 
    MNcdQuery* aQuery )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::QueryReceived() begin");

    // Install query received.
    // Always accept queries.
    // Queries should not be requested from this client.
    TInt trapError( KErrNone );
    if ( aQuery )
        {
        TRAP ( trapError, 
               aQuery->SetResponseL( MNcdQuery::EAccepted );
               aOperation.CompleteQueryL( *aQuery ); );
        // Release needs to be called to the query after it is not used.
        aQuery->Release();        
        }

    if ( ( trapError != KErrNone ) || ( !aQuery ) )
        {
        // Error occurred when query was handled.
        // So, operation can not continue.
        // Cancel operation. Notice, that OperationComplete will be called
        // by the operation when cancel is called.
        aOperation.CancelOperation();
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::QueryReceived() end");
    }


// ---------------------------------------------------------------------------
// CIAUpdateController::OperationComplete
// 
// ---------------------------------------------------------------------------
//
void CIAUpdateController::OperationComplete( 
    MNcdServerReportOperation& aOperation, 
    TInt aError )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::OperationComplete() begin");

    // Report operation has completed.
    
    if ( &aOperation == iReportOperation )
        {
        IAUPDATE_TRACE("[IAUPDATE] Acceptable server report operation");

        // We should always come here
        // because only one operation at a time is going on.

        // Release the operation because we do not need it anymore
        // and because operation reference count was increased when
        // it was created for this class object.        
        iReportOperation->Release();
        iReportOperation = NULL;

        // By checking if the timer has completed its job, we know if
        // the observer should be informed about the completion of the
        // operation. This way we will avoid duplicate callbacks, for example,
        // after timer completion has called callback and the report operation
        // completes after that.
        if ( !iReportTimerCompleted )
            {
            IAUPDATE_TRACE("[IAUPDATE] Timer has not informed observer yet");

            // Because operation is completed, no need for the timer anymore.
            // So, delete timer if it exists. Deletion will automatically
            // cancel the timer operation.
            delete iReportTimer;
            iReportTimer = NULL;

            // Do not call callback function if cancel was started by user. 
            if ( !iCancellingReportOperation )
                {
                IAUPDATE_TRACE("[IAUPDATE] Inform observer.");
                iObserver.ServerReportSent( aError );        
                }
            }
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::OperationComplete() end");
    }


// ---------------------------------------------------------------------------
// CIAUpdateController::TimerComplete
// 
// ---------------------------------------------------------------------------
//
void CIAUpdateController::TimerComplete( TInt aError )
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateController::TimerComplete() begin: %d",
                     aError);

    // If we come here it means that timer has completed before
    // the reports were actually sent and before the operation is released. 

    // Timer has done its job. So delete it.
    delete iReportTimer;
    iReportTimer = NULL;

    // Set the flag. Then, observer will not be informed twice when the
    // report operation is actually completed.
    iReportTimerCompleted = ETrue;

    // Inform the observer that it should continue even if reports are still
    // being sent in the background. Notice, that the report operation most likely
    // will complete later and then OperationComplete will be called.
    iObserver.ServerReportSent( aError );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::TimerComplete() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::DoCancel
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::DoCancel()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::DoCancel() begin");
    IAUPDATE_TRACE_1("[IAUPDATE] ControllerState: %d", iControllerState );

    switch ( iControllerState )
        {
        case EStarting:
            {
            // This controller is trying to create the 
            // provider. Cancel that.
            iEngine->CancelProviderCreation();
            iControllerState = ENotRunning;            
            }
            break;

        case EInLocalLoadOperation:
            {
            // Local loading was cancelled. 
            // Nothing to cancel, because complete of the request already issued
            iControllerState = EIdle;
            }
            break;

        default:
            // We should never come here.
            break;
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::DoCancel() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::RunL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::RunL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::RunL() begin");
    IAUPDATE_TRACE_1("[IAUPDATE] ControllerState: %d", iControllerState);
    IAUPDATE_TRACE_1("[IAUPDATE] Error code: %d", iStatus.Int());        

    // If we were trying to start the provider we may get different
    // error codes that are still acceptable. For example, the DB may have
    // been cleaned because of some error situation. But, then we just have
    // to continue after that normally. 
    // In other cases, KErrNone will inform about success.
    // Possible feedback from the provider contains the following error
    // codes that are interpreted as success: 
    // KErrNone, KNcdDatabasesClearedAfterCrash, KNcdPurchaseHistoryVersionMismatch,
    // KNcdGeneralDatabaseVersionMismatch. 
    // Notice, that the actual error code can be a combination of these numbers.

    // Get the error code.
    TInt errorCode( iStatus.Int() );
 
    // This will leave if error code is negative. 
    // KErrNone and positive error codes are interpreted as success.
    // If leave occurs, let RunError handle it.
    User::LeaveIfError( errorCode );

    switch ( iControllerState )
        {
        case EStarting:
            {
            if ( !iBaseProvider )
                {
                User::Leave( KErrGeneral );
                }

            // Change the error code to iaupdate specific if necessary.
            if ( errorCode > 0 )
                {
                IAUPDATE_TRACE("[IAUPDATE] NCD cache cleared. Change error code to iaupdate specific.");
                errorCode = IAUpdateErrorCodes::KErrCacheCleared;        
                }
                
            iProvider = iBaseProvider->QueryInterfaceL<MNcdProvider>();
            iProvider->SetObserver( this );
            iProvider->SetStringLocalizer( *this );
            
            SetupConfigurationL();

            iServerReportManager = 
                iProvider->QueryInterfaceL<MNcdServerReportManager>();
            // Because we want to send S60 error codes instead of general
            // codes into the server, set the style here.
            iServerReportManager->
                SetReportingStyleL( 
                    MNcdServerReportManager::EReportingStyleS60 );

            // Cancel possible paused operations.
            // So, they do not prevent others to start their operations.
            CancelPausedOperationsL();
            
            // Everything was handled correctly. So, set the state and
            // inform observer.
            iControllerState = EIdle;            
            iObserver.StartupComplete( errorCode );
            }
            break;

        case EInClearCache:
            {
            IAUPDATE_TRACE("[IAUPDATE] Load nodes from net.");
            // Cache has been cleaned.

            // Save the current information to clear file.
            iCacheClearFile->SetCurrentData();
            iCacheClearFile->WriteControllerDataL();

            // Next, load necessary nodes from the net.
            // This starts an asynchronous operation which will end
            // when callback is called.
            iLoader->LoadNodesL();

            iControllerState = EInLoadOperation;
            }
            break;

        case EInLocalLoadOperation:
            {
            // Nodes should be gotten from the local database instead of 
            // from the internet.
            LocalLoadL();
                
            // New state should be idle because after this function
            // everything is done and observer is informed.
            // Notice, that the state is inserted here after all the other
            // functionality has been handeled because if the code above leaves,
            // then RunError will be able to check the state and handle things 
            // correctly.
            iControllerState = EIdle;

            // Inform, the observer.            
            iObserver.RefreshComplete( iNodes, KErrNone );            
            }
            break;
        
        default:
            break;
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::RunL() end");
    }
  
    
// -----------------------------------------------------------------------------
// CIAUpdateController::RunError
//
// -----------------------------------------------------------------------------
//    
TInt CIAUpdateController::RunError( TInt aError )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::RunError() begin");
    IAUPDATE_TRACE_1("[IAUPDATE] ControllerState: %d", iControllerState );

    switch ( iControllerState )
        {
        case EStarting:
            iControllerState = ENotRunning;
            iObserver.StartupComplete( aError );
            break;

        case EInLocalLoadOperation:
        case EInClearCache:
            // Local load left in RunL.
            iControllerState = EIdle;
            iObserver.RefreshComplete( iNodes, aError );
            break;

        default:
            // We should not come here.
            break;
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::RunError() end");    

    return KErrNone;
    }
 

// -----------------------------------------------------------------------------
// CIAUpdateController::LocalLoadL()
//
// -----------------------------------------------------------------------------
// 
void CIAUpdateController::LocalLoadL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LocalLoadL() begin");

    MNcdNode* rootNode( iProvider->RootNodeL() );
    if ( !rootNode )
        {
        IAUPDATE_TRACE("[IAUPDATE] ERROR: NULL root was given.");
        User::Leave( KErrNotFound );
        }

    if ( rootNode->State() == MNcdNode::EStateNotInitialized )
        {
        IAUPDATE_TRACE("[IAUPDATE] Root node uninitialized. Do not continue local load.");
        rootNode->Release();
        return;
        }

    CleanupReleasePushL( *rootNode );

    MNcdNodeContainer* rootContainer = 
        rootNode->QueryInterfaceLC< MNcdNodeContainer >();
    if ( !rootContainer )
        {
        IAUPDATE_TRACE("[IAUPDATE] ERROR: NULL container for root.");
        // Root should always have container interface.
        User::Leave( KErrNotFound );
        }

    // The given node was root node.

    LocalContainerLoadL( *rootContainer );

    CleanupStack::PopAndDestroy( rootContainer );
    rootContainer = NULL;
            
    CleanupStack::PopAndDestroy( rootNode );
    rootNode = NULL;

    // The iNodeContainer now contains all the necessary nodes.
    // Get the nodes from the iNodeContainer.
    // Make sure that the array is clean before adding new items.
    iNodes.Reset();

    // Get references to the node arrays.
    const RPointerArray< CIAUpdateFwNode >& fwNodes = 
        iNodeContainer->FwNodes();
    const RPointerArray< CIAUpdateNode >& headNodes = 
        iNodeContainer->HeadNodesL();

    // Make sure we have enough memory for the array.
    TInt fwNodeCount( fwNodes.Count() );
    TInt headNodeCount( headNodes.Count() );
    iNodes.ReserveL( fwNodeCount + headNodeCount );

    // Insert the firmware nodes into the beginning of the array.
    for ( TInt i = 0; i < fwNodeCount; ++i )
        {
        // Notice, that the ownership is not transferred here.
        MIAUpdateAnyNode* anyNode( fwNodes[ i ] );
        iNodes.AppendL( anyNode );
        }

    // Insert the head nodes into the array.
    for ( TInt i = 0; i < headNodeCount; ++i )
        {
        // Notice, that the ownership is not transferred here.
        MIAUpdateAnyNode* anyNode( headNodes[ i ] );
        iNodes.AppendL( anyNode );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LocalLoadL() end");
    }

    
// -----------------------------------------------------------------------------
// CIAUpdateController::LocalContainerLoadL
//
// -----------------------------------------------------------------------------
//   
void CIAUpdateController::LocalContainerLoadL( 
    MNcdNodeContainer& aContainer )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LocalContainerLoadL() begin");

    // Insert all the item children (that are not uninitilized) 
    // from the container node into the iNodeContainer. 
    // So, iNodeContainer will have all the item children in its list.
    
    TInt childCount( aContainer.ChildCount() );	        
    IAUPDATE_TRACE_1("[IAUPDATE] childcount: %d", childCount);	        

    for ( TInt i = 0; i < childCount; ++i )
        {
        IAUPDATE_TRACE_1("[IAUPDATE] child index: %d", i );
        
        // This call increases the reference count of the child.
        MNcdNode* child( aContainer.ChildL( i ) );

        if ( child )
            {
            // Notice, that we need to check if the child really exists.
            // If a container has been loaded from the net, it has 
            // its child count. But, children may have not been loaded.
            IAUPDATE_TRACE("[IAUPDATE] Child was loaded.");
            if ( child->State() == MNcdNode::EStateNotInitialized )
                {
                IAUPDATE_TRACE("[IAUPDATE] Skip uninitialized child.");
                // Skip uninitialized child.
                // Remember to release it here.
                child->Release();
                child = NULL;
                }
            else
                {
                // Insert child into the cleanup stack. 
                // So, if functions leave, it will be released.
                CleanupReleasePushL( *child );

                // Notice, that this has to be released.
                MNcdNodeContainer* childContainer( 
                    child->QueryInterfaceL< MNcdNodeContainer >() );

                if ( !childContainer )
                    {
                    IAUPDATE_TRACE("[IAUPDATE] Item node");            

                    TBool isFwNode( 
                        IAUpdateNodeFactory::IsFwUpdateL( *child ) ); 

                    // Because the factory takes care of the deletion of the child object,
                    // just pop it from the cleanup stack here. So, no release is called here.
                    CleanupStack::Pop( child );

                    if ( isFwNode  && !IAUpdateUtils::IsFirmwareChangedL() ) 
                        {
                        // if phone's firmware changed after previous successfull network refresh,
                        // firmware nodes are skipped. That's a workaround to hide them from UI just after 
                        // firmware update. 
                                      
                        // Notice, that the IAUpdateNodeFactory and CIAUpdateNodeContainer 
                        // take the ownership and release the node if the creation leaves.
                        CIAUpdateFwNode* node( NULL );
                        // Trap error here. 
                        // If one node fails, others may still be created.
                        TRAP_IGNORE( 
                            node = IAUpdateNodeFactory::CreateFwNodeL( child, *this ) );
                        if ( node )
                            {
                            IAUPDATE_TRACE("[IAUPDATE] Fw node created successfully");
                            // If leave occurs, then AddNodeL will itself delete the created node.
                            // So, do not insert the node into the cleanup stack here.
                            iNodeContainer->AddFwNodeL( node );
                            }
                        }
                    else
                        {
                        // Notice, that the IAUpdateNodeFactory and CIAUpdateNodeContainer 
                        // take the ownership and release the node if the creation leaves.
                        CIAUpdateNode* node( NULL );
                        // Trap error here. 
                        // If one node fails, others may still be created.
                        TRAP_IGNORE( 
                            node = IAUpdateNodeFactory::CreateNodeL( child, *this ) );
                        if ( node )
                            {
                            IAUPDATE_TRACE("[IAUPDATE] Node created successfully");
                            // If leave occurs, then AddNodeL will itself delete the created node.
                            // So, do not insert the node into the cleanup stack here.
                            iNodeContainer->AddNodeL( node );
                            }
                        }
                    }
                else
                    {
                    IAUPDATE_TRACE("[IAUPDATE] Container node");

                    // The ownership of the child was not transferred to
                    // anybody, the node has to be released here.
                    CleanupStack::PopAndDestroy( child );

                    CleanupReleasePushL( *childContainer );

                    // Because this is a container, use recursion to insert its
                    // child items into the node container.
                    LocalContainerLoadL( *childContainer );

                    CleanupStack::PopAndDestroy( childContainer );
                    }
                }
            }
        }
    
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::LocalContainerLoadL() begin");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::SetupConfigurationL
//
// -----------------------------------------------------------------------------
// 
void CIAUpdateController::SetupConfigurationL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::SetupConfigurationL() begin");

    CIAUpdateEngineXmlParser* parser( CIAUpdateEngineXmlParser::NewLC() );
    parser->ParseL();
    const CIAUpdateEngineConfigData& data( parser->ConfigData() );

    CNcdKeyValuePair* pair( NULL );
    

    // Master server (CDB) uri
    IAUPDATE_TRACE_1("[IAUPDATE] master server: %S", &data.MasterServerUri() );
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KMasterServer(), 
                                    data.MasterServerUri() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );
        
    // Max storage size
    // Notice, this value does not have any effect if the cache cleaner is
    // turned off when provider is created.
    const TDesC& storageMaxSize( data.StorageMaxSize() );
    if ( storageMaxSize == KNullDesC )
        {
        IAUPDATE_TRACE("[IAUPDATE] Use default storage max size");
        pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KMaxStorageSize(), 
                                        IAUpdateCtrlConsts::KDefaultStorageMaxSize() );
        }
    else
        {
        IAUPDATE_TRACE("[IAUPDATE] Use config file storage max size");
        pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KMaxStorageSize(), 
                                        storageMaxSize );        
        }
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );

    // Software version
    // Notice, that here we use the hardcoded value instead of parsing the data
    // from the configuration XML file. This should be hardcoded value because the
    // version number is strictly related to the version of the engine. So, the version
    // should not be altered by different configuration files.
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KSoftwareVersion(), 
                                    IAUpdateCtrlConsts::KSoftwareVersion() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );
    
    // Software type
    // Notice, that here we use the hardcoded value instead of parsing the data
    // from the configuration XML file. This should be hardcoded value because the
    // software type is always same for the engine. It should not be allowed to
    // change by defining new value in config file.
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KSoftwareType(), 
                                    IAUpdateCtrlConsts::KSoftwareType() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );

    // Provisioning
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KProvisioning(), 
                                    data.Provisioning() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );

    // Client role
    // This value is used in the server requests.
    pair = CNcdKeyValuePair::NewLC( IAUpdateProtocolConsts::KIAClientRole(), 
                                    data.ClientRole() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );
    iObserver.ClientRole( data.ClientRole() );


    // No need for the parser anymore.
    CleanupStack::PopAndDestroy( parser );
    parser = NULL;

    
    // Language
    TLanguage language( User::Language() );
    HBufC* languageDes( HBufC::NewLC( 32 ) );
    TPtr languagePtr( languageDes->Des() );
    languagePtr.AppendNum( language );
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KSoftwareLanguage(), 
                                    languagePtr );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );
    CleanupStack::PopAndDestroy( languageDes );


    // Capabilities accepted by the client.
    // These capabilities define actions between the client and the server.
    
    // Download report
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KCapability(),
                                    NcdCapabilities::KDownloadReport() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );
    
    // Install report
    pair = CNcdKeyValuePair::NewLC( NcdConfigurationKeys::KCapability(),
                                    NcdCapabilities::KInstallationReport() );
    iProvider->AddConfigurationL( *pair );
    CleanupStack::PopAndDestroy( pair );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::SetupConfigurationL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::NodeFromPurchaseHistoryL
//
// -----------------------------------------------------------------------------
//
MNcdNode* CIAUpdateController::NodeFromPurchaseHistoryL( 
    const CIAUpdaterIdentifier& aIdentifier )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeFromPurchaseHistoryL() begin");

    // This will contain the correct details object.
    CNcdPurchaseDetails* details( NULL );

    // Create filter. So, we will get
    // all the purchase history items.
    CNcdPurchaseHistoryFilter* filter =
        CNcdPurchaseHistoryFilter::NewLC();
    filter->SetNamespaceL( aIdentifier.Namespace() );
    filter->SetEntityIdL( aIdentifier.Id() );
    
    // Add family uid to the filter
    RArray< TUid > uids;
    CleanupClosePushL( uids );
    uids.AppendL( FamilyUid() );
    filter->SetClientUids( uids.Array() );
    CleanupStack::PopAndDestroy( &uids );

    MNcdPurchaseHistory* history( ProviderL().PurchaseHistoryL() );
    CleanupReleasePushL( *history );
    
    // Get the ids. So, we can next get all the corresponding
    // details.
    RArray< TUint > ids = history->PurchaseIdsL( *filter );
    // Temporarily remove history from cleanup stack
    CleanupStack::Pop( history );
    CleanupStack::PopAndDestroy( filter );
    CleanupReleasePushL( *history );
    CleanupClosePushL( ids );
    
    if ( ids.Count() > 0 )
        {
        // If purchase details exist, then use the most up-to-date one.
        details = 
            history->PurchaseDetailsL( ids[ 0 ], EFalse );
        }
    
    CleanupStack::PopAndDestroy( &ids );
    CleanupStack::PopAndDestroy( history );
    
    if ( details == NULL )
        {
        User::Leave( KErrNotFound );
        }
    else
        {
        // The details was created but not inserted into
        // the cleanup stack there. Insert it into the cleanupstack now.
        CleanupStack::PushL( details );
        }

    MNcdNode* node( ProviderL().NodeL( *details ) );
    if ( node == NULL )
        {
        User::Leave( KErrNotFound );
        }

    CleanupStack::PopAndDestroy( details );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::NodeFromPurchaseHistoryL() end");

    return node;
    }


// -----------------------------------------------------------------------------
// CIAUpdateController::NodeFromPurchaseHistoryL
//
// -----------------------------------------------------------------------------
//
void CIAUpdateController::CancelPausedOperationsL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CancelPausedOperationsL() begin");
    
    // Cancel possible paused operations here.
    // IAD Engine does not itself pause operations. 
    // But, in some error cases download operation may have become paused.
    // By cancelling a paused operation, we can avoid locking if multiple IAD UIs 
    // are opened. Else, the paused operations that belong to first opened UI 
    // will block the starting of the operations from another IAD UIs.
    // Because these operations are created for the one IAD, 
    // they will be blocking other UI operations because multiple 
    // simultaneous operations are not allowed for same metadata content.
    // When the IAD is started, the operations that are paused will be
    // created in the NCD Provider and they are part of that IAD UI via NCD
    // Provider Proxy.
    
    // Notice, Release needs to be called for array elements.
    RCatalogsArray< MNcdOperation > operations = 
        iProvider->OperationsL();

    // Push the array into the cleanupstack. So, PopAndDestroy will
    // call Release to the array items and finally reset the array.
    CleanupResetAndDestroyPushL( operations );
        
    TInt count( operations.Count() );
    IAUPDATE_TRACE_1("[IAUPDATE] Pending operation count: %d", count);
    for ( TInt i = 0; i < count; ++i )
        {
        MNcdOperation* operation( operations[ i ] );
        MNcdDownloadOperation* download( 
            operation->QueryInterfaceLC< MNcdDownloadOperation >() );
        if ( download && download->IsPaused() )
            {
            // A download operation has been left hanging as paused.
            // Just, cancel it. So, it will not prevent possible other
            // download attempts from other IAD UIs. We cancel this operations
            // already now, because we may not actually start it later in this
            // UI.
            IAUPDATE_TRACE_1("[IAUPDATE] Cancel paused operation: %d", i);
            operation->CancelOperation();
            CleanupStack::PopAndDestroy( download );
            }
        }
    
    // This will Release array elements and reset the array.
    CleanupStack::PopAndDestroy( &operations );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateController::CancelPausedOperationsL() begin");
    }