/*
* Copyright (c) 2006-2008 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:  
*
*/


#include "catalogshttpsessionmanagerimpl.h"

#include <es_sock.h>

#include "catalogshttpoperation.h"
#include "catalogshttpconfig.h"
#include "catalogstransportoperationid.h"
#include "catalogshttpconnectioncreator.h"
#include "catalogsnetworkmanager.h"
#include "catalogsutils.h"
#include "catalogshttputils.h"

#include "catalogsdebug.h"

// 20 minutes
const TInt KDisconnectionDelayInMicroSeconds = 1000000*60*20;

// 20 seconds after we switch to another APN even if the old one is still
// up
const TInt KApSwitchDelay = 1000000*20;

// ======== MEMBER FUNCTIONS ========
 
CCatalogsNetworkManager* CCatalogsHttpSessionManager::iNetworkManager = NULL;
 
// ---------------------------------------------------------------------------
// ResumeOperationCallback, called by CAsyncCallBack
// ---------------------------------------------------------------------------
//	    
static TInt ResumeOperationCallback( TAny* aData )
    {
    DLTRACEIN((""));
    static_cast<CCatalogsHttpSessionManager*>( aData )->ResumeOperationAction();
    return KErrNone;
    }
    
 
// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//	
CCatalogsHttpSessionManager* CCatalogsHttpSessionManager::NewL(
    CDocumentHandler& aDocHandler )
    {
    CCatalogsHttpSessionManager* self = new( ELeave ) 
        CCatalogsHttpSessionManager( aDocHandler );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//	
CCatalogsHttpSessionManager::~CCatalogsHttpSessionManager()
    {
    DLTRACEIN((""));
    Cancel();
    iConnectionTimer.Close();
    DeletePtr( iMonitor );
    iTransactionQueue.Close();
    iDownloadQueue.Close();
    iGeneralQueue.Close();
    iRunningQueue.Close();
    ReleasePtr( iConnection );
    
    delete iConnectionCreator;
    delete iCallback;    
    
    // set as NULL after delete because iNetworkManager is static    
    DeletePtr( iNetworkManager );
    DLTRACEOUT((""));
    }


// ---------------------------------------------------------------------------
// Add a reference
// ---------------------------------------------------------------------------
//

void CCatalogsHttpSessionManager::AddRef()
    {
    iRefCount++;    
    }


// ---------------------------------------------------------------------------
// Release a reference
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpSessionManager::Release()
    {
    DLTRACEIN( ( "" ) );
    iRefCount--;
    if ( !iRefCount ) 
        {
        delete this;
        return 0;
        }
    return iRefCount;
    }

// ---------------------------------------------------------------------------
// StartOperation
// ---------------------------------------------------------------------------
//	
TInt CCatalogsHttpSessionManager::StartOperation( 
    MCatalogsHttpOperation* aOperation )
    {
    DLTRACEIN((""));
    DLINFO( ("Operation ID: %i, Type: %i", 
        aOperation->OperationId().Id(), aOperation->OperationType() ) );
    DASSERT( aOperation );
    
    UpdateRunningOperations();
    
    DLINFO(("Running operations: %d, transactions: %d, general: %d",
        iRunningOperations, iRunningTransactions, iRunningFromGeneral ));
        
    // Make sure the download is removed from queue
    RemoveFromQueue( aOperation );
    
    TBool isRunning = EFalse;

    // Check if the operation can connect either by using the current AP
    // or by opening a new one
    TRAPD( err, isRunning = ConnectL( *aOperation ) );
    if ( err != KErrNone ) 
        {
        DLTRACEOUT(("Error: %d, err"));
        return err;
        }
        
    if ( !isRunning ) 
        {
        DLTRACEOUT(("Not running, adding to queue"));
        return AddToQueue( aOperation );
        }
        
    // Check whether operation belongs to general queue or not
    if ( aOperation->Config().Priority() > ECatalogsPriorityQueued )
        {        
        // non-general queues
        if ( iRunningOperations < KMaxConcurrentOperations ) 
            {
            DLTRACE(("Not general queue"));
            // Check that transactions leave room for downloads and vice versa
            if ( ( ( aOperation->OperationType() == ECatalogsHttpTransaction ) && 
                 ( iRunningTransactions < KMaxConcurrentOperations - KMinDownloads ) ) ||
                 ( ( aOperation->OperationType() == ECatalogsHttpDownload ) &&
                 ( iRunningOperations - iRunningTransactions < 
                   KMaxConcurrentOperations - KMinTransactions ) ) )
                {
                
                iRunningOperations++;
                if ( aOperation->OperationType() == ECatalogsHttpTransaction ) 
                    {
                    iRunningTransactions++;
                    }               
                     
                DLINFO( ( "Running: %i, TRs: %i", iRunningOperations,
                    iRunningTransactions ) );
                return iRunningQueue.InsertInAddressOrder( aOperation );
                }            
            else 
                {
                DLINFO( ( "Running %i, TRs: %i, DLs: %i", iRunningOperations,
                    iRunningTransactions, 
                    iRunningOperations - iRunningTransactions ) );
                }
            }
        else 
            {
            DLINFO( ( "Running: %i >= Max: %i", iRunningOperations, 
                KMaxConcurrentOperations ) );
            }   
        }
    else if ( !iRunningFromGeneral )    
        {
        // General queue
        DLTRACE(("Operation from general"));        
        return iRunningQueue.InsertInAddressOrder( aOperation );                
        }
        
    return AddToQueue( aOperation );
    }
    
    
// ---------------------------------------------------------------------------
// PauseOperation
// ---------------------------------------------------------------------------
//	    
TInt CCatalogsHttpSessionManager::PauseOperation(   
    MCatalogsHttpOperation* aOperation )
    {
    DLTRACEIN( ("") );
    DASSERT( aOperation );
    
    TInt err = RemoveFromQueue( aOperation );
    if ( err == KErrNone )
        {
        err = iRunningQueue.InsertInAddressOrder( aOperation );
        ResumeOperation();
        }
    else
        {
        DLERROR(( "Error %d", err ));
        }
        
    return err;
    }

    
// ---------------------------------------------------------------------------
// CompleteOperation
// ---------------------------------------------------------------------------
//	    
TInt CCatalogsHttpSessionManager::CompleteOperation( 
    MCatalogsHttpOperation* aOperation )
    {
    DLTRACEIN( ("Running ops: %d, queues: Run: %d, DL: %d, TR: %d, Gen: %d",
        iRunningOperations, iRunningQueue.Count(),
        iDownloadQueue.Count(), iTransactionQueue.Count(),
        iGeneralQueue.Count() ) );
    
    // This shouldn't happen unless the operation is canceled before the
    // connection has been created
    if ( iConnectedOperation == aOperation ) 
        {
        iConnectedOperation = NULL;
        if ( iConnection ) 
            {
            // This prevents AP selection queries after the operation
            // has been canceled. 
            DLTRACE(("Cancel connection creation"));            
            iConnection->Cancel();    
            
            // DLMAIN-545, iConnectionState was left as Disconnecting
            // which prevented the creation of new connections
            iConnectionState = ECatalogsConnectionDisconnected;  
            
            // iConnection will be released in ConnectL/destructor
            }        
        }
    
    TInt index = iRunningQueue.FindInAddressOrder( aOperation );
    if ( index != KErrNotFound ) 
        {
        DLTRACE(("Removing from running ops, count after: %d", 
            iRunningQueue.Count() - 1 ));
            
        iRunningQueue.Remove( index );
        ResumeOperation();        
        }
    else 
        {
        RemoveFromQueue( aOperation );
        // This ensures that connection timer is started if necessary
        UpdateRunningOperations();
        }
    return KErrNone;
    }


// ---------------------------------------------------------------------------
// Priority change handler
// ---------------------------------------------------------------------------
//	
TInt CCatalogsHttpSessionManager::OperationPriorityChanged(
    MCatalogsHttpOperation* aOperation )
    {
    if ( RemoveFromQueue( aOperation ) == KErrNone ) 
        {
        return AddToQueue( aOperation );
        }
    return KErrNotFound;
    }



// ---------------------------------------------------------------------------
// Connection creator
// ---------------------------------------------------------------------------
//	
CCatalogsHttpConnectionCreator& CCatalogsHttpSessionManager::ConnectionCreatorL()
    {
    DLTRACEIN((""));
    if( !iConnectionCreator ) 
        {
        iConnectionCreator = CCatalogsHttpConnectionCreator::NewL();
        }
    return *iConnectionCreator;        
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//	
CCatalogsNetworkManager& CCatalogsHttpSessionManager::NetworkManagerL()
    {
    DLTRACEIN((""));
    if ( !iNetworkManager )
        {
        iNetworkManager = CCatalogsNetworkManager::NewL();         
        }
    return *iNetworkManager;
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//  
CDocumentHandler& CCatalogsHttpSessionManager::DocumentHandler()
    {
    return iDocHandler;
    }

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::SetResumeMode( TBool aResumeQueued )
    {
    iResumeQueued = aResumeQueued;
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//  
void CCatalogsHttpSessionManager::RunL()
    {
    DLTRACEIN(("iStatus: %d", iStatus.Int() ));
    if ( iStatus.Int() == KErrNone )
        {
        if ( iConnection )
            {
            DLTRACE(("Idle timeout, releasing connection"));
            DASSERT( iConnection && iConnection->RefCount() == 1 );
            // Monitor is deleted elsewhere        
            if ( iMonitor ) 
                {
                iMonitor->Cancel();
                }
    
            NetworkManagerL().CloseAccessPointL( iConnection->ConnectionMethod() );
            // We don't need to monitor the shutdown because we're not 
            // switching to an another APN
            ReleasePtr( iConnection );
            iConnectionState = ECatalogsConnectionDisconnected;
            }
        else if ( iSwitchApTimer )
            {
            iSwitchApTimer = EFalse;
            // emulate real connection shutdown
            ConnectionStateChangedL( KConnectionUninitialised );
            }        
        }
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//  
void CCatalogsHttpSessionManager::DoCancel()
    {
    DLTRACEIN((""));
    iConnectionTimer.Cancel();
    iSwitchApTimer = EFalse;
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//  
TInt CCatalogsHttpSessionManager::RunError( TInt aError )
    {
    DLTRACEIN(("aError: %d", aError ));
    (void) aError;
    return KErrNone;
    }


// ---------------------------------------------------------------------------
// CCatalogsHttpSessionManager
// ---------------------------------------------------------------------------
//	
CCatalogsHttpSessionManager::CCatalogsHttpSessionManager(
    CDocumentHandler& aDocHandler ) : 
    CActive( CActive::EPriorityStandard ),
    iRefCount( 1 ),
    iResumeQueued( ETrue ),
    iDocHandler( aDocHandler )
    {
    CActiveScheduler::Add( this );
    }


// ---------------------------------------------------------------------------
// ResumeOperation
// ---------------------------------------------------------------------------
//	    
void CCatalogsHttpSessionManager::ResumeOperation()
    {
    DLTRACEIN((""));
    DLINFO( ( "Tr queue: %i, Dl queue: %i, general: %i", 
        iTransactionQueue.Count(),
        iDownloadQueue.Count(), 
        iGeneralQueue.Count() ) );
    
    if ( !iResumeQueued ) 
        {
        DLTRACEOUT(("Operation resuming disabled"));
        return;
        }    
    
    DLTRACE(("Callback"))
    if ( !iCallback->IsActive() ) 
        {        
        iResumeCount++;
        iCallback->CallBack();
        }
    else
        {
        iResumeCount++;
        }
    }
    

// ---------------------------------------------------------------------------
// ResumeOperationAction
// ---------------------------------------------------------------------------
//	        
TInt CCatalogsHttpSessionManager::ResumeOperationAction()
    {
    DLTRACEIN((""));
    
    RCatalogsHttpOperationArray* array = NULL;    
    UpdateRunningOperations();
    
    if ( !iRunningFromGeneral && iGeneralQueue.Count() )    
        {
        array = &iGeneralQueue;
        }
    else if ( iTransactionQueue.Count() && ( iDownloadQueue.Count() == 0 ||
         iTransactionQueue[0]->Config().Priority() >= 
         iDownloadQueue[0]->Config().Priority() ) )
        {
        DLTRACE( ("Resuming transaction") );
        // Resume transaction
        array = &iTransactionQueue;
        }
    else if ( iDownloadQueue.Count() ) 
        {
        DLTRACE( ("Resuming download") );
        // Resume download
        array = &iDownloadQueue;
        }
    

    if ( array ) 
        {            
        iResumeCount--;
        if ( iResumeCount ) 
            {
            DLTRACE(("Call resume callback again"));
            iCallback->CallBack();
            }
        
        MCatalogsHttpOperation* operation = (*array)[0];
        DLTRACE( ( "Resuming: %x, type: %i, id: %i", 
            operation, operation->OperationType(),
            operation->OperationId().Id() ) );
                
        // Start will remove the operation
        return operation->Start( MCatalogsHttpOperation::EAutomaticResume );            
        }
    
    iResumeCount = 0;
    DLTRACE( ( "Nothing to resume" ) );    
    return KErrNotFound; 
    }


// ---------------------------------------------------------------------------
// ConnectionCreatedL
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::ConnectionCreatedL( 
    const TCatalogsConnectionMethod& aMethod )
    {
    DLTRACEIN(("method: %d, %u, %u",
        aMethod.iType, aMethod.iId, aMethod.iApnId ));
    
    StartMonitoringL( EFalse );
    iLatestConnectionMethod = aMethod;
    iConnectionState = ECatalogsConnectionConnected;
    
    // Update connection to queued operations that require it
    SetConnectionToQueue( 
        aMethod,
        iDownloadQueue );

    SetConnectionToQueue( 
        aMethod,
        iTransactionQueue );

    SetConnectionToQueue( 
        aMethod,
        iGeneralQueue );
    
    DLTRACE(("Starting the operation"));
    
    if ( iConnectedOperation ) 
        {        
        MCatalogsHttpOperation* connect = iConnectedOperation;
        iConnectedOperation = NULL;
        User::LeaveIfError( connect->Start() );    
        }
        
    // Try to fill the running queue by resuming queued operations
    iResumeCount = KMaxConcurrentOperations - 1;
    ResumeOperation();
    
    NetworkManagerL().OpenAccessPointL( aMethod );
    }


// ---------------------------------------------------------------------------
// Sets current connection to those operations that have a matching 
// connection method
// ---------------------------------------------------------------------------
//
void CCatalogsHttpSessionManager::SetConnectionToQueue( 
    const TCatalogsConnectionMethod& aMethod,  
    RCatalogsHttpOperationArray& aArray )
    {
    DASSERT( iConnection );
    TInt count = aArray.Count();
    while ( count-- ) 
        {
        if ( aArray[ count ]->Config().ConnectionMethod().Match( aMethod ) ) 
            {            
            aArray[ count ]->SetConnection( *iConnection );
            }
        }
    }


// ---------------------------------------------------------------------------
// ConnectionError
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::ConnectionError( TInt aError )
    {
    DLTRACEIN(("error: %d", aError ));
    iConnectionState = ECatalogsConnectionDisconnected;
    Cancel();
    // notify operations about failure
        
    ReportConnectionError(
        iConnection->ConnectionMethod(),
        iDownloadQueue,
        aError );

    ReportConnectionError(
        iConnection->ConnectionMethod(),
        iTransactionQueue,
        aError );

    ReportConnectionError(
        iConnection->ConnectionMethod(),
        iGeneralQueue,
        aError );

    iConnectedOperation = NULL;
    
    
    // Try to resume remaining operations in case they get lucky
    ReleasePtr( iConnection );
    iResumeCount = 0;
    ResumeOperation();    
    }
    
    

// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::ConnectionStateChangedL( TInt aStage )
    {
    DLTRACEIN(("aStage: %d", aStage ));
    
    if ( aStage == KConnectionUninitialised ) 
        {
        DLTRACE(("Resuming operations"));
        Cancel();
        
        if ( iMonitor ) 
            {
            // Monitor is deleted elsewhere        
            iMonitor->Cancel();
            }
        
        iConnectionState = ECatalogsConnectionDisconnected;        
        NetworkManagerL().NotifyAccessPointReallyClosedL( 
            iLatestConnectionMethod );
        
        // no point resuming more than one op since it needs to create
        // the connection before other operations can be resumed
        iResumeCount = 0;
        ResumeOperation();            
        }
    else if ( aStage == KErrConnectionTerminated ) 
        {
        DLTRACE(("Connection error: %d", aStage ));

        if ( iMonitor ) 
            {
            // Monitor is deleted elsewhere        
            iMonitor->Cancel();
            }
        
        iConnectionState = ECatalogsConnectionDisconnecting;
        NetworkManagerL().NotifyAccessPointReallyClosedL( 
            iLatestConnectionMethod );

        // notify operations about failure
        SetResumeMode( EFalse );
        
        ReportConnectionError(
            iRunningQueue,
            aStage );
        
        ReportConnectionError(
            iDownloadQueue,
            aStage );

        ReportConnectionError(
            iTransactionQueue,
            aStage );
        
        ReportConnectionError(
            iGeneralQueue,
            aStage );

        TCatalogsConnectionMethod method;
        if ( iConnection ) 
            {
            method = iConnection->ConnectionMethod();
            }
        NetworkManagerL().CloseAccessPointL( method );
        
        SetResumeMode( ETrue );
        iConnectedOperation = NULL;
        
        iConnectionState = ECatalogsConnectionDisconnected;       
        Cancel();
        }
    }
    
    
// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//	    
void CCatalogsHttpSessionManager::ConnectionStateError( TInt  aError )
    {
    DLTRACEIN(("aError: %d", aError ));
    (void) aError;
    iConnectionState = ECatalogsConnectionDisconnected;  
    Cancel();
    // Just try to resume an operation, nothing else we can do
    iResumeCount = 0;
    ResumeOperation();    
        
    }


// ---------------------------------------------------------------------------
// ReportConnectionError
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::ReportConnectionError( 
    const TCatalogsConnectionMethod& aMethod,  
    RCatalogsHttpOperationArray& aArray,
    TInt aError )
    {    
    TInt count = aArray.Count();
    while ( count-- ) 
        {
        // iConnectedOperation-pointer is used to ensure that at least it
        // is notified even if the connection method would have changed
        // for some reason
        if ( aArray[ count ]->Config().ConnectionMethod().Match( aMethod ) ||
             aArray[ count ] == iConnectedOperation ) 
            {            
            aArray[ count ]->ReportConnectionError( aError );
            }
        }
    }


// ---------------------------------------------------------------------------
// ReportConnectionError
// ---------------------------------------------------------------------------
//	
void CCatalogsHttpSessionManager::ReportConnectionError(  
    RCatalogsHttpOperationArray& aArray,
    TInt aError )
    {    
    TInt count = aArray.Count();
    while ( count-- ) 
        {
        aArray[ count ]->ReportConnectionError( aError );
        }
    }


// ---------------------------------------------------------------------------
// AddToQueue
// ---------------------------------------------------------------------------
//	
TInt CCatalogsHttpSessionManager::AddToQueue( 
    MCatalogsHttpOperation* aOperation )
    {
    DLTRACEIN((""));
    DLINFO( ( "Operation ID: %i, Type: %i", 
        aOperation->OperationId().Id(), aOperation->OperationType() ) );
        
    // Insert the operation in priority order
    TLinearOrder<MCatalogsHttpOperation> order( 
        &CCatalogsHttpSessionManager::PrioritizeOperations );

    TInt err = ChooseArray( *aOperation ).InsertInOrderAllowRepeats( 
        aOperation, order );
    if ( err != KErrNone ) 
        {
        DLTRACE( ( "Failed with err: %i", err ) );
        return err;
        }
        
    DLINFO( ( "After Add, Queued TRs: %i, DLs: %i" , 
        iTransactionQueue.Count(), iDownloadQueue.Count() ) );
        
    return KCatalogsHttpOperationQueued;        
    }


// ---------------------------------------------------------------------------
// RemoveFromQueue
// ---------------------------------------------------------------------------
//	
TInt CCatalogsHttpSessionManager::RemoveFromQueue( 
    MCatalogsHttpOperation* aOperation )
    {
    DLTRACEIN((""));
    
    if ( aOperation == iConnectedOperation ) 
        {
        iConnectedOperation = NULL;
        }
    
    TInt index = iRunningQueue.Find( aOperation );
    if ( index != KErrNotFound ) 
        {        
        iRunningQueue.Remove( index );
        DLTRACEOUT(("Removed from running"));
        return KErrNone;
        }
        
    RCatalogsHttpOperationArray& array = ChooseArray( *aOperation );
        
    index = array.Find( aOperation );
    if ( index != KErrNotFound ) 
        {
        array.Remove( index );
        DLINFO( ( "After Remove, Queued TRs: %i, DLs: %i" , 
            iTransactionQueue.Count(), iDownloadQueue.Count() ) );

        return KErrNone;
        }
    return KErrNotFound;        
    }
    

// ---------------------------------------------------------------------------
// Compares the priorities of two operations
// ---------------------------------------------------------------------------
//
TInt CCatalogsHttpSessionManager::PrioritizeOperations( 
    const MCatalogsHttpOperation& aFirst, 
    const MCatalogsHttpOperation& aSecond )
    {        
    return aFirst.Config().Priority() - aSecond.Config().Priority();
    }


// ---------------------------------------------------------------------------
// Choose array
// ---------------------------------------------------------------------------
//
RCatalogsHttpOperationArray& CCatalogsHttpSessionManager::ChooseArray(
    const MCatalogsHttpOperation& aOperation )
    {
    DLTRACEIN((""));
    if ( aOperation.Config().Priority() <= ECatalogsPriorityQueued )
        {
        DLTRACEOUT(("Queued general"));
        return iGeneralQueue;
        }
        
    if ( aOperation.OperationType() == ECatalogsHttpTransaction ) 
        {
        DLTRACEOUT(("Queued transaction"));
        return iTransactionQueue;
        }
    
    DLTRACEOUT(("Queued download"));
    return iDownloadQueue;
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CCatalogsHttpSessionManager::UpdateRunningOperations()
    {
    DLTRACEIN((""));
    const TTimeIntervalMicroSeconds32 KDisconnectionDelay( 
        KDisconnectionDelayInMicroSeconds );
    
    iRunningDownloads = 0;
    iRunningTransactions = 0;
    iRunningFromGeneral = 0;
    for ( TInt i = 0; i < iRunningQueue.Count(); ++i )
        {
        if ( iRunningQueue[i]->Config().Priority() <= ECatalogsPriorityQueued )
            {
            iRunningFromGeneral++;
            }
        else if ( iRunningQueue[i]->OperationType() == ECatalogsHttpTransaction ) 
            {
            iRunningTransactions++;
            }
        else if ( iRunningQueue[i]->State() != 
            ECatalogsHttpOpPaused )
            {
            iRunningDownloads++;
            }
        }
        
    iRunningOperations = iRunningTransactions + iRunningDownloads;
    
    // Handle connection timer: either cancel it if we have running operations
    // or start it if it's not running already
    if ( iRunningOperations + iRunningFromGeneral ) 
        {
        Cancel();
        }
    else if ( !IsActive() && iConnection )
        {
        DLTRACE(("Starting timer, time: %d", 
            KDisconnectionDelayInMicroSeconds ));
        iSwitchApTimer = EFalse;
        // Using RTimer instead of RConnectionMonitor because we can't
        // observe the RConnection used by Download manager 
        iConnectionTimer.After( iStatus, KDisconnectionDelay );
        SetActive();
        }
    
    DLTRACEOUT(("Running dls: %d, trs: %d, paused dls: %d, general: %d", 
        iRunningDownloads, 
        iRunningTransactions, 
        iRunningQueue.Count() - iRunningDownloads - iRunningTransactions,
        iRunningFromGeneral ));
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
void CCatalogsHttpSessionManager::ConstructL()
    {
    DLTRACEIN((""));
    iCallback = new (ELeave) CAsyncCallBack( 
        TCallBack( ResumeOperationCallback, this ),
        CActive::EPriorityStandard );   
    User::LeaveIfError( iConnectionTimer.CreateLocal() );
    }    


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpSessionManager::ConnectL( MCatalogsHttpOperation& aOperation )
    {
    DLTRACEIN((""));
    DLMETHOD( aOperation.Config().ConnectionMethod() );    
        
#ifdef CATALOGS_BUILD_CONFIG_DEBUG    
    if ( iConnection ) 
        {
        DLINFO(("Connection refcount: %d", iConnection->RefCount() ));
        DLMETHOD( iConnection->ConnectionMethod() );    
        }
#endif

    // We must not cancel the timer if we are waiting for the old connection
    // to go down because that same timer is used for switching to new
    // AP after a timeout.
    if ( !iSwitchApTimer ) 
        {
        Cancel();
        }
    
    // Don't even try to connect when connection is going up or down
    if ( iConnectionState == ECatalogsConnectionConnecting ||
         iConnectionState == ECatalogsConnectionDisconnecting ) 
        {
        DLTRACEOUT(("(Dis)connecting..., cannot resume now")); 
        return EFalse;
        }
    
    // Check that the connection is still alive
    TBool connected = EFalse;
    if ( iConnection ) 
        {
        connected = iConnection->IsConnectedL();
        }
        
    // If there's a connection and it matches the wanted connection we can just
    // use that
    if ( connected )
        {
        DLTRACE(( "Connected" ));
        const TCatalogsConnectionMethod& newMethod( 
            aOperation.Config().ConnectionMethod() );
        const TCatalogsConnectionMethod& currentMethod( 
            iConnection->ConnectionMethod() );
        
        // We accept an connection if either type and id match
        // or the connected APN matches with the wanted one
        if ( newMethod.Match( currentMethod ) ||
             ( currentMethod.iApnId != 0 && 
                 ( newMethod.iApnId == currentMethod.iApnId  ||
                     ( newMethod.iType == ECatalogsConnectionMethodTypeAccessPoint &&
                         newMethod.iId == currentMethod.iApnId ) ) ) )
            {
            // Update connected operation in case the old one has been released
            iConnectedOperation = &aOperation;
            DLTRACE(("Not connected, setting connection"));
            aOperation.SetConnection( *iConnection );
    
            return ETrue;
            }
        }
    
    
    if ( DisconnectL() )
        {
        // connection was on so we don't want to recreate a new one until
        // the old one has gone down
        return EFalse;
        }
    

    if ( !iConnection ) 
        {
        DLTRACE(("Creating a new connection"));
        DeletePtr( iMonitor );
        // create connection        
        iConnection = CCatalogsConnection::NewL(
            ConnectionCreatorL(),
            aOperation.ConnectionManager(),
            *this );
        
        iConnectedOperation = &aOperation;        
        iConnection->ConnectL( aOperation.Config().ConnectionMethod() );
        iConnectionState = ECatalogsConnectionConnecting;    
        }
        
    return EFalse;
    }


// ---------------------------------------------------------------------------
// 
// ---------------------------------------------------------------------------
//
TBool CCatalogsHttpSessionManager::StartMonitoringL( TBool aStartSwitchTimer )
    {
    DLTRACEIN((""));
    DeletePtr( iMonitor );        
    
    TUint count = 0;
    
    // RConnection may have been closed already if an operation was canceled
    // before the connection was created
    if ( !iConnection->Connection().SubSessionHandle() ) 
        {
        DLTRACEOUT(("Connection has been closed already"));
        return EFalse;
        }
    
    User::LeaveIfError( iConnection->Connection().EnumerateConnections( count ) );
    TConnectionInfoBuf connectionBuf;
    TBool open = CatalogsHttpUtils::IsConnectionMethodOpen( 
        iConnection->Connection(),
        count,
        iConnection->ConnectionMethod(),
        connectionBuf );
    
    // We don't start monitoring unless the connection is up because we
    // wouldn't get any progress notifications if it is already down
    if ( open ) 
        {
        iMonitor = CCatalogsConnectionMonitor::NewL( *this );
        
        DLTRACE(("Start monitoring for connection shutdown"));
        iMonitor->StartL( connectionBuf );

        if ( aStartSwitchTimer ) 
            {
            Cancel();
            iSwitchApTimer = ETrue;
            // Initiate delay after which we switch to new AP even if we the old
            // connection hasn't closed yet
            iConnectionTimer.After( iStatus, KApSwitchDelay );
            SetActive();
            }

        // Prevent operation from starting by returning EFalse
        return ETrue;
        }
    return EFalse;
    }


TBool CCatalogsHttpSessionManager::DisconnectL()
    {
    DLTRACEIN((""));
    // If the connection is not used anymore we can release it so that a new
    // one can be created
    if ( iConnection &&  
         iConnection->RefCount() == 1 ) 
        {
        DLTRACE(("Releasing unused connection"));
        NetworkManagerL().CloseAccessPointL( iConnection->ConnectionMethod() );
        
        // Starts monitoring the connection shutdown if the connection is up        
        if ( StartMonitoringL( ETrue ) ) 
            {
            DLTRACE(("Stopping the connection"));
            iConnectionState = ECatalogsConnectionDisconnecting;
            ReleasePtr( iConnection );
            
            return ETrue;
            }
        
        DLTRACE(("Connection was not open"));
        iConnectionState = ECatalogsConnectionDisconnected;    
        // Connection was not open, we can try to connect straight away    
        ReleasePtr( iConnection );        
        }
    return EFalse;
    }
