videoconnutility/connutility/src/vcxconnutilimpl.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:20:37 +0100
branchRCL_3
changeset 23 13a33d82ad98
parent 0 822a42b6c3f1
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201029 Kit: 201035

/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:    Class to handle connection creation.*
*/




// INCLUDE FILES
#include <e32cmn.h>
#include <bldvariant.hrh>
#include <commdb.h>            // CMDBSession
#include <commsdattypesv1_1.h> // CCDWAPIPBearerRecord
#include <ConnectionUiUtilities.h> 
#include <cmdestination.h>
#include <cdbcols.h>
#include <commdbconnpref.h>
#include <eikbtgrp.h>
#include <WlanCdbCols.h>
#include <connpref.h>
#include <extendedconnpref.h>
#include <mpxlog.h>

#include <ipvideo/vcxconnectionutility.h>
#include <ipvideo/vcxconnutilengineobserver.h>
#include "vcxconnectionutility.hrh"
#include "vcxconnutilimpl.h"
#include "vcxconnutilengine.h"
#include "vcxconnutilpubsub.h"
#include "vcxconnutilwaitsch.h"

// CONSTANTS
_LIT( KConnUtilRoleSema, "__ConnUtillMasterSlaveRoleSwitchSema__ ");
_LIT( KConnUtilConnectionSema, "__ConnUtillConnectionCreateSema__ ");

const TInt KIptvCenRepAPModeAlwaysAsk = 0;
const TInt KConUtilSemaStartupValue = 1;
const TInt KNbrOfNotFoundConnTries = 30;


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::NewL()
// -----------------------------------------------------------------------------
//
CVcxConnUtilImpl* CVcxConnUtilImpl::NewL( CVcxConnectionUtility* aUiInterface )
    {
    CVcxConnUtilImpl* self = CVcxConnUtilImpl::NewLC( aUiInterface );  
    CleanupStack::Pop( self ); // self;
    return self;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::NewLC()
// -----------------------------------------------------------------------------
//
CVcxConnUtilImpl* CVcxConnUtilImpl::NewLC( CVcxConnectionUtility* aUiInterface )
    {
    CVcxConnUtilImpl* self = new (ELeave) CVcxConnUtilImpl( aUiInterface );
    CleanupStack::PushL( self );
    self->ConstructL( );
    return self;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::ConstructL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::ConstructL( )
    {
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::ConstructL() in");
    
    iWaitHandler = CVcxConnUtilWaitSch::NewL();
    
    iObservers.Reset();
    
    TInt err( KErrNone );
    err = iSemaSwitchRole.CreateGlobal( KConnUtilRoleSema, KConUtilSemaStartupValue );
    if( err == KErrAlreadyExists )
        {
        // semafore exists already, open it
        err = iSemaSwitchRole.OpenGlobal( KConnUtilRoleSema );
        }
    User::LeaveIfError( err );
    
    err = iSemaCreateConn.CreateGlobal( KConnUtilConnectionSema, KConUtilSemaStartupValue );
    if( err == KErrAlreadyExists )
        {
        // semafore exists already, open it
        err = iSemaCreateConn.OpenGlobal( KConnUtilConnectionSema );
        }
    User::LeaveIfError( err );
    
    iEngine = CVcxConnUtilEngine::NewL( this );
        
    iPubsub = CVcxConnUtilPubSub::NewL( this );
            
    // check and set pubsub -values 
    TInt activeIap( 0 );
    User::LeaveIfError( iPubsub->GetValue( EVCxPSIapId, activeIap ) );
    if( activeIap != 0 )
        {
        // check if there really is connection, if not reinit values
        if( !iEngine->IsIapConnected( activeIap ) )
            {
            // no connection, data integrity has been corrupted at some point
            activeIap = 0;
            }
        }
    if( activeIap == 0 ) 
        {
        MPX_DEBUG1("CVcxConnUtilImpl::ConstructL() no connection yet, initing values");
        User::LeaveIfError( iPubsub->SetValue( EVCxPSMasterExists, 0 ) );
        
        User::LeaveIfError( iPubsub->SetValue( EVCxPSIapId, 0 ) );
        
        User::LeaveIfError( iPubsub->SetValue( EVCxPSSnapId, 0 ) );
        
        User::LeaveIfError( iPubsub->SetValue( 
                                 EVCxPSConnectionStatus, EVCxNotConnected ) );
        
        User::LeaveIfError( iPubsub->SetValue( EVCxPSNbrConnInstances, 0 ) );        
        }
    
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::ConstructL() out");
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::CVcxNsConnectionUtility()
// -----------------------------------------------------------------------------
//
CVcxConnUtilImpl::CVcxConnUtilImpl( CVcxConnectionUtility* aUiInterface ) :
iUIInterface( aUiInterface )
    {
    }
    
// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::~CVcxConnectionUtility()
// -----------------------------------------------------------------------------
//
CVcxConnUtilImpl::~CVcxConnUtilImpl()
    {   
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::~CVcxConnUtilImpl in");
    
    iObservers.Close();
    
    if( iEngine && iPubsub )
        {
        TRAP_IGNORE( DisconnectL() );
        }
    
    delete iWaitHandler;
    
    iSemaSwitchRole.Close();
    iSemaCreateConn.Close();
     
    delete iPubsub;
    delete iEngine;
    
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::~CVcxConnUtilImpl out");
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::RegisterObserverL
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::RegisterObserverL( MConnUtilEngineObserver* aObserver )
   {
   MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::RegisterObserverL in");
   iObservers.AppendL( aObserver );
   MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::RegisterObserverL out");
   }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::RemoveObserver
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::RemoveObserver( MConnUtilEngineObserver* aObserver )
   {
   MPX_DEBUG1("Cvcxconnutil ## VcxConnUtilImpl::RemoveObserver in");
   TInt i;
   for ( i = 0; i < iObservers.Count(); i++ )
       {
       if ( aObserver == iObservers[i] )
           {
           iObservers.Remove( i );
           iObservers.Compress();
           break;
           }
       } 
   MPX_DEBUG1("Cvcxconnutil ## VcxConnUtilImpl::RemoveObserver out");
   }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::EngineConnectionStatus
// -----------------------------------------------------------------------------
//
TVCxConnectionStatus CVcxConnUtilImpl::EngineConnectionStatus()
    {
    return iEngine->ConnectionStatus();
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::GetIap
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::GetIap( TUint32& aIapId, TBool aSilent )
    {
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::GetIap in ");
    aIapId = 0;
    TInt err( KErrNone );
    // assume always new connection
    iNewConnection = ETrue;
    
    switch ( iEngine->ConnectionStatus() )
        {
        case EVCxNotConnected:            
        case EVCxDisconnecting:                    
            iSemaCreateConn.Wait();
            err = KErrNotFound;
            // KErrNotFound from connection creation indicates that we have 
            // some destination with unusable iap(s) in it for we cannot connect to
            // in that case, destination id to db is setted as "always ask" and connection
            // creation will be tried again for 30 times
            for(TInt i = 0; i < KNbrOfNotFoundConnTries && err == KErrNotFound; ++i)
                {
                err = CreateConnection( aSilent );
                MPX_DEBUG2("vcxconnutil ## CVcxConnUtilImpl::GetIap - CreateConnection returned %d ", err );
                }
            iSemaCreateConn.Signal();    
            break;             
        case EVCxConnecting:
            {
            // this instance is connecting, meaning engine is waiting for
            // networking to create connection, we need to wait for the engine 
            // to finish it's waiting
            TRAP( err, WaitL( EVCxPSConnectionStatus ) );
            if( iEngine->ConnectionStatus() != EVCxConnected )
                {
                // if main active object didn't managed to create connection,
                // return error. This active object does not know the actual
                // error so return KErrGeneral
                err = KErrGeneral;
                }
            else
                {
                err = HandleFinalizeConnection();
                }
            }
            break;
        case EVCxRoamingAccepted: // pass throught
        case EVCxRoamingRequest:  // pass throught
            TRAP( err, WaitL( EVCxPSConnectionStatus ) );
        default:
            iNewConnection = EFalse; 
            break;
        }          
        
    if( err == KErrNone )
        {   
        // get iap id if any. If this is the first connection for this 
        // instance, iap id will be fetched later on
        aIapId = iEngine->IapId();       
        }

    MPX_DEBUG2("vcxconnutil ## CVcxConnUtilImpl::GetIap out error: %d ", err );
    return err;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::WapIdFromIapIdL
// -----------------------------------------------------------------------------
//
TUint32 CVcxConnUtilImpl::WapIdFromIapIdL( TUint32 aIapId )
    {
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::WapIdFromIapIdL() in ");
    MPX_DEBUG2("vcxconnutil ## CVcxConnUtilImpl::WapIdFromIapIdL() IAP id = %d", aIapId);
    CMDBSession* db = CMDBSession::NewL( CMDBSession::LatestVersion() );
    CleanupStack::PushL( db );
    
    // WapIpBearer table contains the mapping between wap and iap id's.
    CCDWAPIPBearerRecord* wapBearerRecord = 
        static_cast<CCDWAPIPBearerRecord*>( CCDRecordBase::RecordFactoryL( KCDTIdWAPIPBearerRecord ) );
        
    CleanupStack::PushL( wapBearerRecord );
    
    wapBearerRecord->iWAPIAP = aIapId;
    
    TBool found = wapBearerRecord->FindL( *db );
    
    if ( !found )
        {
        MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::WapIdFromIapIdL() Record was not found. Leaving with KErrNotFound.");
        User::Leave(KErrNotFound);
        }

    TUint32 wap = static_cast<TUint32>( wapBearerRecord->iWAPAccessPointId );
    
    CleanupStack::PopAndDestroy( wapBearerRecord );
    CleanupStack::PopAndDestroy( db );
    
    MPX_DEBUG2("vcxconnutil ## CVcxConnUtilImpl::WapIdFromIapIdL() Matching WAP id = %d", wap);
    MPX_DEBUG1("vcxconnutil ## CVcxConnUtilImpl::WapIdFromIapIdL() out ");
    return wap;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::PrepareConnSettings()
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::PrepareConnSettings()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::PrepareConnSettings in");  
    TInt err( KErrNone );
    TInt vcDestinationID( 0 );
    
    if ( !iEngine->QueryConn() )
        {
        TRAP( err, vcDestinationID = 
            iEngine->GetCmmDestinationIdL( CMManager::ESnapPurposeUnknown ) );
        MPX_DEBUG2( "vcxconnutil ## CVcxConnUtilImpl::PrepareConnSettings - destination id %d", vcDestinationID);  
        }
   
    TBool detailsGotten( EFalse );
    TRAP( err, detailsGotten = iEngine->PrepareConnectionDetailsL( vcDestinationID ) );
    if( err == KErrNone && !detailsGotten )
        {
        // not able to resolve connection details, proceed with always ask
        iEngine->ResetConnectionInfo();
        }
    
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::PrepareConnSettings out");  
    return err;            
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::CreateConnection()
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::CreateConnection( TBool aSilent )
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL in");     
    
    TInt err( KErrNone );

    TInt connStatusPS( EVCxNotConnected );
    
    err = PrepareConnSettings();
    if( err != KErrNone )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL error getting connsettings out");
        return err;
        }
    
    err = iPubsub->GetValue( EVCxPSConnectionStatus, connStatusPS );
    if( err != KErrNone )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL error getting PS conn status out");
        return err;
        }
    
    // if there is already an active connection created by some other  
    // instance, use that one
    TInt snapId( 0 );
    TBool masterConnect( EFalse );

    if( connStatusPS == EVCxConnected || connStatusPS == EVCxRoamingRequest )
        {        
        err = iPubsub->GetValue( EVCxPSSnapId, snapId );
        if( err != KErrNone )
            {
            MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL error getting PS snap id, out");                   
            return err;
            }
        if( snapId == 0 )
            {
            MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL not usable snap is in PS");
            // current snap is undefined, set snap id KErrNotFound for 
            // DoCreateConnection to use active iap instead of snap id
            snapId = KErrNotFound;
            }
        }
    else
        {
        masterConnect = ETrue;
        snapId = iEngine->DestinationId();
        }

    err = DoCreateConnection( aSilent, snapId, masterConnect );
    if( err == KErrNotFound && masterConnect )
        {
        // KErrNotFound from connection creation indicates that we have 
        // some destination with unusable iap(s) in it. Enable connection query.
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL - set connection query");
        iEngine->SetQueryConn( ETrue );
        }
    if( err == KErrNone )
        {
        err = HandleFinalizeConnection();
        }
    
    MPX_DEBUG2( "vcxconnutil ## CVcxConnUtilImpl::CreateConnectionL out (%d)", err);
    return err;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::DoCreateConnection()
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::DoCreateConnection(  TBool /*aSilent*/, TInt32 aSnapId, TBool aMasterConnect )
    {
    MPX_DEBUG2( "vcxconnutil ## CVcxConnUtilImpl::DoCreateConnectionL in (snapId=%d)", aSnapId);
    
    TInt err( KErrNone );
    
    if ( aSnapId == KIptvCenRepAPModeAlwaysAsk )
        {  
        //In always ask mode we show always ask dialog
        TCommDbConnPref connPref;
        connPref.SetDialogPreference(  ECommDbDialogPrefPrompt  );
        err = iEngine->StartToConnect( connPref );
        MPX_DEBUG2( "CVcxConnUtilImpl::DoCreateConnectionL connection start always ask err %d", err);
        }
    else if ( aSnapId > KIptvCenRepAPModeAlwaysAsk )
        {
        TConnSnapPref prefs;
        prefs.SetSnap( aSnapId );
        err = iEngine->StartToConnect( prefs, aMasterConnect );
        MPX_DEBUG2( "CVcxConnUtilImpl::DoCreateConnectionL connection start err %d", err);
        }
    else
        {
        TInt iapPS( 0 );
        err = iPubsub->GetValue( EVCxPSIapId, iapPS );
        if( err == KErrNone )
            {
            if( iapPS )
                {
                TCommDbConnPref connPref;
                connPref.SetIapId( iapPS );
                connPref.SetDialogPreference( ECommDbDialogPrefDoNotPrompt );
                err = iEngine->StartToConnect( connPref, EFalse );
                MPX_DEBUG2( "CVcxConnUtilImpl::DoCreateConnectionL connection via iap start err %d", err);
                }
            else
                {
                err = KErrNotFound;
                }            
            }  
        }
    if( err == KErrNone && aMasterConnect )
        {
        err = iEngine->CreateMobility();
        }
    MPX_DEBUG2( "vcxconnutil ## CVcxConnUtilImpl::DoCreateConnectionL out (%d)", err);
    return err;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::HandleFinalizeConnection()
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::HandleFinalizeConnection()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleFinalizeConnection in");
    if( !iNewConnection )
        {
        // if connection is not new for this instance
        // no finalizing required
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleFinalizeConnection no new connection, out");
        return KErrNone;
        }
    if( iEngine->ConnectionStatus() != EVCxConnected  )
        {
        // connection not ok, no point to proceed, return error
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleFinalizeConnection not connected, out");
        return KErrGeneral;
        }

    iNewConnection = EFalse;
    TRAPD( err, iEngine->FillActiveConnectionDetailsL() );
               
    if( err == KErrNone )
        {                
        TRAP( err, iPubsub->IncCounterPubSubL( EVCxPSNbrConnInstances ) );              
        if( err == KErrNone )
            {                                    
            // master role check and switch if necessary
            if( !iMaster )
                {
                err = CheckAndChangeSlaveToMaster();                        
                }
            }
            if( err == KErrNone && iMaster )
                {
                TRAP( err, SaveConnectionToPubSubL() );
                }
            if( err == KErrNone )
                {    
                // start getting events from pubsub
                iPubsub->StartSubscibers();
                }
         }            
     if( err != KErrNone )
         {
         // some operation above failed, internal error
         // try to disconnect and return error       
         TRAPD( errDisc, DisconnectL() );
         // return latest error if disconnecting fails
         if( errDisc != KErrNone )
             {
             err = errDisc;
             }
         }
     MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleFinalizeConnection out");
     return err;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::DisconnectConnection()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::DisconnectL()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::Disconnect in");
    TVCxConnectionStatus connStatus = iEngine->ConnectionStatus();
    
    // if this is a master, and it is roaming we need to wait 
    // for roaming to finish
    if( iMaster )
        {
        // because EVCxRoamingAccepted is never saved as
        // connection status to PS,
        // it is safe to wait PS value key change here
        if( connStatus == EVCxRoamingRequest   ||
            connStatus == EVCxRoamingAccepted   )
            {
            MPX_DEBUG1( "CVcxConnUtilImpl::Disconnect master waiting roaming to finish");                        
            WaitL( EVCxPSConnectionStatus );
            MPX_DEBUG1( "CVcxConnUtilImpl::Disconnect master waiting finished");
            connStatus = iEngine->ConnectionStatus();
            }
        }
    else
        {
        // if slave is about to disconnect
        // and it's internal state is roaming
        // we know that it has been responded to 
        // roaming request. Decrease value here
        if( connStatus == EVCxRoamingRequest   ||
            connStatus == EVCxRoamingAccepted  ||
            connStatus == EVCxPendingClientRequest )
            {
            if( connStatus == EVCxRoamingAccepted )
                {
                MPX_DEBUG1( "CVcxConnUtilImpl::Disconnect slave removes it's acceptance");
                iPubsub->DecCounterPubSubL( EVCxPSNbRoamAccepted );
                }
            MPX_DEBUG1( "CVcxConnUtilImpl::Disconnect slave removes it's response");
            iPubsub->DecCounterPubSubL( EVCxPSNbrRoamResp );     
            }
        }
    
    // disconnecting, no longer interested in pubsub events
    iPubsub->CancelValueSubscribers();
    
    if( connStatus == EVCxNotConnected )
        {
        return;
        }
    
    TInt nbrOfConnUsers( 0 );
    User::LeaveIfError( iPubsub->GetValue( EVCxPSNbrConnInstances, nbrOfConnUsers ) );
                    
    if(  nbrOfConnUsers > 0 )
        {
        iPubsub->DecCounterPubSubL( EVCxPSNbrConnInstances );
        }  
    iPubsub->GetValue( EVCxPSNbrConnInstances, nbrOfConnUsers );
    // we're leaving and there are no other instances using connection
    if( nbrOfConnUsers == 0 )
        {
        // no more connection users, reset PS -values   
        User::LeaveIfError( iPubsub->SetValue( EVCxPSIapId,  0 ) );
          
        User::LeaveIfError( iPubsub->SetValue( EVCxPSSnapId, 0 ) );
            
        User::LeaveIfError( iPubsub->SetValue( 
                            EVCxPSConnectionStatus, EVCxNotConnected ) );
            
        User::LeaveIfError( iPubsub->SetValue( 
                            EVCxPSRoamingRequestStatus, EVCxRoamingNotInit ) );
        }
    iEngine->Disconnect();
       
    // cannot be master anymore if not using connection
    if( iMaster )
        {
        iMaster = EFalse;
        User::LeaveIfError( iPubsub->SetValue( EVCxPSMasterExists,  0 ) );  
        }
        
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::Disconnect out");
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::SaveConnectionToPubSubL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::SaveConnectionToPubSubL()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::SaveConnectionToPubSubL in");
    // publish connection properties
    // IAP id:
    User::LeaveIfError( iPubsub->SetValue( EVCxPSIapId, iEngine->IapId() ) );
    
    // Snap ID:
    User::LeaveIfError( iPubsub->SetValue( EVCxPSSnapId, iEngine->DestinationId() ) );
    
    // connection State
    User::LeaveIfError( iPubsub->SetValue( 
                              EVCxPSConnectionStatus, iEngine->ConnectionStatus() ) );   
    
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::SaveConnectionToPubSubL out");
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::RequestIsRoamingAllowedL()
// -----------------------------------------------------------------------------
//
TBool CVcxConnUtilImpl::RequestIsRoamingAllowedL()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::RequestIsRoamingAllowedL in");
    TBool okToRoam( ETrue );
    
    // if this instance is master, need to wait for slaves to request their status before proceeding
    if( iMaster )
        {        
        // init pubsubs for roaming request status checking
        User::LeaveIfError( iPubsub->SetValue( EVCxPSNbrRoamResp,    0 ) );
        User::LeaveIfError( iPubsub->SetValue( EVCxPSNbRoamAccepted, 0 ) );
        
        // query own state
        okToRoam = DoRequestClientRoamingL();
        // client might have disconnected
        if( iEngine->ConnectionStatus() == EVCxNotConnected )
            {
            MPX_DEBUG1( "CVcxConnUtilImpl::RequestIsRoamingAllowedL master disconnected, out");
            return EFalse;            
            }
             
        User::LeaveIfError( iPubsub->SetValue( EVCxPSConnectionStatus, EVCxRoamingRequest ) );
        TInt nbrOfConnUsers(0);
        User::LeaveIfError( iPubsub->GetValue( EVCxPSNbrConnInstances, nbrOfConnUsers ) );
        if( okToRoam && nbrOfConnUsers > 1 )
            {
            
            // if we have slaves also using connection, 
            // we need to wait for them to request roaming statuses
            // from their clients also. In this case, the status is being 
            // checked via pubsub EVCxPSRoamingRequestStatus to which master
            // updates after enought resposes are received
            MPX_DEBUG1( "CVcxConnUtilImpl::RequestIsRoamingAllowedL master waiting");
            TRAPD( err, WaitL( EVCxPSRoamingRequestStatus ) );
            if( err != KErrNone )
                {
                MPX_DEBUG2( "CVcxConnUtilImpl::RequestIsRoamingAllowedL master wait leaved %d", err );
                }
            MPX_DEBUG1( "CVcxConnUtilImpl::RequestIsRoamingAllowedL master released");
    
            TInt roamingStatus( EVCxRoamingNotInit );
            User::LeaveIfError( iPubsub->GetValue( EVCxPSRoamingRequestStatus, roamingStatus ) );
            if( EVCxRoamingAllowed != roamingStatus )
                {
                okToRoam = EFalse;            
                }
            }
        }    
    // reset roaming PS value
    User::LeaveIfError( iPubsub->SetValue( EVCxPSRoamingRequestStatus, EVCxRoamingNotInit ) );
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::RequestIsRoamingAllowedL out");
    return okToRoam;
    }
 
// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::DoRequestClientRoamingL()
// -----------------------------------------------------------------------------
//
TBool CVcxConnUtilImpl::DoRequestClientRoamingL()
    {   
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::DoRequestClientRoamingL in" );
    
    TBool okToRoam( ETrue );
    TInt err( KErrNone );

    TVCxConnectionStatus oldState = iEngine->ConnectionStatus();
    iEngine->SetConnectionStatus( EVCxPendingClientRequest );
    for ( TInt i = 0; i < iObservers.Count() && okToRoam; i++ )
        {
        TRAP( err, okToRoam = iObservers[i]->RequestIsRoamingAllowedL() );
        if( err != KErrNone || !okToRoam )
            {
            okToRoam = EFalse;
            break;
            }
        }
    if( iEngine->ConnectionStatus() != EVCxNotConnected )
        {
        // operation path back to connection utility, reset state, in case 
        // client has not diconnected during callback        
        iEngine->SetConnectionStatus( oldState );
        
        if( okToRoam && err == KErrNone )
            {
            // roaming ok for this instance, increase nbr of accepted   
            iPubsub->IncCounterPubSubL( EVCxPSNbRoamAccepted );
            MPX_DEBUG1( "CVcxConnUtilImpl::DoRequestClientRoamingL accepted increased" );
            }
        }
    
    // increase nbr of responses
    iPubsub->IncCounterPubSubL( EVCxPSNbrRoamResp );
        
    MPX_DEBUG2( "CVcxConnUtilImpl::DoRequestClientRoamingL allowed %d out", okToRoam );
    
    return okToRoam;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::IapChanged()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::IapChangedL()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::IapChanged in");
    if( !iMaster )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::IapChanged not master out");
        return;
        }
    TInt err( KErrNone );
    
    // current information
    TUint32 oldIap = iEngine->IapId();
    TVCxConnectionStatus engineStatus = iEngine->ConnectionStatus();
    
    // resolve connection information from the network middleware
    iEngine->ResetConnectionInfo();
    if( engineStatus != EVCxNotConnected && 
        engineStatus != EVCxError )
        {
        iEngine->FillActiveConnectionDetailsL();                 
        }        
    
    // if there is no active connection or gotten iap id is invalid
    TUint32 iapID = iEngine->IapId();
    if( iapID == 0 || !( iEngine->IsIapConnected( iapID ) ) ) 
        {        
        if( engineStatus != EVCxError )
            {            
            engineStatus = EVCxNotConnected;
            }
        // signal status before disconnect -call to make sure 
        // slaves react in case master's disconnecting fails
        MPX_DEBUG2( "IapChanged not connected notify %d ", engineStatus );
        iPubsub->SetValue( EVCxPSConnectionStatus, engineStatus );
           
        MPX_DEBUG1( "CVcxConnUtilImpl::IapChanged no active iap, diconnect");
        TRAP( err, DisconnectL() );
        if( err != KErrNone )
            {
            MPX_DEBUG2( "CVcxConnUtilImpl::IapChanged no active iap, diconnect leaves %d", err );
            }
        }
    else
        {
        MPX_DEBUG1( "CVcxConnUtilImpl::IapChanged iap connected");
        engineStatus = EVCxConnected;
        iEngine->SetConnectionStatus( EVCxConnected );
        // refresh PS connection state
        SaveConnectionToPubSubL();
        }
                    
    // notify clients about iap change
    if( engineStatus != EVCxError && ( oldIap != iapID || !iapID ) )
        {
        MPX_DEBUG1( "CVcxConnUtilImpl::IapChanged notify observers");  
        NotifyObserversIAPChanged();        
        }
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::IapChanged out");               
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::WaitL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::WaitL( TUint32 aWaitId )
    {
    iWaitHandler->WaitL( aWaitId );
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::EndWait()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::EndWait( TUint32 aWaitId )
    {
    iWaitHandler->EndWait( aWaitId );
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::IsMaster()
// -----------------------------------------------------------------------------
//
TBool CVcxConnUtilImpl::IsMaster()
    {
    return iMaster;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::ValueChangedL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::ValueChangedL( const TUint32& aKey, const TInt&  aValue )
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::ValueChangedL (TInt) in");   
    EndWait( aKey );
    // pubsub key value changed, check and update 
    // functionality as required.
    switch( aKey )
        {
        case EVCxPSMasterExists:
            {
            HandleMasterChangeL();           
            break;
            }              
        case EVCxPSConnectionStatus: 
            {   
            HandleSlaveConnectionStatusL( aValue );
            break;
            }
        case EVCxPSNbrRoamResp:
            {
            HandleRoamingReponsesL( aValue );
            }
            break;
        case EVCxPSRoamingRequestStatus:
            // NOP
            break;
        default:
            User::Leave( KErrNotFound );
            break;      
        } 
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::ValueChangedL (TInt) out");  
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::HandleMasterChangeL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::HandleMasterChangeL()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleMasterChangeL in"); 
    // if this instance is not connected, don't bother
    // to change. Not connected instance cannot be master
    if( iMaster )
        {
        MPX_DEBUG1( "CVcxConnUtilImpl::HandleMasterChangeL master, out");
        return;
        }
    
    if( iEngine->ConnectionStatus() != EVCxConnected )
        {
        MPX_DEBUG1( "CVcxConnUtilImpl::HandleMasterChangeL not connected, out");
        return;
        }
    
    User::LeaveIfError( CheckAndChangeSlaveToMaster() );

    if( iMaster )
        {                    
        // at this point we need to reinit ex-slave's connection to use 
        // destination (snap) instead of IAP for the ex-slave to be able 
        // to get mobility events. 
        // if original connection was via IAP due "always ask", do nothing
        if( iEngine->DestinationId() )
            {
            // this instance has become master, so it needs to init the mobility object
            if( iEngine->CreateMobility() != KErrNone )
                {
                MPX_DEBUG1( "CVcxConnUtilImpl::HandleMasterChangeL master reinit connection not ok");
                DisconnectL();  
                }
            else
                {
                MPX_DEBUG1( "CVcxConnUtilImpl::HandleMasterChangeL master reinit connection ok");
                iEngine->FillActiveConnectionDetailsL();
                SaveConnectionToPubSubL();
                }
            }  
        }
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleMasterChangeL out"); 
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::CheckAndChangeSlaveToMaster()
// -----------------------------------------------------------------------------
//
TInt CVcxConnUtilImpl::CheckAndChangeSlaveToMaster()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CheckAndChangeSlaveToMaster in"); 
    TInt err( KErrNone );  
    TInt status( 0 );
    
    ///////
    // entering critical section
    iSemaSwitchRole.Wait();                
    err = iPubsub->GetValue( EVCxPSMasterExists, status );
    // if master already exists, do nothing
    if( !status && err == KErrNone)
        {
        MPX_DEBUG1( "CVcxConnUtilImpl::CheckAndChangeSlaveToMaster changing master");
        iMaster = ETrue;
        err = iPubsub->SetValue( EVCxPSMasterExists, iMaster );
        }
    iSemaSwitchRole.Signal();
    // Leaving critical section
    ///////
    
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CheckAndChangeSlaveToMaster out"); 
    return err;
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::HandleSlaveConnectionStatusL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::HandleSlaveConnectionStatusL( const TInt& aNewStatus )
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleSlaveConnectionStatus in"); 
    // if master, do nothing
    if( iMaster )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleSlaveConnectionStatus master out"); 
        return;
        }
    // resolve given connection, this structure is needed  
    // to prevent errors in type check
    TVCxConnectionStatus gottenStatus( EVCxNotConnected );
    switch( aNewStatus )
        {
        case EVCxNotConnected: 
            gottenStatus = EVCxNotConnected; 
            break;
        case EVCxConnecting:
            gottenStatus = EVCxConnecting;
            break;
        case EVCxConnected:
            gottenStatus = EVCxConnected;
            break;
        case EVCxDisconnecting:
            gottenStatus = EVCxDisconnecting;
            break;
        case EVCxRoamingRequest:
            gottenStatus = EVCxRoamingRequest;
            break;
        case EVCxRoamingAccepted:
            gottenStatus = EVCxRoamingAccepted;
            break;  
        case EVCxError:
            gottenStatus = EVCxError;
            break;        
        default:
            User::Leave( KErrGeneral );
            break;
        }
    TVCxConnectionStatus currentStatus = iEngine->ConnectionStatus();  
    
    MPX_DEBUG2( "HandleSlaveConnectionStatus gotten %d", gottenStatus );
    MPX_DEBUG2( "HandleSlaveConnectionStatus current %d", currentStatus );
    
    if( gottenStatus == EVCxRoamingRequest && currentStatus == EVCxConnected )
        {
        // if master is requesting roaming, query all external clients  
        // whether we can roam or not  
        MPX_DEBUG1( "CVcxConnUtilImpl::HandleSlaveConnectionStatus slave check Roaming");
        if ( DoRequestClientRoamingL() )
            {
            gottenStatus = EVCxRoamingAccepted;
            }
        // client might have disconnected during roaming. In that case do not change status
        if( iEngine->ConnectionStatus() != EVCxNotConnected )
            {
            // set connection status explicitly to tell slave we're roaming
            iEngine->SetConnectionStatus( gottenStatus );
            }
        }
    
    else if( ( currentStatus == EVCxRoamingRequest  ||
               currentStatus == EVCxRoamingAccepted || 
               currentStatus == EVCxConnected ) &&
               gottenStatus == EVCxConnected )
        {
        // if current status was roaming or
        // master has notified new connection and state
        // has changed to connected, meaning
        // master has succesfully reinitialized preferred connection
        // slave needs to try to reinit connection via new iap if 
        // new iap differs from current
        
        TInt valueFromPS( 0 );
        User::LeaveIfError( iPubsub->GetValue( EVCxPSIapId, valueFromPS ) );
        MPX_DEBUG2( "HandleSlaveConnectionStatus slave iap %d", iEngine->IapId() );
        MPX_DEBUG2( "HandleSlaveConnectionStatus slave PS iap %d", valueFromPS );
        if( valueFromPS != iEngine->IapId() )
            {
            User::LeaveIfError( iPubsub->GetValue( EVCxPSSnapId, valueFromPS ) );
            iEngine->Disconnect();
            if( DoCreateConnection( ETrue, valueFromPS, EFalse ) == KErrNone )
                { 
                // refresh connection details
                iEngine->FillActiveConnectionDetailsL();                  
                MPX_DEBUG1( "CVcxConnUtilImpl::HandleSlaveConnectionStatus slave restarted ok" );                
                }
            else
                {
                MPX_DEBUG1( "CVcxConnUtilImpl::HandleSlaveConnectionStatus slave restarting not ok" );
                DisconnectL();
                gottenStatus = EVCxNotConnected;
                }            
            NotifyObserversIAPChanged();
            }
        iEngine->SetConnectionStatus( gottenStatus );        
        }
    else 
        {
        if( gottenStatus == EVCxNotConnected || 
            gottenStatus == EVCxError )
            {
            // master has notified disconnecting or error for some reason
            DisconnectL();  
            // notify normal disconnect to observers
            if( gottenStatus == EVCxNotConnected )
                {
                NotifyObserversIAPChanged();
                }
            }
        }
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleSlaveConnectionStatus out");
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::ValueChangedL()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::HandleRoamingReponsesL( const TInt& aNbrOfResps )
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleRoamingReponsesL in"); 
    if( !iMaster )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleRoamingReponsesL slave out");
        return;
        }
    if( iEngine->ConnectionStatus() !=  EVCxRoamingRequest )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleRoamingReponsesL not roaming");
        return;
        }
    // check if all have been responded and if all agreed on roaming
    TInt nbrOfConUsers( 0 );
    TInt nbrOfAgeed( 0 );
    TInt nbrofResponses( aNbrOfResps );
   
    User::LeaveIfError( iPubsub->GetValue( EVCxPSNbrConnInstances, nbrOfConUsers )  );
    User::LeaveIfError( iPubsub->GetValue( EVCxPSNbRoamAccepted, nbrOfAgeed )  );
    
    if( nbrOfConUsers == nbrofResponses )
        {
        if( nbrofResponses == nbrOfAgeed )
            {
            // every instance has responded and all agrees roaming, 
            // change state for master's main thread to proceed
            MPX_DEBUG1( "CVcxConnUtilImpl::HandleRoamingReponsesL EVCxRoamingAllowed");
            
            User::LeaveIfError( iPubsub->SetValue( 
                              EVCxPSRoamingRequestStatus, EVCxRoamingAllowed ) );
            }
        else
            {
            MPX_DEBUG1( "CVcxConnUtilImpl::HandleRoamingReponsesL EVCxRoamingNotInit");
            User::LeaveIfError( iPubsub->SetValue( 
                                  EVCxPSRoamingRequestStatus, EVCxRoamingNotInit ) );
            }
        }  
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::HandleRoamingReponsesL out"); 
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::NotifyObserversIAPChanged()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::NotifyObserversIAPChanged()
    {
    for ( TInt i = 0; i < iObservers.Count(); i++ )
        {
        MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::NotifyObserversIAPChanged notifying observer"); 
        TRAPD( err, iObservers[i]->IapChangedL() );
        if( err != KErrNone )
            {
            MPX_DEBUG2( "vcxconnutil ## NotifyObserversIAPChanged::IapChanged observer leaved %d", err);               
            }        
        } 
    }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::DisplayWaitNote()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::DisplayWaitNote(const TDesC& aConnectionName)
     {
     MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::DisplayWaitNote in"); 
     if( iUIInterface )
         {
         iUIInterface->DisplayWaitNote( aConnectionName );
         }
     MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::DisplayWaitNote out");
     }

// -----------------------------------------------------------------------------
// CVcxConnUtilImpl::CloseWaitNote()
// -----------------------------------------------------------------------------
//
void CVcxConnUtilImpl::CloseWaitNote()
    {
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CloseWaitNote in");
    if( iUIInterface )
         {
         iUIInterface->CloseWaitNote();
         }
    MPX_DEBUG1( "vcxconnutil ## CVcxConnUtilImpl::CloseWaitNote out");
    }

//  End of File